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.
#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;
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)