drwl.h (8100B)
1 /* 2 * drwl - https://codeberg.org/sewn/drwl 3 * 4 * Copyright (c) 2023-2024 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, ColBorder }; /* 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 fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3); 85 return fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR); 86 } 87 88 static Drwl * 89 drwl_create(void) 90 { 91 Drwl *drwl; 92 93 if (!(drwl = calloc(1, sizeof(Drwl)))) 94 return NULL; 95 96 return drwl; 97 } 98 99 static void 100 drwl_setfont(Drwl *drwl, Fnt *font) 101 { 102 if (drwl) 103 drwl->font = font; 104 } 105 106 static void 107 drwl_setimage(Drwl *drwl, Img *image) 108 { 109 if (drwl) 110 drwl->image = image; 111 } 112 113 static Fnt * 114 drwl_font_create(Drwl *drwl, size_t count, 115 const char *names[static count], const char *attributes) 116 { 117 Fnt *font = fcft_from_name(count, names, attributes); 118 if (drwl) 119 drwl_setfont(drwl, font); 120 return font; 121 } 122 123 static void 124 drwl_font_destroy(Fnt *font) 125 { 126 fcft_destroy(font); 127 } 128 129 static inline pixman_color_t 130 convert_color(uint32_t clr) 131 { 132 return (pixman_color_t){ 133 ((clr >> 24) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF, 134 ((clr >> 16) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF, 135 ((clr >> 8) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF, 136 (clr & 0xFF) * 0x101 137 }; 138 } 139 140 static void 141 drwl_setscheme(Drwl *drwl, uint32_t *scm) 142 { 143 if (drwl) 144 drwl->scheme = scm; 145 } 146 147 static Img * 148 drwl_image_create(Drwl *drwl, unsigned int w, unsigned int h, uint32_t *bits) 149 { 150 Img *image; 151 pixman_region32_t clip; 152 153 image = pixman_image_create_bits_no_clear( 154 PIXMAN_a8r8g8b8, w, h, bits, w * 4); 155 if (!image) 156 return NULL; 157 pixman_region32_init_rect(&clip, 0, 0, w, h); 158 pixman_image_set_clip_region32(image, &clip); 159 pixman_region32_fini(&clip); 160 161 if (drwl) 162 drwl_setimage(drwl, image); 163 return image; 164 } 165 166 static void 167 drwl_rect(Drwl *drwl, 168 int x, int y, unsigned int w, unsigned int h, 169 int filled, int invert) 170 { 171 pixman_color_t clr; 172 if (!drwl || !drwl->scheme || !drwl->image) 173 return; 174 175 clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]); 176 if (filled) 177 pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 1, 178 &(pixman_rectangle16_t){x, y, w, h}); 179 else 180 pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 4, 181 (pixman_rectangle16_t[4]){ 182 { x, y, w, 1 }, 183 { x, y + h - 1, w, 1 }, 184 { x, y, 1, h }, 185 { x + w - 1, y, 1, h }}); 186 } 187 188 static int 189 drwl_text(Drwl *drwl, 190 int x, int y, unsigned int w, unsigned int h, 191 unsigned int lpad, const char *text, int invert) 192 { 193 int ty; 194 int render = x || y || w || h; 195 long x_kern; 196 uint32_t cp = 0, last_cp = 0, state; 197 pixman_color_t clr; 198 pixman_image_t *fg_pix = NULL; 199 int noellipsis = 0; 200 const struct fcft_glyph *glyph, *eg = NULL; 201 int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT; 202 203 if (!drwl || (render && (!drwl->scheme || !w || !drwl->image)) || !text || !drwl->font) 204 return 0; 205 206 if (!render) { 207 w = invert ? invert : ~invert; 208 } else { 209 clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]); 210 fg_pix = pixman_image_create_solid_fill(&clr); 211 212 drwl_rect(drwl, x, y, w, h, 1, !invert); 213 214 x += lpad; 215 w -= lpad; 216 } 217 218 if (render && (drwl->scheme[ColBg] & 0xFF) != 0xFF) 219 fcft_subpixel_mode = FCFT_SUBPIXEL_NONE; 220 221 if (render) 222 eg = fcft_rasterize_char_utf32(drwl->font, 0x2026 /* … */, fcft_subpixel_mode); 223 224 for (const char *p = text, *pp; pp = p, *p; p++) { 225 for (state = UTF8_ACCEPT; *p && 226 utf8decode(&state, &cp, *p) > UTF8_REJECT; p++) 227 ; 228 if (!*p || state == UTF8_REJECT) { 229 cp = UTF8_INVALID; 230 if (p > pp) 231 p--; 232 } 233 234 glyph = fcft_rasterize_char_utf32(drwl->font, cp, fcft_subpixel_mode); 235 if (!glyph) 236 continue; 237 238 x_kern = 0; 239 if (last_cp) 240 fcft_kerning(drwl->font, last_cp, cp, &x_kern, NULL); 241 last_cp = cp; 242 243 ty = y + (h - drwl->font->height) / 2 + drwl->font->ascent; 244 245 if (render && !noellipsis && x_kern + glyph->advance.x + eg->advance.x > w && 246 *(p + 1) != '\0') { 247 /* cannot fit ellipsis after current codepoint */ 248 if (drwl_text(drwl, 0, 0, 0, 0, 0, pp, 0) + x_kern <= w) { 249 noellipsis = 1; 250 } else { 251 w -= eg->advance.x; 252 pixman_image_composite32( 253 PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->image, 0, 0, 0, 0, 254 x + eg->x, ty - eg->y, eg->width, eg->height); 255 } 256 } 257 258 if ((x_kern + glyph->advance.x) > w) 259 break; 260 261 x += x_kern; 262 263 if (render && pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) 264 /* pre-rendered glyphs (eg. emoji) */ 265 pixman_image_composite32( 266 PIXMAN_OP_OVER, glyph->pix, NULL, drwl->image, 0, 0, 0, 0, 267 x + glyph->x, ty - glyph->y, glyph->width, glyph->height); 268 else if (render) 269 pixman_image_composite32( 270 PIXMAN_OP_OVER, fg_pix, glyph->pix, drwl->image, 0, 0, 0, 0, 271 x + glyph->x, ty - glyph->y, glyph->width, glyph->height); 272 273 x += glyph->advance.x; 274 w -= glyph->advance.x; 275 } 276 277 if (render) 278 pixman_image_unref(fg_pix); 279 280 return x + (render ? w : 0); 281 } 282 283 static unsigned int 284 drwl_font_getwidth(Drwl *drwl, const char *text) 285 { 286 if (!drwl || !drwl->font || !text) 287 return 0; 288 return drwl_text(drwl, 0, 0, 0, 0, 0, text, 0); 289 } 290 291 static void 292 drwl_image_destroy(Img *image) 293 { 294 pixman_image_unref(image); 295 } 296 297 static void 298 drwl_destroy(Drwl *drwl) 299 { 300 if (drwl->font) 301 drwl_font_destroy(drwl->font); 302 if (drwl->image) 303 drwl_image_destroy(drwl->image); 304 free(drwl); 305 } 306 307 static void 308 drwl_fini(void) 309 { 310 fcft_fini(); 311 }