mirror of
https://github.com/labwc/labwc.git
synced 2025-10-29 05:40:24 -04:00
ssd: rework titlebar button rendering
- fix that icons for normal/hovered/rounded buttons are not placed exactly the same position - fix blurry window button icons in scaled outputs This commit introduces lab_img and scaled_img_buffer and uses them for rendering icons in the window titlebar. Now the process of rendering button icons are split into 2 phases: loading with lab_img_load() and creating scene-nodes for them with scaled_img_buffer_create(). This might incur some additional overhead since we no longer preload icon textures, but the rendering of icon only happens for the first window as backing buffers are shared and the overhead won't be noticeable. This commit also simplifies the process of centering icon buffer in the button, by creating icon buffers in a fixed geometry via lab_img_render().
This commit is contained in:
parent
9a3412324d
commit
16dbdc64e5
25 changed files with 647 additions and 391 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
37
include/common/scaled-img-buffer.h
Normal file
37
include/common/scaled-img-buffer.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef LABWC_SCALED_IMG_BUFFER_H
|
||||
#define LABWC_SCALED_IMG_BUFFER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
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 */
|
||||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@
|
|||
#ifndef LABWC_IMG_SVG_H
|
||||
#define LABWC_IMG_SVG_H
|
||||
|
||||
#include <librsvg/rsvg.h>
|
||||
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
78
include/img/img.h
Normal file
78
include/img/img.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef LABWC_IMG_H
|
||||
#define LABWC_IMG_H
|
||||
|
||||
#include <cairo.h>
|
||||
#include <stdint.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
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 */
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
#include <wlr/render/wlr_renderer.h>
|
||||
#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;
|
||||
|
|
|
|||
54
src/buffer.c
54
src/buffer.c
|
|
@ -29,7 +29,6 @@
|
|||
#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;
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
90
src/common/scaled-img-buffer.c
Normal file
90
src/common/scaled-img-buffer.c
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (C) Johan Malm 2023
|
||||
*/
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <cairo.h>
|
||||
#include <png.h>
|
||||
#include <stdbool.h>
|
||||
|
|
@ -10,9 +11,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <wlr/util/log.h>
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,24 +10,18 @@
|
|||
#include <stdlib.h>
|
||||
#include <wlr/util/log.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <drm_fourcc.h>
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
222
src/img/img.c
Normal file
222
src/img/img.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <assert.h>
|
||||
#include <wlr/util/log.h>
|
||||
#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);
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
labwc_sources += files(
|
||||
'img.c',
|
||||
'img-png.c',
|
||||
'img-xbm.c',
|
||||
'img-xpm.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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
227
src/theme.c
227
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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue