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