mew

fork with password patch and nord colorscheme
git clone https://git.awy.one/mew.git
Log | Files | Refs | README | LICENSE

drwl.h (8267B)


      1 /*
      2  * drwl - https://codeberg.org/sewn/drwl
      3  *
      4  * Copyright (c) 2023-2025 sewn <sewn@disroot.org>
      5  * Copyright (c) 2024 notchoc <notchoc@disroot.org>
      6  * 
      7  * Permission is hereby granted, free of charge, to any person obtaining
      8  * a copy of this software and associated documentation files (the
      9  * "Software"), to deal in the Software without restriction, including
     10  * without limitation the rights to use, copy, modify, merge, publish,
     11  * distribute, sublicense, and/or sell copies of the Software, and to
     12  * permit persons to whom the Software is furnished to do so, subject to
     13  * the following conditions:
     14  * 
     15  * The above copyright notice and this permission notice shall be
     16  * included in all copies or substantial portions of the Software.
     17  * 
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     22  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     23  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     24  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  * The UTF-8 Decoder included is from Bjoern Hoehrmann:
     27  * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
     28  * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
     29  */
     30 #pragma once
     31 
     32 #include <stdlib.h>
     33 #include <fcft/fcft.h>
     34 #include <pixman-1/pixman.h>
     35 
     36 enum { ColFg, ColBg }; /* colorscheme index */
     37 
     38 typedef struct fcft_font Fnt;
     39 typedef pixman_image_t Img;
     40 
     41 typedef struct {
     42 	Img *image;
     43 	Fnt *font;
     44 	uint32_t *scheme;
     45 } Drwl;
     46 
     47 #define UTF8_ACCEPT 0
     48 #define UTF8_REJECT 12
     49 #define UTF8_INVALID 0xFFFD
     50 
     51 static const uint8_t utf8d[] = {
     52 	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     53 	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     54 	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     55 	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     56 	 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
     57 	 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
     58 	 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
     59 	10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
     60 
     61 	 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
     62 	12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
     63 	12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
     64 	12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
     65 	12,36,12,12,12,12,12,12,12,12,12,12,
     66 };
     67 
     68 static inline uint32_t
     69 utf8decode(uint32_t *state, uint32_t *codep, uint8_t byte)
     70 {
     71 	uint32_t type = utf8d[byte];
     72 
     73 	*codep = (*state != UTF8_ACCEPT) ?
     74 		(byte & 0x3fu) | (*codep << 6) :
     75 		(0xff >> type) & (byte);
     76 
     77 	*state = utf8d[256 + *state + type];
     78 	return *state;
     79 }
     80 
     81 static int
     82 drwl_init(void)
     83 {
     84 	return fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR);
     85 }
     86 
     87 static Drwl *
     88 drwl_create(void)
     89 {
     90 	Drwl *drwl;
     91 	
     92 	if (!(drwl = calloc(1, sizeof(Drwl))))
     93 		return NULL;
     94 
     95 	return drwl;
     96 }
     97 
     98 static void
     99 drwl_setfont(Drwl *drwl, Fnt *font)
    100 {
    101 	if (drwl)
    102 		drwl->font = font;
    103 }
    104 
    105 static void
    106 drwl_setimage(Drwl *drwl, Img *image)
    107 {
    108 	if (drwl)
    109 		drwl->image = image;
    110 }
    111 
    112 static Fnt *
    113 drwl_font_create(Drwl *drwl, size_t count,
    114 		const char *names[static count], const char *attributes)
    115 {
    116 	Fnt *font = fcft_from_name(count, names, attributes);
    117 	if (drwl)
    118 		drwl_setfont(drwl, font);
    119 	return font;
    120 }
    121 
    122 static void
    123 drwl_font_destroy(Fnt *font)
    124 {
    125 	fcft_destroy(font);
    126 }
    127 
    128 static inline pixman_color_t
    129 convert_color(uint32_t clr)
    130 {
    131 	return (pixman_color_t){
    132 		((clr >> 24) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
    133 		((clr >> 16) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
    134 		((clr >> 8) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
    135 		(clr & 0xFF) * 0x101
    136 	};
    137 }
    138 
    139 static void
    140 drwl_setscheme(Drwl *drwl, uint32_t *scm)
    141 {
    142 	if (drwl)
    143 		drwl->scheme = scm;
    144 }
    145 
    146 static Img *
    147 drwl_image_create(Drwl *drwl, unsigned int w, unsigned int h, uint32_t *bits)
    148 {
    149 	Img *image;
    150 	pixman_region32_t clip;
    151 
    152 	image = pixman_image_create_bits_no_clear(
    153 		PIXMAN_a8r8g8b8, w, h, bits, w * 4);
    154 	if (!image)
    155 		return NULL;
    156 	pixman_region32_init_rect(&clip, 0, 0, w, h);
    157 	pixman_image_set_clip_region32(image, &clip);
    158 	pixman_region32_fini(&clip);
    159 
    160 	if (drwl)
    161 		drwl_setimage(drwl, image);
    162 	return image;
    163 }
    164 
    165 static void
    166 drwl_rect(Drwl *drwl,
    167 		int x, int y, unsigned int w, unsigned int h,
    168 		int filled, int invert)
    169 {
    170 	pixman_color_t clr;
    171 	if (!drwl || !drwl->scheme || !drwl->image)
    172 		return;
    173 
    174 	clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
    175 	if (filled)
    176 		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 1,
    177 			&(pixman_rectangle16_t){x, y, w, h});
    178 	else
    179 		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 4,
    180 			(pixman_rectangle16_t[4]){
    181 				{ x,         y,         w, 1 },
    182 				{ x,         y + h - 1, w, 1 },
    183 				{ x,         y,         1, h },
    184 				{ x + w - 1, y,         1, h }});
    185 }
    186 
    187 static int
    188 drwl_text(Drwl *drwl,
    189 		int x, int y, unsigned int w, unsigned int h,
    190 		unsigned int lpad, const char *text, int invert)
    191 {
    192 	int ty;
    193 	int render = x || y || w || h;
    194 	long x_kern;
    195 	uint32_t cp = 0, last_cp = 0, state;
    196 	pixman_color_t clr;
    197 	pixman_image_t *fg_pix = NULL;
    198 	int noellipsis = 0;
    199 	const struct fcft_glyph *glyph, *eg = NULL;
    200 	int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT;
    201 
    202 	if (!drwl || (render && (!drwl->scheme || !w || !drwl->image)) || !text || !drwl->font)
    203 		return 0;
    204 
    205 	if (!render) {
    206 		w = invert ? invert : ~invert;
    207 	} else {
    208 		clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
    209 		fg_pix = pixman_image_create_solid_fill(&clr);
    210 
    211 		drwl_rect(drwl, x, y, w, h, 1, !invert);
    212 
    213 		x += lpad;
    214 		w -= lpad;
    215 	}
    216 
    217 	if (render && (drwl->scheme[ColBg] & 0xFF) != 0xFF)
    218 		fcft_subpixel_mode = FCFT_SUBPIXEL_NONE;
    219 
    220 	if (render)
    221 		eg = fcft_rasterize_char_utf32(drwl->font, 0x2026 /* … */, fcft_subpixel_mode);
    222 
    223 	for (const char *p = text, *pp; pp = p, *p; p++) {
    224 		for (state = UTF8_ACCEPT; *p &&
    225 		     utf8decode(&state, &cp, *p) > UTF8_REJECT; p++)
    226 			;
    227 		if (!*p || state == UTF8_REJECT) {
    228 			cp = UTF8_INVALID;
    229 			if (p > pp)
    230 				p--;
    231 		}
    232 
    233 		glyph = fcft_rasterize_char_utf32(drwl->font, cp, fcft_subpixel_mode);
    234 		if (!glyph)
    235 			continue;
    236 
    237 		x_kern = 0;
    238 		if (last_cp)
    239 			fcft_kerning(drwl->font, last_cp, cp, &x_kern, NULL);
    240 		last_cp = cp;
    241 
    242 		ty = y + (h - drwl->font->height) / 2 + drwl->font->ascent;
    243 
    244 		if (render && !noellipsis && x_kern + glyph->advance.x + eg->advance.x > w &&
    245 		    *(p + 1) != '\0') {
    246 			/* cannot fit ellipsis after current codepoint */
    247 			if (drwl_text(drwl, 0, 0, 0, 0, 0, pp, 0) + x_kern <= w) {
    248 				noellipsis = 1;
    249 			} else {
    250 				w -= eg->advance.x;
    251 				pixman_image_composite32(
    252 					PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->image, 0, 0, 0, 0,
    253 					x + eg->x, ty - eg->y, eg->width, eg->height);
    254 			}
    255 		}
    256 
    257 		if ((x_kern + glyph->advance.x) > w)
    258 			break;
    259 
    260 		x += x_kern;
    261 
    262 		if (render && pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)
    263 			/* pre-rendered glyphs (eg. emoji) */
    264 			pixman_image_composite32(
    265 				PIXMAN_OP_OVER, glyph->pix, NULL, drwl->image, 0, 0, 0, 0,
    266 				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
    267 		else if (render)
    268 			pixman_image_composite32(
    269 				PIXMAN_OP_OVER, fg_pix, glyph->pix, drwl->image, 0, 0, 0, 0,
    270 				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
    271 
    272 		x += glyph->advance.x;
    273 		w -= glyph->advance.x;
    274 	}
    275 
    276 	if (render)
    277 		pixman_image_unref(fg_pix);
    278 
    279 	return x + (render ? w : 0);
    280 }
    281 
    282 static unsigned int
    283 drwl_font_getwidth(Drwl *drwl, const char *text)
    284 {
    285 	if (!drwl || !drwl->font || !text)
    286 		return 0;
    287 	return drwl_text(drwl, 0, 0, 0, 0, 0, text, 0);
    288 }
    289 
    290 static unsigned int
    291 drwl_font_getwidth_clamp(Drwl *drwl, const char *text, unsigned int n)
    292 {
    293 	unsigned int tmp = 0;
    294 	if (drwl && drwl->font && text && n)
    295 		tmp = drwl_text(drwl, 0, 0, 0, 0, 0, text, n);
    296 	return tmp < n ? tmp : n;
    297 }
    298 
    299 static void
    300 drwl_image_destroy(Img *image)
    301 {
    302 	pixman_image_unref(image);
    303 }
    304 
    305 static void
    306 drwl_destroy(Drwl *drwl)
    307 {
    308 	if (drwl->font)
    309 		drwl_font_destroy(drwl->font);
    310 	if (drwl->image)
    311 		drwl_image_destroy(drwl->image);
    312 	free(drwl);
    313 }
    314 
    315 static void
    316 drwl_fini(void)
    317 {
    318 	fcft_fini();
    319 }