added scr_lvled, a bunch of libraries, and improved framework code
[raydungeon] / libs / drawtext / font.c
1 /*
2 libdrawtext - a simple library for fast text rendering in OpenGL
3 Copyright (C) 2011-2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #ifndef NO_FREETYPE
19 #define USE_FREETYPE
20 #endif
21
22 #define DTX_DEFINE_COMMON
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <math.h>
28 #include <limits.h>
29 #include <ctype.h>
30 #include <float.h>
31 #include <errno.h>
32 #ifdef USE_FREETYPE
33 #include <ft2build.h>
34 #include FT_FREETYPE_H
35 #endif
36 #include "drawtext.h"
37 #include "drawtext_impl.h"
38
39 struct io {
40         void *data;
41         int size;
42         int (*readchar)(struct io*);
43         void *(*readline)(void *buf, int bsz, struct io*);
44 };
45
46 #define FTSZ_TO_PIXELS(x)       ((x) / 64)
47 #define MAX_IMG_SIZE            8192
48
49 static int opt_padding = 8;
50 static int opt_save_ppm;
51
52 static struct dtx_glyphmap *load_glyphmap(struct io *io);
53
54 #ifdef USE_FREETYPE
55 static int init_freetype(void);
56 static void cleanup(void);
57
58 static int calc_best_size(int total_width, int max_gwidth, int max_gheight,
59                 int padding, int pow2, int *imgw, int *imgh);
60 static int next_pow2(int x);
61
62 static FT_Library ft;
63
64
65 static int init_done;
66
67 static int init_freetype(void)
68 {
69         if(!init_done) {
70                 if(FT_Init_FreeType(&ft) != 0) {
71                         return -1;
72                 }
73                 atexit(cleanup);
74                 init_done = 1;
75         }
76         return 0;
77 }
78
79 static void cleanup(void)
80 {
81         if(init_done) {
82                 FT_Done_FreeType(ft);
83         }
84 }
85 #endif  /* USE_FREETYPE */
86
87 static int find_pow2(int x);
88
89 struct dtx_font *dtx_open_font(const char *fname, int sz)
90 {
91         struct dtx_font *fnt = 0;
92
93 #ifdef USE_FREETYPE
94         init_freetype();
95
96         if(!(fnt = calloc(1, sizeof *fnt))) {
97                 fperror("dtx_open_font: failed to allocate font structure");
98                 return 0;
99         }
100
101         if(FT_New_Face(ft, fname, 0, (FT_Face*)&fnt->face) != 0) {
102                 fprintf(stderr, "failed to open font file: %s\n", fname);
103                 free(fnt);
104                 return 0;
105         }
106
107         /* pre-create the extended ASCII range glyphmap */
108         if(sz) {
109                 dtx_prepare_range(fnt, sz, 0, 256);
110
111                 if(!dtx_font) {
112                         dtx_use_font(fnt, sz);
113                 }
114         }
115 #else
116         fprintf(stderr, "ignoring call to dtx_open_font: not compiled with freetype support!\n");
117 #endif
118
119         return fnt;
120 }
121
122 struct dtx_font *dtx_open_font_mem(void *ptr, int memsz, int fontsz)
123 {
124         struct dtx_font *fnt = 0;
125
126 #ifdef USE_FREETYPE
127         FT_Open_Args args;
128
129         init_freetype();
130
131         if(!(fnt = calloc(1, sizeof *fnt))) {
132                 fperror("dtx_open_font_mem: failed to allocate font structure");
133                 return 0;
134         }
135
136         memset(&args, 0, sizeof args);
137         args.flags = FT_OPEN_MEMORY;
138         args.memory_base = ptr;
139         args.memory_size = memsz;
140
141         if(FT_Open_Face(ft, &args, 0, (FT_Face*)&fnt->face) != 0) {
142                 fprintf(stderr, "failed to open font from memory\n");
143                 free(fnt);
144                 return 0;
145         }
146
147         /* pre-create the extended ASCII range glyphmap */
148         if(fontsz) {
149                 dtx_prepare_range(fnt, fontsz, 0, 256);
150
151                 if(!dtx_font) {
152                         dtx_use_font(fnt, fontsz);
153                 }
154         }
155 #else
156         fprintf(stderr, "ignoring call to dtx_open_font_mem: not compiled with freetype support!\n");
157 #endif
158
159         return fnt;
160 }
161
162 struct dtx_font *dtx_open_font_glyphmap(const char *fname)
163 {
164         struct dtx_font *fnt;
165         struct dtx_glyphmap *gmap;
166
167         if(!(fnt = calloc(1, sizeof *fnt))) {
168                 fperror("dtx_open_font_glyphmap: failed to allocate font structure");
169                 return 0;
170         }
171
172         if(fname) {
173                 if(!(gmap = dtx_load_glyphmap(fname))) {
174                         free(fnt);
175                         return 0;
176                 }
177
178                 dtx_add_glyphmap(fnt, gmap);
179
180                 if(!dtx_font) {
181                         dtx_use_font(fnt, gmap->ptsize);
182                 }
183         }
184         return fnt;
185 }
186
187 struct dtx_font *dtx_open_font_glyphmap_mem(void *ptr, int memsz)
188 {
189         struct dtx_font *fnt;
190         struct dtx_glyphmap *gmap;
191
192         if(!(fnt = calloc(1, sizeof *fnt))) {
193                 fperror("dtx_open_font_glyphmap_mem: failed to allocate font structure");
194                 return 0;
195         }
196
197         if(!(gmap = dtx_load_glyphmap_mem(ptr, memsz))) {
198                 free(fnt);
199                 return 0;
200         }
201
202         dtx_add_glyphmap(fnt, gmap);
203
204         if(!dtx_font) {
205                 dtx_use_font(fnt, gmap->ptsize);
206         }
207         return fnt;
208 }
209
210 void dtx_close_font(struct dtx_font *fnt)
211 {
212         if(!fnt) return;
213
214 #ifdef USE_FREETYPE
215         FT_Done_Face(fnt->face);
216 #endif
217
218         /* destroy the glyphmaps */
219         while(fnt->gmaps) {
220                 void *tmp = fnt->gmaps;
221                 fnt->gmaps = fnt->gmaps->next;
222                 dtx_free_glyphmap(tmp);
223         }
224
225         free(fnt);
226 }
227
228 void dtx_prepare(struct dtx_font *fnt, int sz)
229 {
230         if(!dtx_get_font_glyphmap_range(fnt, sz, 0, 256)) {
231                 fprintf(stderr, "dtx_prepare: failed (sz: %d, range: 0-255 [ascii])\n", sz);
232         }
233 }
234
235 void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend)
236 {
237         if(!dtx_get_font_glyphmap_range(fnt, sz, cstart, cend)) {
238                 fprintf(stderr, "dtx_prepare_range: failed (sz: %d, range: %d-%d)\n", sz, cstart, cend);
239         }
240 }
241
242 int dtx_calc_font_distfield(struct dtx_font *fnt, int scale_numer, int scale_denom)
243 {
244         struct dtx_glyphmap *gm = fnt->gmaps;
245         while(gm) {
246                 if(dtx_calc_glyphmap_distfield(gm) == -1) {
247                         fprintf(stderr, "dtx_calc_glyphmap_distfield: failed to create distfield glyphmap\n");
248                         return -1;
249                 }
250
251                 if(dtx_resize_glyphmap(gm, scale_numer, scale_denom, DTX_LINEAR) == -1) {
252                         fprintf(stderr, "dtx_calc_glyphmap_distfield: failed to resize glyhphmap during distfield conversion\n");
253                 }
254                 gm->tex_valid = 0;
255                 gm = gm->next;
256         }
257         return 0;
258 }
259
260 struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code)
261 {
262         struct dtx_glyphmap *gm;
263
264         /* check to see if the last we've given out fits the bill */
265         if(fnt->last_gmap && code >= fnt->last_gmap->cstart && code < fnt->last_gmap->cend && fnt->last_gmap->ptsize == sz) {
266                 return fnt->last_gmap;
267         }
268
269         /* otherwise search for the appropriate glyphmap */
270         gm = fnt->gmaps;
271         while(gm) {
272                 if(code >= gm->cstart && code < gm->cend && sz == gm->ptsize) {
273                         fnt->last_gmap = gm;
274                         return gm;
275                 }
276                 gm = gm->next;
277         }
278         return 0;
279 }
280
281 struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend)
282 {
283         struct dtx_glyphmap *gm;
284
285         /* search the available glyphmaps to see if we've got one that includes
286          * the requested range
287          */
288         gm = fnt->gmaps;
289         while(gm) {
290                 if(gm->cstart <= cstart && gm->cend >= cend && gm->ptsize == sz) {
291                         return gm;
292                 }
293                 gm = gm->next;
294         }
295
296         /* not found, create one and add it to the list */
297         if(!(gm = dtx_create_glyphmap_range(fnt, sz, cstart, cend))) {
298                 return 0;
299         }
300         return gm;
301 }
302
303 int dtx_get_num_glyphmaps(struct dtx_font *fnt)
304 {
305         int count = 0;
306         struct dtx_glyphmap *gm = fnt->gmaps;
307         while(gm) {
308                 ++count;
309                 gm = gm->next;
310         }
311         return count;
312 }
313
314 struct dtx_glyphmap *dtx_get_glyphmap(struct dtx_font *fnt, int idx)
315 {
316         struct dtx_glyphmap *gm = fnt->gmaps;
317         while(gm && idx--) {
318                 gm = gm->next;
319         }
320         return gm;
321 }
322
323 struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend)
324 {
325         struct dtx_glyphmap *gmap = 0;
326
327 #ifdef USE_FREETYPE
328         FT_Face face = fnt->face;
329         int i, j;
330         int gx, gy;
331         int total_width, max_width, max_height;
332         int half_pad = opt_padding / 2;
333
334         FT_Set_Char_Size(fnt->face, 0, sz * 64, 72, 72);
335
336         if(!(gmap = calloc(1, sizeof *gmap))) {
337                 return 0;
338         }
339
340         gmap->ptsize = sz;
341         gmap->cstart = cstart;
342         gmap->cend = cend;
343         gmap->crange = cend - cstart;
344         gmap->line_advance = FTSZ_TO_PIXELS((float)face->size->metrics.height);
345         gmap->baseline = -FTSZ_TO_PIXELS((float)face->size->metrics.descender);
346
347         if(!(gmap->glyphs = malloc(gmap->crange * sizeof *gmap->glyphs))) {
348                 free(gmap);
349                 return 0;
350         }
351
352         total_width = opt_padding;
353         max_width = max_height = 0;
354
355         for(i=0; i<gmap->crange; i++) {
356                 int w, h;
357
358                 FT_Load_Char(face, i + cstart, 0);
359                 w = FTSZ_TO_PIXELS(face->glyph->metrics.width);
360                 h = FTSZ_TO_PIXELS(face->glyph->metrics.height);
361
362                 if(w > max_width) max_width = w;
363                 if(h > max_height) max_height = h;
364
365                 total_width += w + opt_padding;
366         }
367
368         if(calc_best_size(total_width, max_width, max_height, opt_padding, 1, &gmap->xsz, &gmap->ysz) == -1) {
369                 free(gmap->glyphs);
370                 free(gmap);
371                 return 0;
372         }
373         gmap->xsz_shift = find_pow2(gmap->xsz);
374
375         if(!(gmap->pixels = malloc(gmap->xsz * gmap->ysz))) {
376                 free(gmap->glyphs);
377                 free(gmap);
378                 return 0;
379         }
380         memset(gmap->pixels, 0, gmap->xsz * gmap->ysz);
381
382         gx = opt_padding;
383         gy = opt_padding;
384
385         for(i=0; i<gmap->crange; i++) {
386                 float gwidth, gheight;
387                 unsigned char *src, *dst;
388                 FT_GlyphSlot glyph;
389
390                 FT_Load_Char(face, i + cstart, FT_LOAD_RENDER);
391                 glyph = face->glyph;
392                 gwidth = FTSZ_TO_PIXELS((float)glyph->metrics.width);
393                 gheight = FTSZ_TO_PIXELS((float)glyph->metrics.height);
394
395                 if(gx > gmap->xsz - gwidth - opt_padding) {
396                         gx = opt_padding;
397                         gy += max_height + opt_padding;
398                 }
399
400                 src = glyph->bitmap.buffer;
401                 dst = gmap->pixels + (gy << gmap->xsz_shift) + gx;
402
403                 for(j=0; j<(int)glyph->bitmap.rows; j++) {
404                         memcpy(dst, src, glyph->bitmap.width);
405                         dst += gmap->xsz;
406                         src += glyph->bitmap.pitch;
407                 }
408
409                 gmap->glyphs[i].code = i;
410                 gmap->glyphs[i].x = gx - half_pad;
411                 gmap->glyphs[i].y = gy - half_pad;
412                 gmap->glyphs[i].width = gwidth + half_pad * 2;
413                 gmap->glyphs[i].height = gheight + half_pad * 2;
414                 gmap->glyphs[i].orig_x = -FTSZ_TO_PIXELS((float)glyph->metrics.horiBearingX) + 1;
415                 gmap->glyphs[i].orig_y = FTSZ_TO_PIXELS((float)glyph->metrics.height - glyph->metrics.horiBearingY) + 1;
416                 gmap->glyphs[i].advance = FTSZ_TO_PIXELS((float)glyph->metrics.horiAdvance);
417                 /* also precalc normalized */
418                 gmap->glyphs[i].nx = (float)gmap->glyphs[i].x / (float)gmap->xsz;
419                 gmap->glyphs[i].ny = (float)gmap->glyphs[i].y / (float)gmap->ysz;
420                 gmap->glyphs[i].nwidth = (float)gmap->glyphs[i].width / (float)gmap->xsz;
421                 gmap->glyphs[i].nheight = (float)gmap->glyphs[i].height / (float)gmap->ysz;
422
423                 gx += gwidth + opt_padding;
424         }
425
426         /* add it to the glyphmaps list of the font */
427         dtx_add_glyphmap(fnt, gmap);
428 #endif  /* USE_FREETYPE */
429
430         return gmap;
431 }
432
433 void dtx_free_glyphmap(struct dtx_glyphmap *gmap)
434 {
435         if(gmap) {
436                 free(gmap->pixels);
437                 free(gmap->glyphs);
438                 free(gmap);
439         }
440 }
441
442 #define CHECK_BOUNDS(gm, x, y) ((x) >= 0 && (x) < (gm)->xsz && (y) >= 0 && (y) < (gm)->ysz)
443 #define GET_PIXEL(gm, x, y) ((gm)->pixels[((y) << (gm)->xsz_shift) + (x)])
444
445 static int calc_distance(struct dtx_glyphmap *gmap, int x, int y, int max_dist)
446 {
447         int i, j, startx, starty, endx, endy, px, py;
448         int bwidth, bheight;
449         int min_distsq = INT_MAX;
450         unsigned char cpix = GET_PIXEL(gmap, x, y);
451         int dist;
452
453         if(max_dist > 128) max_dist = 128;
454
455         startx = x >= max_dist ? x - max_dist : 0;
456         starty = y >= max_dist ? y - max_dist : 0;
457         endx = x + max_dist < gmap->xsz ? x + max_dist : gmap->xsz - 1;
458         endy = y + max_dist < gmap->ysz ? y + max_dist : gmap->ysz - 1;
459
460         /* try the cardinal directions first to find the search bounding box */
461         for(i=0; i<4; i++) {
462                 int max_dist = x - startx;
463                 for(j=0; j<max_dist; j++) {
464                         if(GET_PIXEL(gmap, x - j, y) != cpix) {
465                                 startx = x - j;
466                                 break;
467                         }
468                 }
469                 max_dist = endx + 1 - x;
470                 for(j=0; j<max_dist; j++) {
471                         if(GET_PIXEL(gmap, x + j, y) != cpix) {
472                                 endx = x + j;
473                                 break;
474                         }
475                 }
476                 max_dist = y - starty;
477                 for(j=0; j<max_dist; j++) {
478                         if(GET_PIXEL(gmap, x, y - j) != cpix) {
479                                 starty = y - j;
480                                 break;
481                         }
482                 }
483                 max_dist = endy + 1 - y;
484                 for(j=0; j<max_dist; j++) {
485                         if(GET_PIXEL(gmap, x, y + j) != cpix) {
486                                 endy = y + j;
487                                 break;
488                         }
489                 }
490         }
491
492         /* find the minimum squared distance inside the bounding box */
493         bwidth = endx + 1 - startx;
494         bheight = endy + 1 - starty;
495
496         py = starty;
497         for(i=0; i<bheight; i++) {
498                 px = startx;
499                 for(j=0; j<bwidth; j++) {
500                         if(GET_PIXEL(gmap, px, py) != cpix) {
501                                 int dx = px - x;
502                                 int dy = py - y;
503                                 int distsq = dx * dx + dy * dy;
504
505                                 if(distsq < min_distsq) {
506                                         min_distsq = distsq;
507                                 }
508                         }
509                         ++px;
510                 }
511                 ++py;
512         }
513
514         dist = (int)sqrt(min_distsq);
515         if(dist > 127) dist = 127;
516
517         return cpix ? dist + 128 : 127 - dist;
518 }
519
520 struct distcalc_data {
521         struct dtx_glyphmap *gmap;
522         int scanline;
523         unsigned char *pixels;
524 };
525
526 static void distcalc_func(void *cls)
527 {
528         int i;
529         struct distcalc_data *data = cls;
530         struct dtx_glyphmap *gmap = data->gmap;
531
532         printf("scanline %d of %d\n", data->scanline + 1, gmap->ysz);
533         for(i=0; i<gmap->xsz; i++) {
534                 *data->pixels++ = calc_distance(gmap, i, data->scanline, 64);
535         }
536 }
537
538 int dtx_calc_glyphmap_distfield(struct dtx_glyphmap *gmap)
539 {
540         int i, num_pixels = gmap->xsz * gmap->ysz;
541         unsigned char *new_pixels;
542         unsigned char *dptr;
543
544         /* first quantize the glyphmap to 1bit */
545         dptr = gmap->pixels;
546         for(i=0; i<num_pixels; i++) {
547                 unsigned char c = *dptr;
548                 *dptr++ = c < 128 ? 0 : 255;
549         }
550
551         if(!(new_pixels = malloc(num_pixels))) {
552                 fprintf(stderr, "dtx_calc_glyphmap_distfield: failed to allocate %dx%d pixel buffer\n", gmap->xsz, gmap->ysz);
553                 return -1;
554         }
555         dptr = new_pixels;
556
557         for(i=0; i<gmap->ysz; i++) {
558                 struct distcalc_data d;
559                 d.gmap = gmap;
560                 d.scanline = i;
561                 d.pixels = new_pixels + (i << gmap->xsz_shift);
562                 distcalc_func(&d);
563         }
564
565         free(gmap->pixels);
566         gmap->pixels = new_pixels;
567         return 0;
568 }
569
570 static unsigned char sample_area(struct dtx_glyphmap *gm, float x, float y, float area)
571 {
572         int i, j;
573         int ksz = (int)(area + 0.5);
574         int half_ksz = ksz / 2;
575
576         int sum = 0, nsamples = 0;
577
578         for(i=0; i<ksz; i++) {
579                 for(j=0; j<ksz; j++) {
580                         int sx = x + j - half_ksz;
581                         int sy = y + i - half_ksz;
582
583                         if(sx < 0 || sx >= gm->xsz || sy < 0 || sy >= gm->ysz) {
584                                 continue;
585                         }
586
587                         sum += gm->pixels[(sy << gm->xsz_shift) + sx];
588                         ++nsamples;
589                 }
590         }
591
592         if(nsamples != 0) {
593                 sum /= nsamples;
594         }
595         return sum > 255 ? 255 : sum;
596 }
597
598 static unsigned char sample_pixel(struct dtx_glyphmap *gm, int x, int y)
599 {
600         if(CHECK_BOUNDS(gm, x, y)) {
601                 return gm->pixels[(y << gm->xsz_shift) + x];
602         }
603         return 0;
604 }
605
606 static int count_bits(int x)
607 {
608         int i, n = 0;
609         for(i=0; i<sizeof x * CHAR_BIT; i++) {
610                 n += x & 1;
611                 x >>= 1;
612         }
613         return n;
614 }
615
616 int dtx_resize_glyphmap(struct dtx_glyphmap *gmap, int snum, int sdenom, int filter)
617 {
618         int i, j, nxsz, nysz;
619         unsigned char *dptr, *new_pixels;
620         float scale, inv_scale, area;
621
622         if(snum == sdenom) return 0;
623
624         if((count_bits(snum) | count_bits(sdenom)) != 1) {
625                 fprintf(stderr, "dtx_resize_glyphmap: invalid scale fraction %d/%d (not power of 2)\n", snum, sdenom);
626                 return -1;
627         }
628
629         /* normalize the fraction */
630         if(snum > sdenom) {
631                 snum /= sdenom;
632                 sdenom /= sdenom;
633         } else {
634                 snum /= snum;
635                 sdenom /= snum;
636         }
637
638         if(snum != 1 && sdenom != 1) {
639                 fprintf(stderr, "dtx_resize_glyphmap: invalid scale fraction %d/%d (neither is 1)\n", snum, sdenom);
640                 return -1;
641         }
642
643         nxsz = snum * gmap->xsz / sdenom;
644         nysz = snum * gmap->ysz / sdenom;
645
646         if(nxsz < 1 || nysz < 1) {
647                 return -1;
648         }
649
650         new_pixels = malloc(nxsz * nysz);
651         if(!new_pixels) {
652                 fprintf(stderr, "dtx_resize_glyphmap: failed to allocate %dx%d pixel buffer\n", nxsz, nysz);
653                 return -1;
654         }
655
656         dptr = new_pixels;
657
658         scale = (float)snum / (float)sdenom;
659         inv_scale = 1.0 / scale;
660         area = scale <= 1.0 ? inv_scale : 2.0;
661
662         if(filter == DTX_NEAREST) {
663                 /* no filtering, nearest neighbor */
664                 for(i=0; i<nysz; i++) {
665                         for(j=0; j<nxsz; j++) {
666                                 *dptr++ = sample_pixel(gmap, j * inv_scale, i * inv_scale);
667                         }
668                 }
669         } else {
670                 /* bilinear filtering */
671                 for(i=0; i<nysz; i++) {
672                         for(j=0; j<nxsz; j++) {
673                                 *dptr++ = sample_area(gmap, j * inv_scale, i * inv_scale, area);
674                         }
675                 }
676         }
677
678         free(gmap->pixels);
679         gmap->pixels = new_pixels;
680         gmap->xsz = nxsz;
681         gmap->ysz = nysz;
682         gmap->xsz_shift = find_pow2(nxsz);
683
684         /* also scale all the metrics accordingly */
685         for(i=0; i<gmap->crange; i++) {
686                 struct glyph *g = gmap->glyphs + i;
687                 g->x *= scale;
688                 g->y *= scale;
689                 g->width *= scale;
690                 g->height *= scale;
691                 g->orig_x *= scale;
692                 g->orig_y *= scale;
693                 g->advance *= scale;
694         }
695         gmap->ptsize = snum * gmap->ptsize / sdenom;
696         gmap->line_advance *= scale;
697         return 0;
698 }
699
700 unsigned char *dtx_get_glyphmap_pixels(struct dtx_glyphmap *gmap)
701 {
702         return gmap->pixels;
703 }
704
705 int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap)
706 {
707         return gmap->xsz;
708 }
709
710 int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap)
711 {
712         return gmap->ysz;
713 }
714
715 int dtx_get_glyphmap_ptsize(struct dtx_glyphmap *gmap)
716 {
717         return gmap->ptsize;
718 }
719
720 struct dtx_glyphmap *dtx_load_glyphmap(const char *fname)
721 {
722         FILE *fp;
723         struct dtx_glyphmap *gmap;
724
725         if(!(fp = fopen(fname, "rb"))) {
726                 return 0;
727         }
728         gmap = dtx_load_glyphmap_stream(fp);
729         fclose(fp);
730         return gmap;
731 }
732
733
734 static int file_readchar(struct io *io)
735 {
736         return fgetc(io->data);
737 }
738
739 static void *file_readline(void *buf, int bsz, struct io *io)
740 {
741         return fgets(buf, bsz, io->data);
742 }
743
744 static int mem_readchar(struct io *io)
745 {
746         char *p = io->data;
747
748         if(io->size-- <= 0) {
749                 return -1;
750         }
751         io->data = p + 1;
752         return *p;
753 }
754
755 static void *mem_readline(void *buf, int bsz, struct io *io)
756 {
757         int c;
758         char *ptr = buf;
759
760         while(--bsz > 0 && (c = mem_readchar(io)) != -1) {
761                 *ptr++ = c;
762                 if(c == '\n') break;
763         }
764         *ptr = 0;
765
766         return buf;
767 }
768
769 struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp)
770 {
771         struct io io;
772         io.data = fp;
773         io.readchar = file_readchar;
774         io.readline = file_readline;
775         return load_glyphmap(&io);
776 }
777
778 struct dtx_glyphmap *dtx_load_glyphmap_mem(void *ptr, int memsz)
779 {
780         struct io io;
781         io.data = ptr;
782         io.size = memsz;
783         io.readchar = mem_readchar;
784         io.readline = mem_readline;
785         return load_glyphmap(&io);
786 }
787
788 static struct dtx_glyphmap *load_glyphmap(struct io *io)
789 {
790         char buf[512];
791         int hdr_lines = 0;
792         struct dtx_glyphmap *gmap;
793         struct glyph *glyphs = 0;
794         struct glyph *g;
795         int min_code = INT_MAX;
796         int max_code = INT_MIN;
797         int i, max_pixval = 255, num_pixels;
798         int greyscale = 0;
799
800         if(!(gmap = calloc(1, sizeof *gmap))) {
801                 fperror("load_glyphmap: failed to allocate glyphmap");
802                 return 0;
803         }
804         gmap->ptsize = -1;
805         gmap->line_advance = FLT_MIN;
806
807         while(hdr_lines < 3) {
808                 char *line = buf;
809                 if(!io->readline(buf, sizeof buf, io)) {
810                         fperror("load_glyphmap: unexpected end of file");
811                         goto err;
812                 }
813
814                 while(isspace(*line)) {
815                         line++;
816                 }
817
818                 if(line[0] == '#') {
819                         int c, res;
820                         float x, y, xsz, ysz, orig_x, orig_y, adv, line_adv;
821                         int ptsize;
822
823                         if((res = sscanf(line + 1, " size: %d\n", &ptsize)) == 1) {
824                                 gmap->ptsize = ptsize;
825
826                         } else if((res = sscanf(line + 1, " advance: %f\n", &line_adv)) == 1) {
827                                 gmap->line_advance = line_adv;
828
829                         } else if((res = sscanf(line + 1, " %d: %fx%f+%f+%f o:%f,%f adv:%f\n",
830                                                         &c, &xsz, &ysz, &x, &y, &orig_x, &orig_y, &adv)) == 8) {
831                                 if(!(g = malloc(sizeof *g))) {
832                                         fperror("load_glyphmap: failed to allocate glyph");
833                                         goto err;
834                                 }
835                                 g->code = c;
836                                 g->x = x;
837                                 g->y = y;
838                                 g->width = xsz;
839                                 g->height = ysz;
840                                 g->orig_x = orig_x;
841                                 g->orig_y = orig_y;
842                                 g->advance = adv;
843                                 /* normalized coordinates will be precalculated after everything is loaded */
844
845                                 g->next = glyphs;
846                                 glyphs = g;
847
848                                 if(c < min_code) {
849                                         min_code = c;
850                                 }
851                                 if(c > max_code) {
852                                         max_code = c;
853                                 }
854
855                         } else {
856                                 fprintf(stderr, "load_glyphmap: invalid glyph info line\n");
857                                 goto err;
858                         }
859
860                 } else {
861                         switch(hdr_lines) {
862                         case 0:
863                                 if(line[0] != 'P' || !(line[1] == '6' || line[1] == '5')) {
864                                         fprintf(stderr, "load_glyphmap: invalid file format (magic)\n");
865                                         goto err;
866                                 }
867                                 greyscale = line[1] == '5';
868                                 break;
869
870                         case 1:
871                                 if(sscanf(line, "%d %d", &gmap->xsz, &gmap->ysz) != 2) {
872                                         fprintf(stderr, "load_glyphmap: invalid file format (dim)\n");
873                                         goto err;
874                                 }
875                                 break;
876
877                         case 2:
878                                 {
879                                         char *endp;
880                                         max_pixval = strtol(line, &endp, 10);
881                                         if(endp == line) {
882                                                 fprintf(stderr, "load_glyphmap: invalid file format (maxval)\n");
883                                                 goto err;
884                                         }
885                                 }
886                                 break;
887
888                         default:
889                                 break;  /* can't happen */
890                         }
891                         hdr_lines++;
892                 }
893         }
894
895         if(gmap->ptsize == -1 || gmap->line_advance == FLT_MIN) {
896                 fprintf(stderr, "load_glyphmap: invalid glyphmap, insufficient information in ppm comments\n");
897                 goto err;
898         }
899
900         /* precalculate normalized glyph coordinates */
901         g = glyphs;
902         while(g) {
903                 g->nx = g->x / gmap->xsz;
904                 g->ny = g->y / gmap->ysz;
905                 g->nwidth = g->width / gmap->xsz;
906                 g->nheight = g->height / gmap->ysz;
907                 g = g->next;
908         }
909
910         num_pixels = gmap->xsz * gmap->ysz;
911         if(!(gmap->pixels = malloc(num_pixels))) {
912                 fperror("load_glyphmap: failed to allocate pixels");
913                 goto err;
914         }
915
916         for(i=0; i<num_pixels; i++) {
917                 long c = io->readchar(io);
918                 if(c == -1) {
919                         fprintf(stderr, "unexpected end of file while reading pixels\n");
920                         goto err;
921                 }
922                 gmap->pixels[i] = 255 * c / max_pixval;
923                 if(!greyscale) {
924                         io->readchar(io);
925                         io->readchar(io);
926                 }
927         }
928
929         gmap->xsz_shift = find_pow2(gmap->xsz);
930         gmap->cstart = min_code;
931         gmap->cend = max_code + 1;
932         gmap->crange = gmap->cend - gmap->cstart;
933
934         if(!(gmap->glyphs = calloc(gmap->crange, sizeof *gmap->glyphs))) {
935                 fperror("load_glyphmap: failed to allocate glyph info");
936                 goto err;
937         }
938
939         while(glyphs) {
940                 struct glyph *g = glyphs;
941                 glyphs = glyphs->next;
942
943                 gmap->glyphs[g->code - gmap->cstart] = *g;
944                 free(g);
945         }
946         return gmap;
947
948 err:
949         dtx_free_glyphmap(gmap);
950         while(glyphs) {
951                 void *tmp = glyphs;
952                 glyphs = glyphs->next;
953                 free(tmp);
954         }
955         return 0;
956 }
957
958 int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap)
959 {
960         FILE *fp;
961         int res;
962
963         if(!(fp = fopen(fname, "wb"))) {
964                 fprintf(stderr, "dtx_save_glyphmap: failed to open file: %s: %s\n", fname, strerror(errno));
965                 return -1;
966         }
967         res = dtx_save_glyphmap_stream(fp, gmap);
968         fclose(fp);
969         return res;
970 }
971
972 int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap)
973 {
974         int i, num_pixels;
975         struct glyph *g = gmap->glyphs;
976
977         fprintf(fp, "P%d\n%d %d\n", opt_save_ppm ? 6 : 5, gmap->xsz, gmap->ysz);
978         fprintf(fp, "# size: %d\n", gmap->ptsize);
979         fprintf(fp, "# advance: %g\n", gmap->line_advance);
980         for(i=0; i<gmap->crange; i++) {
981                 fprintf(fp, "# %d: %gx%g+%g+%g o:%g,%g adv:%g\n", g->code + gmap->cstart,
982                                 g->width, g->height, g->x, g->y, g->orig_x, g->orig_y, g->advance);
983                 g++;
984         }
985         fprintf(fp, "255\n");
986
987         num_pixels = gmap->xsz * gmap->ysz;
988         for(i=0; i<num_pixels; i++) {
989                 int c = gmap->pixels[i];
990                 fputc(c, fp);
991                 if(opt_save_ppm) {
992                         fputc(c, fp);
993                         fputc(c, fp);
994                 }
995         }
996         return 0;
997 }
998
999 void dtx_add_glyphmap(struct dtx_font *fnt, struct dtx_glyphmap *gmap)
1000 {
1001         gmap->next = fnt->gmaps;
1002         fnt->gmaps = gmap;
1003 }
1004
1005
1006 void dtx_set(enum dtx_option opt, int val)
1007 {
1008         switch(opt) {
1009         case DTX_PADDING:
1010                 opt_padding = val;
1011                 break;
1012
1013         case DTX_SAVE_PPM:
1014                 opt_save_ppm = val;
1015                 break;
1016
1017         default:
1018                 dtx_gl_setopt(opt, val);
1019                 dtx_rast_setopt(opt, val);
1020         }
1021 }
1022
1023 int dtx_get(enum dtx_option opt)
1024 {
1025         int val;
1026
1027         switch(opt) {
1028         case DTX_PADDING:
1029                 return opt_padding;
1030
1031         case DTX_SAVE_PPM:
1032                 return opt_save_ppm;
1033
1034         default:
1035                 break;
1036         }
1037
1038         if(dtx_gl_getopt(opt, &val) != -1) {
1039                 return val;
1040         }
1041         if(dtx_rast_getopt(opt, &val) != -1) {
1042                 return val;
1043         }
1044         return -1;
1045 }
1046
1047 void dtx_use_font(struct dtx_font *fnt, int sz)
1048 {
1049         if(!dtx_drawchar) {
1050                 dtx_target_opengl();
1051         }
1052
1053         dtx_font = fnt;
1054         dtx_font_sz = sz;
1055 }
1056
1057 float dtx_line_height(void)
1058 {
1059         struct dtx_glyphmap *gmap = dtx_get_glyphmap(dtx_font, 0);
1060
1061         return gmap->line_advance;
1062 }
1063
1064 float dtx_baseline(void)
1065 {
1066         struct dtx_glyphmap *gmap = dtx_get_glyphmap(dtx_font, 0);
1067
1068         return gmap->baseline;
1069 }
1070
1071 void dtx_glyph_box(int code, struct dtx_box *box)
1072 {
1073         int cidx;
1074         struct dtx_glyphmap *gmap;
1075         gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code);
1076
1077         cidx = code - gmap->cstart;
1078
1079         box->x = gmap->glyphs[cidx].orig_x;
1080         box->y = gmap->glyphs[cidx].orig_y;
1081         box->width = gmap->glyphs[cidx].width;
1082         box->height = gmap->glyphs[cidx].height;
1083 }
1084
1085 float dtx_glyph_width(int code)
1086 {
1087         struct dtx_box box;
1088         dtx_glyph_box(code, &box);
1089         return box.width;
1090 }
1091
1092 float dtx_glyph_height(int code)
1093 {
1094         struct dtx_box box;
1095         dtx_glyph_box(code, &box);
1096         return box.height;
1097 }
1098
1099 void dtx_string_box(const char *str, struct dtx_box *box)
1100 {
1101         dtx_substring_box(str, 0, INT_MAX, box);
1102 }
1103
1104 void dtx_substring_box(const char *str, int start, int end, struct dtx_box *box)
1105 {
1106         int code;
1107         float pos_x = 0.0f, pos_y = 0.0f;
1108         struct glyph *g = 0;
1109         float x0, y0, x1, y1;
1110
1111         x0 = y0 = FLT_MAX;
1112         x1 = y1 = -FLT_MAX;
1113
1114         /* skip start characters */
1115         while(*str && start > 0) {
1116                 str = dtx_utf8_next_char((char*)str);
1117                 --start;
1118                 --end;
1119         }
1120
1121         while(*str && --end >= 0) {
1122                 float px, py;
1123                 struct dtx_glyphmap *gmap;
1124
1125                 code = dtx_utf8_char_code(str);
1126                 str = dtx_utf8_next_char((char*)str);
1127
1128                 px = pos_x;
1129                 py = pos_y;
1130
1131                 if((gmap = dtx_proc_char(code, &pos_x, &pos_y))) {
1132                         g = gmap->glyphs + code - gmap->cstart;
1133
1134                         if(px + g->orig_x < x0) {
1135                                 x0 = px + g->orig_x;
1136                         }
1137                         if(py - g->orig_y < y0) {
1138                                 y0 = py - g->orig_y;
1139                         }
1140                         if(px + g->orig_x + g->width > x1) {
1141                                 x1 = px + g->orig_x + g->width;
1142                         }
1143                         if(py - g->orig_y + g->height > y1) {
1144                                 y1 = py - g->orig_y + g->height;
1145                         }
1146                 }
1147         }
1148
1149         box->x = x0;
1150         box->y = y0;
1151         box->width = x1 - x0;
1152         box->height = y1 - y0;
1153 }
1154
1155 float dtx_string_width(const char *str)
1156 {
1157         struct dtx_box box;
1158
1159         dtx_string_box(str, &box);
1160         return box.width;
1161 }
1162
1163 float dtx_string_height(const char *str)
1164 {
1165         struct dtx_box box;
1166
1167         dtx_string_box(str, &box);
1168         return box.height;
1169 }
1170
1171 float dtx_char_pos(const char *str, int n)
1172 {
1173         int i, code;
1174         float pos = 0.0;
1175         struct dtx_glyphmap *gmap;
1176
1177         for(i=0; i<n; i++) {
1178                 if(!*str) break;
1179
1180                 code = dtx_utf8_char_code(str);
1181                 str = dtx_utf8_next_char((char*)str);
1182
1183                 if((gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
1184                         pos += gmap->glyphs[code - gmap->cstart].advance;
1185                 }
1186         }
1187         return pos;
1188 }
1189
1190 int dtx_char_at_pt(const char *str, float pt)
1191 {
1192         int i;
1193         float prev_pos = 0.0f, pos = 0.0f;
1194         struct dtx_glyphmap *gmap;
1195
1196         for(i=0; *str; i++) {
1197                 int code = dtx_utf8_char_code(str);
1198                 str = dtx_utf8_next_char((char*)str);
1199
1200                 if((gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
1201                         pos += gmap->glyphs[code - gmap->cstart].advance;
1202
1203                         if(fabs(pt - prev_pos) < fabs(pt - pos)) {
1204                                 break;
1205                         }
1206                 }
1207                 prev_pos = pos;
1208         }
1209         return i;
1210 }
1211
1212 struct dtx_glyphmap *dtx_proc_char(int code, float *xpos, float *ypos)
1213 {
1214         struct dtx_glyphmap *gmap;
1215         gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code);
1216
1217         switch(code) {
1218         case '\n':
1219                 *xpos = 0.0;
1220                 if(gmap) {
1221                         *ypos -= gmap->line_advance;
1222                 }
1223                 return 0;
1224
1225         case '\t':
1226                 if(gmap) {
1227                         *xpos = (fmod(*xpos, 4.0) + 4.0) * gmap->glyphs[0].advance;
1228                 }
1229                 return 0;
1230
1231         case '\r':
1232                 *xpos = 0.0;
1233                 return 0;
1234
1235         default:
1236                 break;
1237         }
1238
1239         if(gmap) {
1240                 *xpos += gmap->glyphs[code - gmap->cstart].advance;
1241         }
1242         return gmap;
1243 }
1244
1245 #ifdef USE_FREETYPE
1246 static int calc_best_size(int total_width, int max_gwidth, int max_gheight, int padding, int pow2, int *imgw, int *imgh)
1247 {
1248         int xsz, ysz, num_rows;
1249         float aspect;
1250
1251         /* the widest glyph won't fit in the maximum image size */
1252         if(max_gwidth > MAX_IMG_SIZE) {
1253                 return -1;
1254         }
1255
1256         for(xsz=2; xsz<=MAX_IMG_SIZE; xsz *= 2) {
1257                 num_rows = total_width / xsz + 1;
1258
1259                 /* assume worst case, all last glyphs will float to the next line
1260                  * so let's add extra rows for that. */
1261                 num_rows += (padding + (max_gwidth + padding) * num_rows + xsz - 1) / xsz;
1262
1263                 ysz = num_rows * (max_gheight + padding) + padding;
1264                 if(ysz <= 0 || ysz > MAX_IMG_SIZE) continue;
1265
1266                 if(pow2) {
1267                         ysz = next_pow2(ysz);
1268                 }
1269                 aspect = (float)xsz / (float)ysz;
1270
1271                 if(aspect >= 1.0) {
1272                         break;
1273                 }
1274         }
1275
1276         if(xsz > MAX_IMG_SIZE || ysz > MAX_IMG_SIZE || ysz <= 0) {
1277                 return -1;
1278         }
1279
1280         *imgw = xsz;
1281         *imgh = ysz;
1282         return 0;
1283 }
1284
1285
1286 static int next_pow2(int x)
1287 {
1288         x--;
1289         x = (x >> 1) | x;
1290         x = (x >> 2) | x;
1291         x = (x >> 4) | x;
1292         x = (x >> 8) | x;
1293         x = (x >> 16) | x;
1294         return x + 1;
1295 }
1296 #endif
1297
1298 static int find_pow2(int x)
1299 {
1300         int i;
1301         for(i=0; i<sizeof x * CHAR_BIT; i++) {
1302                 if((1 << i) == x) {
1303                         return i;
1304                 }
1305         }
1306         return 0;
1307 }