diff --git a/include/buffer.h b/include/buffer.h index 52fa2573..c034a874 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -61,18 +61,6 @@ 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/include/common/box.h b/include/common/box.h index 651fcff0..3d8e917f 100644 --- a/include/common/box.h +++ b/include/common/box.h @@ -13,14 +13,13 @@ void box_union(struct wlr_box *box_dest, struct wlr_box *box_a, struct wlr_box *box_b); /* - * Fits and centers a content box (width & height) within a bounding box - * (max_width & max_height). The content box is downscaled if necessary - * (preserving aspect ratio) but not upscaled. + * Fits and centers a content box (width & height) within a bounding box. + * The content box is downscaled if necessary (preserving aspect ratio) but + * not upscaled. * * The returned x & y coordinates are the centered content position * relative to the top-left corner of the bounding box. */ -struct wlr_box box_fit_within(int width, int height, int max_width, - int max_height); +struct wlr_box box_fit_within(int width, int height, struct wlr_box *bounding_box); #endif /* LABWC_BOX_H */ diff --git a/include/common/scaled-img-buffer.h b/include/common/scaled-img-buffer.h new file mode 100644 index 00000000..c83c84ec --- /dev/null +++ b/include/common/scaled-img-buffer.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_SCALED_IMG_BUFFER_H +#define LABWC_SCALED_IMG_BUFFER_H + +#include + +struct wlr_scene_tree; +struct wlr_scene_node; +struct wlr_scene_buffer; +struct lab_img; + +struct scaled_img_buffer { + struct scaled_scene_buffer *scaled_buffer; + struct wlr_scene_buffer *scene_buffer; + struct lab_img *img; + int width; + int height; + int padding; +}; + +/* + * Create an auto scaling image buffer, providing a wlr_scene_buffer node for + * display. It gets destroyed automatically when the backing scaled_scene_buffer + * is being destroyed which in turn happens automatically when the backing + * wlr_scene_buffer (or one of its parents) is being destroyed. + */ +struct scaled_img_buffer *scaled_img_buffer_create(struct wlr_scene_tree *parent, + struct lab_img *img, int width, int height, int padding); + +/* Update image, width, height and padding of the scaled_img_buffer */ +void scaled_img_buffer_update(struct scaled_img_buffer *self, + struct lab_img *img, int width, int height, int padding); + +/* Obtain scaled_img_buffer from wlr_scene_node */ +struct scaled_img_buffer *scaled_img_buffer_from_node(struct wlr_scene_node *node); + +#endif /* LABWC_SCALED_IMG_BUFFER_H */ diff --git a/include/desktop-entry.h b/include/desktop-entry.h index 42b47081..75643067 100644 --- a/include/desktop-entry.h +++ b/include/desktop-entry.h @@ -7,7 +7,7 @@ struct server; void desktop_entry_init(struct server *server); void desktop_entry_finish(struct server *server); -struct lab_data_buffer *desktop_entry_icon_lookup(struct server *server, +struct lab_img *desktop_entry_icon_lookup(struct server *server, const char *app_id, int size, float scale); /** diff --git a/include/img/img-png.h b/include/img/img-png.h index 2e28c152..8a5f7607 100644 --- a/include/img/img-png.h +++ b/include/img/img-png.h @@ -2,9 +2,6 @@ #ifndef LABWC_IMG_PNG_H #define LABWC_IMG_PNG_H -struct lab_data_buffer; - -void img_png_load(const char *filename, struct lab_data_buffer **buffer, - int size, float scale); +struct lab_data_buffer *img_png_load(const char *filename); #endif /* LABWC_IMG_PNG_H */ diff --git a/include/img/img-svg.h b/include/img/img-svg.h index c8acc71c..8790c268 100644 --- a/include/img/img-svg.h +++ b/include/img/img-svg.h @@ -2,9 +2,13 @@ #ifndef LABWC_IMG_SVG_H #define LABWC_IMG_SVG_H +#include + struct lab_data_buffer; -void img_svg_load(const char *filename, struct lab_data_buffer **buffer, - int size, float scale); +RsvgHandle *img_svg_load(const char *filename); + +struct lab_data_buffer *img_svg_render(RsvgHandle *svg, int w, int h, + int padding, double scale); #endif /* LABWC_IMG_SVG_H */ diff --git a/include/img/img-xbm.h b/include/img/img-xbm.h index 24ad24d8..ba517aca 100644 --- a/include/img/img-xbm.h +++ b/include/img/img-xbm.h @@ -5,18 +5,15 @@ struct lab_data_buffer; /** - * img_xbm_from_bitmap() - create button from monochrome bitmap + * img_xbm_load_from_bitmap() - create button from monochrome bitmap * @bitmap: bitmap data array in hexadecimal xbm format - * @buffer: cairo-surface-buffer to create * @rgba: color * * Example bitmap: char button[6] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }; */ -void img_xbm_from_bitmap(const char *bitmap, struct lab_data_buffer **buffer, - float *rgba); +struct lab_data_buffer *img_xbm_load_from_bitmap(const char *bitmap, float *rgba); /* img_xbm_load - Convert xbm file to buffer with cairo surface */ -void img_xbm_load(const char *filename, struct lab_data_buffer **buffer, - float *rgba); +struct lab_data_buffer *img_xbm_load(const char *filename, float *rgba); #endif /* LABWC_IMG_XBM_H */ diff --git a/include/img/img-xpm.h b/include/img/img-xpm.h index 65d7c2b3..ee1b79c5 100644 --- a/include/img/img-xpm.h +++ b/include/img/img-xpm.h @@ -4,7 +4,6 @@ struct lab_data_buffer; -void img_xpm_load(const char *filename, struct lab_data_buffer **buffer, - int size, float scale); +struct lab_data_buffer *img_xpm_load(const char *filename); #endif /* LABWC_IMG_XPM_H */ diff --git a/include/img/img.h b/include/img/img.h new file mode 100644 index 00000000..cc7abed2 --- /dev/null +++ b/include/img/img.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_IMG_H +#define LABWC_IMG_H + +#include +#include +#include + +struct lab_img_cache; + +enum lab_img_type { + LAB_IMG_PNG, + LAB_IMG_SVG, + LAB_IMG_XBM, + LAB_IMG_XPM, +}; + +struct lab_img { + struct theme *theme; /* Used by modifier functions */ + struct wl_array modifiers; /* lab_img_modifier_func_t */ + struct lab_img_cache *cache; +}; + +struct lab_img *lab_img_load(enum lab_img_type type, const char *path, + float *xbm_color); + +/** + * lab_img_load_from_bitmap() - create button from monochrome bitmap + * @bitmap: bitmap data array in hexadecimal xbm format + * @rgba: color + * + * Example bitmap: char button[6] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }; + */ +struct lab_img *lab_img_load_from_bitmap(const char *bitmap, float *rgba); + +typedef void (*lab_img_modifier_func_t)(struct theme *theme, cairo_t *cairo, + int w, int h); + +/** + * lab_img_copy() - Copy lab_img + * @img: source image + * + * This function duplicates lab_img, but its internal cache for the image is + * shared. + */ +struct lab_img *lab_img_copy(struct lab_img *img); + +/** + * lab_img_add_modifier() - Add a modifier function to lab_img + * @img: source image + * @modifier: function that applies modifications to the image. + * @theme: pointer to theme passed to @modifier. + * + * "Modifiers" are functions that perform some additional drawing operation + * after the image is rendered on a buffer with lab_img_render(). For example, + * hover effects for window buttons can be drawn over the rendered image. + */ +void lab_img_add_modifier(struct lab_img *img, lab_img_modifier_func_t modifier, + struct theme *theme); + +/** + * lab_img_render() - Render lab_img to a buffer + * @img: source image + * @width: width of the created buffer + * @height: height of the created buffer + * @padding: padding around the rendered image in the buffer + * @scale: scale of the created buffer + */ +struct lab_data_buffer *lab_img_render(struct lab_img *img, + int width, int height, int padding, double scale); + +/** + * lab_img_destroy() - destroy lab_img + * @img: lab_img to destroy + */ +void lab_img_destroy(struct lab_img *img); + +#endif /* LABWC_IMG_H */ diff --git a/include/ssd-internal.h b/include/ssd-internal.h index d3454335..3600b1c6 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -79,6 +79,7 @@ struct ssd { } title; char *app_id; + struct lab_img *icon_img; } state; /* An invisible area around the view which allows resizing */ @@ -146,10 +147,8 @@ struct ssd_part *add_scene_buffer( struct wlr_scene_tree *parent, struct wlr_buffer *buffer, int x, int y); struct ssd_part *add_scene_button(struct wl_list *part_list, enum ssd_part_type type, struct wlr_scene_tree *parent, - struct lab_data_buffer *buffers[LAB_BS_ALL + 1], int x, int y, + struct lab_img *buffers[LAB_BS_ALL + 1], int x, int y, struct view *view); -void update_window_icon_buffer(struct wlr_scene_node *button_node, - struct lab_data_buffer *buffer); /* SSD internal helpers */ struct ssd_part *ssd_get_part( diff --git a/include/theme.h b/include/theme.h index a297e05e..88b43e28 100644 --- a/include/theme.h +++ b/include/theme.h @@ -12,6 +12,8 @@ #include #include "ssd.h" +struct lab_img; + enum lab_justification { LAB_JUSTIFY_LEFT, LAB_JUSTIFY_CENTER, @@ -82,7 +84,7 @@ struct theme { * * Elements in buttons[0] are all NULL since LAB_SSD_BUTTON_FIRST is 1. */ - struct lab_data_buffer *buttons + struct lab_img *button_imgs [LAB_SSD_BUTTON_LAST + 1][LAB_BS_ALL + 1]; struct lab_data_buffer *corner_top_left_normal; diff --git a/src/buffer.c b/src/buffer.c index 7b626bc9..3e50264c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -29,7 +29,6 @@ #include #include #include "buffer.h" -#include "common/box.h" #include "common/mem.h" static const struct wlr_buffer_impl data_buffer_impl; @@ -130,59 +129,6 @@ 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 = cairo_create(buffer->surface); - 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 cairo surface & context */ - cairo_surface_destroy(surface); - cairo_destroy(cairo); - } - - return buffer; -} - struct lab_data_buffer * buffer_create_from_data(void *pixel_data, uint32_t width, uint32_t height, uint32_t stride) diff --git a/src/common/box.c b/src/common/box.c index eefa535c..978108fd 100644 --- a/src/common/box.c +++ b/src/common/box.c @@ -49,27 +49,27 @@ box_union(struct wlr_box *box_dest, struct wlr_box *box_a, struct wlr_box *box_b } struct wlr_box -box_fit_within(int width, int height, int max_width, int max_height) +box_fit_within(int width, int height, struct wlr_box *bound) { struct wlr_box box; - if (width <= max_width && height <= max_height) { + if (width <= bound->width && height <= bound->height) { /* No downscaling needed */ box.width = width; box.height = height; - } else if (width * max_height > height * max_width) { + } else if (width * bound->height > height * bound->width) { /* Wider content, fit width */ - box.width = max_width; - box.height = (height * max_width + (width / 2)) / width; + box.width = bound->width; + box.height = (height * bound->width + (width / 2)) / width; } else { /* Taller content, fit height */ - box.width = (width * max_height + (height / 2)) / height; - box.height = max_height; + box.width = (width * bound->height + (height / 2)) / height; + box.height = bound->height; } /* Compute centered position */ - box.x = (max_width - box.width) / 2; - box.y = (max_height - box.height) / 2; + box.x = bound->x + (bound->width - box.width) / 2; + box.y = bound->y + (bound->height - box.height) / 2; return box; } diff --git a/src/common/meson.build b/src/common/meson.build index d6ad2c64..457cffa1 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -14,6 +14,7 @@ labwc_sources += files( 'parse-bool.c', 'parse-double.c', 'scaled-font-buffer.c', + 'scaled-img-buffer.c', 'scaled-rect-buffer.c', 'scaled-scene-buffer.c', 'scene-helpers.c', diff --git a/src/common/scaled-img-buffer.c b/src/common/scaled-img-buffer.c new file mode 100644 index 00000000..27d65b9c --- /dev/null +++ b/src/common/scaled-img-buffer.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include "buffer.h" +#include "common/list.h" +#include "common/mem.h" +#include "common/scaled-img-buffer.h" +#include "common/scaled-scene-buffer.h" +#include "img/img.h" +#include "node.h" + +static struct wl_list cached_buffers = WL_LIST_INIT(&cached_buffers); + +static struct lab_data_buffer * +_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) +{ + struct scaled_img_buffer *self = scaled_buffer->data; + struct lab_data_buffer *buffer = lab_img_render(self->img, + self->width, self->height, self->padding, scale); + return buffer; +} + +static void +_destroy(struct scaled_scene_buffer *scaled_buffer) +{ + struct scaled_img_buffer *self = scaled_buffer->data; + free(self); +} + +static bool +_equal(struct scaled_scene_buffer *scaled_buffer_a, + struct scaled_scene_buffer *scaled_buffer_b) +{ + struct scaled_img_buffer *a = scaled_buffer_a->data; + struct scaled_img_buffer *b = scaled_buffer_b->data; + + return a->img == b->img + && a->width == b->width + && a->height == b->height + && a->padding == b->padding; +} + +static struct scaled_scene_buffer_impl impl = { + .create_buffer = _create_buffer, + .destroy = _destroy, + .equal = _equal, +}; + +struct scaled_img_buffer * +scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, + int width, int height, int padding) +{ + struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create( + parent, &impl, &cached_buffers, /* drop_buffer */ true); + struct scaled_img_buffer *self = znew(*self); + self->scaled_buffer = scaled_buffer; + self->scene_buffer = scaled_buffer->scene_buffer; + self->img = img; + self->width = width; + self->height = height; + self->padding = padding; + + scaled_buffer->data = self; + + scaled_scene_buffer_request_update(scaled_buffer, width, height); + + return self; +} + +void +scaled_img_buffer_update(struct scaled_img_buffer *self, struct lab_img *img, + int width, int height, int padding) +{ + self->img = img; + self->width = width; + self->height = height; + self->padding = padding; + scaled_scene_buffer_request_update(self->scaled_buffer, width, height); +} + +struct scaled_img_buffer * +scaled_img_buffer_from_node(struct wlr_scene_node *node) +{ + struct scaled_scene_buffer *scaled_buffer = + node_scaled_scene_buffer_from_node(node); + assert(scaled_buffer->impl == &impl); + return scaled_buffer->data; +} diff --git a/src/desktop-entry.c b/src/desktop-entry.c index 404bc989..fe5c2379 100644 --- a/src/desktop-entry.c +++ b/src/desktop-entry.c @@ -8,14 +8,8 @@ #include "common/macros.h" #include "common/mem.h" #include "common/string-helpers.h" -#include "config.h" #include "desktop-entry.h" -#include "img/img-png.h" -#include "img/img-xpm.h" - -#if HAVE_RSVG -#include "img/img-svg.h" -#endif +#include "img/img.h" #include "labwc.h" @@ -267,7 +261,22 @@ get_desktop_entry(struct sfdo *sfdo, const char *app_id) return entry; } -struct lab_data_buffer * +static enum lab_img_type +convert_img_type(enum sfdo_icon_file_format fmt) +{ + switch (fmt) { + case SFDO_ICON_FILE_FORMAT_PNG: + return LAB_IMG_PNG; + case SFDO_ICON_FILE_FORMAT_SVG: + return LAB_IMG_SVG; + case SFDO_ICON_FILE_FORMAT_XPM: + return LAB_IMG_XPM; + default: + abort(); + } +} + +struct lab_img * desktop_entry_icon_lookup(struct server *server, const char *app_id, int size, float scale) { @@ -308,26 +317,12 @@ desktop_entry_icon_lookup(struct server *server, const char *app_id, int size, return NULL; } - struct lab_data_buffer *icon_buffer = NULL; - wlr_log(WLR_DEBUG, "loading icon file %s", ctx.path); - - switch (ctx.format) { - case SFDO_ICON_FILE_FORMAT_PNG: - img_png_load(ctx.path, &icon_buffer, size, scale); - break; - case SFDO_ICON_FILE_FORMAT_SVG: -#if HAVE_RSVG - img_svg_load(ctx.path, &icon_buffer, size, scale); -#endif - break; - case SFDO_ICON_FILE_FORMAT_XPM: - img_xpm_load(ctx.path, &icon_buffer, size, scale); - break; - } + struct lab_img *img = lab_img_load(convert_img_type(ctx.format), ctx.path, NULL); free(ctx.path); - return icon_buffer; + + return img; } const char * diff --git a/src/img/img-png.c b/src/img/img-png.c index cff84726..8824e925 100644 --- a/src/img/img-png.c +++ b/src/img/img-png.c @@ -3,6 +3,7 @@ * Copyright (C) Johan Malm 2023 */ #define _POSIX_C_SOURCE 200809L +#include #include #include #include @@ -10,9 +11,8 @@ #include #include #include "buffer.h" -#include "img/img-png.h" #include "common/string-helpers.h" -#include "labwc.h" +#include "img/img-png.h" /* * cairo_image_surface_create_from_png() does not gracefully handle non-png @@ -42,27 +42,22 @@ ispng(const char *filename) #undef PNG_BYTES_TO_CHECK -void -img_png_load(const char *filename, struct lab_data_buffer **buffer, int size, - float scale) +struct lab_data_buffer * +img_png_load(const char *filename) { - if (*buffer) { - wlr_buffer_drop(&(*buffer)->base); - *buffer = NULL; - } if (string_null_or_empty(filename)) { - return; + return NULL; } if (!ispng(filename)) { - return; + return NULL; } cairo_surface_t *image = cairo_image_surface_create_from_png(filename); if (cairo_surface_status(image)) { - wlr_log(WLR_ERROR, "error reading png button '%s'", filename); + wlr_log(WLR_ERROR, "error reading png file '%s'", filename); cairo_surface_destroy(image); - return; + return NULL; } - *buffer = buffer_convert_cairo_surface_for_icon(image, size, scale); + return buffer_adopt_cairo_surface(image); } diff --git a/src/img/img-svg.c b/src/img/img-svg.c index f217d72a..4900e2cc 100644 --- a/src/img/img-svg.c +++ b/src/img/img-svg.c @@ -10,24 +10,18 @@ #include #include #include "buffer.h" -#include "img/img-svg.h" #include "common/string-helpers.h" +#include "img/img-svg.h" #include "labwc.h" -void -img_svg_load(const char *filename, struct lab_data_buffer **buffer, int size, - float scale) +RsvgHandle * +img_svg_load(const char *filename) { - if (*buffer) { - wlr_buffer_drop(&(*buffer)->base); - *buffer = NULL; - } if (string_null_or_empty(filename)) { - return; + return NULL; } GError *err = NULL; - RsvgRectangle viewport = { .width = size, .height = size }; RsvgHandle *svg = rsvg_handle_new_from_file(filename, &err); if (err) { wlr_log(WLR_DEBUG, "error reading svg %s-%s", filename, err->message); @@ -36,33 +30,43 @@ img_svg_load(const char *filename, struct lab_data_buffer **buffer, int size, * rsvg_handle_new_from_file() returns NULL if an error occurs, * so there is no need to free svg here. */ - return; + return NULL; } + return svg; +} - *buffer = buffer_create_cairo(size, size, scale); - cairo_surface_t *image = (*buffer)->surface; +struct lab_data_buffer * +img_svg_render(RsvgHandle *svg, int w, int h, int padding, double scale) +{ + struct lab_data_buffer *buffer = buffer_create_cairo(w, h, scale); + cairo_surface_t *image = buffer->surface; cairo_t *cr = cairo_create(image); + GError *err = NULL; + RsvgRectangle viewport = { + .x = padding, + .y = padding, + .width = w - 2 * padding, + .height = h - 2 * padding, + }; rsvg_handle_render_document(svg, cr, &viewport, &err); if (err) { - wlr_log(WLR_ERROR, "error rendering svg %s-%s", filename, err->message); + wlr_log(WLR_ERROR, "error rendering svg: %s", err->message); g_error_free(err); goto error; } - if (cairo_surface_status(image)) { - wlr_log(WLR_ERROR, "error reading svg button '%s'", filename); + wlr_log(WLR_ERROR, "error reading svg file"); goto error; } - cairo_surface_flush(image); + cairo_surface_flush(buffer->surface); cairo_destroy(cr); - g_object_unref(svg); - return; + return buffer; error: - wlr_buffer_drop(&(*buffer)->base); - *buffer = NULL; + wlr_buffer_drop(&buffer->base); cairo_destroy(cr); g_object_unref(svg); + return NULL; } diff --git a/src/img/img-xbm.c b/src/img/img-xbm.c index af9c90ac..fb2b953f 100644 --- a/src/img/img-xbm.c +++ b/src/img/img-xbm.c @@ -13,6 +13,7 @@ #include #include #include +#include "img/img.h" #include "img/img-xbm.h" #include "common/grab-file.h" #include "common/mem.h" @@ -255,32 +256,23 @@ parse_xbm_builtin(const char *button, int size) return pixmap; } -void -img_xbm_from_bitmap(const char *bitmap, struct lab_data_buffer **buffer, - float *rgba) +struct lab_data_buffer * +img_xbm_load_from_bitmap(const char *bitmap, float *rgba) { struct pixmap pixmap = {0}; - if (*buffer) { - wlr_buffer_drop(&(*buffer)->base); - *buffer = NULL; - } color = argb32(rgba); pixmap = parse_xbm_builtin(bitmap, 6); - *buffer = buffer_create_from_data(pixmap.data, pixmap.width, pixmap.height, + + return buffer_create_from_data(pixmap.data, pixmap.width, pixmap.height, pixmap.width * 4); } -void -img_xbm_load(const char *filename, struct lab_data_buffer **buffer, - float *rgba) +struct lab_data_buffer * +img_xbm_load(const char *filename, float *rgba) { struct pixmap pixmap = {0}; - if (*buffer) { - wlr_buffer_drop(&(*buffer)->base); - *buffer = NULL; - } if (string_null_or_empty(filename)) { - return; + return NULL; } color = argb32(rgba); @@ -295,12 +287,9 @@ img_xbm_load(const char *filename, struct lab_data_buffer **buffer, } buf_reset(&token_buf); if (!pixmap.data) { - return; + return NULL; } - /* Create buffer */ - if (pixmap.data) { - *buffer = buffer_create_from_data(pixmap.data, pixmap.width, - pixmap.height, pixmap.width * 4); - } + return buffer_create_from_data(pixmap.data, pixmap.width, + pixmap.height, pixmap.width * 4); } diff --git a/src/img/img-xpm.c b/src/img/img-xpm.c index 3e6598d8..beb025bc 100644 --- a/src/img/img-xpm.c +++ b/src/img/img-xpm.c @@ -397,30 +397,26 @@ out: return surface; } -void -img_xpm_load(const char *filename, struct lab_data_buffer **buffer, int size, - float scale) +struct lab_data_buffer * +img_xpm_load(const char *filename) { - if (*buffer) { - wlr_buffer_drop(&(*buffer)->base); - *buffer = NULL; - } - struct file_handle h = {0}; h.infile = fopen(filename, "rb"); if (!h.infile) { wlr_log(WLR_ERROR, "error opening '%s'", filename); - return; + return NULL; } cairo_surface_t *surface = xpm_load_to_surface(&h); + struct lab_data_buffer *buffer = NULL; if (surface) { - *buffer = buffer_convert_cairo_surface_for_icon(surface, size, - scale); + buffer = buffer_adopt_cairo_surface(surface); } else { wlr_log(WLR_ERROR, "error loading '%s'", filename); } fclose(h.infile); buf_reset(&h.buf); + + return buffer; } diff --git a/src/img/img.c b/src/img/img.c new file mode 100644 index 00000000..241c911c --- /dev/null +++ b/src/img/img.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "buffer.h" +#include "config.h" +#include "common/box.h" +#include "common/graphic-helpers.h" +#include "common/macros.h" +#include "common/mem.h" +#include "common/string-helpers.h" +#include "img/img.h" +#include "img/img-png.h" +#if HAVE_RSVG +#include "img/img-svg.h" +#endif +#include "img/img-xbm.h" +#include "img/img-xpm.h" +#include "labwc.h" +#include "theme.h" + +struct lab_img_cache { + enum lab_img_type type; + /* lab_img_cache is refcounted to be shared by multiple lab_imgs */ + int refcount; + + /* Handler for the loaded image file */ + struct lab_data_buffer *buffer; /* for PNG/XBM/XPM image */ +#if HAVE_RSVG + RsvgHandle *svg; /* for SVG image */ +#endif +}; + +static struct lab_img * +create_img(struct lab_img_cache *cache) +{ + struct lab_img *img = znew(*img); + img->cache = cache; + cache->refcount++; + wl_array_init(&img->modifiers); + return img; +} + +struct lab_img * +lab_img_load(enum lab_img_type type, const char *path, float *xbm_color) +{ + if (string_null_or_empty(path)) { + return NULL; + } + + struct lab_img_cache *cache = znew(*cache); + cache->type = type; + + switch (type) { + case LAB_IMG_PNG: + cache->buffer = img_png_load(path); + break; + case LAB_IMG_XBM: + assert(xbm_color); + cache->buffer = img_xbm_load(path, xbm_color); + break; + case LAB_IMG_XPM: + cache->buffer = img_xpm_load(path); + break; + case LAB_IMG_SVG: +#if HAVE_RSVG + cache->svg = img_svg_load(path); +#endif + break; + } + + bool img_is_loaded = (bool)cache->buffer; +#if HAVE_RSVG + img_is_loaded |= (bool)cache->svg; +#endif + + if (img_is_loaded) { + return create_img(cache); + } else { + free(cache); + return NULL; + } +} + +struct lab_img * +lab_img_load_from_bitmap(const char *bitmap, float *rgba) +{ + struct lab_data_buffer *buffer = img_xbm_load_from_bitmap(bitmap, rgba); + if (!buffer) { + return NULL; + } + + struct lab_img_cache *cache = znew(*cache); + cache->type = LAB_IMG_XBM; + cache->buffer = buffer; + + return create_img(cache); +} + +struct lab_img * +lab_img_copy(struct lab_img *img) +{ + struct lab_img *new_img = create_img(img->cache); + wl_array_copy(&new_img->modifiers, &img->modifiers); + return new_img; +} + +void +lab_img_add_modifier(struct lab_img *img, lab_img_modifier_func_t modifier, + struct theme *theme) +{ + img->theme = theme; + lab_img_modifier_func_t *mod = wl_array_add(&img->modifiers, sizeof(*mod)); + *mod = modifier; +} + +/* + * Takes a source surface from PNG/XBM/XPM file and output a buffer for the + * given size. The source surface is placed at the center of the output buffer + * and shrunk if it overflows from the output buffer. + */ +static struct lab_data_buffer * +render_cairo_surface(cairo_surface_t *surface, int width, int height, + int padding, double scale) +{ + assert(surface); + int src_w = cairo_image_surface_get_width(surface); + int src_h = cairo_image_surface_get_height(surface); + + struct lab_data_buffer *buffer = + buffer_create_cairo(width, height, scale); + cairo_t *cairo = cairo_create(buffer->surface); + + struct wlr_box container = { + .x = padding, + .y = padding, + .width = width - 2 * padding, + .height = height - 2 * padding, + }; + + struct wlr_box src_box = box_fit_within(src_w, src_h, &container); + double scene_scale = MIN(1.0, (double_t)container.width / (double)src_w); + cairo_translate(cairo, src_box.x, src_box.y); + cairo_scale(cairo, scene_scale, scene_scale); + cairo_set_source_surface(cairo, surface, 0, 0); + cairo_pattern_set_filter(cairo_get_source(cairo), CAIRO_FILTER_GOOD); + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_paint(cairo); + + cairo_destroy(cairo); + + return buffer; +} + +struct lab_data_buffer * +lab_img_render(struct lab_img *img, int width, int height, int padding, + double scale) +{ + struct lab_data_buffer *buffer = NULL; + + /* Render the image into the buffer for the given size */ + switch (img->cache->type) { + case LAB_IMG_PNG: + case LAB_IMG_XBM: + case LAB_IMG_XPM: + buffer = render_cairo_surface(img->cache->buffer->surface, + width, height, padding, scale); + break; +#if HAVE_RSVG + case LAB_IMG_SVG: + buffer = img_svg_render(img->cache->svg, width, height, + padding, scale); + break; +#endif + default: + break; + } + + if (!buffer) { + return NULL; + } + + /* Apply modifiers to the buffer (e.g. draw hover overlay) */ + cairo_t *cairo = cairo_create(buffer->surface); + lab_img_modifier_func_t *modifier; + wl_array_for_each(modifier, &img->modifiers) { + cairo_save(cairo); + (*modifier)(img->theme, cairo, width, height); + cairo_restore(cairo); + } + + cairo_surface_flush(buffer->surface); + cairo_destroy(cairo); + + return buffer; +} + +void +lab_img_destroy(struct lab_img *img) +{ + if (!img) { + return; + } + + struct lab_img_cache *cache = img->cache; + cache->refcount--; + + if (cache->refcount == 0) { + if (cache->buffer) { + wlr_buffer_drop(&cache->buffer->base); + } +#if HAVE_RSVG + if (cache->svg) { + g_object_unref(cache->svg); + } +#endif + free(cache); + } + + wl_array_release(&img->modifiers); + free(img); +} diff --git a/src/img/meson.build b/src/img/meson.build index 4e31ac04..3f168825 100644 --- a/src/img/meson.build +++ b/src/img/meson.build @@ -1,4 +1,5 @@ labwc_sources += files( + 'img.c', 'img-png.c', 'img-xbm.c', 'img-xpm.c' diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index af09df24..fc4d6139 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -5,6 +5,7 @@ #include "common/box.h" #include "common/list.h" #include "common/mem.h" +#include "common/scaled-img-buffer.h" #include "labwc.h" #include "node.h" #include "ssd-internal.h" @@ -80,35 +81,10 @@ add_scene_buffer(struct wl_list *list, enum ssd_part_type type, return part; } -static struct wlr_box -get_scale_box(struct lab_data_buffer *buffer, int container_width, - int container_height) -{ - return box_fit_within(buffer->logical_width, buffer->logical_height, - container_width, container_height); -} - -void -update_window_icon_buffer(struct wlr_scene_node *button_node, - struct lab_data_buffer *buffer) -{ - struct wlr_scene_buffer *scene_buffer = - wlr_scene_buffer_from_node(button_node); - - struct wlr_box icon_geo = get_scale_box(buffer, - rc.theme->window_button_width, - rc.theme->window_button_height); - - wlr_scene_buffer_set_buffer(scene_buffer, &buffer->base); - wlr_scene_buffer_set_dest_size(scene_buffer, - icon_geo.width, icon_geo.height); - wlr_scene_node_set_position(button_node, icon_geo.x, icon_geo.y); -} - struct ssd_part * add_scene_button(struct wl_list *part_list, enum ssd_part_type type, struct wlr_scene_tree *parent, - struct lab_data_buffer *buffers[LAB_BS_ALL + 1], + struct lab_img *imgs[LAB_BS_ALL + 1], int x, int y, struct view *view) { struct ssd_part *button_root = add_scene_part(part_list, type); @@ -125,18 +101,15 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, /* Icons */ struct wlr_scene_node *nodes[LAB_BS_ALL + 1] = {0}; for (uint8_t state_set = 0; state_set <= LAB_BS_ALL; state_set++) { - if (!buffers[state_set]) { + if (!imgs[state_set]) { continue; } - struct lab_data_buffer *icon_buffer = buffers[state_set]; - struct wlr_box icon_geo = get_scale_box(icon_buffer, - rc.theme->window_button_width, rc.theme->window_button_height); - struct ssd_part *icon_part = add_scene_buffer(part_list, type, - parent, &icon_buffer->base, icon_geo.x, icon_geo.y); - /* Make sure big icons are scaled down if necessary */ - wlr_scene_buffer_set_dest_size( - wlr_scene_buffer_from_node(icon_part->node), - icon_geo.width, icon_geo.height); + struct ssd_part *icon_part = add_scene_part(part_list, type); + struct scaled_img_buffer *img_buffer = scaled_img_buffer_create( + parent, imgs[state_set], rc.theme->window_button_width, + rc.theme->window_button_height, /* padding */ 0); + assert(img_buffer); + icon_part->node = &img_buffer->scene_buffer->node; wlr_scene_node_set_enabled(icon_part->node, false); nodes[state_set] = icon_part->node; } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index eae98815..9c24b431 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -7,11 +7,13 @@ #include "config.h" #include "common/mem.h" #include "common/scaled-font-buffer.h" +#include "common/scaled-img-buffer.h" #include "common/scene-helpers.h" #include "common/string-helpers.h" #if HAVE_LIBSFDO #include "desktop-entry.h" #endif +#include "img/img.h" #include "labwc.h" #include "node.h" #include "ssd-internal.h" @@ -73,20 +75,20 @@ ssd_titlebar_create(struct ssd *ssd) int y = (theme->titlebar_height - theme->window_button_height) / 2; wl_list_for_each(b, &rc.title_buttons_left, link) { - struct lab_data_buffer **buffers = - theme->window[active].buttons[b->type]; + struct lab_img **imgs = + theme->window[active].button_imgs[b->type]; add_scene_button(&subtree->parts, b->type, parent, - buffers, x, y, view); + imgs, x, y, view); x += theme->window_button_width + theme->window_button_spacing; } x = width - theme->window_titlebar_padding_width + theme->window_button_spacing; wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { x -= theme->window_button_width + theme->window_button_spacing; - struct lab_data_buffer **buffers = - theme->window[active].buttons[b->type]; + struct lab_img **imgs = + theme->window[active].button_imgs[b->type]; add_scene_button(&subtree->parts, b->type, parent, - buffers, x, y, view); + imgs, x, y, view); } } FOR_EACH_END @@ -343,6 +345,9 @@ ssd_titlebar_destroy(struct ssd *ssd) if (ssd->state.app_id) { zfree(ssd->state.app_id); } + if (ssd->state.icon_img) { + lab_img_destroy(ssd->state.icon_img); + } wlr_scene_node_destroy(&ssd->titlebar.tree->node); ssd->titlebar.tree = NULL; @@ -593,9 +598,9 @@ ssd_update_window_icon(struct ssd *ssd) * was considered, but these settings have distinct purposes * already and are zero by default. */ - int hpad = theme->window_button_width / 10; - int icon_size = MIN(theme->window_button_width - 2 * hpad, - theme->window_button_height); + int icon_padding = theme->window_button_width / 10; + int icon_size = MIN(theme->window_button_width - 2 * icon_padding, + theme->window_button_height - 2 * icon_padding); /* * Load/render icons at the max scale of any usable output (at @@ -607,9 +612,9 @@ ssd_update_window_icon(struct ssd *ssd) */ float icon_scale = output_max_scale(ssd->view->server); - struct lab_data_buffer *icon_buffer = desktop_entry_icon_lookup( + struct lab_img *icon_img = desktop_entry_icon_lookup( ssd->view->server, app_id, icon_size, icon_scale); - if (!icon_buffer) { + if (!icon_img) { wlr_log(WLR_DEBUG, "icon could not be loaded for %s", app_id); return; } @@ -625,14 +630,22 @@ ssd_update_window_icon(struct ssd *ssd) /* Replace all the buffers in the button with the window icon */ struct ssd_button *button = node_ssd_button_from_node(part->node); for (uint8_t state_set = 0; state_set <= LAB_BS_ALL; state_set++) { - if (button->nodes[state_set]) { - update_window_icon_buffer(button->nodes[state_set], - icon_buffer); + struct wlr_scene_node *node = button->nodes[state_set]; + if (node) { + struct scaled_img_buffer *img_buffer = + scaled_img_buffer_from_node(node); + scaled_img_buffer_update(img_buffer, icon_img, + theme->window_button_width, + theme->window_button_height, + icon_padding); } } } FOR_EACH_END - wlr_buffer_drop(&icon_buffer->base); + if (ssd->state.icon_img) { + lab_img_destroy(ssd->state.icon_img); + } + ssd->state.icon_img = icon_img; #endif } diff --git a/src/theme.c b/src/theme.c index 2a7111b2..a0853d7c 100644 --- a/src/theme.c +++ b/src/theme.c @@ -29,14 +29,8 @@ #include "common/parse-double.h" #include "common/string-helpers.h" #include "config/rcxml.h" -#include "img/img-png.h" +#include "img/img.h" #include "labwc.h" - -#if HAVE_RSVG -#include "img/img-svg.h" -#endif - -#include "img/img-xbm.h" #include "theme.h" #include "buffer.h" #include "ssd.h" @@ -80,134 +74,63 @@ zdrop(struct lab_data_buffer **buffer) } } -struct icon_drawing_context { - struct lab_data_buffer *buffer; - cairo_t *cairo; -}; - -static struct icon_drawing_context -copy_icon_buffer(struct theme *theme, struct lab_data_buffer *icon_buffer) -{ - assert(icon_buffer); - - cairo_surface_t *surface = icon_buffer->surface; - int icon_width = cairo_image_surface_get_width(surface); - int icon_height = cairo_image_surface_get_height(surface); - - int width = theme->window_button_width; - int height = theme->window_button_height; - - /* - * Proportionately increase size of hover_buffer if the non-hover - * 'donor' buffer is larger than the allocated space. It will get - * scaled down again by wlroots when rendered and as required by the - * current output scale. - * - * This ensures that icons > width or > height keep their aspect ratio - * and are rendered the same as without the hover overlay. - */ - double scale = (width && height) ? - MAX((double)icon_width / width, (double)icon_height / height) : 1.0; - if (scale < 1.0) { - scale = 1.0; - } - int buffer_width = (double)width * scale; - int buffer_height = (double)height * scale; - struct lab_data_buffer *buffer = buffer_create_cairo( - buffer_width, buffer_height, 1.0); - cairo_t *cairo = cairo_create(buffer->surface); - - cairo_set_source_surface(cairo, surface, - (buffer_width - icon_width) / 2, (buffer_height - icon_height) / 2); - cairo_paint(cairo); - - /* - * Scale cairo context so that we can draw hover overlay or rounded - * corner on this buffer in the scene coordinates. - */ - cairo_scale(cairo, scale, scale); - - return (struct icon_drawing_context){ - .buffer = buffer, - .cairo = cairo, - }; -} - +/* Draw rounded-rectangular hover overlay on the button buffer */ static void -create_hover_fallback(struct theme *theme, - struct lab_data_buffer **hover_buffer, - struct lab_data_buffer *icon_buffer) +draw_hover_overlay_on_button(struct theme *theme, cairo_t *cairo, int w, int h) { - assert(icon_buffer); - assert(!*hover_buffer); - - int width = theme->window_button_width; - int height = theme->window_button_height; - - struct icon_drawing_context ctx = copy_icon_buffer(theme, icon_buffer); - *hover_buffer = ctx.buffer; - cairo_t *cairo = ctx.cairo; - /* Overlay (pre-multiplied alpha) */ float overlay_color[4] = { 0.15f, 0.15f, 0.15f, 0.3f}; set_cairo_color(cairo, overlay_color); - int radius = theme->window_button_hover_bg_corner_radius; + int r = theme->window_button_hover_bg_corner_radius; cairo_new_sub_path(cairo); - cairo_arc(cairo, radius, radius, radius, 180 * deg, 270 * deg); - cairo_line_to(cairo, width - radius, 0); - cairo_arc(cairo, width - radius, radius, radius, -90 * deg, 0 * deg); - cairo_line_to(cairo, width, height - radius); - cairo_arc(cairo, width - radius, height - radius, radius, 0 * deg, 90 * deg); - cairo_line_to(cairo, radius, height); - cairo_arc(cairo, radius, height - radius, radius, 90 * deg, 180 * deg); + cairo_arc(cairo, r, r, r, 180 * deg, 270 * deg); + cairo_line_to(cairo, w - r, 0); + cairo_arc(cairo, w - r, r, r, -90 * deg, 0 * deg); + cairo_line_to(cairo, w, h - r); + cairo_arc(cairo, w - r, h - r, r, 0 * deg, 90 * deg); + cairo_line_to(cairo, r, h); + cairo_arc(cairo, r, h - r, r, 90 * deg, 180 * deg); cairo_close_path(cairo); cairo_fill(cairo); - - cairo_surface_flush(cairo_get_target(cairo)); - cairo_destroy(cairo); } +/* Round the buffer for the leftmost button in the titlebar */ static void -create_rounded_buffer(struct theme *theme, enum corner corner, - struct lab_data_buffer **rounded_buffer, - struct lab_data_buffer *icon_buffer) +round_left_corner_button(struct theme *theme, cairo_t *cairo, int w, int h) { - struct icon_drawing_context ctx = copy_icon_buffer(theme, icon_buffer); - *rounded_buffer = ctx.buffer; - cairo_t *cairo = ctx.cairo; - - int width = theme->window_button_width; - int height = theme->window_button_height; - /* - * Round the corner button by cropping the region within the window - * border. See the picture in #2189 for reference. + * Position of the topleft corner of the titlebar relative to the + * leftmost button */ - int margin_x = theme->window_titlebar_padding_width; - int margin_y = (theme->titlebar_height - theme->window_button_height) / 2; - float white[4] = {1, 1, 1, 1}; - struct rounded_corner_ctx rounded_ctx = { - .box = &(struct wlr_box){ - .width = margin_x + width, - .height = margin_y + height, - }, - .radius = rc.corner_radius, - .line_width = theme->border_width, - .fill_color = white, - .border_color = white, - .corner = corner, - }; - struct lab_data_buffer *mask_buffer = rounded_rect(&rounded_ctx); - cairo_set_operator(cairo, CAIRO_OPERATOR_DEST_IN); - cairo_set_source_surface(cairo, mask_buffer->surface, - (corner == LAB_CORNER_TOP_LEFT) ? -margin_x : 0, - -margin_y); - cairo_paint(cairo); + double x = -theme->window_titlebar_padding_width; + double y = -(theme->titlebar_height - theme->window_button_height) / 2; - cairo_surface_flush(cairo_get_target(cairo)); - cairo_destroy(cairo); - wlr_buffer_drop(&mask_buffer->base); + double r = rc.corner_radius - (double)theme->border_width / 2.0; + + cairo_new_sub_path(cairo); + cairo_arc(cairo, x + r, y + r, r, deg * 180, deg * 270); + cairo_line_to(cairo, w, y); + cairo_line_to(cairo, w, h); + cairo_line_to(cairo, x, h); + cairo_close_path(cairo); + + cairo_set_source_rgba(cairo, 1, 1, 1, 1); + cairo_set_operator(cairo, CAIRO_OPERATOR_DEST_IN); + cairo_fill(cairo); +} + +/* Round the buffer for the rightmost button in the titlebar */ +static void +round_right_corner_button(struct theme *theme, cairo_t *cairo, int w, int h) +{ + /* + * Horizontally flip the cairo context so we can reuse + * round_left_corner_button() for rounding the rightmost button. + */ + cairo_scale(cairo, -1, 1); + cairo_translate(cairo, -w, 0); + round_left_corner_button(theme, cairo, w, h); } /* @@ -243,45 +166,42 @@ get_button_filename(char *buf, size_t len, const char *name, const char *postfix static void load_button(struct theme *theme, struct button *b, int active) { - struct lab_data_buffer *(*buttons)[LAB_BS_ALL + 1] = - theme->window[active].buttons; - struct lab_data_buffer **buffer = &buttons[b->type][b->state_set]; + struct lab_img *(*button_imgs)[LAB_BS_ALL + 1] = + theme->window[active].button_imgs; + struct lab_img **img = &button_imgs[b->type][b->state_set]; float *rgba = theme->window[active].button_colors[b->type]; char filename[4096]; - zdrop(buffer); - - int size = theme->window_button_height; - float scale = 1; /* TODO: account for output scale */ + assert(!*img); /* PNG */ get_button_filename(filename, sizeof(filename), b->name, active ? "-active.png" : "-inactive.png"); - img_png_load(filename, buffer, size, scale); + *img = lab_img_load(LAB_IMG_PNG, filename, rgba); #if HAVE_RSVG /* SVG */ - if (!*buffer) { + if (!*img) { get_button_filename(filename, sizeof(filename), b->name, active ? "-active.svg" : "-inactive.svg"); - img_svg_load(filename, buffer, size, scale); + *img = lab_img_load(LAB_IMG_SVG, filename, rgba); } #endif /* XBM */ - if (!*buffer) { + if (!*img) { get_button_filename(filename, sizeof(filename), b->name, ".xbm"); - img_xbm_load(filename, buffer, rgba); + *img = lab_img_load(LAB_IMG_XBM, filename, rgba); } /* * XBM (alternative name) * For example max_hover_toggled instead of max_toggled_hover */ - if (!*buffer && b->alt_name) { + if (!*img && b->alt_name) { get_button_filename(filename, sizeof(filename), b->alt_name, ".xbm"); - img_xbm_load(filename, buffer, rgba); + *img = lab_img_load(LAB_IMG_XBM, filename, rgba); } /* @@ -290,32 +210,36 @@ load_button(struct theme *theme, struct button *b, int active) * Applicable to basic buttons such as max, max_toggled and iconify. * There are no bitmap fallbacks for *_hover icons. */ - if (!*buffer && b->fallback_button) { - img_xbm_from_bitmap(b->fallback_button, buffer, rgba); + if (!*img && b->fallback_button) { + *img = lab_img_load_from_bitmap(b->fallback_button, rgba); } /* * If hover-icons do not exist, add fallbacks by copying the non-hover * variant and then adding an overlay. */ - if (!*buffer && (b->state_set & LAB_BS_HOVERD)) { - uint8_t non_hover_state_set = b->state_set & ~LAB_BS_HOVERD; - create_hover_fallback(theme, buffer, - buttons[b->type][non_hover_state_set]); + if (!*img && (b->state_set & LAB_BS_HOVERD)) { + struct lab_img *non_hover_img = + button_imgs[b->type][b->state_set & ~LAB_BS_HOVERD]; + *img = lab_img_copy(non_hover_img); + lab_img_add_modifier(*img, + draw_hover_overlay_on_button, theme); } /* * If the loaded button is at the corner of the titlebar, also create * rounded variants. */ - uint8_t rounded_state_set = b->state_set | LAB_BS_ROUNDED; + struct lab_img **rounded_img = + &button_imgs[b->type][b->state_set | LAB_BS_ROUNDED]; struct title_button *leftmost_button; wl_list_for_each(leftmost_button, &rc.title_buttons_left, link) { if (leftmost_button->type == b->type) { - create_rounded_buffer(theme, LAB_CORNER_TOP_LEFT, - &buttons[b->type][rounded_state_set], *buffer); + *rounded_img = lab_img_copy(*img); + lab_img_add_modifier(*rounded_img, + round_left_corner_button, theme); } break; } @@ -323,8 +247,9 @@ load_button(struct theme *theme, struct button *b, int active) wl_list_for_each_reverse(rightmost_button, &rc.title_buttons_right, link) { if (rightmost_button->type == b->type) { - create_rounded_buffer(theme, LAB_CORNER_TOP_RIGHT, - &buttons[b->type][rounded_state_set], *buffer); + *rounded_img = lab_img_copy(*img); + lab_img_add_modifier(*rounded_img, + round_right_corner_button, theme); } break; } @@ -1588,6 +1513,12 @@ theme_init(struct theme *theme, struct server *server, const char *theme_name) create_shadows(theme); } +static void destroy_img(struct lab_img **img) +{ + lab_img_destroy(*img); + *img = NULL; +} + void theme_finish(struct theme *theme) { @@ -1595,10 +1526,10 @@ theme_finish(struct theme *theme) type <= LAB_SSD_BUTTON_LAST; type++) { for (uint8_t state_set = 0; state_set <= LAB_BS_ALL; state_set++) { - zdrop(&theme->window[THEME_INACTIVE] - .buttons[type][state_set]); - zdrop(&theme->window[THEME_ACTIVE] - .buttons[type][state_set]); + destroy_img(&theme->window[THEME_INACTIVE] + .button_imgs[type][state_set]); + destroy_img(&theme->window[THEME_ACTIVE] + .button_imgs[type][state_set]); } }