commit d33c804

hovercats  ·  2026-04-16 04:44:32 +0000 UTC
parent 505dc1c
wld: add wip patches for better font rendering
4 files changed,  +464, -1
+1, -0
1@@ -167,6 +167,7 @@
2 [submodule "pkg/wld/src"]
3 	path = pkg/wld/src
4 	url = https://github.com/michaelforney/wld
5+	ignore = all
6 [submodule "pkg/wayland-protocols/src"]
7 	path = pkg/wayland-protocols/src
8 	url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
+296, -0
  1@@ -0,0 +1,296 @@
  2+From f3798978fc437ef7c6f62cc5be226332bc73a3e2 Mon Sep 17 00:00:00 2001
  3+From: shrub <maybeshrub@gmail.com>
  4+Date: Sun, 1 Feb 2026 18:24:41 +0000
  5+Subject: [PATCH] text: support grayscale freetype glyphs
  6+
  7+---
  8+ font.c    |  5 ++--
  9+ intel.c   | 26 +++++++++++++++++--
 10+ nouveau.c | 72 +++++++++++++++++++++++++++++++++++++++++++---------
 11+ pixman.c  | 76 ++++++++++++++++++++++++++++++++++++++++---------------
 12+ 4 files changed, 142 insertions(+), 37 deletions(-)
 13+
 14+diff --git a/font.c b/font.c
 15+index 41fd9ca..cbb187c 100644
 16+--- a/font.c
 17++++ b/font.c
 18+@@ -172,7 +172,8 @@ font_ensure_glyph(struct font *font, FT_UInt glyph_index)
 19+ 			if (!glyph)
 20+ 				return false;
 21+ 
 22+-			FT_Load_Glyph(font->face, glyph_index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO);
 23++			FT_Load_Glyph(font->face, glyph_index,
 24++			              FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL);
 25+ 
 26+ 			FT_Bitmap_New(&glyph->bitmap);
 27+ 
 28+@@ -217,7 +218,7 @@ wld_font_text_extents_n(struct wld_font *font_base,
 29+ 
 30+ 	extents->advance = 0;
 31+ 
 32+-	while ((ret = FcUtf8ToUcs4((FcChar8 *)text, &c, length) > 0) && c != '\0') {
 33++	while ((ret = FcUtf8ToUcs4((FcChar8 *)text, &c, length)) > 0 && c != '\0') {
 34+ 		length -= ret;
 35+ 		text += ret;
 36+ 		glyph_index = FT_Get_Char_Index(font->face, c);
 37+diff --git a/intel.c b/intel.c
 38+index 95624e9..8cf0a21 100644
 39+--- a/intel.c
 40++++ b/intel.c
 41+@@ -29,6 +29,7 @@
 42+ 
 43+ #include <i915_drm.h>
 44+ #include <intel_bufmgr.h>
 45++#include <string.h>
 46+ #include <unistd.h>
 47+ 
 48+ struct intel_context {
 49+@@ -57,6 +58,20 @@ IMPL(intel_context, wld_context)
 50+ IMPL(intel_renderer, wld_renderer)
 51+ IMPL(intel_buffer, wld_buffer)
 52+ 
 53++static void
 54++pack_gray_row_to_mono(uint8_t *dst, const uint8_t *src, uint32_t width)
 55++{
 56++	uint32_t x, bytes_per_row;
 57++
 58++	bytes_per_row = (width + 7) / 8;
 59++	memset(dst, 0, bytes_per_row);
 60++
 61++	for (x = 0; x < width; ++x) {
 62++		if (src[x] >= 128)
 63++			dst[x / 8] |= 0x80 >> (x & 7);
 64++	}
 65++}
 66++
 67+ /**** DRM driver ****/
 68+ bool
 69+ driver_device_supported(uint32_t vendor_id, uint32_t device_id)
 70+@@ -315,8 +330,15 @@ renderer_draw_text(struct wld_renderer *base,
 71+ 
 72+ 		/* XY_TEXT_IMMEDIATE requires a pitch with no extra bytes */
 73+ 		for (row = 0; row < glyph->bitmap.rows; ++row) {
 74+-			memcpy(byte, glyph->bitmap.buffer + (row * glyph->bitmap.pitch),
 75+-			       (glyph->bitmap.width + 7) / 8);
 76++			const uint8_t *src = glyph->bitmap.buffer +
 77++			                     (row * glyph->bitmap.pitch);
 78++			uint32_t bytes_per_row = (glyph->bitmap.width + 7) / 8;
 79++
 80++			if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
 81++				memcpy(byte, src, bytes_per_row);
 82++			} else {
 83++				pack_gray_row_to_mono(byte, src, glyph->bitmap.width);
 84++			}
 85+ 			byte += (glyph->bitmap.width + 7) / 8;
 86+ 		}
 87+ 
 88+diff --git a/nouveau.c b/nouveau.c
 89+index 6742f25..6bd7ab1 100644
 90+--- a/nouveau.c
 91++++ b/nouveau.c
 92+@@ -34,6 +34,7 @@
 93+ #include "pixman.h"
 94+ 
 95+ #include <nouveau.h>
 96++#include <string.h>
 97+ #include <sys/mman.h>
 98+ 
 99+ enum nv_architecture {
100+@@ -75,6 +76,20 @@ IMPL(nouveau_context, wld_context)
101+ IMPL(nouveau_renderer, wld_renderer)
102+ IMPL(nouveau_buffer, wld_buffer)
103+ 
104++static void
105++pack_gray_row_to_mono(uint8_t *dst, const uint8_t *src, uint32_t width)
106++{
107++	uint32_t x, bytes_per_row;
108++
109++	bytes_per_row = (width + 7) / 8;
110++	memset(dst, 0, bytes_per_row);
111++
112++	for (x = 0; x < width; ++x) {
113++		if (src[x] >= 128)
114++			dst[x / 8] |= 0x80 >> (x & 7);
115++	}
116++}
117++
118+ /**** DRM driver ****/
119+ bool
120+ driver_device_supported(uint32_t vendor_id, uint32_t device_id)
121+@@ -594,22 +609,55 @@ renderer_draw_text(struct wld_renderer *base,
122+ 		if (glyph->bitmap.width == 0 || glyph->bitmap.rows == 0)
123+ 			goto advance;
124+ 
125+-		count = (glyph->bitmap.pitch * glyph->bitmap.rows + 3) / 4;
126++		if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
127++			count = (glyph->bitmap.pitch * glyph->bitmap.rows + 3) / 4;
128++		} else {
129++			uint32_t bytes_per_row = (glyph->bitmap.width + 7) / 8;
130++			count = (bytes_per_row * glyph->bitmap.rows + 3) / 4;
131++		}
132+ 
133+ 		if (!ensure_space(renderer->pushbuf, 12 + count))
134+ 			return;
135+ 
136+-		nvc0_2d(renderer->pushbuf, G80_2D_SIFC_WIDTH, 10,
137+-		        /* Use the pitch instead of width to ensure the correct
138+-			 * alignment is used. */
139+-		        glyph->bitmap.pitch * 8, glyph->bitmap.rows,
140+-		        0, 1, 0, 1,
141+-		        0, origin_x + glyph->x, 0, y + glyph->y);
142+-		nv_add_dword(renderer->pushbuf,
143+-		             nvc0_command(GF100_COMMAND_TYPE_NON_INCREASING,
144+-		                          GF100_SUBCHANNEL_2D,
145+-		                          G80_2D_SIFC_DATA, count));
146+-		nv_add_data(renderer->pushbuf, glyph->bitmap.buffer, count);
147++		if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
148++			nvc0_2d(renderer->pushbuf, G80_2D_SIFC_WIDTH, 10,
149++			        /* Use the pitch instead of width to ensure the correct
150++				 * alignment is used. */
151++			        glyph->bitmap.pitch * 8, glyph->bitmap.rows,
152++			        0, 1, 0, 1,
153++			        0, origin_x + glyph->x, 0, y + glyph->y);
154++			nv_add_dword(renderer->pushbuf,
155++			             nvc0_command(GF100_COMMAND_TYPE_NON_INCREASING,
156++			                          GF100_SUBCHANNEL_2D,
157++			                          G80_2D_SIFC_DATA, count));
158++			nv_add_data(renderer->pushbuf, glyph->bitmap.buffer, count);
159++		} else {
160++			uint32_t bytes_per_row = (glyph->bitmap.width + 7) / 8;
161++			uint32_t row;
162++			uint8_t *mono = malloc(bytes_per_row * glyph->bitmap.rows);
163++			uint8_t *dst = mono;
164++
165++			if (!mono)
166++				return;
167++
168++			for (row = 0; row < glyph->bitmap.rows; ++row) {
169++				const uint8_t *src = glyph->bitmap.buffer +
170++				                     (row * glyph->bitmap.pitch);
171++				pack_gray_row_to_mono(dst, src, glyph->bitmap.width);
172++				dst += bytes_per_row;
173++			}
174++
175++			nvc0_2d(renderer->pushbuf, G80_2D_SIFC_WIDTH, 10,
176++			        bytes_per_row * 8, glyph->bitmap.rows,
177++			        0, 1, 0, 1,
178++			        0, origin_x + glyph->x, 0, y + glyph->y);
179++			nv_add_dword(renderer->pushbuf,
180++			             nvc0_command(GF100_COMMAND_TYPE_NON_INCREASING,
181++			                          GF100_SUBCHANNEL_2D,
182++			                          G80_2D_SIFC_DATA, count));
183++			nv_add_data(renderer->pushbuf, mono, count);
184++			free(mono);
185++		}
186+ 
187+ 	advance:
188+ 		origin_x += glyph->advance;
189+diff --git a/pixman.c b/pixman.c
190+index bd95f8b..3012ecd 100644
191+--- a/pixman.c
192++++ b/pixman.c
193+@@ -24,6 +24,8 @@
194+ #include "pixman.h"
195+ #include "wld-private.h"
196+ 
197++#include <string.h>
198++
199+ #define PIXMAN_COLOR(c)                              \
200+ 	{                                            \
201+ 		.alpha = ((c >> 24) & 0xff) * 0x101, \
202+@@ -344,6 +346,58 @@ reverse(uint8_t byte)
203+ 	return byte;
204+ }
205+ 
206++static pixman_image_t *
207++glyph_bitmap_to_pixman_image(struct glyph *glyph)
208++{
209++	FT_Bitmap *bitmap = &glyph->bitmap;
210++	pixman_image_t *image;
211++	uint8_t *src, *dst;
212++	uint32_t row, byte_index, bytes_per_row, pitch;
213++
214++	switch (bitmap->pixel_mode) {
215++	case FT_PIXEL_MODE_MONO:
216++		image = pixman_image_create_bits(PIXMAN_a1, bitmap->width,
217++		                                 bitmap->rows, NULL, bitmap->pitch);
218++		if (!image)
219++			return NULL;
220++
221++		pitch = pixman_image_get_stride(image);
222++		bytes_per_row = (bitmap->width + 7) / 8;
223++		src = bitmap->buffer;
224++		dst = (uint8_t *)pixman_image_get_data(image);
225++
226++		for (row = 0; row < bitmap->rows; ++row) {
227++			/* Pixman's A1 format expects the bits in the opposite order
228++			 * that Freetype gives us. */
229++			for (byte_index = 0; byte_index < bytes_per_row; ++byte_index)
230++				dst[byte_index] = reverse(src[byte_index]);
231++
232++			dst += pitch;
233++			src += bitmap->pitch;
234++		}
235++		return image;
236++	case FT_PIXEL_MODE_GRAY:
237++		image = pixman_image_create_bits(PIXMAN_a8, bitmap->width,
238++		                                 bitmap->rows, NULL, 0);
239++		if (!image)
240++			return NULL;
241++
242++		pitch = pixman_image_get_stride(image);
243++		src = bitmap->buffer;
244++		dst = (uint8_t *)pixman_image_get_data(image);
245++
246++		for (row = 0; row < bitmap->rows; ++row) {
247++			memset(dst, 0, pitch);
248++			memcpy(dst, src, bitmap->width);
249++			dst += pitch;
250++			src += bitmap->pitch;
251++		}
252++		return image;
253++	default:
254++		return NULL;
255++	}
256++}
257++
258+ void
259+ renderer_draw_text(struct wld_renderer *base,
260+                    struct font *font, uint32_t color,
261+@@ -385,32 +439,12 @@ renderer_draw_text(struct wld_renderer *base,
262+ 		/* If we don't have the glyph in our cache, do some conversions to make
263+ 		 * pixman happy, and then insert it. */
264+ 		if (!glyphs[index].glyph) {
265+-			uint8_t *src, *dst;
266+-			uint32_t row, byte_index, bytes_per_row, pitch;
267+ 			pixman_image_t *image;
268+-			FT_Bitmap *bitmap;
269+-
270+-			bitmap = &glyph->bitmap;
271+-			image = pixman_image_create_bits(PIXMAN_a1, bitmap->width, bitmap->rows, NULL, bitmap->pitch);
272++			image = glyph_bitmap_to_pixman_image(glyph);
273+ 
274+ 			if (!image)
275+ 				goto advance;
276+ 
277+-			pitch = pixman_image_get_stride(image);
278+-			bytes_per_row = (bitmap->width + 7) / 8;
279+-			src = bitmap->buffer;
280+-			dst = (uint8_t *)pixman_image_get_data(image);
281+-
282+-			for (row = 0; row < bitmap->rows; ++row) {
283+-				/* Pixman's A1 format expects the bits in the opposite order
284+-				 * that Freetype gives us. Sigh... */
285+-				for (byte_index = 0; byte_index < bytes_per_row; ++byte_index)
286+-					dst[byte_index] = reverse(src[byte_index]);
287+-
288+-				dst += pitch;
289+-				src += bitmap->pitch;
290+-			}
291+-
292+ 			/* Insert the glyph into the cache. */
293+ 			pixman_glyph_cache_freeze(renderer->glyph_cache);
294+ 			glyphs[index].glyph = pixman_glyph_cache_insert(renderer->glyph_cache, font, glyph,
295+-- 
296+2.49.0
297+
+166, -0
  1@@ -0,0 +1,166 @@
  2+From c4801596c8ecc0f6476d0bf14582d2086f531853 Mon Sep 17 00:00:00 2001
  3+From: dalem <lain@dalem.foo>
  4+Date: Sat, 14 Mar 2026 19:30:03 +0100
  5+Subject: [PATCH] renderer: add circle and line primitives
  6+
  7+---
  8+ interface/renderer.h |  2 +
  9+ renderer.c           | 95 ++++++++++++++++++++++++++++++++++++++++++++
 10+ wld-private.h        |  4 ++
 11+ wld.h                |  6 +++
 12+ 4 files changed, 107 insertions(+)
 13+
 14+diff --git a/interface/renderer.h b/interface/renderer.h
 15+index 021aea1..0dabbfd 100644
 16+--- a/interface/renderer.h
 17++++ b/interface/renderer.h
 18+@@ -61,6 +61,8 @@ static const struct wld_renderer_impl wld_renderer_impl = {
 19+ 	.fill_region = &default_fill_region,
 20+ 	.copy_region = &default_copy_region,
 21+ #endif
 22++	.draw_circle = &wld_draw_circle,
 23++	.draw_line = &wld_draw_line,
 24+ 	.draw_text = &renderer_draw_text,
 25+ 	.flush = &renderer_flush,
 26+ 	.destroy = &renderer_destroy
 27+diff --git a/renderer.c b/renderer.c
 28+index 569d6ba..e8a59c9 100644
 29+--- a/renderer.c
 30++++ b/renderer.c
 31+@@ -139,6 +139,101 @@ wld_copy_region(struct wld_renderer *renderer,
 32+ 	                            dst_x, dst_y, region);
 33+ }
 34+ 
 35++static void
 36++circle_points(struct wld_renderer *renderer, uint32_t color, 
 37++              int32_t x1, int32_t y1, 
 38++              int32_t x2, int32_t y2, bool fill)
 39++{
 40++	if (fill) {
 41++		renderer->impl->fill_rectangle(renderer, color, x1-x2, y1+y2,
 42++				                       2*x2+1, 1);
 43++		renderer->impl->fill_rectangle(renderer, color, x1-x2, y1-y2,
 44++				                       2*x2+1, 1);
 45++		renderer->impl->fill_rectangle(renderer, color, x1-y2, y1+x2,
 46++				                       2*y2+1, 1);
 47++		renderer->impl->fill_rectangle(renderer, color, x1-y2, y1-x2,
 48++				                       2*y2+1, 1);
 49++	} else {
 50++		renderer->impl->fill_rectangle(renderer, color, x1+x2, y1+y2, 1, 1);
 51++		renderer->impl->fill_rectangle(renderer, color, x1-x2, y1+y2, 1, 1);
 52++		renderer->impl->fill_rectangle(renderer, color, x1+x2, y1-y2, 1, 1);
 53++		renderer->impl->fill_rectangle(renderer, color, x1-x2, y1-y2, 1, 1);
 54++		renderer->impl->fill_rectangle(renderer, color, x1+y2, y1+x2, 1, 1);
 55++		renderer->impl->fill_rectangle(renderer, color, x1-y2, y1+x2, 1, 1);
 56++		renderer->impl->fill_rectangle(renderer, color, x1+y2, y1-x2, 1, 1);
 57++		renderer->impl->fill_rectangle(renderer, color, x1-y2, y1-x2, 1, 1);
 58++	}
 59++}
 60++
 61++EXPORT
 62++void
 63++wld_draw_circle(struct wld_renderer *renderer, uint32_t color,
 64++                int32_t x1, int32_t y1, 
 65++                uint32_t r, bool fill)
 66++{
 67++	int32_t x2 = 0, y2 = r;
 68++	int32_t d = 3 - (2*r);
 69++
 70++	circle_points(renderer, color, x1, y1, x2, y2, fill);
 71++
 72++	while (y2 >= x2) {
 73++		if (d > 0) {
 74++			y2--;
 75++			d = d + 4*(x2-y2) + 10;
 76++		}
 77++		else
 78++			d = d + 4*x2 + 6;
 79++
 80++		x2++;
 81++
 82++		circle_points(renderer, color, x1, y1, x2, y2, fill);
 83++	}
 84++}
 85++
 86++
 87++EXPORT
 88++void
 89++wld_draw_line(struct wld_renderer *renderer, uint32_t color,
 90++              int32_t x1, int32_t y1,
 91++              int32_t x2, int32_t y2)
 92++{
 93++	int32_t dx = abs(x2-x1),  sx = x1 < x2 ? 1 : -1;
 94++	int32_t dy = -abs(y2-y1), sy = y1 < y2 ? 1 : -1;
 95++	int32_t err = dx + dy, e2;
 96++
 97++	/* just use fill_rectangle for purely vertical and horizontal line */
 98++	if (y1 == y2) {
 99++		int32_t lx = sx > 0 ? x1 : x2;
100++		renderer->impl->fill_rectangle(renderer, color, lx, y1, dx + 1, 1);
101++		return;
102++	}
103++
104++	if (x1 == x2) {
105++		int32_t ly = sy > 0 ? y1 : y2;
106++		renderer->impl->fill_rectangle(renderer, color, x1, ly, 1, -dy + 1);
107++		return;
108++	}
109++
110++	while (true) {
111++		renderer->impl->fill_rectangle(renderer, color, x1, y1, 1, 1);
112++
113++		if (x1 == x2 && y1 == y2)
114++			break;
115++
116++		e2 = 2 * err;
117++
118++		if (e2 >= dy) {
119++			err += dy;
120++			x1 += sx;
121++		}
122++
123++		if (e2 <= dx) {
124++			err += dx;
125++			y1 += sy;
126++		}
127++	}
128++}
129++
130+ EXPORT
131+ void
132+ wld_draw_text(struct wld_renderer *renderer,
133+diff --git a/wld-private.h b/wld-private.h
134+index 5bdaed3..0953edb 100644
135+--- a/wld-private.h
136++++ b/wld-private.h
137+@@ -108,6 +108,10 @@ struct wld_renderer_impl {
138+ 	void (*copy_region)(struct wld_renderer *renderer, struct buffer *src,
139+ 	                    int32_t dst_x, int32_t dst_y,
140+ 	                    pixman_region32_t *region);
141++	void (*draw_circle)(struct wld_renderer *renderer, uint32_t color,
142++                        int32_t x1, int32_t y1, uint32_t r, bool fill);
143++	void (*draw_line)(struct wld_renderer *renderer, uint32_t color,
144++                      int32_t x1, int32_t y1, int32_t x2, int32_t y2);
145+ 	void (*draw_text)(struct wld_renderer *renderer,
146+ 	                  struct font *font, uint32_t color,
147+ 	                  int32_t x, int32_t y, const char *text, uint32_t length,
148+diff --git a/wld.h b/wld.h
149+index 9eabdd6..b1c53c6 100644
150+--- a/wld.h
151++++ b/wld.h
152+@@ -249,6 +249,12 @@ void wld_copy_rectangle(struct wld_renderer *renderer,
153+                         int32_t src_x, int32_t src_y,
154+                         uint32_t width, uint32_t height);
155+ 
156++void wld_draw_circle(struct wld_renderer *renderer, uint32_t color,
157++                     int32_t x1, int32_t y1, uint32_t r, bool fill);
158++
159++void wld_draw_line(struct wld_renderer *renderer, uint32_t color,
160++                   int32_t x1, int32_t y1, int32_t x2, int32_t y2);
161++
162+ void wld_copy_region(struct wld_renderer *renderer,
163+                      struct wld_buffer *buffer,
164+                      int32_t dst_x, int32_t dst_y, pixman_region32_t *region);
165+-- 
166+2.49.0
167+
+1, -1
1@@ -1 +1 @@
2-3fe15e769c r0
3+3fe15e769c r1