mirror of
https://github.com/labwc/labwc.git
synced 2026-02-16 22:05:27 -05: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
|
|
@ -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'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue