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
This commit is contained in:
John Lindgren 2024-10-06 22:28:33 -04:00
parent 22e50aa4e2
commit 2e9b5886ce
2 changed files with 65 additions and 0 deletions

View 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, struct lab_data_buffer *buffer_create_cairo(uint32_t logical_width,
uint32_t logical_height, float scale); 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 * Create a buffer which holds (and takes ownership of) raw pixel data
* in pre-multiplied ARGB32 format. * in pre-multiplied ARGB32 format.

View file

@ -29,6 +29,7 @@
#include <drm_fourcc.h> #include <drm_fourcc.h>
#include <wlr/interfaces/wlr_buffer.h> #include <wlr/interfaces/wlr_buffer.h>
#include "buffer.h" #include "buffer.h"
#include "common/box.h"
#include "common/mem.h" #include "common/mem.h"
static const struct wlr_buffer_impl data_buffer_impl; 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; 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 * struct lab_data_buffer *
buffer_create_from_data(void *pixel_data, uint32_t width, uint32_t height, buffer_create_from_data(void *pixel_data, uint32_t width, uint32_t height,
uint32_t stride) uint32_t stride)