ref: 62022bf26323631825e31b497ab96973e4678457
dir: /libpt/pt.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <keyboard.h>
#include <memdraw.h>
#include <mouse.h>
#include <thread.h>
#include "pt.h"
#define MAX(a,b) ((a)>=(b)?(a):(b))
#define rchr(s) (be ? ((s)[0]<<8 | (s)[1]) : ((s)[1]<<8 | (s)[0]))
static const uchar mark[] = {0x00, 0x00, 0xc0, 0xe0, 0xf0};
static int
utf16to8(uchar *o, int osz, const uchar *s, int sz)
{
int i, be, c, c2, wr, j;
i = 0;
be = 1;
if(s[0] == 0xfe && s[1] == 0xff)
i += 2;
else if(s[0] == 0xff && s[1] == 0xfe){
be = 0;
i += 2;
}
for(; i < sz-1 && osz > 1;){
c = rchr(&s[i]);
i += 2;
if(c >= 0xd800 && c <= 0xdbff && i < sz-1){
c2 = rchr(&s[i]);
if(c2 >= 0xdc00 && c2 <= 0xdfff){
c = 0x10000 | (c - 0xd800)<<10 | (c2 - 0xdc00);
i += 2;
}else
return -1;
}else if(c >= 0xdc00 && c <= 0xdfff)
return -1;
if(c < 0x80)
wr = 1;
else if(c < 0x800)
wr = 2;
else if(c < 0x10000)
wr = 3;
else
wr = 4;
osz -= wr;
if(osz < 1)
break;
o += wr;
for(j = wr; j > 1; j--){
*(--o) = (c & 0xbf) | 0x80;
c >>= 6;
}
*(--o) = c | mark[wr];
o += wr;
}
*o = 0;
return i;
}
#define STB_TRUETYPE_IMPLEMENTATION
static void *
STBTT_malloc(int x, void *)
{
return malloc(x);
}
static void
STBTT_free(void *p, void *)
{
free(p);
}
#define STBTT_malloc STBTT_malloc
#define STBTT_free STBTT_free
#define STBTT_ifloor(x) ((int)floor(x))
#define STBTT_iceil(x) ((int)ceil(x))
#define STBTT_sqrt(x) sqrt(x)
#define STBTT_pow(x,y) pow(x,y)
#define STBTT_fmod(x,y) fmod(x,y)
#define STBTT_cos(x) cos(x)
#define STBTT_acos(x) acos(x)
#define STBTT_fabs(x) fabs(x)
#define STBTT_assert(x) assert(x)
#define STBTT_strlen(x) strlen(x)
#define STBTT_memcpy memmove
#define STBTT_memset memset
typedef usize size_t;
#define NULL nil
#define STBTT_RASTERIZER_VERSION 2 /* that one is definitely FASTER on amd64 */
#include "pt_private.h"
Sfont *
pt_font(uchar *data, float height)
{
Sfont *f;
f = mallocz(sizeof(*f), 1);
if(stbtt_InitFont(&f->fi, data, stbtt_GetFontOffsetForIndex(data, 0)) == 0){
werrstr("stbtt_InitFont: %r");
goto err;
}
f->scale = stbtt_ScaleForPixelHeight(&f->fi, height);
f->height = height;
stbtt_GetFontVMetrics(&f->fi, &f->asc, &f->desc, &f->linegap);
f->lineh = f->asc - f->desc + f->linegap;
stbtt_GetFontBoundingBox(&f->fi, &f->bb.min.x, &f->bb.min.y, &f->bb.max.x, &f->bb.max.y);
f->bb.max.x = ceil(f->scale*(f->bb.max.x - f->bb.min.x));
f->bb.max.y = ceil(f->scale*(f->bb.max.y - f->bb.min.y));
f->bb.min.x = 0;
f->bb.min.y = 0;
f->bb.max.x = (f->bb.max.x + 3) & ~3;
f->bb.max.y = (f->bb.max.y + 3) & ~3;
if(badrect(f->bb)){
werrstr("invalid glyph bbox: %R", f->bb);
goto err;
}
if((f->g = allocmemimage(f->bb, GREY8)) == nil)
goto err;
if((f->b = allocmemimage(f->bb, GREY8)) == nil)
goto err;
memfillcolor(f->b, DWhite);
return f;
err:
werrstr("pt_font: %r");
pt_freefont(f);
return nil;
}
void
pt_freefont(Sfont *f)
{
if(f == nil)
return;
freememimage(f->g);
freememimage(f->b);
free(f);
}
static u32int *
bsearch(u32int c, u32int *t, int n)
{
u32int *p;
int m;
while(n > 1){
m = n/2;
p = t + m;
if(c >= p[0]) {
t = p;
n = n-m;
} else
n = m;
}
if(n && c >= t[0])
return t;
return 0;
}
static int
code2glyph(Sfont *f, u32int c)
{
return stbtt_FindGlyphIndex(&f->fi, c);
}
Rectangle
pt_textrect(Sfont *f, char *s)
{
int i, n;
int glyph, prevglyph, adv, kern, leftb;
int w, h, maxw;
Rune r;
w = maxw = 0;
h = f->lineh;
prevglyph = 0;
for(i = 0; s[i]; i += n){
n = chartorune(&r, s+i);
glyph = code2glyph(f, r);
if(r != '\n' && glyph > 0){
stbtt_GetGlyphHMetrics(&f->fi, glyph, &adv, &leftb);
kern = prevglyph ? stbtt_GetGlyphKernAdvance(&f->fi, prevglyph, glyph) : 0;
w += adv + kern;
}else
glyph = 0;
prevglyph = glyph;
maxw = MAX(maxw, w);
if(r == '\n'){
h += f->lineh;
w = 0;
}
}
w = ceil(f->scale*maxw);
h = ceil(f->scale*(h+1));
return Rect(0, 0, w, h);
}
Image *
pt_textdraw(Sfont *f, char *s, Rectangle rect, Sdopts *opts)
{
int i, base, n, x0, y0, prefilter;
int glyph, prevglyph, adv, leftb;
float x, shift, subx, suby;
Rune r;
Rectangle o;
Memimage *mi;
Image *image;
uchar *p;
if((mi = allocmemimage(rect, GREY8)) == nil)
return nil;
memfillcolor(mi, DWhite);
prevglyph = 0;
x = 0.0f;
o = rect;
base = 0;
prefilter = opts != nil ? opts->prefilter : 0;
if(prefilter > STBTT_MAX_OVERSAMPLE)
prefilter = STBTT_MAX_OVERSAMPLE;
for(i = 0; s[i]; i += n){
n = chartorune(&r, s+i);
glyph = code2glyph(f, r);
if(prevglyph > 0 && glyph > 0 && r != '\n')
x += stbtt_GetGlyphKernAdvance(&f->fi, prevglyph, glyph) * f->scale;
shift = x - floor(x);
if(r != '\n' && glyph > 0){
memfillcolor(f->g, DBlack);
if(prefilter > 0){
stbtt_MakeGlyphBitmapSubpixelPrefilter(
&f->fi,
byteaddr(f->g, f->bb.min),
Dx(f->bb), Dy(f->bb),
bytesperline(f->bb, f->g->depth),
f->scale, f->scale,
shift, 0.0f,
prefilter, prefilter,
&subx, &suby,
glyph
);
}else{
stbtt_MakeGlyphBitmapSubpixel(
&f->fi,
byteaddr(f->g, f->bb.min),
Dx(f->bb), Dy(f->bb),
bytesperline(f->bb, f->g->depth),
f->scale, f->scale,
shift, 0.0f,
glyph
);
}
stbtt_GetGlyphBitmapBoxSubpixel(
&f->fi,
glyph,
f->scale, f->scale,
shift, 0.0f,
&x0, &y0, nil, nil
);
o.min.x = floor(x) + x0;
o.min.y = base + f->asc*f->scale + y0;
memimagedraw(mi, o, f->b, ZP, f->g, ZP, DoutS);
stbtt_GetGlyphHMetrics(&f->fi, glyph, &adv, &leftb);
x += adv * f->scale;
}else
glyph = 0;
prevglyph = glyph;
if(r == '\n'){
x = x0 = 0;
prevglyph = 0;
base += ceil(f->scale * f->lineh);
}
}
n = 60 + Dx(rect)*Dy(rect);
p = malloc(n);
n = unloadmemimage(mi, rect, p, n);
freememimage(mi);
image = allocimage(display, rect, GREY8, 0, DNofill);
if(opts != nil && opts->gamma != 1.0){
float igamma = 1.0/opts->gamma;
for(i = 0; i < n; i++){
if(p[i] < 255){
float g = pow(p[i], igamma);
p[i] = g < 256 ? g : 255;
}
}
}
for(i = 0; i < n; i++)
p[i] = 255 - p[i];
loadimage(image, rect, p, n);
free(p);
return image;
}