#include "test.h" enum { Guard = 8, Testw = 6 * Fontsz, Testh = 3 * Fontsz, Testn = Testw * Testh, Rgbmask = 0xffffff, }; static u32int guardcolor = 0x5a5a5a5a; static void fillpixels(u32int *p, int n, u32int color) { int i; for(i = 0; i < n; i++) p[i] = color; } static int hasink(u32int *p, int n, u32int bg) { int i; bg &= Rgbmask; for(i = 0; i < n; i++) if((p[i] & Rgbmask) != bg) return 1; return 0; } static int hascolors(u32int *p, int n, u32int bg) { u32int c, first; int b, found, g, i, r; bg &= Rgbmask; first = 0; found = 0; for(i = 0; i < n; i++){ c = p[i] & Rgbmask; if(c == bg) continue; r = c >> 16 & 0xff; g = c >> 8 & 0xff; b = c & 0xff; if(r == g && g == b) continue; if(!found){ first = c; found = 1; }else if(c != first) return 1; } return 0; } static void checkguards(struct ct *t, u32int *mem) { int i; for(i = 0; i < Guard; i++){ CT_EQ_UINT(t, guardcolor, mem[i]); CT_EQ_UINT(t, guardcolor, mem[Guard + Testn + i]); } } static void checkoutside(struct ct *t, u32int *buf, u32int bg, int row) { int x, y, y0, y1; bg &= Rgbmask; y0 = max(row, 0); y1 = min(row + Fontsz, Testh); for(y = 0; y < Testh; y++){ if(y >= y0 && y < y1) continue; for(x = 0; x < Testw; x++) CT_EQ_UINT(t, bg, buf[y * Testw + x] & Rgbmask); } } static int inkbounds(u32int *p, u32int bg, int *minx, int *miny, int *maxx, int *maxy) { int x, y; bg &= Rgbmask; *minx = Testw; *miny = Testh; *maxx = -1; *maxy = -1; for(y = 0; y < Testh; y++){ for(x = 0; x < Testw; x++){ if((p[y * Testw + x] & Rgbmask) == bg) continue; *minx = min(*minx, x); *miny = min(*miny, y); *maxx = max(*maxx, x); *maxy = max(*maxy, y); } } return *maxx >= 0; } static void checktext(struct ct *t, u32int *buf, char *utf) { Str s; int maxx, maxy, minx, miny, w; s = mkstr(utf); w = textwidth(&s); CT_CHECK(t, w > 0); CT_CHECK(t, w < Testw - Fontsz); fillpixels(buf, Testn, Colbg); textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s); if(!CT_CHECK(t, inkbounds(buf, Colbg, &minx, &miny, &maxx, &maxy))) return; CT_CHECK(t, minx >= 0 && miny >= 0); CT_CHECK(t, maxx < Testw && maxy < Testh); CT_CHECK(t, maxx - minx + 1 <= w); } static void checkcolor(struct ct *t, u32int *buf, char *utf) { Str s; int w; s = mkstr(utf); w = textwidth(&s); CT_CHECK(t, w > 0); if(s.n > 1) CT_CHECK(t, w < s.n * Fontsz); fillpixels(buf, Testn, Colbg); textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s); CT_CHECK(t, hasink(buf, Testn, Colbg)); CT_CHECK(t, hascolors(buf, Testn, Colbg)); } static void checkshape(struct ct *t, u32int *buf, char *utf) { Str s; int w; s = mkstr(utf); w = textwidth(&s); CT_CHECK(t, w > 0); CT_CHECK(t, w < s.n * Fontsz); fillpixels(buf, Testn, Colbg); textdraw(buf, Testw, Testh, Fontsz, Fontsz, &s); CT_CHECK(t, hasink(buf, Testn, Colbg)); } static void checkclip(struct ct *t, u32int *mem, int x, int y, Str *s) { u32int *buf; buf = mem + Guard; fillpixels(buf, Testn, Colbg); textdraw(buf, Testw, Testh, x, y, s); CT_CHECK(t, hasink(buf, Testn, Colbg)); checkoutside(t, buf, Colbg, y); checkguards(t, mem); } void font_render(struct ct *t) { static char *plain[] = { "A", "가", "あ", "漢", "☆", "𠀋" }; u32int *buf, *mem; Str a, heart, longrow, missing; int i, maxx, maxy, minx, miny, natural, w, x, y; mem = emalloc((Testn + 2 * Guard) * sizeof mem[0]); fillpixels(mem, Testn + 2 * Guard, guardcolor); buf = mem + Guard; fillpixels(buf, Testn, Colbg); a = mkstr("A"); if(!CT_CHECK(t, textinit())){ free(mem); return; } for(i = 0; i < nelem(plain); i++) checktext(t, buf, plain[i]); checkcolor(t, buf, "😀"); checkcolor(t, buf, "❤️"); checkcolor(t, buf, "☕️"); checkshape(t, buf, "👩‍💻"); checkshape(t, buf, "👍🏽"); checkshape(t, buf, "🇰🇷"); checkshape(t, buf, "☕︎"); heart = mkstr("❤️"); fillpixels(buf, Testn, Colsel); textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart); CT_CHECK(t, hascolors(buf, Testn, Colsel)); CT_EQ_UINT(t, Colsel, buf[(Fontsz + Fontsz/2) * Testw + Testw - 1] & Rgbmask); checkoutside(t, buf, Colsel, Fontsz); checkguards(t, mem); w = textwidth(&a); checkclip(t, mem, -w / 2, Fontsz, &a); checkclip(t, mem, Testw - w / 2, Fontsz, &a); checkclip(t, mem, Fontsz, -Fontsz / 2, &a); checkclip(t, mem, Fontsz, Testh - Fontsz / 2, &a); longrow = mkstr("abcdefghijklmnopqrstuvwxyz"); natural = textwidth(&longrow); CT_CHECK(t, natural > 2*Fontsz); fillpixels(buf, Testn, Colbg); textdrawfit(buf, Testw, Testh, 2*Fontsz, Fontsz, 2*Fontsz, &longrow); CT_CHECK(t, hasink(buf, Testn, Colbg)); for(y = Fontsz; y < 2*Fontsz; y++){ for(x = 0; x < 2*Fontsz; x++) CT_EQ_UINT(t, Colbg, buf[y * Testw + x] & Rgbmask); for(x = 4*Fontsz; x < Testw; x++) CT_EQ_UINT(t, Colbg, buf[y * Testw + x] & Rgbmask); } CT_EQ_INT(t, natural, textwidth(&longrow)); fillpixels(buf, Testn, Colsel); textdrawfitcolor(buf, Testw, Testh, 2*Fontsz, Fontsz, 2*Fontsz, Colselfg, &longrow); CT_CHECK(t, (Colselfg & Rgbmask) != (Colsel & Rgbmask)); if(CT_CHECK(t, inkbounds(buf, Colsel, &minx, &miny, &maxx, &maxy))){ CT_CHECK(t, minx >= 2*Fontsz); CT_CHECK(t, maxx < 4*Fontsz); CT_CHECK(t, miny >= Fontsz); CT_CHECK(t, maxy < 2*Fontsz); } checkoutside(t, buf, Colsel, Fontsz); checkguards(t, mem); CT_EQ_INT(t, natural, textwidth(&longrow)); missing = mkstr("\xF4\x8F\xBF\xBF"); for(i = 0; i < 32; i++){ textdraw(buf, Testw, Testh, Fontsz, Fontsz, &heart); textdraw(buf, Testw, Testh, 0, 0, &missing); } checkguards(t, mem); free(mem); }