[retro-gtk/wip/aplazas/c-port: 6/25] Port CairoDisplay to C
- From: Adrien Plazas <aplazas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [retro-gtk/wip/aplazas/c-port: 6/25] Port CairoDisplay to C
- Date: Tue, 10 Oct 2017 16:39:20 +0000 (UTC)
commit d6568dcb29dba824220037ab65794f0f65f8449d
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.c | 466 ++++++++++++++++++++++++++++++++++++
retro-gtk/retro-cairo-display.h | 41 ++++
retro-gtk/retro-core-view.c | 1 +
retro-gtk/video/cairo-display.vala | 143 -----------
5 files changed, 510 insertions(+), 144 deletions(-)
---
diff --git a/retro-gtk/Makefile.am b/retro-gtk/Makefile.am
index 9f3b46c..271ca3c 100644
--- a/retro-gtk/Makefile.am
+++ b/retro-gtk/Makefile.am
@@ -48,6 +48,7 @@ retro_gtk_public_h_sources = \
retro_gtk_private_h_sources = \
input/retro-keyboard-key.h \
libretro-environment.h \
+ retro-cairo-display.h \
retro-core-private.h \
retro-core-view-input-device.h \
retro-disk-control-callback.h \
@@ -78,6 +79,7 @@ libretro_gtk_la_SOURCES = \
retro.vala \
retro-analog-id.c \
retro-analog-index.c \
+ retro-cairo-display.c \
retro-core-descriptor.vala \
retro-core-descriptor-error.vala \
retro-core-view.c \
@@ -102,7 +104,6 @@ libretro_gtk_la_SOURCES = \
retro-core.c \
retro-environment.c \
\
- video/cairo-display.vala \
video/retro-video-converter.c \
$(NULL)
diff --git a/retro-gtk/retro-cairo-display.c b/retro-gtk/retro-cairo-display.c
new file mode 100644
index 0000000..3a14662
--- /dev/null
+++ b/retro-gtk/retro-cairo-display.c
@@ -0,0 +1,466 @@
+// 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);
+}
+
+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);
+}
+
+/* Public */
+
+/**
+ * retro_cairo_display_set_core:
+ * @self: a #RetroCairoDisplay
+ * @core: (nullable): a #RetroCore, or %NULL
+ *
+ * Sets @core as the #RetroCore displayed by @self.
+ */
+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);
+ }
+}
+
+/**
+ * retro_cairo_display_get_pixbuf:
+ * @self: a #RetroCairoDisplay
+ *
+ * Gets the currently displayed video frame.
+ *
+ * Returns: (transfer none): a #GdkPixbuf
+ */
+GdkPixbuf *
+retro_cairo_display_get_pixbuf (RetroCairoDisplay *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ return self->pixbuf;
+}
+
+/**
+ * retro_cairo_display_set_pixbuf:
+ * @self: a #RetroCairoDisplay
+ * @pixbuf: a #GdkPixbuf
+ *
+ * Sets @pixbuf as the currently displayed video frame.
+ */
+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]);
+}
+
+/**
+ * retro_cairo_display_set_filter:
+ * @self: a #RetroCairoDisplay
+ * @filter: a #RetroVideoFilter
+ *
+ * Sets the video filter to use to render the core's video on @self.
+ */
+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));
+}
+
+/**
+ * retro_cairo_display_get_coordinates_on_display:
+ * @self: a #RetroCairoDisplay
+ * @widget_x: the abscissa on @self
+ * @widget_y: the ordinate on @self
+ * @display_x: return location for a the abscissa on the core's video display
+ * @display_y: return location for a the ordinate on the core's video display
+ *
+ * Gets coordinates on the core's video output from coordinates on @self, and
+ * whether the point is inside the core's video display.
+ *
+ * Returns: whether the coordinates are on the core's video display
+ */
+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);
+}
+
+/**
+ * retro_cairo_display_show_video:
+ * @self: a #RetroCairoDisplay
+ *
+ * Shows the video display of the core.
+ */
+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));
+}
+
+/**
+ * retro_cairo_display_hide_video:
+ * @self: a #RetroCairoDisplay
+ *
+ * Hides the video display of the core.
+ */
+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));
+}
+
+/**
+ * retro_cairo_display_new:
+ *
+ * Creates a new #RetroCairoDisplay.
+ *
+ * Returns: (transfer full): a new #RetroCairoDisplay
+ */
+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..1a54c28
--- /dev/null
+++ b/retro-gtk/retro-cairo-display.h
@@ -0,0 +1,41 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#ifndef RETRO_CAIRO_DISPLAY_H
+#define RETRO_CAIRO_DISPLAY_H
+
+#if !defined(__RETRO_GTK_INSIDE__) && !defined(RETRO_GTK_COMPILATION)
+# error "Only <retro-gtk.h> can be included directly."
+#endif
+
+#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.c b/retro-gtk/retro-core-view.c
index 6bd8ec6..89ad450 100644
--- a/retro-gtk/retro-core-view.c
+++ b/retro-gtk/retro-core-view.c
@@ -2,6 +2,7 @@
#include <linux/input-event-codes.h>
#include "retro-gtk-internal.h"
+#include "retro-cairo-display.h"
#include "retro-core-view.h"
#include "retro-core-view-input-device.h"
#include "retro-joypad-id.h"
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]