[retro-gtk/wip/aplazas/0.13: 2/8] Port CairoDisplay to C



commit 3609ff8f2113cf71462df17c9d4546a2a8418a2e
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Wed Sep 6 14:21:14 2017 +0200

    Port CairoDisplay to C

 retro-gtk/Makefile.am               |    3 +-
 retro-gtk/retro-cairo-display-bak.c |  590 +++++++++++++++++++++++++++++++++++
 retro-gtk/retro-cairo-display.c     |  403 ++++++++++++++++++++++++
 retro-gtk/retro-cairo-display.h     |   37 +++
 retro-gtk/retro-core-view.h         |    1 +
 retro-gtk/video/cairo-display.vala  |  143 ---------
 6 files changed, 1033 insertions(+), 144 deletions(-)
---
diff --git a/retro-gtk/Makefile.am b/retro-gtk/Makefile.am
index 6230ed5..6d4b6b0 100644
--- a/retro-gtk/Makefile.am
+++ b/retro-gtk/Makefile.am
@@ -43,6 +43,7 @@ libretro_gtk_la_SOURCES = \
        core-error.vala \
        memory.vala \
        retro.vala \
+       retro-cairo-display.c \
        retro-core-descriptor.vala \
        retro-core-descriptor-error.vala \
        retro-core-view.c \
@@ -61,7 +62,6 @@ libretro_gtk_la_SOURCES = \
        retro-environment.c \
        libretro-environment.h \
        \
-       video/cairo-display.vala \
        video/retro-video-converter.c \
        $(NULL)
 
@@ -107,6 +107,7 @@ retro_gtkincludedir = $(includedir)/retro-gtk-0.12
 retro_gtkinclude_HEADERS = \
        retro-analog-id.h \
        retro-analog-index.h \
+       retro-cairo-display.h \
        retro-core-view.h \
        retro-device-type.h \
        retro-gtk.h \
diff --git a/retro-gtk/retro-cairo-display-bak.c b/retro-gtk/retro-cairo-display-bak.c
new file mode 100644
index 0000000..b4c23c0
--- /dev/null
+++ b/retro-gtk/retro-cairo-display-bak.c
@@ -0,0 +1,590 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#include "retro-cairo-display.h"
+
+/*
+ * Because gdk-pixbuf saves dpi as integer we have to multiply it by big enough
+ * number to represent aspect ratio precisely.
+ */
+#define RETRO_CAIRO_DISPLAY_Y_DPI (1000000.0f)
+
+struct _RetroCairoDisplay
+{
+  GtkDrawingArea parent_instance;
+  RetroCore *core;
+  GdkPixbuf *pixbuf;
+  RetroVideoFilter filter;
+  gfloat aspect_ratio;
+  gboolean show_surface;
+  gulong on_video_output_id;
+};
+
+G_DEFINE_TYPE (RetroCairoDisplay, retro_cairo_display, GTK_TYPE_DRAWING_AREA)
+
+enum {
+  PROP_PIXBUF,
+  N_PROPS,
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+retro_cairo_display_finalize (GObject *object)
+{
+  RetroCairoDisplay *self = (RetroCairoDisplay *)object;
+
+  if (self->core != NULL)
+    g_object_unref (self->core);
+  if (self->pixbuf != NULL)
+    g_object_unref (self->pixbuf);
+
+  G_OBJECT_CLASS (retro_cairo_display_parent_class)->finalize (object);
+}
+
+static void
+retro_cairo_display_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  RetroCairoDisplay *self = RETRO_CAIRO_DISPLAY (object);
+
+  switch (prop_id) {
+  case PROP_PIXBUF:
+    g_value_set_object (value, retro_cairo_display_get_pixbuf (self));
+
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+
+    break;
+  }
+}
+
+static void
+retro_cairo_display_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  RetroCairoDisplay *self = RETRO_CAIRO_DISPLAY (object);
+
+  switch (prop_id) {
+  case PROP_PIXBUF:
+    retro_cairo_display_set_pixbuf (self, g_value_get_object (value));
+
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+
+    break;
+  }
+}
+
+static void
+retro_cairo_display_class_init (RetroCairoDisplayClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  ((GtkWidgetClass *) klass)->draw = (gboolean (*) (GtkWidget *, cairo_t *)) retro_cairo_display_real_draw;
+
+  object_class->finalize = retro_cairo_display_finalize;
+  object_class->get_property = retro_cairo_display_get_property;
+  object_class->set_property = retro_cairo_display_set_property;
+
+  properties[PROP_PIXBUF] =
+    g_param_spec_object ("pixbuf",
+                         "pixbuf",
+                         "pixbuf",
+                         gdk_pixbuf_get_type (),
+                         G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | 
G_PARAM_STATIC_BLURB);
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PIXBUF, properties[PROP_PIXBUF]);
+}
+
+static void
+retro_cairo_display_init (RetroCairoDisplay *self)
+{
+  self->filter = RETRO_VIDEO_FILTER_SMOOTH;
+  self->show_surface = TRUE;
+  g_signal_connect_object (G_OBJECT (self),
+                           "notify::sensitive",
+                           (GCallback) _gtk_widget_queue_draw_g_object_notify,
+                           GTK_WIDGET (self),
+                           0);
+  g_signal_connect_object (G_OBJECT (self),
+                           "notify::pixbuf",
+                           (GCallback) _gtk_widget_queue_draw_g_object_notify,
+                           GTK_WIDGET (self),
+                           0);
+}
+
+GdkPixbuf *
+retro_cairo_display_get_pixbuf (RetroCairoDisplay *self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return self->pixbuf;
+}
+
+void
+retro_cairo_display_set_pixbuf (RetroCairoDisplay *self,
+                                GdkPixbuf         *pixbuf)
+{
+  g_return_if_fail (self != NULL);
+
+  if (self->pixbuf == pixbuf)
+    return;
+
+  g_clear_object (&self->pixbuf);
+
+  if (pixbuf != NULL)
+    self->pixbuf = g_object_ref (pixbuf);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PIXBUF]);
+}
+
+void
+retro_cairo_display_set_core (RetroCairoDisplay *self,
+                              RetroCore *core)
+{
+  RetroCore* _tmp0_;
+  RetroCore* _tmp3_;
+  RetroCore* _tmp4_;
+  RetroCore* _tmp5_;
+  g_return_if_fail (self != NULL);
+  _tmp0_ = self->core;
+  if (_tmp0_ != NULL) {
+    RetroCore* _tmp1_;
+    gulong _tmp2_;
+    _tmp1_ = self->core;
+    _tmp2_ = self->on_video_output_id;
+    g_signal_handler_disconnect ((GObject*) _tmp1_, _tmp2_);
+  }
+  _tmp3_ = core;
+  _tmp4_ = _g_object_ref0 (_tmp3_);
+  _g_object_unref0 (self->core);
+  self->core = _tmp4_;
+  _tmp5_ = self->core;
+  if (_tmp5_ != NULL) {
+    RetroCore* _tmp6_;
+    gulong _tmp7_;
+    _tmp6_ = core;
+    _tmp7_ = g_signal_connect_object (_tmp6_, "video-output", (GCallback) 
_retro_cairo_display_on_video_output_retro_core_video_output, self, 0);
+    self->on_video_output_id = _tmp7_;
+  }
+}
+
+void
+retro_cairo_display_set_filter (RetroCairoDisplay *self,
+                                RetroVideoFilter filter)
+{
+  RetroVideoFilter _tmp0_;
+  g_return_if_fail (self != NULL);
+  _tmp0_ = filter;
+  self->filter = _tmp0_;
+  gtk_widget_queue_draw ((GtkWidget*) self);
+}
+
+gboolean
+retro_cairo_display_get_coordinates_on_display (RetroCairoDisplay *self,
+                                                gdouble            widget_x,
+                                                gdouble            widget_y,
+                                                gdouble           *display_x,
+                                                gdouble           *display_y)
+{
+  gdouble _vala_display_x = 0.0;
+  gdouble _vala_display_y = 0.0;
+  gboolean result = FALSE;
+  gdouble w = 0.0;
+  gdouble h = 0.0;
+  gdouble x = 0.0;
+  gdouble y = 0.0;
+  gdouble _tmp0_ = 0.0;
+  gdouble _tmp1_ = 0.0;
+  gdouble _tmp2_ = 0.0;
+  gdouble _tmp3_ = 0.0;
+  gdouble _tmp4_;
+  gdouble _tmp5_;
+  gdouble _tmp6_;
+  gdouble _tmp7_;
+  gdouble _tmp8_;
+  gdouble _tmp9_;
+  gdouble _tmp10_;
+  gdouble _tmp11_;
+  gboolean _tmp12_ = FALSE;
+  gdouble _tmp13_;
+  gdouble _tmp14_;
+  g_return_val_if_fail (self != NULL, FALSE);
+  retro_cairo_display_get_video_box (self, &_tmp0_, &_tmp1_, &_tmp2_, &_tmp3_);
+  w = _tmp0_;
+  h = _tmp1_;
+  x = _tmp2_;
+  y = _tmp3_;
+  _tmp4_ = widget_x;
+  _tmp5_ = x;
+  _tmp6_ = w;
+  _tmp7_ = w;
+  _vala_display_x = (((_tmp4_ - _tmp5_) * 2.0) - _tmp6_) / _tmp7_;
+  _tmp8_ = widget_y;
+  _tmp9_ = y;
+  _tmp10_ = h;
+  _tmp11_ = h;
+  _vala_display_y = (((_tmp8_ - _tmp9_) * 2.0) - _tmp10_) / _tmp11_;
+  _tmp13_ = _vala_display_x;
+  _tmp14_ = _tmp13_;
+  if (((-1.0) <= _tmp14_) && (_tmp14_ <= 1.0)) {
+    gdouble _tmp15_;
+    gdouble _tmp16_;
+    _tmp15_ = _vala_display_y;
+    _tmp16_ = _tmp15_;
+    _tmp12_ = ((-1.0) <= _tmp16_) && (_tmp16_ <= 1.0);
+  } else {
+    _tmp12_ = FALSE;
+  }
+  result = _tmp12_;
+  if (display_x) {
+    *display_x = _vala_display_x;
+  }
+  if (display_y) {
+    *display_y = _vala_display_y;
+  }
+  return result;
+}
+
+void
+retro_cairo_display_show_video (RetroCairoDisplay *self)
+{
+  g_return_if_fail (self != NULL);
+  self->show_surface = TRUE;
+  gtk_widget_queue_draw ((GtkWidget*) self);
+}
+
+void
+retro_cairo_display_hide_video (RetroCairoDisplay *self)
+{
+  g_return_if_fail (self != NULL);
+  self->show_surface = FALSE;
+  gtk_widget_queue_draw ((GtkWidget*) self);
+}
+
+RetroCairoDisplay *
+retro_cairo_display_new (void)
+{
+  return g_object_new (RETRO_TYPE_CAIRO_DISPLAY, NULL);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <retro-video-filter.h>
+#include <float.h>
+#include <math.h>
+#include <cairo.h>
+#include <retro-pixel-format.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gdk/gdk.h>
+
+static void _retro_cairo_display_on_video_output_retro_core_video_output (RetroCore* _sender, guint8* data, 
int data_length1, guint width, guint height, gsize pitch, RetroPixelFormat pixel_format, gfloat aspect_ratio, 
gpointer self) {
+  retro_cairo_display_on_video_output ((RetroCairoDisplay*) self, data, data_length1, width, height, pitch, 
pixel_format, aspect_ratio);
+}
+
+static void retro_cairo_display_on_video_output (RetroCairoDisplay* self, guint8* data, int data_length1, 
guint width, guint height, gsize pitch, RetroPixelFormat pixel_format, gfloat aspect_ratio) {
+  gfloat _tmp0_;
+  guint8* _tmp1_;
+  gint _tmp1__length1;
+  guint _tmp2_;
+  guint _tmp3_;
+  gsize _tmp4_;
+  RetroPixelFormat _tmp5_;
+  GdkPixbuf* _tmp6_;
+  GdkPixbuf* _tmp7_;
+  gfloat x_dpi = 0.0F;
+  gfloat _tmp8_;
+  gchar* x_dpi_string = NULL;
+  gchar* _tmp9_;
+  gchar* y_dpi_string = NULL;
+  gchar* _tmp10_;
+  GdkPixbuf* _tmp11_;
+  GdkPixbuf* _tmp12_;
+  g_return_if_fail (self != NULL);
+  _tmp0_ = aspect_ratio;
+  self->aspect_ratio = _tmp0_;
+  _tmp1_ = data;
+  _tmp1__length1 = data_length1;
+  _tmp2_ = width;
+  _tmp3_ = height;
+  _tmp4_ = pitch;
+  _tmp5_ = pixel_format;
+  _tmp6_ = gdk_pixbuf_new_from_video (_tmp1_, _tmp2_, _tmp3_, _tmp4_, _tmp5_);
+  _tmp7_ = _tmp6_;
+  retro_cairo_display_set_pixbuf (self, _tmp7_);
+  _g_object_unref0 (_tmp7_);
+  _tmp8_ = aspect_ratio;
+  x_dpi = _tmp8_ * RETRO_CAIRO_DISPLAY_Y_DPI;
+  _tmp9_ = g_strdup_printf ("%g", x_dpi);
+  x_dpi_string = _tmp9_;
+  _tmp10_ = g_strdup_printf ("%g", RETRO_CAIRO_DISPLAY_Y_DPI);
+  y_dpi_string = _tmp10_;
+  _tmp11_ = self->pixbuf;
+  gdk_pixbuf_set_option (_tmp11_, "x-dpi", x_dpi_string);
+  _tmp12_ = self->pixbuf;
+  gdk_pixbuf_set_option (_tmp12_, "y-dpi", y_dpi_string);
+  _g_free0 (y_dpi_string);
+  _g_free0 (x_dpi_string);
+}
+
+static gpointer _cairo_pattern_reference0 (gpointer self) {
+  return self ? cairo_pattern_reference (self) : NULL;
+}
+
+static gboolean retro_cairo_display_real_draw (GtkWidget* base, cairo_t* cr) {
+  RetroCairoDisplay * self;
+  gboolean result = FALSE;
+  cairo_t* _tmp0_;
+  gboolean _tmp1_;
+  GdkPixbuf* to_draw = NULL;
+  GdkPixbuf* _tmp2_;
+  GdkPixbuf* _tmp3_;
+  GdkPixbuf* _tmp4_;
+  gboolean _tmp5_;
+  gboolean _tmp6_;
+  cairo_surface_t* surface = NULL;
+  GdkPixbuf* _tmp21_;
+  cairo_surface_t* _tmp22_;
+  gdouble w = 0.0;
+  gdouble h = 0.0;
+  gdouble x = 0.0;
+  gdouble y = 0.0;
+  gdouble _tmp23_ = 0.0;
+  gdouble _tmp24_ = 0.0;
+  gdouble _tmp25_ = 0.0;
+  gdouble _tmp26_ = 0.0;
+  gdouble xs = 0.0;
+  gdouble _tmp27_;
+  GdkPixbuf* _tmp28_;
+  gint _tmp29_;
+  gint _tmp30_;
+  gdouble ys = 0.0;
+  gdouble _tmp31_;
+  GdkPixbuf* _tmp32_;
+  gint _tmp33_;
+  gint _tmp34_;
+  cairo_t* _tmp35_;
+  gdouble _tmp36_;
+  gdouble _tmp37_;
+  cairo_t* _tmp38_;
+  cairo_surface_t* _tmp39_;
+  gdouble _tmp40_;
+  gdouble _tmp41_;
+  gdouble _tmp42_;
+  gdouble _tmp43_;
+  cairo_pattern_t* source = NULL;
+  cairo_t* _tmp44_;
+  cairo_pattern_t* _tmp45_;
+  cairo_pattern_t* _tmp46_;
+  RetroVideoFilter _tmp47_;
+  cairo_t* _tmp50_;
+  self = (RetroCairoDisplay*) base;
+  g_return_val_if_fail (cr != NULL, FALSE);
+  _tmp0_ = cr;
+  retro_cairo_display_draw_background (self, _tmp0_);
+  _tmp1_ = self->show_surface;
+  if (!_tmp1_) {
+    result = FALSE;
+    return result;
+  }
+  _tmp2_ = self->pixbuf;
+  _tmp3_ = _g_object_ref0 (_tmp2_);
+  to_draw = _tmp3_;
+  _tmp4_ = to_draw;
+  if (_tmp4_ == NULL) {
+    result = FALSE;
+    _g_object_unref0 (to_draw);
+    return result;
+  }
+  _tmp5_ = gtk_widget_get_sensitive ((GtkWidget*) self);
+  _tmp6_ = _tmp5_;
+  if (!_tmp6_) {
+    GdkPixbuf* desaturated = NULL;
+    GdkPixbuf* _tmp7_;
+    gboolean _tmp8_;
+    gboolean _tmp9_;
+    GdkPixbuf* _tmp10_;
+    gint _tmp11_;
+    gint _tmp12_;
+    GdkPixbuf* _tmp13_;
+    gint _tmp14_;
+    gint _tmp15_;
+    GdkPixbuf* _tmp16_;
+    GdkPixbuf* _tmp17_;
+    GdkPixbuf* _tmp18_;
+    GdkPixbuf* _tmp19_;
+    GdkPixbuf* _tmp20_;
+    _tmp7_ = to_draw;
+    _tmp8_ = gdk_pixbuf_get_has_alpha (_tmp7_);
+    _tmp9_ = _tmp8_;
+    _tmp10_ = to_draw;
+    _tmp11_ = gdk_pixbuf_get_width (_tmp10_);
+    _tmp12_ = _tmp11_;
+    _tmp13_ = to_draw;
+    _tmp14_ = gdk_pixbuf_get_height (_tmp13_);
+    _tmp15_ = _tmp14_;
+    _tmp16_ = gdk_pixbuf_new (GDK_COLORSPACE_RGB, _tmp9_, 8, _tmp12_, _tmp15_);
+    desaturated = _tmp16_;
+    _tmp17_ = to_draw;
+    _tmp18_ = desaturated;
+    gdk_pixbuf_saturate_and_pixelate (_tmp17_, _tmp18_, 0.0f, FALSE);
+    _tmp19_ = desaturated;
+    _tmp20_ = _g_object_ref0 (_tmp19_);
+    _g_object_unref0 (to_draw);
+    to_draw = _tmp20_;
+    _g_object_unref0 (desaturated);
+  }
+  _tmp21_ = to_draw;
+  _tmp22_ = gdk_cairo_surface_create_from_pixbuf (_tmp21_, 1, NULL);
+  surface = _tmp22_;
+  retro_cairo_display_get_video_box (self, &_tmp23_, &_tmp24_, &_tmp25_, &_tmp26_);
+  w = _tmp23_;
+  h = _tmp24_;
+  x = _tmp25_;
+  y = _tmp26_;
+  _tmp27_ = w;
+  _tmp28_ = to_draw;
+  _tmp29_ = gdk_pixbuf_get_width (_tmp28_);
+  _tmp30_ = _tmp29_;
+  xs = _tmp27_ / _tmp30_;
+  _tmp31_ = h;
+  _tmp32_ = to_draw;
+  _tmp33_ = gdk_pixbuf_get_height (_tmp32_);
+  _tmp34_ = _tmp33_;
+  ys = _tmp31_ / _tmp34_;
+  _tmp35_ = cr;
+  _tmp36_ = xs;
+  _tmp37_ = ys;
+  cairo_scale (_tmp35_, _tmp36_, _tmp37_);
+  _tmp38_ = cr;
+  _tmp39_ = surface;
+  _tmp40_ = x;
+  _tmp41_ = xs;
+  _tmp42_ = y;
+  _tmp43_ = ys;
+  cairo_set_source_surface (_tmp38_, _tmp39_, _tmp40_ / _tmp41_, _tmp42_ / _tmp43_);
+  _tmp44_ = cr;
+  _tmp45_ = cairo_get_source (_tmp44_);
+  _tmp46_ = _cairo_pattern_reference0 (_tmp45_);
+  source = _tmp46_;
+  _tmp47_ = self->filter;
+  switch (_tmp47_) {
+    case RETRO_VIDEO_FILTER_SHARP:
+    {
+      cairo_pattern_t* _tmp48_;
+      _tmp48_ = source;
+      cairo_pattern_set_filter (_tmp48_, CAIRO_FILTER_NEAREST);
+      break;
+    }
+    default:
+    case RETRO_VIDEO_FILTER_SMOOTH:
+    {
+      cairo_pattern_t* _tmp49_;
+      _tmp49_ = source;
+      cairo_pattern_set_filter (_tmp49_, CAIRO_FILTER_BILINEAR);
+      break;
+    }
+  }
+  _tmp50_ = cr;
+  cairo_paint (_tmp50_);
+  result = TRUE;
+  _cairo_pattern_destroy0 (source);
+  _cairo_surface_destroy0 (surface);
+  _g_object_unref0 (to_draw);
+  return result;
+}
+
+static void retro_cairo_display_draw_background (RetroCairoDisplay* self, cairo_t* cr) {
+  cairo_t* _tmp0_;
+  cairo_t* _tmp1_;
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (cr != NULL);
+  _tmp0_ = cr;
+  cairo_set_source_rgb (_tmp0_, (gdouble) 0, (gdouble) 0, (gdouble) 0);
+  _tmp1_ = cr;
+  cairo_paint (_tmp1_);
+}
+
+static void retro_cairo_display_get_video_box (RetroCairoDisplay *self,
+                                               gdouble           *width,
+                                               gdouble           *height,
+                                               gdouble           *x,
+                                               gdouble           *y)
+{
+  gdouble _vala_width = 0.0;
+  gdouble _vala_height = 0.0;
+  gdouble _vala_x = 0.0;
+  gdouble _vala_y = 0.0;
+  gdouble w = 0.0;
+  gdouble h = 0.0;
+  gdouble display_ratio = 0.0;
+  gdouble allocated_ratio = 0.0;
+  gdouble _tmp13_;
+  gdouble _tmp14_;
+  gdouble _tmp15_;
+  gdouble _tmp16_;
+
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (width != NULL);
+  g_return_if_fail (height != NULL);
+  g_return_if_fail (x != NULL);
+  g_return_if_fail (y != NULL);
+
+  w = (gdouble) gtk_widget_get_allocated_width (GTK_WIDGET (self));
+  h = (gdouble) gtk_widget_get_allocated_height (GTK_WIDGET (self));
+
+  // Set the size of the display.
+  display_ratio = (gdouble) self->aspect_ratio;
+  allocated_ratio = w / h;
+
+  // If the screen is wider than the video…
+  if (allocated_ratio > display_ratio) {
+    *height = h;
+    *width = (gdouble) (h * display_ratio);
+  }
+  else {
+    *width = w;
+    *height = (gdouble) (w / display_ratio);
+  }
+
+  // Set the position of the display.
+  *x = (w - *width) / 2;
+  *y = (h - *height) / 2;
+}
+
+static void _gtk_widget_queue_draw_g_object_notify (GObject* _sender, GParamSpec* pspec, gpointer self) {
+  gtk_widget_queue_draw ((GtkWidget*) self);
+}
+
diff --git a/retro-gtk/retro-cairo-display.c b/retro-gtk/retro-cairo-display.c
new file mode 100644
index 0000000..fe14269
--- /dev/null
+++ b/retro-gtk/retro-cairo-display.c
@@ -0,0 +1,403 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#include "retro-cairo-display.h"
+
+#include "retro-pixel-format.h"
+
+// FIXME Remove as soon as possible.
+GdkPixbuf *
+gdk_pixbuf_new_from_video (gconstpointer src,
+                           guint         width,
+                           guint         height,
+                           gsize         pitch,
+                           gint          pixel_format);
+
+/*
+ * Because gdk-pixbuf saves dpi as integer we have to multiply it by big enough
+ * number to represent aspect ratio precisely.
+ */
+#define RETRO_CAIRO_DISPLAY_Y_DPI (1000000.0f)
+
+struct _RetroCairoDisplay
+{
+  GtkDrawingArea parent_instance;
+  RetroCore *core;
+  GdkPixbuf *pixbuf;
+  RetroVideoFilter filter;
+  gfloat aspect_ratio;
+  gboolean show_surface;
+  gulong on_video_output_id;
+};
+
+G_DEFINE_TYPE (RetroCairoDisplay, retro_cairo_display, GTK_TYPE_DRAWING_AREA)
+
+enum {
+  PROP_PIXBUF = 1,
+  N_PROPS,
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/* Private */
+
+static void
+retro_cairo_display_get_video_box (RetroCairoDisplay *self,
+                                   gdouble           *width,
+                                   gdouble           *height,
+                                   gdouble           *x,
+                                   gdouble           *y)
+{
+  gdouble w;
+  gdouble h;
+  gdouble display_ratio;
+  gdouble allocated_ratio;
+
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (width != NULL);
+  g_return_if_fail (height != NULL);
+  g_return_if_fail (x != NULL);
+  g_return_if_fail (y != NULL);
+
+  w = (gdouble) gtk_widget_get_allocated_width (GTK_WIDGET (self));
+  h = (gdouble) gtk_widget_get_allocated_height (GTK_WIDGET (self));
+
+  // Set the size of the display.
+  display_ratio = (gdouble) self->aspect_ratio;
+  allocated_ratio = w / h;
+
+  // If the screen is wider than the video…
+  if (allocated_ratio > display_ratio) {
+    *height = h;
+    *width = (gdouble) (h * display_ratio);
+  }
+  else {
+    *width = w;
+    *height = (gdouble) (w / display_ratio);
+  }
+
+  // Set the position of the display.
+  *x = (w - *width) / 2;
+  *y = (h - *height) / 2;
+}
+
+static void
+retro_cairo_display_draw_background (RetroCairoDisplay *self,
+                                     cairo_t           *cr)
+{
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (cr != NULL);
+
+  cairo_set_source_rgb (cr, (gdouble) 0, (gdouble) 0, (gdouble) 0);
+  cairo_paint (cr);
+}
+
+static gboolean
+retro_cairo_display_real_draw (GtkWidget *base,
+                               cairo_t   *cr)
+{
+  RetroCairoDisplay *self = RETRO_CAIRO_DISPLAY (base);
+  GdkPixbuf *to_draw;
+  gboolean has_alpha;
+  gint width;
+  gint height;
+
+  cairo_surface_t *surface;
+  gdouble w = 0.0;
+  gdouble h = 0.0;
+  gdouble x = 0.0;
+  gdouble y = 0.0;
+  gdouble xs;
+  gdouble ys;
+  cairo_pattern_t *source;
+
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (cr != NULL, FALSE);
+
+  retro_cairo_display_draw_background (self, cr);
+
+  if (!self->show_surface)
+    return FALSE;
+
+  if (self->pixbuf == NULL)
+    return FALSE;
+
+  if (gtk_widget_get_sensitive (GTK_WIDGET (self)))
+    to_draw = g_object_ref (self->pixbuf);
+  else {
+    has_alpha = gdk_pixbuf_get_has_alpha (self->pixbuf);
+    width = gdk_pixbuf_get_width (self->pixbuf);
+    height = gdk_pixbuf_get_height (self->pixbuf);
+    to_draw = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, width, height);
+    gdk_pixbuf_saturate_and_pixelate (self->pixbuf, to_draw, 0.0f, FALSE);
+  }
+
+  surface = gdk_cairo_surface_create_from_pixbuf (to_draw, 1, NULL);
+  retro_cairo_display_get_video_box (self, &w, &h, &x, &y);
+  xs = w / gdk_pixbuf_get_width (to_draw);
+  ys = h / gdk_pixbuf_get_height (to_draw);
+
+  cairo_scale (cr, xs, ys);
+  cairo_set_source_surface (cr, surface, x / xs, y / ys);
+  source = cairo_get_source (cr);
+  switch (self->filter) {
+  case RETRO_VIDEO_FILTER_SHARP:
+    cairo_pattern_set_filter (source, CAIRO_FILTER_NEAREST);
+
+    break;
+  default:
+  case RETRO_VIDEO_FILTER_SMOOTH:
+    cairo_pattern_set_filter (source, CAIRO_FILTER_BILINEAR);
+
+    break;
+  }
+  cairo_paint (cr);
+
+  cairo_surface_destroy (surface);
+  g_object_unref (to_draw);
+
+  return TRUE;
+}
+static void
+retro_cairo_display_finalize (GObject *object)
+{
+  RetroCairoDisplay *self = (RetroCairoDisplay *) object;
+
+  if (self->core != NULL)
+    g_object_unref (self->core);
+  if (self->pixbuf != NULL)
+    g_object_unref (self->pixbuf);
+
+  G_OBJECT_CLASS (retro_cairo_display_parent_class)->finalize (object);
+}
+
+static void
+retro_cairo_display_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  RetroCairoDisplay *self = RETRO_CAIRO_DISPLAY (object);
+
+  switch (prop_id) {
+  case PROP_PIXBUF:
+    g_value_set_object (value, retro_cairo_display_get_pixbuf (self));
+
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+
+    break;
+  }
+}
+
+static void
+retro_cairo_display_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  RetroCairoDisplay *self = RETRO_CAIRO_DISPLAY (object);
+
+  switch (prop_id) {
+  case PROP_PIXBUF:
+    retro_cairo_display_set_pixbuf (self, g_value_get_object (value));
+
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+
+    break;
+  }
+}
+
+static void
+retro_cairo_display_class_init (RetroCairoDisplayClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  ((GtkWidgetClass *) klass)->draw = (gboolean (*) (GtkWidget *, cairo_t *)) retro_cairo_display_real_draw;
+
+  object_class->finalize = retro_cairo_display_finalize;
+  object_class->get_property = retro_cairo_display_get_property;
+  object_class->set_property = retro_cairo_display_set_property;
+
+  properties[PROP_PIXBUF] =
+    g_param_spec_object ("pixbuf",
+                         "Pixbuf",
+                         "The displayed pixbuf",
+                         gdk_pixbuf_get_type (),
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_NAME |
+                         G_PARAM_STATIC_NICK |
+                         G_PARAM_STATIC_BLURB);
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PIXBUF, properties[PROP_PIXBUF]);
+}
+
+static void
+queue_draw (GObject    *sender,
+            GParamSpec *pspec,
+            gpointer    self)
+{
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+retro_cairo_display_init (RetroCairoDisplay *self)
+{
+  self->filter = RETRO_VIDEO_FILTER_SMOOTH;
+  self->show_surface = TRUE;
+  g_signal_connect_object (G_OBJECT (self),
+                           "notify::sensitive",
+                           (GCallback) queue_draw,
+                           GTK_WIDGET (self),
+                           0);
+  g_signal_connect_object (G_OBJECT (self),
+                           "notify::pixbuf",
+                           (GCallback) queue_draw,
+                           GTK_WIDGET (self),
+                           0);
+}
+
+GdkPixbuf *
+retro_cairo_display_get_pixbuf (RetroCairoDisplay *self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return self->pixbuf;
+}
+
+void
+retro_cairo_display_set_pixbuf (RetroCairoDisplay *self,
+                                GdkPixbuf         *pixbuf)
+{
+  g_return_if_fail (self != NULL);
+
+  if (self->pixbuf == pixbuf)
+    return;
+
+  g_clear_object (&self->pixbuf);
+
+  if (pixbuf != NULL)
+    self->pixbuf = g_object_ref (pixbuf);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PIXBUF]);
+}
+
+static void
+retro_cairo_display_on_video_output (RetroCore        *sender,
+                                     guint8           *data,
+                                     int               data_length1,
+                                     guint             width,
+                                     guint             height,
+                                     gsize             pitch,
+                                     RetroPixelFormat  pixel_format,
+                                     gfloat            aspect_ratio,
+                                     gpointer          user_data)
+{
+  RetroCairoDisplay *self = RETRO_CAIRO_DISPLAY (user_data);
+
+  GdkPixbuf *pixbuf;
+  gfloat x_dpi;
+  gchar *x_dpi_string;
+  gchar *y_dpi_string;
+
+  g_return_if_fail (self != NULL);
+
+  self->aspect_ratio = aspect_ratio;
+  pixbuf = gdk_pixbuf_new_from_video (data, width, height, pitch, pixel_format);
+  retro_cairo_display_set_pixbuf (self, pixbuf);
+
+  if (pixbuf != NULL)
+    g_object_unref (pixbuf);
+
+  if (self->pixbuf == NULL)
+    return;
+
+  x_dpi = aspect_ratio * RETRO_CAIRO_DISPLAY_Y_DPI;
+  x_dpi_string = g_strdup_printf ("%g", x_dpi);
+  y_dpi_string = g_strdup_printf ("%g", RETRO_CAIRO_DISPLAY_Y_DPI);
+  gdk_pixbuf_set_option (self->pixbuf, "x-dpi", x_dpi_string);
+  gdk_pixbuf_set_option (self->pixbuf, "y-dpi", y_dpi_string);
+  g_free (y_dpi_string);
+  g_free (x_dpi_string);
+}
+
+void
+retro_cairo_display_set_core (RetroCairoDisplay *self,
+                              RetroCore         *core)
+{
+  g_return_if_fail (self != NULL);
+
+  if (self->core == core)
+    return;
+
+  if (self->core != NULL) {
+    g_signal_handler_disconnect (G_OBJECT (self->core), self->on_video_output_id);
+    g_clear_object (&self->core);
+  }
+
+  if (core != NULL) {
+    self->core = g_object_ref (core);
+    self->on_video_output_id = g_signal_connect_object (core, "video-output", (GCallback) 
retro_cairo_display_on_video_output, self, 0);
+  }
+}
+
+void
+retro_cairo_display_set_filter (RetroCairoDisplay *self,
+                                RetroVideoFilter   filter)
+{
+  g_return_if_fail (self != NULL);
+
+  self->filter = filter;
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+gboolean
+retro_cairo_display_get_coordinates_on_display (RetroCairoDisplay *self,
+                                                gdouble            widget_x,
+                                                gdouble            widget_y,
+                                                gdouble           *display_x,
+                                                gdouble           *display_y)
+{
+  gdouble w = 0.0;
+  gdouble h = 0.0;
+  gdouble x = 0.0;
+  gdouble y = 0.0;
+
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (display_x != NULL, FALSE);
+  g_return_val_if_fail (display_y != NULL, FALSE);
+
+  retro_cairo_display_get_video_box (self, &w, &h, &x, &y);
+
+  // Return coordinates as a [-1.0, 1.0] scale, (0.0, 0.0) is the center.
+  *display_x = ((widget_x - x) * 2.0 - w) / w;
+  *display_y = ((widget_y - y) * 2.0 - h) / h;
+
+  return (-1.0 <= *display_x) && (*display_x <= 1.0) &&
+         (-1.0 <= *display_y) && (*display_y <= 1.0);
+}
+
+void
+retro_cairo_display_show_video (RetroCairoDisplay *self)
+{
+  g_return_if_fail (self != NULL);
+
+  self->show_surface = TRUE;
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+void
+retro_cairo_display_hide_video (RetroCairoDisplay *self)
+{
+  g_return_if_fail (self != NULL);
+
+  self->show_surface = FALSE;
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+RetroCairoDisplay *
+retro_cairo_display_new (void)
+{
+  return g_object_new (RETRO_TYPE_CAIRO_DISPLAY, NULL);
+}
diff --git a/retro-gtk/retro-cairo-display.h b/retro-gtk/retro-cairo-display.h
new file mode 100644
index 0000000..b39394c
--- /dev/null
+++ b/retro-gtk/retro-cairo-display.h
@@ -0,0 +1,37 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#ifndef RETRO_CAIRO_DISPLAY_H
+#define RETRO_CAIRO_DISPLAY_H
+
+#include <gtk/gtk.h>
+#include "retro-video-filter.h"
+
+G_BEGIN_DECLS
+
+// FIXME Remove as soon as possible.
+typedef struct _RetroCore RetroCore;
+
+#define RETRO_TYPE_CAIRO_DISPLAY (retro_cairo_display_get_type())
+
+G_DECLARE_FINAL_TYPE (RetroCairoDisplay, retro_cairo_display, RETRO, CAIRO_DISPLAY, GtkDrawingArea)
+
+RetroCairoDisplay *retro_cairo_display_new (void);
+GdkPixbuf *retro_cairo_display_get_pixbuf (RetroCairoDisplay *self);
+void retro_cairo_display_set_pixbuf (RetroCairoDisplay *self,
+                                     GdkPixbuf         *pixbuf);
+void retro_cairo_display_set_core (RetroCairoDisplay *self,
+                                   RetroCore         *core);
+void retro_cairo_display_set_filter (RetroCairoDisplay *self,
+                                     RetroVideoFilter   filter);
+gboolean retro_cairo_display_get_coordinates_on_display (RetroCairoDisplay *self,
+                                                         gdouble            widget_x,
+                                                         gdouble            widget_y,
+                                                         gdouble           *display_x,
+                                                         gdouble           *display_y);
+void retro_cairo_display_show_video (RetroCairoDisplay *self);
+void retro_cairo_display_hide_video (RetroCairoDisplay *self);
+
+G_END_DECLS
+
+#endif /* RETRO_CAIRO_DISPLAY_H */
+
diff --git a/retro-gtk/retro-core-view.h b/retro-gtk/retro-core-view.h
index 70a1589..1bc3665 100644
--- a/retro-gtk/retro-core-view.h
+++ b/retro-gtk/retro-core-view.h
@@ -2,6 +2,7 @@
 #define RETRO_CORE_VIEW_H
 
 #include <gtk/gtk.h>
+#include "retro-cairo-display.h"
 #include "retro-device-type.h"
 #include "retro-input-device.h"
 #include "retro-video-filter.h"


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]