Skip to content

Commit

Permalink
Utilize pixman for efficient pixel manipulation
Browse files Browse the repository at this point in the history
Replace original image compositing with Pixman function. Breifly,
include:
 - pixman_image_composite: image compositing
 - pixman_image_fill_rectangles: fill the image with rectangles

Originally, the built-in renderer offers simplicity and works well for
RISC-V platforms without vector or SIMD extensions. However, modern CPU
architectures have rich ISA extensions, and Pixman provides faster pixel
manipulation. Hence, we support Pixman-based implementation to make it
advantageous.

We just switch to Pixman based rendering in Mado system configuration.
The following benchmark results as:
width, height, iteration, operation | built-in (sec) | pixman (sec) |
---------------------------------------------------------------------
100  , 100   , 20000    , source    | 0.065616       | 0.020530     |
                        , over      | 0.176866       | 0.053927     |
---------------------------------------------------------------------
200  , 200   , 10000    , source    | 0.147732       | 0.051592     |
                        , over      | 0.352873       | 0.132560     |
---------------------------------------------------------------------
1200 , 800   , 200      , source    | 0.058365       | 0.041629     |
                        , over      | 0.458145       | 0.058690     |
---------------------------------------------------------------------
Note: the table shows that Pixman method is better than built-in
method for image compositing runtime.

Close #6
  • Loading branch information
Bennctu committed Nov 5, 2024
1 parent a846095 commit 589e2b3
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 589e2b3

Please sign in to comment.