330 lines
12 KiB
C
330 lines
12 KiB
C
#define WRASTER_NO_XLIB /* Wayland-only file: no libX11 deps */
|
|
|
|
|
|
#include "WINGsP.h"
|
|
#include <pixman.h>
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
|
|
typedef struct {
|
|
W_Screen *scr;
|
|
} RenderCtxWL;
|
|
|
|
static void *
|
|
render_wl_screen_init(W_Screen *scr)
|
|
{
|
|
RenderCtxWL *ctx = wmalloc(sizeof(*ctx));
|
|
ctx->scr = scr;
|
|
return ctx;
|
|
}
|
|
|
|
static void
|
|
render_wl_screen_fini(void *rctx)
|
|
{
|
|
wfree(rctx);
|
|
}
|
|
|
|
/* Resolve a WMDrawable to a pixman_image_t*.
|
|
* Synthetic IDs: 0x10000000-0x1FFFFFFF = WM frame_buf windows,
|
|
* 0x20000000-0x2FFFFFFF = WINGs view windows.
|
|
* Both registered via W_RegisterBacking. */
|
|
extern void *W_GetViewBacking(unsigned long id);
|
|
static inline pixman_image_t *
|
|
resolve_drawable(WMDrawable d)
|
|
{
|
|
if ((unsigned long)d >= 0x10000000 && (unsigned long)d < 0x30000000)
|
|
return (pixman_image_t *)W_GetViewBacking((unsigned long)d);
|
|
return (pixman_image_t *)d;
|
|
}
|
|
|
|
static WMDrawable
|
|
render_wl_pixmap_create(void *rctx, unsigned int width, unsigned int height, int depth)
|
|
{
|
|
(void)rctx;
|
|
pixman_format_code_t fmt = (depth == 1) ? PIXMAN_a1 : PIXMAN_a8r8g8b8;
|
|
pixman_image_t *img = pixman_image_create_bits(fmt, width, height, NULL, 0);
|
|
return (WMDrawable)img;
|
|
}
|
|
|
|
static void
|
|
render_wl_pixmap_free(void *rctx, WMDrawable pm)
|
|
{
|
|
(void)rctx;
|
|
if (pm)
|
|
pixman_image_unref((pixman_image_t *)pm);
|
|
}
|
|
|
|
extern W_View *W_GetViewForWindow(WNativeWindow id);
|
|
extern void (*W_ViewCommitBackingFn)(unsigned long id);
|
|
extern int wl_redisplay_depth;
|
|
|
|
static void
|
|
render_wl_pixmap_copy(void *rctx, WMDrawable src, WMDrawable dst,
|
|
int sx, int sy, unsigned int w, unsigned int h, int dx, int dy)
|
|
{
|
|
(void)rctx;
|
|
pixman_image_t *s = (pixman_image_t *)src;
|
|
pixman_image_t *d = resolve_drawable(dst);
|
|
if (!d) return;
|
|
pixman_image_composite32(PIXMAN_OP_SRC, s, NULL, d,
|
|
sx, sy, 0, 0, dx, dy, w, h);
|
|
|
|
/* If dst is a view backing (DOUBLE_BUFFER copy-to-window), composite
|
|
* up through the parent chain so the change becomes visible. */
|
|
if ((unsigned long)dst >= 0x10000000 && (unsigned long)dst < 0x30000000) {
|
|
W_View *view = W_GetViewForWindow((WNativeWindow)(unsigned long)dst);
|
|
if (view && view->flags.alreadyDead) return;
|
|
while (view && view->parent && !view->flags.alreadyDead) {
|
|
pixman_image_t *vs = (pixman_image_t *)W_GetViewBacking(view->window);
|
|
pixman_image_t *pd = (pixman_image_t *)W_GetViewBacking(view->parent->window);
|
|
if (vs && pd) {
|
|
pixman_image_composite32(PIXMAN_OP_OVER, vs, NULL, pd,
|
|
0, 0, 0, 0,
|
|
view->pos.x, view->pos.y,
|
|
view->size.width, view->size.height);
|
|
}
|
|
view = view->parent;
|
|
}
|
|
/* Commit the toplevel surface */
|
|
if (view && !view->flags.alreadyDead && W_ViewCommitBackingFn)
|
|
W_ViewCommitBackingFn((unsigned long)view->window);
|
|
}
|
|
}
|
|
|
|
static inline pixman_color_t
|
|
wm_color_to_pixman(WMColor *color)
|
|
{
|
|
pixman_color_t pc;
|
|
pc.red = color->color.red;
|
|
pc.green = color->color.green;
|
|
pc.blue = color->color.blue;
|
|
pc.alpha = color->alpha ? color->alpha : 0xFFFF;
|
|
return pc;
|
|
}
|
|
|
|
static void
|
|
render_wl_draw_line(void *rctx, WMDrawable d, WMColor *color,
|
|
int x1, int y1, int x2, int y2)
|
|
{
|
|
(void)rctx;
|
|
pixman_image_t *dst = resolve_drawable(d); if (!dst) return;
|
|
pixman_color_t pc = wm_color_to_pixman(color);
|
|
pixman_image_t *fill = pixman_image_create_solid_fill(&pc);
|
|
|
|
/* Bresenham for arbitrary lines; fast paths for h/v */
|
|
int dx = abs(x2 - x1), dy = abs(y2 - y1);
|
|
int sx = x1 < x2 ? 1 : -1;
|
|
int sy = y1 < y2 ? 1 : -1;
|
|
int err = dx - dy;
|
|
|
|
while (1) {
|
|
pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst,
|
|
0, 0, 0, 0, x1, y1, 1, 1);
|
|
if (x1 == x2 && y1 == y2) break;
|
|
int e2 = 2 * err;
|
|
if (e2 > -dy) { err -= dy; x1 += sx; }
|
|
if (e2 < dx) { err += dx; y1 += sy; }
|
|
}
|
|
pixman_image_unref(fill);
|
|
}
|
|
|
|
static void
|
|
render_wl_draw_rect(void *rctx, WMDrawable d, WMColor *color,
|
|
int x, int y, unsigned int w, unsigned int h)
|
|
{
|
|
(void)rctx;
|
|
pixman_image_t *dst = resolve_drawable(d); if (!dst) return;
|
|
pixman_color_t pc = wm_color_to_pixman(color);
|
|
|
|
/* top */
|
|
pixman_image_fill_rectangles(PIXMAN_OP_OVER, dst, &pc, 1,
|
|
&(pixman_rectangle16_t){x, y, w + 1, 1});
|
|
/* bottom */
|
|
pixman_image_fill_rectangles(PIXMAN_OP_OVER, dst, &pc, 1,
|
|
&(pixman_rectangle16_t){x, y + h, w + 1, 1});
|
|
/* left */
|
|
pixman_image_fill_rectangles(PIXMAN_OP_OVER, dst, &pc, 1,
|
|
&(pixman_rectangle16_t){x, y, 1, h + 1});
|
|
/* right */
|
|
pixman_image_fill_rectangles(PIXMAN_OP_OVER, dst, &pc, 1,
|
|
&(pixman_rectangle16_t){x + w, y, 1, h + 1});
|
|
}
|
|
|
|
static void
|
|
render_wl_fill_rect(void *rctx, WMDrawable d, WMColor *color,
|
|
int x, int y, unsigned int w, unsigned int h)
|
|
{
|
|
(void)rctx;
|
|
pixman_image_t *dst = resolve_drawable(d); if (!dst) return;
|
|
pixman_color_t pc = wm_color_to_pixman(color);
|
|
pixman_image_fill_rectangles(PIXMAN_OP_OVER, dst, &pc, 1,
|
|
&(pixman_rectangle16_t){x, y, w, h});
|
|
/* Outside a full redisplay cycle, propagate dirty region up the
|
|
* view tree and commit the toplevel so the change becomes visible
|
|
* immediately (e.g. menu highlight on pointer motion). */
|
|
if (wl_redisplay_depth == 0 && (unsigned long)d >= 0x10000000
|
|
&& (unsigned long)d < 0x30000000) {
|
|
W_View *v = W_GetViewForWindow((WNativeWindow)(unsigned long)d);
|
|
if (v && !v->flags.alreadyDead) {
|
|
while (v && v->parent && !v->flags.alreadyDead) {
|
|
pixman_image_t *vs = W_GetViewBacking(v->window);
|
|
pixman_image_t *pd = W_GetViewBacking(v->parent->window);
|
|
if (vs && pd)
|
|
pixman_image_composite32(PIXMAN_OP_OVER, vs, NULL, pd,
|
|
0, 0, 0, 0,
|
|
v->pos.x, v->pos.y,
|
|
v->size.width, v->size.height);
|
|
v = v->parent;
|
|
}
|
|
if (v && !v->flags.alreadyDead && W_ViewCommitBackingFn)
|
|
W_ViewCommitBackingFn((unsigned long)v->window);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
render_wl_fill_stippled(void *rctx, WMDrawable d, WMColor *color,
|
|
int x, int y, unsigned int w, unsigned int h)
|
|
{
|
|
(void)rctx;
|
|
pixman_image_t *dst = resolve_drawable(d); if (!dst) return;
|
|
pixman_color_t pc = wm_color_to_pixman(color);
|
|
pixman_image_t *fill = pixman_image_create_solid_fill(&pc);
|
|
|
|
/* Draw every other pixel in a checkerboard pattern */
|
|
for (unsigned int row = 0; row < h; row++) {
|
|
for (unsigned int col = (row & 1); col < w; col += 2) {
|
|
pixman_image_composite32(PIXMAN_OP_OVER, fill, NULL, dst,
|
|
0, 0, 0, 0, x + col, y + row, 1, 1);
|
|
}
|
|
}
|
|
pixman_image_unref(fill);
|
|
}
|
|
|
|
static void
|
|
render_wl_composite_pixmap(void *rctx, WMDrawable dst, W_Pixmap *src,
|
|
int sx, int sy, unsigned int w, unsigned int h,
|
|
int dx, int dy)
|
|
{
|
|
(void)rctx;
|
|
pixman_image_t *d = resolve_drawable(dst); if (!d) return;
|
|
pixman_image_t *s = (pixman_image_t *)(uintptr_t)src->pixmap;
|
|
pixman_image_t *m = src->mask ? (pixman_image_t *)(uintptr_t)src->mask : NULL;
|
|
|
|
pixman_image_composite32(PIXMAN_OP_OVER, s, m, d,
|
|
sx, sy, sx, sy, dx, dy, w, h);
|
|
}
|
|
|
|
static void
|
|
render_wl_fill_polygon(void *rctx, WMDrawable d, WMColor *color,
|
|
void *points_v, int npoints)
|
|
{
|
|
WMShortPoint *points = points_v;
|
|
(void)rctx;
|
|
if (npoints < 3) return;
|
|
pixman_image_t *dst = resolve_drawable(d); if (!dst) return;
|
|
pixman_color_t pc = wm_color_to_pixman(color);
|
|
|
|
/* Find bounding box */
|
|
int minY = points[0].y, maxY = points[0].y;
|
|
for (int i = 1; i < npoints; i++) {
|
|
if (points[i].y < minY) minY = points[i].y;
|
|
if (points[i].y > maxY) maxY = points[i].y;
|
|
}
|
|
|
|
/* Scanline fill for convex polygon */
|
|
for (int y = minY; y <= maxY; y++) {
|
|
int minX = 32767, maxX = -32768;
|
|
for (int i = 0; i < npoints; i++) {
|
|
int j = (i + 1) % npoints;
|
|
int y0 = points[i].y, y1 = points[j].y;
|
|
int x0 = points[i].x, x1 = points[j].x;
|
|
if ((y0 <= y && y1 > y) || (y1 <= y && y0 > y)) {
|
|
int x = x0 + (y - y0) * (x1 - x0) / (y1 - y0);
|
|
if (x < minX) minX = x;
|
|
if (x > maxX) maxX = x;
|
|
}
|
|
}
|
|
if (minX <= maxX) {
|
|
pixman_image_fill_rectangles(PIXMAN_OP_OVER, dst, &pc, 1,
|
|
&(pixman_rectangle16_t){minX, y, maxX - minX + 1, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
render_wl_fill_arc(void *rctx, WMDrawable d, WMColor *color,
|
|
int x, int y, unsigned int w, unsigned int h,
|
|
int angle1, int angle2)
|
|
{
|
|
(void)rctx;
|
|
pixman_image_t *dst = resolve_drawable(d); if (!dst) return;
|
|
pixman_color_t pc = wm_color_to_pixman(color);
|
|
|
|
/* X11 angles are in 64ths of a degree */
|
|
double a1 = angle1 / (64.0 * 180.0) * 3.14159265358979;
|
|
double a2 = (angle1 + angle2) / (64.0 * 180.0) * 3.14159265358979;
|
|
double cx = x + w / 2.0, cy = y + h / 2.0;
|
|
double rx = w / 2.0, ry = h / 2.0;
|
|
|
|
/* Scanline fill the arc sector */
|
|
for (unsigned int row = 0; row < h; row++) {
|
|
double py = (y + row) - cy;
|
|
double half_w = rx * sqrt(1.0 - (py * py) / (ry * ry));
|
|
if (half_w < 0) continue;
|
|
int x0 = (int)(cx - half_w);
|
|
int x1 = (int)(cx + half_w);
|
|
/* For partial arcs, clip to angle range */
|
|
if (angle2 < 360 * 64) {
|
|
/* Simple: just fill the full ellipse row for now */
|
|
/* Full arc clipping would need per-pixel angle checks */
|
|
}
|
|
if (x0 < x1) {
|
|
pixman_image_fill_rectangles(PIXMAN_OP_OVER, dst, &pc, 1,
|
|
&(pixman_rectangle16_t){x0, y + row, x1 - x0, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
static WMNativePixmap
|
|
render_wl_create_disabled_mask(void *rctx, WMNativePixmap src_mask,
|
|
unsigned int width, unsigned int height)
|
|
{
|
|
(void)rctx;
|
|
pixman_image_t *mask = pixman_image_create_bits(PIXMAN_a8, width, height, NULL, 0);
|
|
pixman_image_t *src = src_mask ? (pixman_image_t *)(uintptr_t)src_mask : NULL;
|
|
uint8_t *mdata = (uint8_t *)pixman_image_get_data(mask);
|
|
int stride = pixman_image_get_stride(mask);
|
|
uint8_t *sdata = src ? (uint8_t *)pixman_image_get_data(src) : NULL;
|
|
int sstride = src ? pixman_image_get_stride(src) : 0;
|
|
|
|
for (unsigned int y = 0; y < height; y++) {
|
|
for (unsigned int x = 0; x < width; x++) {
|
|
int stipple = ((x + y) & 1);
|
|
int src_opaque = 1;
|
|
if (sdata)
|
|
src_opaque = sdata[y * sstride + x] > 0;
|
|
mdata[y * stride + x] = (stipple && src_opaque) ? 0xFF : 0x00;
|
|
}
|
|
}
|
|
|
|
return (WMNativePixmap)(uintptr_t)mask;
|
|
}
|
|
|
|
const W_RenderBackend W_RenderBackendWL = {
|
|
.screen_init = render_wl_screen_init,
|
|
.screen_fini = render_wl_screen_fini,
|
|
.pixmap_create = render_wl_pixmap_create,
|
|
.pixmap_free = render_wl_pixmap_free,
|
|
.pixmap_copy = render_wl_pixmap_copy,
|
|
.draw_line = render_wl_draw_line,
|
|
.draw_rect = render_wl_draw_rect,
|
|
.fill_rect = render_wl_fill_rect,
|
|
.fill_stippled = render_wl_fill_stippled,
|
|
.composite_pixmap = render_wl_composite_pixmap,
|
|
.fill_polygon = render_wl_fill_polygon,
|
|
.fill_arc = render_wl_fill_arc,
|
|
.create_disabled_mask = render_wl_create_disabled_mask,
|
|
|
|
};
|