diff options
Diffstat (limited to 'drwl.h')
-rw-r--r-- | drwl.h | 311 |
1 files changed, 311 insertions, 0 deletions
@@ -0,0 +1,311 @@ +/* + * drwl - https://codeberg.org/sewn/drwl + * + * Copyright (c) 2023-2024 sewn <sewn@disroot.org> + * Copyright (c) 2024 notchoc <notchoc@disroot.org> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The UTF-8 Decoder included is from Bjoern Hoehrmann: + * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> + * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + */ +#pragma once + +#include <stdlib.h> +#include <fcft/fcft.h> +#include <pixman-1/pixman.h> + +enum { ColFg, ColBg, ColBorder }; /* colorscheme index */ + +typedef struct fcft_font Fnt; +typedef pixman_image_t Img; + +typedef struct { + Img *image; + Fnt *font; + uint32_t *scheme; +} Drwl; + +#define UTF8_ACCEPT 0 +#define UTF8_REJECT 12 +#define UTF8_INVALID 0xFFFD + +static const uint8_t utf8d[] = { + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, + 12,36,12,12,12,12,12,12,12,12,12,12, +}; + +static inline uint32_t +utf8decode(uint32_t *state, uint32_t *codep, uint8_t byte) +{ + uint32_t type = utf8d[byte]; + + *codep = (*state != UTF8_ACCEPT) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state + type]; + return *state; +} + +static int +drwl_init(void) +{ + fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3); + return fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR); +} + +static Drwl * +drwl_create(void) +{ + Drwl *drwl; + + if (!(drwl = calloc(1, sizeof(Drwl)))) + return NULL; + + return drwl; +} + +static void +drwl_setfont(Drwl *drwl, Fnt *font) +{ + if (drwl) + drwl->font = font; +} + +static void +drwl_setimage(Drwl *drwl, Img *image) +{ + if (drwl) + drwl->image = image; +} + +static Fnt * +drwl_font_create(Drwl *drwl, size_t count, + const char *names[static count], const char *attributes) +{ + Fnt *font = fcft_from_name(count, names, attributes); + if (drwl) + drwl_setfont(drwl, font); + return font; +} + +static void +drwl_font_destroy(Fnt *font) +{ + fcft_destroy(font); +} + +static inline pixman_color_t +convert_color(uint32_t clr) +{ + return (pixman_color_t){ + ((clr >> 24) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF, + ((clr >> 16) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF, + ((clr >> 8) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF, + (clr & 0xFF) * 0x101 + }; +} + +static void +drwl_setscheme(Drwl *drwl, uint32_t *scm) +{ + if (drwl) + drwl->scheme = scm; +} + +static Img * +drwl_image_create(Drwl *drwl, unsigned int w, unsigned int h, uint32_t *bits) +{ + Img *image; + pixman_region32_t clip; + + image = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, w, h, bits, w * 4); + if (!image) + return NULL; + pixman_region32_init_rect(&clip, 0, 0, w, h); + pixman_image_set_clip_region32(image, &clip); + pixman_region32_fini(&clip); + + if (drwl) + drwl_setimage(drwl, image); + return image; +} + +static void +drwl_rect(Drwl *drwl, + int x, int y, unsigned int w, unsigned int h, + int filled, int invert) +{ + pixman_color_t clr; + if (!drwl || !drwl->scheme || !drwl->image) + return; + + clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]); + if (filled) + pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 1, + &(pixman_rectangle16_t){x, y, w, h}); + else + pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 4, + (pixman_rectangle16_t[4]){ + { x, y, w, 1 }, + { x, y + h - 1, w, 1 }, + { x, y, 1, h }, + { x + w - 1, y, 1, h }}); +} + +static int +drwl_text(Drwl *drwl, + int x, int y, unsigned int w, unsigned int h, + unsigned int lpad, const char *text, int invert) +{ + int ty; + int render = x || y || w || h; + long x_kern; + uint32_t cp = 0, last_cp = 0, state; + pixman_color_t clr; + pixman_image_t *fg_pix = NULL; + int noellipsis = 0; + const struct fcft_glyph *glyph, *eg = NULL; + int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT; + + if (!drwl || (render && (!drwl->scheme || !w || !drwl->image)) || !text || !drwl->font) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } else { + clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]); + fg_pix = pixman_image_create_solid_fill(&clr); + + drwl_rect(drwl, x, y, w, h, 1, !invert); + + x += lpad; + w -= lpad; + } + + if (render && (drwl->scheme[ColBg] & 0xFF) != 0xFF) + fcft_subpixel_mode = FCFT_SUBPIXEL_NONE; + + if (render) + eg = fcft_rasterize_char_utf32(drwl->font, 0x2026 /* … */, fcft_subpixel_mode); + + for (const char *p = text, *pp; pp = p, *p; p++) { + for (state = UTF8_ACCEPT; *p && + utf8decode(&state, &cp, *p) > UTF8_REJECT; p++) + ; + if (!*p || state == UTF8_REJECT) { + cp = UTF8_INVALID; + if (p > pp) + p--; + } + + glyph = fcft_rasterize_char_utf32(drwl->font, cp, fcft_subpixel_mode); + if (!glyph) + continue; + + x_kern = 0; + if (last_cp) + fcft_kerning(drwl->font, last_cp, cp, &x_kern, NULL); + last_cp = cp; + + ty = y + (h - drwl->font->height) / 2 + drwl->font->ascent; + + if (render && !noellipsis && x_kern + glyph->advance.x + eg->advance.x > w && + *(p + 1) != '\0') { + /* cannot fit ellipsis after current codepoint */ + if (drwl_text(drwl, 0, 0, 0, 0, 0, pp, 0) + x_kern <= w) { + noellipsis = 1; + } else { + w -= eg->advance.x; + pixman_image_composite32( + PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->image, 0, 0, 0, 0, + x + eg->x, ty - eg->y, eg->width, eg->height); + } + } + + if ((x_kern + glyph->advance.x) > w) + break; + + x += x_kern; + + if (render && pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8) + /* pre-rendered glyphs (eg. emoji) */ + pixman_image_composite32( + PIXMAN_OP_OVER, glyph->pix, NULL, drwl->image, 0, 0, 0, 0, + x + glyph->x, ty - glyph->y, glyph->width, glyph->height); + else if (render) + pixman_image_composite32( + PIXMAN_OP_OVER, fg_pix, glyph->pix, drwl->image, 0, 0, 0, 0, + x + glyph->x, ty - glyph->y, glyph->width, glyph->height); + + x += glyph->advance.x; + w -= glyph->advance.x; + } + + if (render) + pixman_image_unref(fg_pix); + + return x + (render ? w : 0); +} + +static unsigned int +drwl_font_getwidth(Drwl *drwl, const char *text) +{ + if (!drwl || !drwl->font || !text) + return 0; + return drwl_text(drwl, 0, 0, 0, 0, 0, text, 0); +} + +static void +drwl_image_destroy(Img *image) +{ + pixman_image_unref(image); +} + +static void +drwl_destroy(Drwl *drwl) +{ + if (drwl->font) + drwl_font_destroy(drwl->font); + if (drwl->image) + drwl_image_destroy(drwl->image); + free(drwl); +} + +static void +drwl_fini(void) +{ + fcft_fini(); +} |