Skip to content

Commit

Permalink
Shape: Offscreen alpha mask with render callback
Browse files Browse the repository at this point in the history
Wrap rendering of alpha shapes in a class caching the picture instances
by comparing the render function and its data.

Use this in EmojiButton to avoid countless instances of the same
triangle picture.
  • Loading branch information
Zirias committed Jul 18, 2024
1 parent 0683c72 commit 164847a
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 61 deletions.
139 changes: 78 additions & 61 deletions src/bin/xmoji/emojibutton.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "flowgrid.h"
#include "flyout.h"
#include "shape.h"
#include "textlabel.h"
#include "textrenderer.h"
#include "unistr.h"
Expand All @@ -24,23 +25,18 @@ struct EmojiButton
Object base;
FlowGrid *flowgrid;
Flyout *flyout;
xcb_pixmap_t pixmap;
xcb_render_picture_t pic;
Shape *triangle;
xcb_render_picture_t pen;
Color pencolor;
Size trianglesize;
int selected;
};

static void destroy(void *obj)
{
EmojiButton *self = obj;
if (self->pen)
{
xcb_connection_t *c = X11Adapter_connection();
xcb_render_free_picture(c, self->pen);
xcb_render_free_picture(c, self->pic);
xcb_free_pixmap(c, self->pixmap);
}
if (self->pen) xcb_render_free_picture(X11Adapter_connection(), self->pen);
Shape_destroy(self->triangle);
free(self);
}

Expand Down Expand Up @@ -85,62 +81,70 @@ static void renderCallback(void *ctx, TextRenderer *renderer)
}
}

static void prerender(EmojiButton *self, Size newSize)
static xcb_render_picture_t renderTriangle(xcb_render_picture_t ownerpic,
const void *data)
{
if (!self->flyout) return;

const Size *sz = data;
xcb_connection_t *c = X11Adapter_connection();
xcb_render_picture_t picture = Widget_picture(self);
xcb_screen_t *s = X11Adapter_screen();

if (!self->pen)
{
xcb_pixmap_t p = xcb_generate_id(c);
CHECK(xcb_create_pixmap(c, 24, p, X11Adapter_screen()->root, 1, 1),
"Cannot create pen pixmap for 0x%x", (unsigned)picture);
self->pen = xcb_generate_id(c);
uint32_t repeat = XCB_RENDER_REPEAT_NORMAL;
CHECK(xcb_render_create_picture(c, self->pen, p,
X11Adapter_rgbformat(), XCB_RENDER_CP_REPEAT, &repeat),
"Cannot create pen for 0x%x", (unsigned)picture);
Color fill = 0xffffffff;
xcb_rectangle_t rect = {0, 0, 1, 1};
CHECK(xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_OVER,
self->pen, Color_xcb(fill), 1, &rect),
"Canot colorize pen for 0x%x", (unsigned)picture);
xcb_free_pixmap(c, p);
}
if (self->pic)
{
xcb_render_free_picture(c, self->pic);
xcb_free_pixmap(c, self->pixmap);
}
self->pixmap = xcb_generate_id(c);
CHECK(xcb_create_pixmap(c, 8, self->pixmap,
X11Adapter_screen()->root,
newSize.width, newSize.height),
"Cannot create triangle pixmap for 0x%x",
(unsigned)picture);
self->pic = xcb_generate_id(c);
CHECK(xcb_render_create_picture(c, self->pic, self->pixmap,
X11Adapter_alphaformat(), 0, 0),
"Cannot create triangle picture for 0x%x",
(unsigned)picture);
Color clear = 0;
xcb_rectangle_t rect = { 0, 0, newSize.width, newSize.height };
xcb_pixmap_t tmp = xcb_generate_id(c);
CHECK(xcb_create_pixmap(c, 8, tmp, s->root, 1, 1),
"Cannot create triangle pen pixmap for 0x%x", (unsigned)ownerpic);
xcb_render_picture_t pen = xcb_generate_id(c);
uint32_t repeat = XCB_RENDER_REPEAT_NORMAL;
CHECK(xcb_render_create_picture(c, pen, tmp, X11Adapter_alphaformat(),
XCB_RENDER_CP_REPEAT, &repeat),
"Cannot create triangle pen for 0x%x", (unsigned)ownerpic);
xcb_free_pixmap(c, tmp);
Color color = 0xffffffff;
xcb_rectangle_t rect = {0, 0, 1, 1};
CHECK(xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_OVER,
pen, Color_xcb(color), 1, &rect),
"Cannot colorize triangle pen for 0x%x", (unsigned)ownerpic);

tmp = xcb_generate_id(c);
CHECK(xcb_create_pixmap(c, 8, tmp, s->root, sz->width, sz->height),
"Cannot create triangle pixmap for 0x%x", (unsigned)ownerpic);
xcb_render_picture_t pic = xcb_generate_id(c);
CHECK(xcb_render_create_picture(c, pic, tmp, X11Adapter_alphaformat(),
0, 0),
"Cannot create triangle picture for 0x%x", (unsigned)ownerpic);
xcb_free_pixmap(c, tmp);
color = 0;
rect.width = sz->width;
rect.height = sz->height;
CHECK(xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_SRC,
self->pic, Color_xcb(clear), 1, &rect),
"Cannot clear triangle picture for 0x%x",
(unsigned)picture);
uint32_t x = newSize.width << 16;
uint32_t y = newSize.height << 16;
pic, Color_xcb(color), 1, &rect),
"Cannot clear triangle picture for 0x%x", (unsigned)ownerpic);
uint32_t x = sz->width << 16;
uint32_t y = sz->height << 16;
xcb_render_triangle_t triangle = {
{ x - (newSize.width << 14), y },
{ x - (sz->width << 14), y },
{ x, y },
{ x, y - (newSize.height << 14) }
{ x, y - (sz->height << 14) }
};
CHECK(xcb_render_triangles(c, XCB_RENDER_PICT_OP_OVER,
self->pen, self->pic, 0, 0, 0, 1, &triangle),
"Cannot render fly-out indicator for 0x%x", (unsigned)picture);
pen, pic, 0, 0, 0, 1, &triangle),
"Cannot render triangle for 0x%x", (unsigned)ownerpic);
xcb_render_free_picture(c, pen);

return pic;
}

static void prerender(EmojiButton *self, Size newSize)
{
if (self->flyout && newSize.width && newSize.height
&& newSize.width != self->trianglesize.width
&& newSize.height != self->trianglesize.height)
{
self->trianglesize = newSize;
Shape *triangle = Shape_create(renderTriangle,
sizeof self->trianglesize, &self->trianglesize);
if (self->triangle) Shape_destroy(self->triangle);
Shape_render(triangle, Widget_picture(self));
self->triangle = triangle;
}
}

static int draw(void *obj, xcb_render_picture_t picture)
Expand All @@ -151,6 +155,19 @@ static int draw(void *obj, xcb_render_picture_t picture)
if (rc == 0 && picture && self->flyout)
{
xcb_connection_t *c = X11Adapter_connection();
if (!self->pen)
{
xcb_pixmap_t tmp = xcb_generate_id(c);
CHECK(xcb_create_pixmap(c, 24, tmp,
X11Adapter_screen()->root, 1, 1),
"Cannot create pen pixmap for 0x%x", (unsigned)picture);
self->pen = xcb_generate_id(c);
uint32_t repeat = XCB_RENDER_REPEAT_NORMAL;
CHECK(xcb_render_create_picture(c, self->pen, tmp,
X11Adapter_rgbformat(), XCB_RENDER_CP_REPEAT, &repeat),
"Cannot create pen for 0x%x", (unsigned)picture);
xcb_free_pixmap(c, tmp);
}
Rect geom = Widget_geometry(self);
if (!self->pencolor) prerender(self, geom.size);
Color pencolor = Widget_color(self, COLOR_ACTIVE);
Expand All @@ -163,7 +180,7 @@ static int draw(void *obj, xcb_render_picture_t picture)
"Canot colorize pen for 0x%x", (unsigned)picture);
}
CHECK(xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, self->pen,
self->pic, picture, 0, 0, 0, 0,
Shape_picture(self->triangle), picture, 0, 0, 0, 0,
geom.pos.x, geom.pos.y, geom.size.width, geom.size.height),
"Cannot composite fly-out indicator on 0x%x",
(unsigned)picture);
Expand Down Expand Up @@ -210,7 +227,7 @@ static void sizeChanged(void *receiver, void *sender, void *args)
EmojiButton *self = receiver;
SizeChangedEventArgs *ea = args;

if (ea->newSize.width && ea->newSize.height) prerender(self, ea->newSize);
prerender(self, ea->newSize);
}

EmojiButton *EmojiButton_createBase(void *derived,
Expand All @@ -220,10 +237,10 @@ EmojiButton *EmojiButton_createBase(void *derived,
CREATEBASE(Button, name, parent);
self->flowgrid = 0;
self->flyout = 0;
self->pixmap = 0;
self->pic = 0;
self->triangle = 0;
self->pen = 0;
self->pencolor = 0;
self->trianglesize = (Size){0, 0};
self->selected = 0;

Button_setBorderWidth(self, 0);
Expand Down
70 changes: 70 additions & 0 deletions src/bin/xmoji/shape.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include "shape.h"

#include "x11adapter.h"

#include <poser/core.h>
#include <stdlib.h>
#include <string.h>

struct Shape
{
ShapeRenderer renderer;
const void *data;
size_t datasz;
xcb_render_picture_t picture;
unsigned refcnt;
};

static PSC_List *shapes;

Shape *Shape_create(ShapeRenderer renderer, size_t datasz, const void *data)
{
if (!shapes) shapes = PSC_List_create();
for (size_t i = 0; i < PSC_List_size(shapes); ++i)
{
Shape *shape = PSC_List_at(shapes, i);
if (shape->renderer == renderer && shape->datasz == datasz
&& !memcmp(shape->data, data, datasz))
{
++shape->refcnt;
return shape;
}
}
Shape *self = PSC_malloc(sizeof *self);
self->renderer = renderer;
self->data = data;
self->datasz = datasz;
self->picture = 0;
self->refcnt = 1;
PSC_List_append(shapes, self, 0);
return self;
}

void Shape_render(Shape *self, xcb_render_picture_t ownerpic)
{
if (self->picture) return;
self->picture = self->renderer(ownerpic, self->data);
}

xcb_render_picture_t Shape_picture(const Shape *self)
{
return self->picture;
}

void Shape_destroy(Shape *self)
{
if (!self) return;
if (--self->refcnt) return;
PSC_List_remove(shapes, self);
if (!PSC_List_size(shapes))
{
PSC_List_destroy(shapes);
shapes = 0;
}
if (self->picture)
{
xcb_render_free_picture(X11Adapter_connection(), self->picture);
}
free(self);
}

18 changes: 18 additions & 0 deletions src/bin/xmoji/shape.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef XMOJI_SHAPE_H
#define XMOJI_SHAPE_H

#include <poser/decl.h>
#include <xcb/render.h>

C_CLASS_DECL(Shape);

typedef xcb_render_picture_t (*ShapeRenderer)(
xcb_render_picture_t ownerpic, const void *data);

Shape *Shape_create(ShapeRenderer renderer,
size_t datasz, const void *data) ATTR_RETNONNULL;
void Shape_render(Shape *self, xcb_render_picture_t ownerpic) CMETHOD;
xcb_render_picture_t Shape_picture(const Shape *self) CMETHOD ATTR_PURE;
void Shape_destroy(Shape *self);

#endif
1 change: 1 addition & 0 deletions src/bin/xmoji/xmoji.mk
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ xmoji_MODULES= button \
object \
pixmap \
scrollbox \
shape \
surface \
tabbox \
textbox \
Expand Down

0 comments on commit 164847a

Please sign in to comment.