popup: replace FreeType renderer with PangoCairo

This commit is contained in:
2026-08-14 14:37:30 +09:00
parent 279b9230a4
commit 54355b8190
12 changed files with 349 additions and 266 deletions

View File

@@ -3,14 +3,15 @@
enum
{
Guard = 8,
Testw = 3 * Fontsz,
Testw = 6 * Fontsz,
Testh = 3 * Fontsz,
Testn = Testw * Testh,
Missingn = Fontsz * Fontsz,
Rgbmask = 0xffffff,
};
static u32int guardcolor = 0x5a5a5a5a;
static char *testfonts[] = {
"/usr/share/fonts/noto/NotoColorEmoji.ttf",
"/usr/share/fonts/TTF/DejaVuSans.ttf",
"/usr/share/fonts/TTF/Jigmo.ttf",
"/usr/share/fonts/TTF/Jigmo2.ttf",
@@ -29,38 +30,84 @@ fillpixels(u32int *p, int n, u32int color)
}
static int
hasink(u32int *p, int n)
hasink(u32int *p, int n, u32int bg)
{
int i;
bg &= Rgbmask;
for(i = 0; i < n; i++)
if(p[i] != Colbg)
if((p[i] & Rgbmask) != bg)
return 1;
return 0;
}
static int
renders(Rune r)
hascolors(u32int *p, int n, u32int bg)
{
u32int buf[Fontsz * Fontsz];
u32int c, first;
int b, found, g, i, r;
fillpixels(buf, nelem(buf), Colbg);
putfont(buf, Fontsz, Fontsz, 0, 0, r);
return hasink(buf, nelem(buf));
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, int *minx, int *miny, int *maxx, int *maxy)
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] == Colbg)
if((p[y * Testw + x] & Rgbmask) == bg)
continue;
*minx = min(*minx, x);
*miny = min(*miny, y);
@@ -72,86 +119,124 @@ inkbounds(u32int *p, int *minx, int *miny, int *maxx, int *maxy)
}
static void
checkclip(struct ct *t, u32int *ref, int dx, int dy)
checktext(struct ct *t, u32int *buf, char *utf)
{
u32int *buf, *mem, *want;
int i, sx, sy, x, y;
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;
mem = emalloc((Testn + 2 * Guard) * sizeof mem[0]);
want = emalloc(Testn * sizeof want[0]);
fillpixels(mem, Testn + 2 * Guard, guardcolor);
buf = mem + Guard;
fillpixels(buf, Testn, Colbg);
fillpixels(want, Testn, Colbg);
for(y = 0; y < Testh; y++){
for(x = 0; x < Testw; x++){
sx = x - dx;
sy = y - dy;
if(sx >= 0 && sx < Testw && sy >= 0 && sy < Testh)
want[y * Testw + x] = ref[sy * Testw + sx];
}
}
putfont(buf, Testw, Testh, Fontsz + dx, Fontsz + dy, 'A');
CT_CHECK(t, hasink(buf, Testn));
CT_EQ_MEM(t, want, buf, Testn * sizeof buf[0]);
for(i = 0; i < Guard; i++){
CT_EQ_UINT(t, guardcolor, mem[i]);
CT_EQ_UINT(t, guardcolor, mem[Guard + Testn + i]);
}
free(want);
free(mem);
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)
{
u32int *buf, *missing, *ref;
int i, minx, miny, maxx, maxy;
static char *plain[] = { "A", "", "", "", "", "𠀋" };
u32int *buf, *mem;
Str a, heart, missing;
int i, w;
missing = emalloc((Missingn + 2 * Guard) * sizeof missing[0]);
fillpixels(missing, Missingn + 2 * Guard, guardcolor);
buf = missing + Guard;
fillpixels(buf, Missingn, Colbg);
CT_CHECK(t, !fontinit(missingfonts, nelem(missingfonts)));
putfont(buf, Fontsz, Fontsz, 0, 0, 'A');
for(i = 0; i < Missingn; i++)
mem = emalloc((Testn + 2 * Guard) * sizeof mem[0]);
fillpixels(mem, Testn + 2 * Guard, guardcolor);
buf = mem + Guard;
fillpixels(buf, Testn, Colbg);
a = mkstr("A");
CT_CHECK(t, !textinit(missingfonts, nelem(missingfonts)));
CT_EQ_INT(t, 0, textwidth(&a));
textdraw(buf, Testw, Testh, 0, 0, &a);
for(i = 0; i < Testn; i++)
CT_EQ_UINT(t, Colbg, buf[i]);
for(i = 0; i < Guard; i++){
CT_EQ_UINT(t, guardcolor, missing[i]);
CT_EQ_UINT(t, guardcolor, missing[Guard + Missingn + i]);
}
checkguards(t, mem);
if(!CT_CHECK(t, fontinit(testfonts, nelem(testfonts)))){
free(missing);
if(!CT_CHECK(t, textinit(testfonts, nelem(testfonts)))){
free(mem);
return;
}
CT_CHECK(t, renders('A'));
CT_CHECK(t, renders(0xac00));
CT_CHECK(t, renders(0x4e00));
CT_CHECK(t, renders(0x1f600));
CT_CHECK(t, renders(0x2000b));
fillpixels(missing, Missingn + 2 * Guard, guardcolor);
buf = missing + Guard;
fillpixels(buf, Missingn, Colbg);
putfont(buf, Fontsz, Fontsz, 0, 0, 0x10ffff);
for(i = 0; i < Missingn; i++)
CT_EQ_UINT(t, Colbg, buf[i]);
for(i = 0; i < Guard; i++){
CT_EQ_UINT(t, guardcolor, missing[i]);
CT_EQ_UINT(t, guardcolor, missing[Guard + Missingn + i]);
}
free(missing);
for(i = 0; i < nelem(plain); i++)
checktext(t, buf, plain[i]);
ref = emalloc(Testn * sizeof ref[0]);
fillpixels(ref, Testn, Colbg);
putfont(ref, Testw, Testh, Fontsz, Fontsz, 'A');
if(!CT_CHECK(t, inkbounds(ref, &minx, &miny, &maxx, &maxy))){
free(ref);
return;
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);
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);
}
checkclip(t, ref, -minx - 1, 0);
checkclip(t, ref, Testw - maxx, 0);
checkclip(t, ref, 0, -miny - 1);
checkclip(t, ref, 0, Testh - maxy);
free(ref);
checkguards(t, mem);
free(mem);
}