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