[gtk/subsurface-test: 68/68] Steal enough Weston test code to show a yuv surface




commit 10d075ee47e951b8a0d747f4fd3ea04e9f9911ab
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Jun 8 15:36:11 2022 -0400

    Steal enough Weston test code to show a yuv surface
    
    This isn't mean to be mergable. It is just a quick hack
    to get a yuv surface onscreen. It only runs under weston
    atm, since mutter does not support yuv yet.

 tests/chocolate-cake.png | Bin 0 -> 110022 bytes
 tests/meson.build        |   4 +-
 tests/subsurface.c       | 255 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 258 insertions(+), 1 deletion(-)
---
diff --git a/tests/chocolate-cake.png b/tests/chocolate-cake.png
new file mode 100644
index 0000000000..5059fea150
Binary files /dev/null and b/tests/chocolate-cake.png differ
diff --git a/tests/meson.build b/tests/meson.build
index 2466fa9656..c89872baf0 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -128,6 +128,8 @@ if x11_enabled
   gtk_tests += [['testerrors']]
 endif
 
+libpixman = cc.find_library('pixman-1', required: true)
+
 # Pass the source dir here so programs can change into the source directory
 # and find .ui files and .png files and such that they load at runtime
 test_args = ['-DGTK_SRCDIR="@0@"'.format(meson.current_source_dir())]
@@ -139,7 +141,7 @@ foreach t: gtk_tests
     sources: test_srcs,
     include_directories: [confinc, gdkinc],
     c_args: test_args + common_cflags,
-    dependencies: [libgtk_dep, libm],
+    dependencies: [libgtk_dep, libm, libpixman],
   )
 endforeach
 
diff --git a/tests/subsurface.c b/tests/subsurface.c
index ef12812fbf..50fc05e6e6 100644
--- a/tests/subsurface.c
+++ b/tests/subsurface.c
@@ -21,11 +21,260 @@
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <unistd.h>
+#include <pixman.h>
+#include <drm/drm_fourcc.h>
 
 static struct wl_compositor *wl_compositor;
 static struct wl_subcompositor *wl_subcompositor;
 static struct wl_shm *wl_shm;
 
+struct yuv_buffer {
+        void *data;
+        size_t bytes;
+        struct wl_buffer *proxy;
+        int width;
+        int height;
+};
+
+static struct yuv_buffer *
+yuv_buffer_create(size_t bytes,
+                  int width,
+                  int height,
+                  int stride_bytes,
+                  uint32_t drm_format)
+{
+        struct wl_shm_pool *pool;
+        struct yuv_buffer *buf;
+        int fd;
+        const char *xdg_runtime_dir;
+
+        buf = g_malloc(sizeof *buf);
+        buf->bytes = bytes;
+        buf->width = width;
+        buf->height = height;
+
+        xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
+        fd = open (xdg_runtime_dir, O_TMPFILE|O_RDWR|O_EXCL, 0600);
+        ftruncate (fd, buf->bytes);
+        g_assert(fd >= 0);
+
+        buf->data = mmap(NULL, buf->bytes,
+                         PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+        if (buf->data == MAP_FAILED) {
+                close(fd);
+                g_assert(buf->data != MAP_FAILED);
+        }
+
+        pool = wl_shm_create_pool(wl_shm, fd, buf->bytes);
+        buf->proxy = wl_shm_pool_create_buffer(pool, 0, buf->width, buf->height,
+                                               stride_bytes, drm_format);
+        wl_shm_pool_destroy(pool);
+        close(fd);
+
+        return buf;
+}
+
+/*
+ * Based on Rec. ITU-R BT.601-7
+ *
+ * This is intended to be obvious and accurate, not fast.
+ */
+static void
+x8r8g8b8_to_ycbcr8_bt601(uint32_t xrgb,
+                         uint8_t *y_out, uint8_t *cb_out, uint8_t *cr_out)
+{
+        double y, cb, cr;
+        double r = (xrgb >> 16) & 0xff;
+        double g = (xrgb >> 8) & 0xff;
+        double b = (xrgb >> 0) & 0xff;
+
+        /* normalize to [0.0, 1.0] */
+        r /= 255.0;
+        g /= 255.0;
+        b /= 255.0;
+
+        /* Y normalized to [0.0, 1.0], Cb and Cr [-0.5, 0.5] */
+        y = 0.299 * r + 0.587 * g + 0.114 * b;
+        cr = (r - y) / 1.402;
+        cb = (b - y) / 1.772;
+
+        /* limited range quantization to 8 bit */
+        *y_out = round(219.0 * y + 16.0);
+        if (cr_out)
+                *cr_out = round(224.0 * cr + 128.0);
+        if (cb_out)
+                *cb_out = round(224.0 * cb + 128.0);
+}
+
+static struct yuv_buffer *
+yuv420_create_buffer(pixman_image_t *rgb_image)
+{
+        struct yuv_buffer *buf;
+        size_t bytes;
+        int width;
+        int height;
+        int x, y;
+        void *rgb_pixels;
+        int rgb_stride_bytes;
+        uint32_t *rgb_row;
+        uint8_t *y_base;
+        uint8_t *u_base;
+        uint8_t *v_base;
+        uint8_t *y_row;
+        uint8_t *u_row;
+        uint8_t *v_row;
+        uint32_t argb;
+        uint32_t drm_format = DRM_FORMAT_YUV420;
+
+        g_assert(drm_format == DRM_FORMAT_YUV420);
+
+        width = pixman_image_get_width(rgb_image);
+        height = pixman_image_get_height(rgb_image);
+        rgb_pixels = pixman_image_get_data(rgb_image);
+        rgb_stride_bytes = pixman_image_get_stride(rgb_image);
+
+        /* Full size Y, quarter U and V */
+        bytes = width * height + (width / 2) * (height / 2) * 2;
+        buf = yuv_buffer_create(bytes, width, height, width, drm_format);
+
+        y_base = buf->data;
+        u_base = y_base + width * height;
+        v_base = u_base + (width / 2) * (height / 2);
+
+        for (y = 0; y < height; y++) {
+                rgb_row = rgb_pixels + (y / 2 * 2) * rgb_stride_bytes;
+                y_row = y_base + y * width;
+                u_row = u_base + (y / 2) * (width / 2);
+                v_row = v_base + (y / 2) * (width / 2);
+
+                for (x = 0; x < width; x++) {
+                        /*
+                         * Sub-sample the source image instead, so that U and V
+                         * sub-sampling does not require proper
+                         * filtering/averaging/siting.
+                         */
+                        argb = *(rgb_row + x / 2 * 2);
+
+                        /*
+                         * A stupid way of "sub-sampling" chroma. This does not
+                         * do the necessary filtering/averaging/siting or
+                         * alternate Cb/Cr rows.
+                         */
+                        if ((y & 1) == 0 && (x & 1) == 0) {
+                                x8r8g8b8_to_ycbcr8_bt601(argb, y_row + x,
+                                                         u_row + x / 2,
+                                                         v_row + x / 2);
+                        } else {
+                                x8r8g8b8_to_ycbcr8_bt601(argb, y_row + x,
+                                                         NULL, NULL);
+                        }
+                }
+        }
+
+        return buf;
+}
+
+static void
+destroy_cairo_surface(pixman_image_t *image, void *data)
+{
+        cairo_surface_t *surface = data;
+
+        cairo_surface_destroy(surface);
+}
+
+struct format_map_entry {
+        cairo_format_t cairo;
+        pixman_format_code_t pixman;
+};
+
+static const struct format_map_entry format_map[] = {
+        { CAIRO_FORMAT_ARGB32,    PIXMAN_a8r8g8b8 },
+        { CAIRO_FORMAT_RGB24,     PIXMAN_x8r8g8b8 },
+        { CAIRO_FORMAT_A8,        PIXMAN_a8 },
+        { CAIRO_FORMAT_RGB16_565, PIXMAN_r5g6b5 },
+};
+
+static pixman_format_code_t
+format_cairo2pixman(cairo_format_t fmt)
+{
+        unsigned i;
+
+        for (i = 0; i < G_N_ELEMENTS(format_map); i++)
+                if (format_map[i].cairo == fmt)
+                        return format_map[i].pixman;
+
+        g_assert_not_reached ();
+}
+
+static pixman_image_t *
+image_convert_to_a8r8g8b8(pixman_image_t *image)
+{
+        pixman_image_t *ret;
+        int width;
+        int height;
+
+        if (pixman_image_get_format(image) == PIXMAN_a8r8g8b8)
+                return pixman_image_ref(image);
+
+        width = pixman_image_get_width(image);
+        height = pixman_image_get_height(image);
+
+        ret = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, width, height,
+                                                NULL, 0);
+        g_assert(ret);
+
+        pixman_image_composite32(PIXMAN_OP_SRC, image, NULL, ret,
+                                 0, 0, 0, 0, 0, 0, width, height);
+
+        return ret;
+}
+
+static pixman_image_t *
+load_image_from_png(const char *fname)
+{
+        pixman_image_t *image;
+        pixman_image_t *converted;
+        cairo_format_t cairo_fmt;
+        pixman_format_code_t pixman_fmt;
+        cairo_surface_t *reference_cairo_surface;
+        cairo_status_t status;
+        int width;
+        int height;
+        int stride;
+        void *data;
+
+        reference_cairo_surface = cairo_image_surface_create_from_png(fname);
+        cairo_surface_flush(reference_cairo_surface);
+        status = cairo_surface_status(reference_cairo_surface);
+        if (status != CAIRO_STATUS_SUCCESS) {
+                g_error ("Could not open %s: %s\n", fname,
+                        cairo_status_to_string(status));
+                cairo_surface_destroy(reference_cairo_surface);
+                return NULL;
+        }
+
+        cairo_fmt = cairo_image_surface_get_format(reference_cairo_surface);
+        pixman_fmt = format_cairo2pixman(cairo_fmt);
+
+        width = cairo_image_surface_get_width(reference_cairo_surface);
+        height = cairo_image_surface_get_height(reference_cairo_surface);
+        stride = cairo_image_surface_get_stride(reference_cairo_surface);
+        data = cairo_image_surface_get_data(reference_cairo_surface);
+
+        /* The Cairo surface will own the data, so we keep it around. */
+        image = pixman_image_create_bits_no_clear(pixman_fmt,
+                                                  width, height, data, stride);
+        g_assert(image);
+
+        pixman_image_set_destroy_function(image, destroy_cairo_surface,
+                                          reference_cairo_surface);
+
+        converted = image_convert_to_a8r8g8b8(image);
+        pixman_image_unref(image);
+
+        return converted;
+}
+
 static void
 gdk_registry_handle_global (void               *data,
                             struct wl_registry *registry,
@@ -101,6 +350,7 @@ surface_fill (struct wl_surface *surface,
   struct wl_shm_pool *pool;
   struct wl_buffer *buffer;
 
+#if 0
   size = width * height * 4;
   xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
   fd = open (xdg_runtime_dir, O_TMPFILE|O_RDWR|O_EXCL, 0600);
@@ -118,6 +368,11 @@ surface_fill (struct wl_surface *surface,
   wl_shm_pool_destroy (pool);
 
   close (fd);
+#else
+  pixman_image_t *img = load_image_from_png ("tests/chocolate-cake.png");
+  struct yuv_buffer *buf = yuv420_create_buffer (img);
+  buffer = buf->proxy;
+#endif
 
   wl_surface_attach (surface, buffer, 0, 0);
   wl_surface_commit (surface);


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