Showing the changes to test.c (1 of 5 files). Show all
Unified · Split
| @@ -5,115 +5,22 @@ · 4 unmodified lines | |||
| 5 | #include <memdraw.h> | 5 | #include <memdraw.h> |
| 6 | #include <mouse.h> | 6 | #include <mouse.h> |
| 7 | #include <thread.h> | 7 | #include <thread.h> |
| 8 | #define STB_TRUETYPE_IMPLEMENTATION | 8 | #include "pt.h" |
| 9 | #define STBTT_ifloor(x) ((int)floor(x)) | ||
| 10 | #define STBTT_iceil(x) ((int)ceil(x)) | ||
| 11 | #define STBTT_sqrt(x) sqrt(x) | ||
| 12 | #define STBTT_pow(x,y) pow(x,y) | ||
| 13 | #define STBTT_fmod(x,y) fmod(x,y) | ||
| 14 | #define STBTT_cos(x) cos(x) | ||
| 15 | #define STBTT_acos(x) acos(x) | ||
| 16 | #define STBTT_fabs(x) fabs(x) | ||
| 17 | #define STBTT_malloc(x,u) ((void)(u),malloc(x)) | ||
| 18 | #define STBTT_free(x,u) ((void)(u),free(x)) | ||
| 19 | #define STBTT_assert(x) assert(x) | ||
| 20 | #define STBTT_strlen(x) strlen(x) | ||
| 21 | #define STBTT_memcpy memmove | ||
| 22 | #define STBTT_memset memset | ||
| 23 | typedef usize size_t; | ||
| 24 | #define NULL nil | ||
| 25 | #define STBTT_RASTERIZER_VERSION 1 /* that one is definitely FASTER on amd64 */ | ||
| 26 | static int utf16to8(uchar *o, int osz, const uchar *s, int sz); | ||
| 27 | #include "stb_truetype.h" | ||
| 28 | 9 | ||
| 29 | #define MAX(a,b) ((a)>=(b)?(a):(b)) | 10 | #define MAX(a,b) ((a)>=(b)?(a):(b)) |
| 30 | 11 | ||
| 31 | typedef struct Sfont Sfont; | 12 | static Sfont *fo; |
| 32 | |||
| 33 | struct Sfont { | ||
| 34 | Memimage *g; | ||
| 35 | Memimage *white; | ||
| 36 | |||
| 37 | stbtt_code2glyph *c2g; | ||
| 38 | |||
| 39 | Rectangle bb; | ||
| 40 | float scale; | ||
| 41 | float height; | ||
| 42 | int asc; | ||
| 43 | int desc; | ||
| 44 | int linegap; | ||
| 45 | int lineh; | ||
| 46 | |||
| 47 | stbtt_fontinfo fi; | ||
| 48 | }; | ||
| 49 | |||
| 50 | static Sfont fo; | ||
| 51 | static Image *image; | 13 | static Image *image; |
| 52 | static int invert, prefilter; | 14 | static int invert, prefilter; |
| 53 | static Point offset = {8, 8}; | 15 | static Point offset = {8, 8}; |
| 54 | static float gamma = 1.0; | 16 | static Sdopts opts = { |
| 17 | .invert = 0, | ||
| 18 | .prefilter = 0, | ||
| 19 | .gamma = 1.0, | ||
| 20 | }; | ||
| 55 | static char info[128]; | 21 | static char info[128]; |
| 56 | int mainstacksize = 32768; | 22 | int mainstacksize = 32768; |
| 57 | 23 | ||
| 58 | uvlong nanosec(void); | ||
| 59 | |||
| 60 | #define rchr(s) (be ? ((s)[0]<<8 | (s)[1]) : ((s)[1]<<8 | (s)[0])) | ||
| 61 | |||
| 62 | static const uchar mark[] = {0x00, 0x00, 0xc0, 0xe0, 0xf0}; | ||
| 63 | |||
| 64 | static int | ||
| 65 | utf16to8(uchar *o, int osz, const uchar *s, int sz) | ||
| 66 | { | ||
| 67 | int i, be, c, c2, wr, j; | ||
| 68 | |||
| 69 | i = 0; | ||
| 70 | be = 1; | ||
| 71 | if(s[0] == 0xfe && s[1] == 0xff) | ||
| 72 | i += 2; | ||
| 73 | else if(s[0] == 0xff && s[1] == 0xfe){ | ||
| 74 | be = 0; | ||
| 75 | i += 2; | ||
| 76 | } | ||
| 77 | |||
| 78 | for(; i < sz-1 && osz > 1;){ | ||
| 79 | c = rchr(&s[i]); | ||
| 80 | i += 2; | ||
| 81 | if(c >= 0xd800 && c <= 0xdbff && i < sz-1){ | ||
| 82 | c2 = rchr(&s[i]); | ||
| 83 | if(c2 >= 0xdc00 && c2 <= 0xdfff){ | ||
| 84 | c = 0x10000 | (c - 0xd800)<<10 | (c2 - 0xdc00); | ||
| 85 | i += 2; | ||
| 86 | }else | ||
| 87 | return -1; | ||
| 88 | }else if(c >= 0xdc00 && c <= 0xdfff) | ||
| 89 | return -1; | ||
| 90 | |||
| 91 | if(c < 0x80) | ||
| 92 | wr = 1; | ||
| 93 | else if(c < 0x800) | ||
| 94 | wr = 2; | ||
| 95 | else if(c < 0x10000) | ||
| 96 | wr = 3; | ||
| 97 | else | ||
| 98 | wr = 4; | ||
| 99 | |||
| 100 | osz -= wr; | ||
| 101 | if(osz < 1) | ||
| 102 | break; | ||
| 103 | |||
| 104 | o += wr; | ||
| 105 | for(j = wr; j > 1; j--){ | ||
| 106 | *(--o) = (c & 0xbf) | 0x80; | ||
| 107 | c >>= 6; | ||
| 108 | } | ||
| 109 | *(--o) = c | mark[wr]; | ||
| 110 | o += wr; | ||
| 111 | } | ||
| 112 | |||
| 113 | *o = 0; | ||
| 114 | return i; | ||
| 115 | } | ||
| 116 | |||
| 117 | static void | 24 | static void |
| 118 | redraw(void) | 25 | redraw(void) |
| 119 | { | 26 | { |
| @@ -168,230 +75,6 @@ end: · 48 unmodified lines | |||
| 168 | return d; | 75 | return d; |
| 169 | } | 76 | } |
| 170 | 77 | ||
| 171 | int | ||
| 172 | initfont(Sfont *f, uchar *data, float height) | ||
| 173 | { | ||
| 174 | if(data != nil && stbtt_InitFont(&f->fi, data, stbtt_GetFontOffsetForIndex(data, 0)) == 0) | ||
| 175 | return -1; | ||
| 176 | |||
| 177 | free(f->c2g); | ||
| 178 | f->c2g = stbtt_Code2Glyph(&f->fi); | ||
| 179 | |||
| 180 | f->scale = stbtt_ScaleForPixelHeight(&f->fi, height); | ||
| 181 | f->height = height; | ||
| 182 | stbtt_GetFontVMetrics(&f->fi, &f->asc, &f->desc, &f->linegap); | ||
| 183 | f->lineh = f->asc - f->desc + f->linegap; | ||
| 184 | |||
| 185 | stbtt_GetFontBoundingBox(&f->fi, &f->bb.min.x, &f->bb.min.y, &f->bb.max.x, &f->bb.max.y); | ||
| 186 | f->bb.max.x = ceil(f->scale*(f->bb.max.x - f->bb.min.x)); | ||
| 187 | f->bb.max.y = ceil(f->scale*(f->bb.max.y - f->bb.min.y)); | ||
| 188 | f->bb.min.x = 0; | ||
| 189 | f->bb.min.y = 0; | ||
| 190 | |||
| 191 | f->bb.max.x = (f->bb.max.x + 3) & ~3; | ||
| 192 | f->bb.max.y = (f->bb.max.y + 3) & ~3; | ||
| 193 | |||
| 194 | freememimage(f->g); | ||
| 195 | if((f->g = allocmemimage(f->bb, GREY8)) == nil) | ||
| 196 | return -1; | ||
| 197 | |||
| 198 | freememimage(f->white); | ||
| 199 | if((f->white = allocmemimage(f->bb, GREY8)) == nil) | ||
| 200 | return -1; | ||
| 201 | memfillcolor(f->white, DWhite); | ||
| 202 | |||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | static u32int * | ||
| 207 | bsearch(u32int c, u32int *t, int n) | ||
| 208 | { | ||
| 209 | u32int *p; | ||
| 210 | int m; | ||
| 211 | |||
| 212 | while(n > 1){ | ||
| 213 | m = n/2; | ||
| 214 | p = t + m; | ||
| 215 | if(c >= p[0]) { | ||
| 216 | t = p; | ||
| 217 | n = n-m; | ||
| 218 | } else | ||
| 219 | n = m; | ||
| 220 | } | ||
| 221 | if(n && c >= t[0]) | ||
| 222 | return t; | ||
| 223 | return 0; | ||
| 224 | } | ||
| 225 | |||
| 226 | int | ||
| 227 | code2glyph(Sfont *f, u32int c) | ||
| 228 | { | ||
| 229 | u32int *r; | ||
| 230 | |||
| 231 | if((r = bsearch(c, f->c2g->code, f->c2g->num)) != nil) | ||
| 232 | return f->c2g->glyph[r - f->c2g->code]; | ||
| 233 | return 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | Rectangle | ||
| 237 | textrect(Sfont *f, char *s) | ||
| 238 | { | ||
| 239 | int i, n; | ||
| 240 | int glyph, prevglyph, adv, kern, leftb; | ||
| 241 | int w, h, maxw; | ||
| 242 | Rune r; | ||
| 243 | |||
| 244 | w = maxw = 0; | ||
| 245 | h = f->lineh; | ||
| 246 | prevglyph = 0; | ||
| 247 | for(i = 0; s[i]; i += n){ | ||
| 248 | n = chartorune(&r, s+i); | ||
| 249 | glyph = code2glyph(f, r); | ||
| 250 | if(r != '\n' && glyph > 0){ | ||
| 251 | stbtt_GetGlyphHMetrics(&f->fi, glyph, &adv, &leftb); | ||
| 252 | kern = prevglyph ? stbtt_GetGlyphKernAdvance(&f->fi, prevglyph, glyph) : 0; | ||
| 253 | w += adv + kern; | ||
| 254 | }else | ||
| 255 | glyph = 0; | ||
| 256 | prevglyph = glyph; | ||
| 257 | maxw = MAX(maxw, w); | ||
| 258 | if(r == '\n'){ | ||
| 259 | h += f->lineh; | ||
| 260 | w = 0; | ||
| 261 | } | ||
| 262 | } | ||
| 263 | w = ceil(f->scale*maxw); | ||
| 264 | h = ceil(f->scale*(h+1)); | ||
| 265 | |||
| 266 | return Rect(0, 0, w, h); | ||
| 267 | } | ||
| 268 | |||
| 269 | static void | ||
| 270 | prepare(Sfont *f, char *s) | ||
| 271 | { | ||
| 272 | int i, base, n, x0, y0; | ||
| 273 | int glyph, prevglyph, adv, leftb; | ||
| 274 | float x, shift, subx, suby; | ||
| 275 | Rune r; | ||
| 276 | Rectangle rect, o; | ||
| 277 | Memimage *mi; | ||
| 278 | uchar *p; | ||
| 279 | uvlong t[8][3]; | ||
| 280 | |||
| 281 | memset(t, 0, sizeof(t)); | ||
| 282 | #define STATSTART(i) do{ \ | ||
| 283 | t[i][0] = nanosec(); \ | ||
| 284 | }while(0) | ||
| 285 | #define STATEND(i) do{ \ | ||
| 286 | t[i][1] += nanosec()-t[i][0]; \ | ||
| 287 | t[i][2]++; \ | ||
| 288 | }while(0) | ||
| 289 | |||
| 290 | STATSTART(0); | ||
| 291 | |||
| 292 | rect = textrect(f, s); | ||
| 293 | if((mi = allocmemimage(rect, GREY8)) == nil) | ||
| 294 | sysfatal("%r"); | ||
| 295 | memfillcolor(mi, DWhite); | ||
| 296 | prevglyph = 0; | ||
| 297 | x = 0.0f; | ||
| 298 | o = rect; | ||
| 299 | base = 0; | ||
| 300 | |||
| 301 | for(i = 0; s[i]; i += n){ | ||
| 302 | STATSTART(1); | ||
| 303 | n = chartorune(&r, s+i); | ||
| 304 | glyph = code2glyph(f, r); | ||
| 305 | |||
| 306 | if(prevglyph > 0 && glyph > 0 && r != '\n') | ||
| 307 | x += stbtt_GetGlyphKernAdvance(&f->fi, prevglyph, glyph) * f->scale; | ||
| 308 | shift = x - floor(x); | ||
| 309 | |||
| 310 | if(r != '\n' && glyph > 0){ | ||
| 311 | memfillcolor(f->g, DBlack); | ||
| 312 | STATEND(1); | ||
| 313 | STATSTART(2); | ||
| 314 | if(prefilter) | ||
| 315 | stbtt_MakeGlyphBitmapSubpixelPrefilter( | ||
| 316 | &f->fi, | ||
| 317 | byteaddr(f->g, f->bb.min), | ||
| 318 | Dx(f->bb), Dy(f->bb), | ||
| 319 | bytesperline(f->bb, f->g->depth), | ||
| 320 | f->scale, f->scale, | ||
| 321 | shift, 0.0f, | ||
| 322 | prefilter, prefilter, | ||
| 323 | &subx, &suby, | ||
| 324 | glyph | ||
| 325 | ); | ||
| 326 | else | ||
| 327 | stbtt_MakeGlyphBitmapSubpixel( | ||
| 328 | &f->fi, | ||
| 329 | byteaddr(f->g, f->bb.min), | ||
| 330 | Dx(f->bb), Dy(f->bb), | ||
| 331 | bytesperline(f->bb, f->g->depth), | ||
| 332 | f->scale, f->scale, | ||
| 333 | shift, 0.0f, | ||
| 334 | glyph | ||
| 335 | ); | ||
| 336 | STATEND(2); | ||
| 337 | STATSTART(3); | ||
| 338 | stbtt_GetGlyphBitmapBoxSubpixel( | ||
| 339 | &f->fi, | ||
| 340 | glyph, | ||
| 341 | f->scale, f->scale, | ||
| 342 | shift, 0.0f, | ||
| 343 | &x0, &y0, nil, nil | ||
| 344 | ); | ||
| 345 | STATEND(3); | ||
| 346 | STATSTART(4); | ||
| 347 | o.min.x = floor(x) + x0; | ||
| 348 | o.min.y = base + f->asc*f->scale + y0; | ||
| 349 | memimagedraw(mi, o, f->white, ZP, f->g, ZP, DoutS); | ||
| 350 | stbtt_GetGlyphHMetrics(&f->fi, glyph, &adv, &leftb); | ||
| 351 | x += adv * f->scale; | ||
| 352 | STATEND(4); | ||
| 353 | }else | ||
| 354 | glyph = 0; | ||
| 355 | prevglyph = glyph; | ||
| 356 | if(r == '\n'){ | ||
| 357 | x = x0 = 0; | ||
| 358 | prevglyph = 0; | ||
| 359 | base += ceil(f->scale * f->lineh); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | n = 60 + Dx(rect)*Dy(rect); | ||
| 364 | p = malloc(n); | ||
| 365 | n = unloadmemimage(mi, rect, p, n); | ||
| 366 | freememimage(mi); | ||
| 367 | |||
| 368 | lockdisplay(display); | ||
| 369 | freeimage(image); | ||
| 370 | image = allocimage(display, rect, GREY8, 0, DNofill); | ||
| 371 | if(gamma != 1.0){ | ||
| 372 | for(i = 0; i < n; i++){ | ||
| 373 | if(p[i] < 255){ | ||
| 374 | float g = pow(p[i], 1.0/gamma); | ||
| 375 | p[i] = g < 256 ? g : 255; | ||
| 376 | } | ||
| 377 | } | ||
| 378 | } | ||
| 379 | if(invert){ | ||
| 380 | for(i = 0; i < n; i++) | ||
| 381 | p[i] = 255 - p[i]; | ||
| 382 | } | ||
| 383 | loadimage(image, rect, p, n); | ||
| 384 | unlockdisplay(display); | ||
| 385 | free(p); | ||
| 386 | |||
| 387 | STATEND(0); | ||
| 388 | |||
| 389 | s = info; | ||
| 390 | s += sprint(s, "%dpx prefilter=%d gamma=%.3g ", (int)f->height, prefilter, gamma); | ||
| 391 | for(i = 0; i < 4; i++) | ||
| 392 | s = seprint(s, info+sizeof(info), "%zd(%zd) ", (t[i][1]/t[i][2])/1000ULL, t[i][2]); | ||
| 393 | } | ||
| 394 | |||
| 395 | void | 78 | void |
| 396 | threadmain(int argc, char **argv) | 79 | threadmain(int argc, char **argv) |
| 397 | { | 80 | { |
| @@ -410,6 +93,7 @@ threadmain(int argc, char **argv) · 12 unmodified lines | |||
| 410 | int oldheight, height; | 93 | int oldheight, height; |
| 411 | Point oldxy, oldoffset; | 94 | Point oldxy, oldoffset; |
| 412 | int oldb; | 95 | int oldb; |
| 96 | Rectangle rect; | ||
| 413 | 97 | ||
| 414 | quotefmtinstall(); | 98 | quotefmtinstall(); |
| 415 | 99 | ||
| @@ -439,13 +123,7 @@ threadmain(int argc, char **argv) · 23 unmodified lines | |||
| 439 | oldheight = height; | 123 | oldheight = height; |
| 440 | oldxy = ZP; | 124 | oldxy = ZP; |
| 441 | 125 | ||
| 442 | if(initfont(&fo, data, height) != 0) | 126 | goto changed; |
| 443 | sysfatal("initfont"); | ||
| 444 | prepare(&fo, s); | ||
| 445 | print("%s %s\n", fo.fi.family, fo.fi.subfamily); | ||
| 446 | print("max glyphs: %d\n", fo.fi.numGlyphs); | ||
| 447 | print("underlinePosition: %d\n", fo.fi.underlinePosition); | ||
| 448 | print("underlineThickness: %d\n", fo.fi.underlineThickness); | ||
| 449 | 127 | ||
| 450 | for(;;){ | 128 | for(;;){ |
| 451 | oldb = m.buttons; | 129 | oldb = m.buttons; |
| @@ -486,37 +164,38 @@ threadmain(int argc, char **argv) · 34 unmodified lines | |||
| 486 | height += 1; | 164 | height += 1; |
| 487 | goto changed; | 165 | goto changed; |
| 488 | case 'i': | 166 | case 'i': |
| 489 | invert = !invert; | 167 | opts.invert = !opts.invert; |
| 490 | goto changed; | 168 | goto changed; |
| 491 | case Kleft: | 169 | case Kleft: |
| 492 | prefilter--; | 170 | opts.prefilter--; |
| 493 | if(prefilter < 0) | 171 | if(prefilter < 0) |
| 494 | prefilter = 0; | 172 | prefilter = 0; |
| 495 | goto changed; | 173 | goto changed; |
| 496 | case Kright: | 174 | case Kright: |
| 497 | prefilter++; | 175 | opts.prefilter++; |
| 498 | if(prefilter > STBTT_MAX_OVERSAMPLE) | ||
| 499 | prefilter = STBTT_MAX_OVERSAMPLE; | ||
| 500 | goto changed; | 176 | goto changed; |
| 501 | case Kup: | 177 | case Kup: |
| 502 | gamma += 0.01; | 178 | opts.gamma += 0.01; |
| 503 | goto changed; | 179 | goto changed; |
| 504 | case Kdown: | 180 | case Kdown: |
| 505 | gamma -= 0.01; | 181 | opts.gamma -= 0.01; |
| 506 | if(gamma < 0.4) | 182 | if(opts.gamma < 0.4) |
| 507 | gamma = 0.4; | 183 | opts.gamma = 0.4; |
| 508 | goto changed; | 184 | goto changed; |
| 509 | break; | 185 | break; |
| 510 | case '\n': | 186 | case '\n': |
| 511 | gamma = 1.0; | 187 | opts.gamma = 1.0; |
| 512 | goto changed; | 188 | goto changed; |
| 513 | } | 189 | } |
| 514 | } | 190 | } |
| 515 | continue; | 191 | continue; |
| 516 | 192 | ||
| 517 | changed: | 193 | changed: |
| 518 | initfont(&fo, data, height); | 194 | pt_freefont(fo); |
| 519 | prepare(&fo, s); | 195 | fo = pt_font(data, height); |
| 196 | rect = pt_textrect(fo, s); | ||
| 197 | freeimage(image); | ||
| 198 | image = pt_textdraw(fo, s, rect, &opts); | ||
| 520 | } | 199 | } |
| 521 | 200 | ||
| 522 | end: | 201 | end: |