From 61764af270c3b2ef67bdf56f9d624f99465a8fae Mon Sep 17 00:00:00 2001 From: Wei-Hsin Yeh Date: Sun, 6 Oct 2024 23:09:19 +0800 Subject: [PATCH] Add the Gaussian function to blur a pixmap Use Pascal's Triangle to approximate a 5x5 Gaussian Kernel, and apply Gaussian blur on the 'twin_pixmap_t' to implement the blurring technique. See: sysprog21#34 --- include/twin.h | 2 ++ include/twin_private.h | 15 +++++++++++++ mk/common.mk | 3 ++- src/draw.c | 49 ++++++++++++++++++++++++++++++++++++++++++ src/image-png.c | 1 + 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/include/twin.h b/include/twin.h index 356ccaa..7d5c051 100644 --- a/include/twin.h +++ b/include/twin.h @@ -648,6 +648,8 @@ void twin_fill(twin_pixmap_t *dst, void twin_premultiply_alpha(twin_pixmap_t *px); +void twin_gaussian_blur(twin_pixmap_t *px); + /* * event.c */ diff --git a/include/twin_private.h b/include/twin_private.h index 99586d3..598ba48 100644 --- a/include/twin_private.h +++ b/include/twin_private.h @@ -586,6 +586,21 @@ void _twin_button_init(twin_button_t *button, /* utility */ +#define min(x, y) \ + ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; \ + }) + +#define max(x, y) \ + ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; \ + }) #ifdef _MSC_VER #include static inline int twin_clz(uint32_t v) diff --git a/mk/common.mk b/mk/common.mk index 4bd99bb..d9a135e 100644 --- a/mk/common.mk +++ b/mk/common.mk @@ -101,7 +101,8 @@ RM := rm -rf MKDIR := mkdir -p __CFLAGS := -Wall -Wextra -__CFLAGS += -O2 +__CFLAGS += -O2 -pipe -g +__CFLAGS += -fsanitize=address -static-libasan __CFLAGS += $(CFLAGS) uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') diff --git a/src/draw.c b/src/draw.c index e3f1655..3ce3716 100644 --- a/src/draw.c +++ b/src/draw.c @@ -723,6 +723,55 @@ void twin_premultiply_alpha(twin_pixmap_t *px) } } +static twin_argb32_t _twin_apply_gaussian(twin_argb32_t v, + uint8_t wght, + twin_argb32_t *r, + twin_argb32_t *g, + twin_argb32_t *b) +{ + *r += ((((v & 0x00ff0000) >> 16) * (twin_argb32_t) wght) << 8) & 0x00ff0000; + *g += (((v & 0x0000ff00) >> 8) * (twin_argb32_t) wght) & 0x0000ff00; + *b += ((((v & 0x000000ff) >> 0) * (twin_argb32_t) wght) >> 8) & 0x000000ff; +} + +void twin_gaussian_blur(twin_pixmap_t *px) +{ + if (px->format != TWIN_ARGB32) + return; + + uint8_t kernel[5][5] = {{0x01, 0x04, 0x06, 0x04, 0x01}, + {0x04, 0x10, 0x18, 0x10, 0x04}, + {0x06, 0x18, 0x24, 0x18, 0x06}, + {0x04, 0x10, 0x18, 0x10, 0x04}, + {0x01, 0x04, 0x06, 0x04, 0x01}}; + twin_pointer_t ptr, tmp_ptr; + twin_pixmap_t *tmp_px = + twin_pixmap_create(px->format, px->width, px->height); + memcpy(tmp_px->p.v, px->p.v, + px->width * px->height * twin_bytes_per_pixel(px->format)); + + int radius = 2, _y, _x; + twin_argb32_t r, g, b; + for (int screen_y = 0; screen_y < px->height; screen_y++) + for (int screen_x = 0; screen_x < px->width; screen_x++) { + r = 0, g = 0, b = 0; + ptr = twin_pixmap_pointer(px, screen_x, screen_y); + for (int y = -radius; y <= radius; y++) { + _y = min(max(screen_y + y, 0), px->height - 1); + for (int x = -radius; x <= radius; x++) { + _x = min(max(screen_x + x, 0), px->width - 1); + tmp_ptr = twin_pixmap_pointer(tmp_px, _x, _y); + _twin_apply_gaussian(*tmp_ptr.argb32, + kernel[x + radius][y + radius], &r, &g, + &b); + } + } + *ptr.argb32 = (*ptr.argb32 & 0xff000000) | r | g | b; + } + + twin_pixmap_destroy(tmp_px); +} + /* * array primary index is OVER SOURCE * array secondary index is ARGB32 RGB16 A8 diff --git a/src/image-png.c b/src/image-png.c index 96e3f60..c0e5545 100644 --- a/src/image-png.c +++ b/src/image-png.c @@ -139,6 +139,7 @@ twin_pixmap_t *_twin_png_to_pixmap(const char *filepath, twin_format_t fmt) _convertBGRtoARGB(pix->p.b, width, height); #endif twin_premultiply_alpha(pix); + twin_gaussian_blur(pix); } bail_free: