Skip to content

Commit

Permalink
Merge pull request #55 from Bennctu/pixmap_dev
Browse files Browse the repository at this point in the history
Utilize pixman for efficient pixel manipulation
  • Loading branch information
jserv authored Nov 5, 2024
2 parents baba5c4 + 589e2b3 commit 11665d3
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 4 deletions.
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ libtwin.a_files-y = \
src/pattern.c \
src/spline.c \
src/work.c \
src/draw.c \
src/hull.c \
src/icon.c \
src/pixmap.c \
Expand All @@ -61,6 +60,15 @@ libtwin.a_includes-y := \
# Features
libtwin.a_files-$(CONFIG_LOGGING) += src/log.c
libtwin.a_files-$(CONFIG_CURSOR) += src/cursor.c
libtwin.a_files-$(CONFIG_RENDERER_BUILTIN) += src/draw.c

## Pixman
libtwin.a_files-$(CONFIG_RENDERER_PIXMAN) += src/pixman.c
libtwin.a_cflags-$(CONFIG_RENDERER_PIXMAN) += $(shell pkg-config --cflags pixman-1)
ifeq ($(CONFIG_RENDERER_PIXMAN), y)
TARGET_LIBS += $(shell pkg-config --libs pixman-1)
CFLAGS += $(shell pkg-config --cflags pixman-1)
endif

# Image loaders

Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ benefiting the entire application stack.
`Mado` is built with a minimalist design in mind. However, its verification
relies on certain third-party packages for full functionality and access to all
its features. We encourage the development environment to be installed with all optional
packages, including [libjpeg](https://www.ijg.org/) and [libpng](https://github.com/pnggroup/libpng).
packages, including [libjpeg](https://www.ijg.org/), [libpng](https://github.com/pnggroup/libpng)
and [pixman](https://pixman.org/).

In the meantime, ensure that you choose a graphics backend and install the necessary packages beforehand.

For SDL backend, install the [SDL2 library](https://www.libsdl.org/).
* macOS: `brew install sdl2 jpeg libpng`
* Ubuntu Linux / Debian: `sudo apt install libsdl2-dev libjpeg-dev libpng-dev`
* macOS: `brew install sdl2 jpeg libpng pixman`
* Ubuntu Linux / Debian: `sudo apt install libsdl2-dev libjpeg-dev libpng-dev libpixman-1-dev`

For the VNC backend, please note that it has only been tested on GNU/Linux, and the prebuilt [neatvnc](https://github.com/any1/neatvnc) package might be outdated. To ensure you have the latest version, you can build the required packages from source by running the script:
```shell
Expand Down
12 changes: 12 additions & 0 deletions configs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ config BACKEND_VNC
bool "VNC server output support"
endchoice

choice
prompt "Renderer Selection"
default RENDERER_BUILTIN

config RENDERER_BUILTIN
bool "Built-in pixel manipulation"

config RENDERER_PIXMAN
bool "Pixman based rendering"

endchoice

menu "Features"

config LOGGING
Expand Down
192 changes: 192 additions & 0 deletions src/pixman.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Twin - A Tiny Window System
* Copyright (c) 2024 National Cheng Kung University, Taiwan
* All rights reserved.
*/

#include <pixman.h>
#include "twin_private.h"

static void twin_argb32_to_pixman_color(twin_argb32_t argb,
pixman_color_t *color)
{
/* Extract ARGB every Byte */
uint16_t a = (argb >> 24) & 0xFF;
uint16_t r = (argb >> 16) & 0xFF;
uint16_t g = (argb >> 8) & 0xFF;
uint16_t b = argb & 0xFF;

/* 8bits -> 16bits (255 -> 65535) */
color->alpha = (a << 8) + a;
color->red = (r << 8) + r;
color->green = (g << 8) + g;
color->blue = (b << 8) + b;
}

static const pixman_format_code_t twin_pixman_format[3] = {
[TWIN_A8] = PIXMAN_a8,
[TWIN_RGB16] = PIXMAN_r5g6b5,
[TWIN_ARGB32] = PIXMAN_a8r8g8b8};

static pixman_format_code_t twin_to_pixman_format(
const twin_format_t twin_format)
{
return twin_pixman_format[twin_format];
}

static const pixman_op_t twin_pixman_op[2] =
{[TWIN_OVER] = PIXMAN_OP_OVER, [TWIN_SOURCE] = PIXMAN_OP_SRC};

static pixman_op_t twin_to_pixman_op(const twin_operator_t twin_op)
{
return twin_pixman_op[twin_op];
}

#define create_pixman_image_from_twin_pixmap(pixmap) \
({ \
typeof(pixmap) _pixmap = (pixmap); \
pixman_image_create_bits(twin_to_pixman_format((_pixmap)->format), \
(_pixmap)->width, (_pixmap)->height, \
(_pixmap)->p.argb32, (_pixmap)->stride); \
})

static void pixmap_matrix_scale(pixman_image_t *src, twin_matrix_t *matrix)
{
pixman_transform_t transform;
pixman_transform_init_identity(&transform);

pixman_fixed_t sx = matrix->m[0][0], sy = matrix->m[1][1];

pixman_transform_scale(&transform, NULL, sx, sy);
pixman_image_set_transform(src, &transform);
}

void twin_composite(twin_pixmap_t *_dst,
twin_coord_t dst_x,
twin_coord_t dst_y,
twin_operand_t *_src,
twin_coord_t src_x,
twin_coord_t src_y,
twin_operand_t *_msk,
twin_coord_t msk_x,
twin_coord_t msk_y,
twin_operator_t operator,
twin_coord_t width,
twin_coord_t height)
{
pixman_image_t *src;
if (_src->source_kind == TWIN_SOLID) {
pixman_color_t source_pixel;
twin_argb32_to_pixman_color(_src->u.argb, &source_pixel);
src = pixman_image_create_solid_fill(&source_pixel);
} else {
twin_pixmap_t *src_pixmap = _src->u.pixmap;
src = create_pixman_image_from_twin_pixmap(src_pixmap);

if (!twin_matrix_is_identity(&(src_pixmap->transform)))
pixmap_matrix_scale(src, &(src_pixmap->transform));
}

pixman_image_t *dst = create_pixman_image_from_twin_pixmap(_dst);

/* Set origin */
twin_coord_t ox, oy;
twin_pixmap_get_origin(_dst, &ox, &oy);
ox += dst_x;
oy += dst_y;

if (!_msk) {
pixman_image_composite(twin_to_pixman_op(operator), src, NULL, dst,
src_x, src_y, 0, 0, ox, oy, width, height);
} else {
pixman_image_t *msk =
create_pixman_image_from_twin_pixmap(_msk->u.pixmap);
pixman_image_composite(twin_to_pixman_op(operator), src, msk, dst,
src_x, src_y, msk_x, msk_y, ox, oy, width,
height);
pixman_image_unref(msk);
}

pixman_image_unref(src);
pixman_image_unref(dst);
}

void twin_fill(twin_pixmap_t *_dst,
twin_argb32_t pixel,
twin_operator_t operator,
twin_coord_t left,
twin_coord_t top,
twin_coord_t right,
twin_coord_t bottom)
{
/* offset */
left += _dst->origin_x;
top += _dst->origin_y;
right += _dst->origin_x;
bottom += _dst->origin_y;

/* clip */
if (left < _dst->clip.left)
left = _dst->clip.left;
if (right > _dst->clip.right)
right = _dst->clip.right;
if (top < _dst->clip.top)
top = _dst->clip.top;
if (bottom > _dst->clip.bottom)
bottom = _dst->clip.bottom;

pixman_image_t *dst = create_pixman_image_from_twin_pixmap(_dst);
pixman_color_t color;
twin_argb32_to_pixman_color(pixel, &color);
pixman_image_fill_rectangles(
twin_to_pixman_op(operator), dst, &color, 1,
&(pixman_rectangle16_t){left, top, right - left, bottom - top});

twin_pixmap_damage(_dst, left, top, right, bottom);

pixman_image_unref(dst);
}

/* Same function in draw.c */
static twin_argb32_t _twin_apply_alpha(twin_argb32_t v)
{
uint16_t t1, t2, t3;
twin_a8_t alpha = twin_get_8(v,
#if __BYTE_ORDER == __BIG_ENDIAN
0
#else
24
#endif
);

/* clear RGB data if alpha is zero */
if (!alpha)
return 0;

#if __BYTE_ORDER == __BIG_ENDIAN
/* twin needs ARGB format */
return alpha << 24 | twin_int_mult(twin_get_8(v, 24), alpha, t1) << 16 |
twin_int_mult(twin_get_8(v, 16), alpha, t2) << 8 |
twin_int_mult(twin_get_8(v, 8), alpha, t3) << 0;
#else
return alpha << 24 | twin_int_mult(twin_get_8(v, 0), alpha, t1) << 16 |
twin_int_mult(twin_get_8(v, 8), alpha, t2) << 8 |
twin_int_mult(twin_get_8(v, 16), alpha, t3) << 0;
#endif
}

void twin_premultiply_alpha(twin_pixmap_t *px)
{
int x, y;
twin_pointer_t p;

if (px->format != TWIN_ARGB32)
return;

for (y = 0; y < px->height; y++) {
p.b = px->p.b + y * px->stride;

for (x = 0; x < px->width; x++)
p.argb32[x] = _twin_apply_alpha(p.argb32[x]);
}
}
9 changes: 9 additions & 0 deletions src/pixmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,20 @@

#include "twin_private.h"

#define IS_ALIGNED(p, alignment) ((p % alignment) == 0)
#define ALIGN_UP(sz, alignment) \
(((alignment) & ((alignment) - 1)) == 0 \
? (((sz) + (alignment) - 1) & ~((alignment) - 1)) \
: ((((sz) + (alignment) - 1) / (alignment)) * (alignment)))
twin_pixmap_t *twin_pixmap_create(twin_format_t format,
twin_coord_t width,
twin_coord_t height)
{
twin_coord_t stride = twin_bytes_per_pixel(format) * width;
/* Align stride to 4 bytes for proper uint32_t access in Pixman. */
if (!IS_ALIGNED(stride, 4))
stride = ALIGN_UP(stride, 4);

twin_area_t space = (twin_area_t) stride * height;
twin_area_t size = sizeof(twin_pixmap_t) + space;
twin_pixmap_t *pixmap = malloc(size);
Expand Down

0 comments on commit 11665d3

Please sign in to comment.