mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-06-19 14:33:16 -04:00
opt: Reduce unnecessary text drawing
This commit is contained in:
parent
a515ad9b91
commit
a7acc7f5f3
10 changed files with 308 additions and 194 deletions
|
|
@ -1,19 +1,14 @@
|
|||
#include "text-node.h"
|
||||
|
||||
#include <cairo.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <glib.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/interfaces/wlr_buffer.h>
|
||||
|
||||
// 自定义 wlr_buffer,用于将 Cairo Surface 包装并注入 wlr_scene
|
||||
struct mango_text_buffer {
|
||||
struct wlr_buffer base;
|
||||
cairo_surface_t *surface;
|
||||
};
|
||||
|
||||
// 全局字体描述缓存
|
||||
static GHashTable *font_desc_cache = NULL;
|
||||
|
||||
static PangoFontDescription *get_cached_font_desc(const char *font_desc) {
|
||||
|
|
@ -39,10 +34,8 @@ void mango_text_global_finish(void) {
|
|||
}
|
||||
}
|
||||
|
||||
// wlr_buffer 实现
|
||||
static void text_buffer_destroy(struct wlr_buffer *wlr_buffer) {
|
||||
struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base);
|
||||
cairo_surface_destroy(buf->surface);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
|
|
@ -78,23 +71,26 @@ struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
node->fg_color[0] = data.fg_color[0];
|
||||
node->fg_color[1] = data.fg_color[1];
|
||||
node->fg_color[2] = data.fg_color[2];
|
||||
node->fg_color[3] = data.fg_color[3];
|
||||
node->bg_color[0] = data.bg_color[0];
|
||||
node->bg_color[1] = data.bg_color[1];
|
||||
node->bg_color[2] = data.bg_color[2];
|
||||
node->bg_color[3] = data.bg_color[3];
|
||||
node->border_color[0] = data.border_color[0];
|
||||
node->border_color[1] = data.border_color[1];
|
||||
node->border_color[2] = data.border_color[2];
|
||||
node->border_color[3] = data.border_color[3];
|
||||
memcpy(node->fg_color, data.fg_color, sizeof(node->fg_color));
|
||||
memcpy(node->bg_color, data.bg_color, sizeof(node->bg_color));
|
||||
memcpy(node->border_color, data.border_color, sizeof(node->border_color));
|
||||
node->border_width = data.border_width;
|
||||
node->corner_radius = data.corner_radius;
|
||||
node->padding_x = data.padding_x;
|
||||
node->padding_y = data.padding_y;
|
||||
node->font_desc = data.font_desc ? data.font_desc : "monospace Bold 24";
|
||||
node->font_desc =
|
||||
g_strdup(data.font_desc ? data.font_desc : "monospace Bold 24");
|
||||
|
||||
node->cached_text = NULL;
|
||||
node->cached_scale = -1.0f;
|
||||
node->cached_font_desc = NULL;
|
||||
|
||||
node->measure_surface =
|
||||
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
|
||||
node->measure_cr = cairo_create(node->measure_surface);
|
||||
node->measure_context = pango_cairo_create_context(node->measure_cr);
|
||||
node->measure_layout = pango_layout_new(node->measure_context);
|
||||
node->measure_scale = 1.0f;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
|
@ -103,7 +99,31 @@ void mango_text_node_destroy(struct mango_text_node *node) {
|
|||
if (!node)
|
||||
return;
|
||||
|
||||
if (node->buffer) {
|
||||
wlr_buffer_drop(&node->buffer->base);
|
||||
node->buffer = NULL;
|
||||
}
|
||||
|
||||
if (node->surface) {
|
||||
cairo_surface_destroy(node->surface);
|
||||
node->surface = NULL;
|
||||
}
|
||||
|
||||
if (node->measure_layout)
|
||||
g_object_unref(node->measure_layout);
|
||||
if (node->measure_context)
|
||||
g_object_unref(node->measure_context);
|
||||
if (node->measure_cr)
|
||||
cairo_destroy(node->measure_cr);
|
||||
if (node->measure_surface)
|
||||
cairo_surface_destroy(node->measure_surface);
|
||||
|
||||
wlr_scene_node_destroy(&node->scene_buffer->node);
|
||||
|
||||
g_free(node->font_desc);
|
||||
g_free(node->cached_text);
|
||||
g_free(node->cached_font_desc);
|
||||
|
||||
free(node);
|
||||
}
|
||||
|
||||
|
|
@ -118,28 +138,42 @@ void mango_text_node_set_background(struct mango_text_node *node, float r,
|
|||
}
|
||||
|
||||
void mango_text_node_set_border(struct mango_text_node *node, float r, float g,
|
||||
float b, float a, float width, float radius) {
|
||||
float b, float a, int32_t width,
|
||||
int32_t radius) {
|
||||
if (!node)
|
||||
return;
|
||||
node->border_color[0] = r;
|
||||
node->border_color[1] = g;
|
||||
node->border_color[2] = b;
|
||||
node->border_color[3] = a;
|
||||
node->border_width = width > 0.0f ? width : 0.0f;
|
||||
node->corner_radius = radius >= 0.0f ? radius : 0.0f;
|
||||
node->border_width = width > 0 ? width : 0;
|
||||
node->corner_radius = radius;
|
||||
}
|
||||
|
||||
void mango_text_node_set_padding(struct mango_text_node *node, float pad_x,
|
||||
float pad_y) {
|
||||
void mango_text_node_set_padding(struct mango_text_node *node, int32_t pad_x,
|
||||
int32_t pad_y) {
|
||||
if (!node)
|
||||
return;
|
||||
node->padding_x = pad_x >= 0.0f ? pad_x : 0.0f;
|
||||
node->padding_y = pad_y >= 0.0f ? pad_y : 0.0f;
|
||||
node->padding_x = pad_x >= 0 ? pad_x : 0;
|
||||
node->padding_y = pad_y >= 0 ? pad_y : 0;
|
||||
}
|
||||
|
||||
static void get_text_pixel_size(struct mango_text_node *node, const char *text,
|
||||
float scale, int32_t *out_w, int32_t *out_h) {
|
||||
if (node->measure_scale != scale) {
|
||||
pango_cairo_context_set_resolution(node->measure_context, 96.0 * scale);
|
||||
node->measure_scale = scale;
|
||||
}
|
||||
|
||||
PangoFontDescription *desc = get_cached_font_desc(node->font_desc);
|
||||
pango_layout_set_font_description(node->measure_layout, desc);
|
||||
pango_layout_set_text(node->measure_layout, text, -1);
|
||||
|
||||
pango_layout_get_pixel_size(node->measure_layout, out_w, out_h);
|
||||
}
|
||||
|
||||
static void draw_rounded_rect(cairo_t *cr, double x, double y, double w,
|
||||
double h, double r) {
|
||||
// 使用 Cairo 的标准圆角矩形路径
|
||||
double degrees = G_PI / 180.0;
|
||||
cairo_new_sub_path(cr);
|
||||
cairo_arc(cr, x + w - r, y + r, r, -90 * degrees, 0 * degrees);
|
||||
|
|
@ -150,86 +184,133 @@ static void draw_rounded_rect(cairo_t *cr, double x, double y, double w,
|
|||
}
|
||||
|
||||
void mango_text_node_update(struct mango_text_node *node, const char *text,
|
||||
|
||||
float scale) {
|
||||
if (!node || !text)
|
||||
return;
|
||||
if (scale <= 0.0f)
|
||||
scale = 1.0f;
|
||||
|
||||
const char *font_desc = node->font_desc;
|
||||
PangoFontDescription *desc = get_cached_font_desc(font_desc);
|
||||
|
||||
// 测量文字像素尺寸(无缩放的实际像素)
|
||||
cairo_surface_t *dummy_surface =
|
||||
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
|
||||
cairo_t *dummy_cr = cairo_create(dummy_surface);
|
||||
PangoContext *dummy_ctx = pango_cairo_create_context(dummy_cr);
|
||||
pango_cairo_context_set_resolution(dummy_ctx, 96.0 * scale);
|
||||
PangoLayout *dummy_layout = pango_layout_new(dummy_ctx);
|
||||
pango_layout_set_font_description(dummy_layout, desc);
|
||||
pango_layout_set_text(dummy_layout, text, -1);
|
||||
|
||||
int text_pixel_w, text_pixel_h;
|
||||
pango_layout_get_pixel_size(dummy_layout, &text_pixel_w, &text_pixel_h);
|
||||
|
||||
g_object_unref(dummy_layout);
|
||||
g_object_unref(dummy_ctx);
|
||||
cairo_destroy(dummy_cr);
|
||||
cairo_surface_destroy(dummy_surface);
|
||||
|
||||
if (text_pixel_w <= 0 || text_pixel_h <= 0) {
|
||||
wlr_scene_buffer_set_buffer(node->scene_buffer, NULL);
|
||||
node->logical_width = 0;
|
||||
node->logical_height = 0;
|
||||
/* 脏检查 */
|
||||
if (node->cached_scale == scale && node->cached_font_desc &&
|
||||
strcmp(node->cached_font_desc, node->font_desc) == 0 &&
|
||||
node->cached_text && strcmp(node->cached_text, text) == 0 &&
|
||||
memcmp(node->cached_fg_color, node->fg_color, sizeof(node->fg_color)) ==
|
||||
0 &&
|
||||
memcmp(node->cached_bg_color, node->bg_color, sizeof(node->bg_color)) ==
|
||||
0 &&
|
||||
memcmp(node->cached_border_color, node->border_color,
|
||||
sizeof(node->border_color)) == 0 &&
|
||||
node->cached_border_width == node->border_width &&
|
||||
node->cached_corner_radius == node->corner_radius &&
|
||||
node->cached_padding_x == node->padding_x &&
|
||||
node->cached_padding_y == node->padding_y) {
|
||||
return;
|
||||
}
|
||||
|
||||
float logical_text_w = text_pixel_w / scale;
|
||||
float logical_text_h = text_pixel_h / scale;
|
||||
/* 更新缓存 */
|
||||
g_free(node->cached_text);
|
||||
node->cached_text = g_strdup(text);
|
||||
g_free(node->cached_font_desc);
|
||||
node->cached_font_desc = g_strdup(node->font_desc);
|
||||
node->cached_scale = scale;
|
||||
memcpy(node->cached_fg_color, node->fg_color, sizeof(node->fg_color));
|
||||
memcpy(node->cached_bg_color, node->bg_color, sizeof(node->bg_color));
|
||||
memcpy(node->cached_border_color, node->border_color,
|
||||
sizeof(node->border_color));
|
||||
node->cached_border_width = node->border_width;
|
||||
node->cached_corner_radius = node->corner_radius;
|
||||
node->cached_padding_x = node->padding_x;
|
||||
node->cached_padding_y = node->padding_y;
|
||||
|
||||
float pad_x = node->padding_x;
|
||||
float pad_y = node->padding_y;
|
||||
float box_logical_w = logical_text_w + 2.0f * pad_x;
|
||||
float box_logical_h = logical_text_h + 2.0f * pad_y;
|
||||
int32_t text_pixel_w, text_pixel_h;
|
||||
get_text_pixel_size(node, text, scale, &text_pixel_w, &text_pixel_h);
|
||||
|
||||
float border = node->border_width;
|
||||
bool draw_border = (border > 0.0f) && (node->border_color[3] > 0.0f);
|
||||
bool draw_bg = (node->bg_color[3] > 0.0f);
|
||||
if (text_pixel_w <= 0 || text_pixel_h <= 0) {
|
||||
wlr_scene_buffer_set_buffer(node->scene_buffer, NULL);
|
||||
if (node->buffer) {
|
||||
wlr_buffer_drop(&node->buffer->base);
|
||||
node->buffer = NULL;
|
||||
}
|
||||
if (node->surface) {
|
||||
cairo_surface_destroy(node->surface);
|
||||
node->surface = NULL;
|
||||
}
|
||||
node->logical_width = 0;
|
||||
node->logical_height = 0;
|
||||
wlr_scene_buffer_set_dest_size(node->scene_buffer, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int surface_pixel_w = (int)((box_logical_w + 2.0f * border) * scale + 0.5f);
|
||||
int surface_pixel_h = (int)((box_logical_h + 2.0f * border) * scale + 0.5f);
|
||||
/* 逻辑尺寸:文本 + 内边距 + 边框(整数计算) */
|
||||
int32_t logical_text_w = (int32_t)(text_pixel_w / scale + 0.5f);
|
||||
int32_t logical_text_h = (int32_t)(text_pixel_h / scale + 0.5f);
|
||||
int32_t box_logical_w = logical_text_w + 2 * node->padding_x;
|
||||
int32_t box_logical_h = logical_text_h + 2 * node->padding_y;
|
||||
|
||||
if (surface_pixel_w < 1)
|
||||
surface_pixel_w = 1;
|
||||
if (surface_pixel_h < 1)
|
||||
surface_pixel_h = 1;
|
||||
/* 加上边框后,乘以 scale 得到物理像素(表面尺寸),向上取整 */
|
||||
int32_t required_pixel_w =
|
||||
(int32_t)((box_logical_w + 2 * node->border_width) * scale + 0.5f);
|
||||
int32_t required_pixel_h =
|
||||
(int32_t)((box_logical_h + 2 * node->border_width) * scale + 0.5f);
|
||||
if (required_pixel_w < 1)
|
||||
required_pixel_w = 1;
|
||||
if (required_pixel_h < 1)
|
||||
required_pixel_h = 1;
|
||||
|
||||
cairo_surface_t *surface = cairo_image_surface_create(
|
||||
CAIRO_FORMAT_ARGB32, surface_pixel_w, surface_pixel_h);
|
||||
cairo_t *cr = cairo_create(surface);
|
||||
bool surface_size_changed = (!node->surface) ||
|
||||
(node->surface_pixel_w != required_pixel_w) ||
|
||||
(node->surface_pixel_h != required_pixel_h);
|
||||
|
||||
// 清空为全透明
|
||||
if (surface_size_changed) {
|
||||
if (node->buffer) {
|
||||
wlr_buffer_drop(&node->buffer->base);
|
||||
node->buffer = NULL;
|
||||
}
|
||||
if (node->surface) {
|
||||
cairo_surface_destroy(node->surface);
|
||||
node->surface = NULL;
|
||||
}
|
||||
|
||||
node->surface = cairo_image_surface_create(
|
||||
CAIRO_FORMAT_ARGB32, required_pixel_w, required_pixel_h);
|
||||
node->surface_pixel_w = required_pixel_w;
|
||||
node->surface_pixel_h = required_pixel_h;
|
||||
}
|
||||
|
||||
cairo_t *cr = cairo_create(node->surface);
|
||||
|
||||
/* 清空为透明 */
|
||||
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint(cr);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||||
|
||||
double bg_x = border * scale;
|
||||
double bg_y = border * scale;
|
||||
/* 计算背景矩形(物理像素) */
|
||||
double border = node->border_width * scale;
|
||||
double bg_x = border;
|
||||
double bg_y = border;
|
||||
double bg_w = box_logical_w * scale;
|
||||
double bg_h = box_logical_h * scale;
|
||||
|
||||
double radius = node->corner_radius * scale;
|
||||
|
||||
if (radius < 0) {
|
||||
/* 圆角半径(物理像素) */
|
||||
double radius;
|
||||
if (node->corner_radius < 0) {
|
||||
/* 负数表示自动取半宽/半高作为圆角 */
|
||||
radius = (bg_w < bg_h ? bg_w : bg_h) / 2.0;
|
||||
} else {
|
||||
radius = node->corner_radius * scale;
|
||||
}
|
||||
/* 限制最大圆角 */
|
||||
if (radius > bg_w / 2.0)
|
||||
radius = bg_w / 2.0;
|
||||
if (radius > bg_h / 2.0)
|
||||
radius = bg_h / 2.0;
|
||||
|
||||
bool draw_bg = (node->bg_color[3] > 0.0f);
|
||||
bool draw_border =
|
||||
(node->border_width > 0) && (node->border_color[3] > 0.0f);
|
||||
|
||||
/* 绘制背景 */
|
||||
if (draw_bg) {
|
||||
cairo_set_source_rgba(cr, node->bg_color[0], node->bg_color[1],
|
||||
node->bg_color[2], node->bg_color[3]);
|
||||
|
|
@ -242,12 +323,16 @@ void mango_text_node_update(struct mango_text_node *node, const char *text,
|
|||
}
|
||||
}
|
||||
|
||||
/* 绘制文本 */
|
||||
cairo_save(cr);
|
||||
cairo_translate(cr, (border + pad_x) * scale, (border + pad_y) * scale);
|
||||
double text_x = (node->border_width + node->padding_x) * scale;
|
||||
double text_y = (node->border_width + node->padding_y) * scale;
|
||||
cairo_translate(cr, text_x, text_y);
|
||||
|
||||
PangoContext *ctx = pango_cairo_create_context(cr);
|
||||
pango_cairo_context_set_resolution(ctx, 96.0 * scale);
|
||||
PangoLayout *layout = pango_layout_new(ctx);
|
||||
PangoFontDescription *desc = get_cached_font_desc(node->font_desc);
|
||||
pango_layout_set_font_description(layout, desc);
|
||||
pango_layout_set_text(layout, text, -1);
|
||||
|
||||
|
|
@ -259,50 +344,51 @@ void mango_text_node_update(struct mango_text_node *node, const char *text,
|
|||
g_object_unref(ctx);
|
||||
cairo_restore(cr);
|
||||
|
||||
/* 绘制边框 */
|
||||
if (draw_border) {
|
||||
cairo_set_source_rgba(cr, node->border_color[0], node->border_color[1],
|
||||
node->border_color[2], node->border_color[3]);
|
||||
cairo_set_line_width(cr, border * scale);
|
||||
cairo_set_line_width(cr, border);
|
||||
|
||||
double half_lw = border * scale * 0.5;
|
||||
double half_lw = border * 0.5;
|
||||
double bx = bg_x - half_lw;
|
||||
double by = bg_y - half_lw;
|
||||
double bw = bg_w + border * scale;
|
||||
double bh = bg_h + border * scale;
|
||||
double bw = bg_w + border;
|
||||
double bh = bg_h + border;
|
||||
|
||||
if (radius > 0.0) {
|
||||
double outer_radius = radius + half_lw;
|
||||
if (outer_radius < 0.0)
|
||||
outer_radius = 0.0;
|
||||
draw_rounded_rect(cr, bx, by, bw, bh, outer_radius);
|
||||
cairo_stroke(cr);
|
||||
} else {
|
||||
cairo_rectangle(cr, bx, by, bw, bh);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
cairo_surface_flush(surface);
|
||||
cairo_surface_flush(node->surface);
|
||||
cairo_destroy(cr);
|
||||
|
||||
/* 更新 wlr_buffer */
|
||||
if (node->buffer) {
|
||||
wlr_buffer_drop(&node->buffer->base);
|
||||
node->buffer = NULL;
|
||||
}
|
||||
|
||||
// 包装成 wlr_buffer
|
||||
struct mango_text_buffer *buf = calloc(1, sizeof(*buf));
|
||||
if (!buf) {
|
||||
cairo_surface_destroy(surface);
|
||||
cairo_destroy(cr);
|
||||
return;
|
||||
}
|
||||
wlr_buffer_init(&buf->base, &text_buffer_impl, surface_pixel_w,
|
||||
surface_pixel_h);
|
||||
buf->surface = surface;
|
||||
wlr_buffer_init(&buf->base, &text_buffer_impl, node->surface_pixel_w,
|
||||
node->surface_pixel_h);
|
||||
buf->surface = node->surface;
|
||||
node->buffer = buf;
|
||||
|
||||
wlr_scene_buffer_set_buffer(node->scene_buffer, &buf->base);
|
||||
wlr_buffer_drop(&buf->base);
|
||||
|
||||
// 最终逻辑大小 = 背景框逻辑尺寸 + 边框逻辑尺寸
|
||||
node->logical_width = (int)(box_logical_w + 2.0f * border);
|
||||
node->logical_height = (int)(box_logical_h + 2.0f * border);
|
||||
node->logical_width = box_logical_w + 2 * node->border_width;
|
||||
node->logical_height = box_logical_h + 2 * node->border_width;
|
||||
wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width,
|
||||
node->logical_height);
|
||||
|
||||
cairo_destroy(cr);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue