From 2e9b5886ce8c6aac5c8657094b54648e5baa0afc Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sun, 6 Oct 2024 22:28:33 -0400 Subject: [PATCH] buffer: add buffer_convert_cairo_surface_for_icon() 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 | 12 +++++++++++ src/buffer.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/include/buffer.h b/include/buffer.h index 67d494ce..7bf3fbe5 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -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. diff --git a/src/buffer.c b/src/buffer.c index 56d9b046..d9cb8726 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -29,6 +29,7 @@ #include #include #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)