]> git.mdlowis.com Git - proto/labwc.git/commitdiff
buffer: add buffer_convert_cairo_surface_for_icon()
authorJohn Lindgren <john@jlindgren.net>
Mon, 7 Oct 2024 02:28:33 +0000 (22:28 -0400)
committerJohn Lindgren <john@jlindgren.net>
Mon, 7 Oct 2024 02:30:46 +0000 (22:30 -0400)
Which handles:
 - conversion from other pixel formats to ARGB32
 - setting the logical size to the desired display size
 - downscaling very large images using CAIRO_FILTER_GOOD

include/buffer.h
src/buffer.c

index 67d494cea9b3a81282385ca0488a3637bd995423..7bf3fbe56078705a2527846bdfabcbde9eeba671 100644 (file)
@@ -61,6 +61,18 @@ struct lab_data_buffer *buffer_adopt_cairo_surface(cairo_surface_t *surface);
 struct lab_data_buffer *buffer_create_cairo(uint32_t logical_width,
        uint32_t logical_height, float scale);
 
+/*
+ * Create a buffer from an image surface, for display as an icon.
+ *
+ * The surface is either adopted by the buffer (which takes ownership),
+ * or copied and then destroyed.
+ *
+ * This function allows non-ARGB32 source images and converts to
+ * CAIRO_FORMAT_ARGB32 if needed.
+ */
+struct lab_data_buffer *buffer_convert_cairo_surface_for_icon(
+       cairo_surface_t *surface, uint32_t icon_size, float scale);
+
 /*
  * Create a buffer which holds (and takes ownership of) raw pixel data
  * in pre-multiplied ARGB32 format.
index 56d9b046d273ddeacf240898f779ac2ad0f02cba..d9cb8726af4e4caed424e61716e921d35afffb5e 100644 (file)
@@ -29,6 +29,7 @@
 #include <drm_fourcc.h>
 #include <wlr/interfaces/wlr_buffer.h>
 #include "buffer.h"
+#include "common/box.h"
 #include "common/mem.h"
 
 static const struct wlr_buffer_impl data_buffer_impl;
@@ -134,6 +135,58 @@ buffer_create_cairo(uint32_t logical_width, uint32_t logical_height, float scale
        return buffer;
 }
 
+struct lab_data_buffer *
+buffer_convert_cairo_surface_for_icon(cairo_surface_t *surface,
+               uint32_t icon_size, float scale)
+{
+       assert(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+       /*
+        * Compute logical size for display and decide whether we can
+        * use the image data directly (fast path). Requirements are:
+        *
+        *  - The pixel format must be ARGB32.
+        *  - The image must not be so large as to need downsampling by
+        *    more than 2x when displayed at the target scale. wlr_scene
+        *    uses linear interpolation without pixel averaging, which
+        *    starts to skip samples if downsampling more than 2x,
+        *    resulting in a grainy look.
+        */
+       int width = cairo_image_surface_get_width(surface);
+       int height = cairo_image_surface_get_height(surface);
+       struct wlr_box logical =
+               box_fit_within(width, height, icon_size, icon_size);
+       struct lab_data_buffer *buffer;
+
+       if (cairo_image_surface_get_format(surface) == CAIRO_FORMAT_ARGB32
+                       && width <= 2 * logical.width * scale
+                       && height <= 2 * logical.height * scale) {
+               buffer = buffer_adopt_cairo_surface(surface);
+               /* set logical size for display */
+               buffer->logical_width = logical.width;
+               buffer->logical_height = logical.height;
+       } else {
+               /* convert to ARGB32 and scale for display (slow path) */
+               buffer = buffer_create_cairo(logical.width,
+                       logical.height, scale);
+
+               cairo_t *cairo = buffer->cairo;
+               cairo_scale(cairo, (double)logical.width / width,
+                       (double)logical.height / height);
+               cairo_set_source_surface(cairo, surface, 0, 0);
+               cairo_pattern_set_filter(cairo_get_source(cairo),
+                       CAIRO_FILTER_GOOD);
+               cairo_paint(cairo);
+
+               /* ensure pixel data is updated */
+               cairo_surface_flush(buffer->surface);
+               /* destroy original surface */
+               cairo_surface_destroy(surface);
+       }
+
+       return buffer;
+}
+
 struct lab_data_buffer *
 buffer_create_from_data(void *pixel_data, uint32_t width, uint32_t height,
                uint32_t stride)