popuplayout had two refit stages for a work area shorter than one padded row — 40 pixels — and popupdraw re-checked the layout it had just been handed, against an Imgh that bounded no buffer any more. Pango and Cairo abort rather than return nil, so textinit is void and the layout is never nil; the stride Cairo computes for RGB24 is w*4 by definition; and textdraw set the layout's width and ellipsis twice.
258 lines
5.6 KiB
C
258 lines
5.6 KiB
C
#include "dat.h"
|
|
#include "fn.h"
|
|
#include "test.h"
|
|
|
|
enum
|
|
{
|
|
Guard = 8,
|
|
Rgbmask = 0xffffff,
|
|
};
|
|
|
|
#define Testw (6 * Fontsz)
|
|
#define Testh (3 * Fontsz)
|
|
#define Testn (Testw * Testh)
|
|
|
|
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, Testw, Colfg, &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, Testw, Colfg, &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, Testw, Colfg, &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, Testw, Colfg, 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");
|
|
textinit();
|
|
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, Testw, Colfg, &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);
|
|
textdraw(buf, Testw, Testh, 2*Fontsz, Fontsz, 2*Fontsz, Colfg,
|
|
&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);
|
|
textdraw(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, Testw, Colfg, &heart);
|
|
textdraw(buf, Testw, Testh, 0, 0, Testw, Colfg, &missing);
|
|
}
|
|
checkguards(t, mem);
|
|
free(mem);
|
|
textclose();
|
|
}
|