Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utilize pixman for efficient pixel manipulation #55

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See if the function can be accelerated by Pixman.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Observing the difference between using (left image) and not using (right image) _twin_apply_alpha, we know that _twin_apply_alpha performs the conversion from ABGR to ARGB.
imageimage
According to the implementation in _twin_apply_alpha,

    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;

it also multiplies RBG channels with alpha value.

However, the public API in pixman.h doesn't provide the functions about ABGR to ARGB conversion and alpha premultiplication for user.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we know that _twin_apply_alpha performs the conversion from ABGR to ARGB.

Check the inner function _convertBGRtoARGB in file src/image-png.c which was a workaround for macOS.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran mado with gdb. The function _convertBGRtoARGB isn't executed because my operating system is not macOS.

/src/pixman.c#L172 is executed because my computer is a little-endian machine. The PNG data is originally in RGBA format as the following image, but it becomes ABGR format on a little-endian machine. In /src/pixman.c#L172, mado has already performed the ABGR-to-ARGB conversion for the requirement of the pixmap's pixel data.

image

{
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