1 | /* |
2 | libdrawtext - a simple library for fast text rendering in OpenGL |
3 | Copyright (C) 2011-2014 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 | |
31 | #ifdef USE_FREETYPE |
32 | #include <ft2build.h> |
33 | #include FT_FREETYPE_H |
34 | #endif |
35 | #include "drawtext.h" |
36 | #include "drawtext_impl.h" |
37 | |
38 | #define FTSZ_TO_PIXELS(x) ((x) / 64) |
39 | #define MAX_IMG_WIDTH 4096 |
40 | |
41 | |
42 | #ifdef USE_FREETYPE |
43 | static int init_freetype(void); |
44 | static void cleanup(void); |
45 | |
46 | static void calc_best_size(int total_width, int max_gwidth, int max_gheight, |
47 | int padding, int pow2, int *imgw, int *imgh); |
48 | static int next_pow2(int x); |
49 | |
50 | static FT_Library ft; |
51 | |
52 | |
53 | static int init_done; |
54 | |
55 | static int init_freetype(void) |
56 | { |
57 | if(!init_done) { |
58 | if(FT_Init_FreeType(&ft) != 0) { |
59 | return -1; |
60 | } |
61 | atexit(cleanup); |
62 | init_done = 1; |
63 | } |
64 | return 0; |
65 | } |
66 | |
67 | static void cleanup(void) |
68 | { |
69 | if(init_done) { |
70 | FT_Done_FreeType(ft); |
71 | } |
72 | } |
73 | #endif /* USE_FREETYPE */ |
74 | |
75 | struct dtx_font *dtx_open_font(const char *fname, int sz) |
76 | { |
77 | struct dtx_font *fnt = 0; |
78 | |
79 | #ifdef USE_FREETYPE |
80 | init_freetype(); |
81 | |
82 | if(!(fnt = calloc(1, sizeof *fnt))) { |
83 | fperror("failed to allocate font structure" ); |
84 | return 0; |
85 | } |
86 | |
87 | if(FT_New_Face(ft, fname, 0, (FT_Face*)&fnt->face) != 0) { |
88 | fprintf(stderr, "failed to open font file: %s\n" , fname); |
89 | return 0; |
90 | } |
91 | |
92 | /* pre-create the extended ASCII range glyphmap */ |
93 | if(sz) { |
94 | dtx_prepare_range(fnt, sz, 0, 256); |
95 | |
96 | if(!dtx_font) { |
97 | dtx_use_font(fnt, sz); |
98 | } |
99 | } |
100 | #else |
101 | fprintf(stderr, "ignoring call to dtx_open_font: not compiled with freetype support!\n" ); |
102 | #endif |
103 | |
104 | return fnt; |
105 | } |
106 | |
107 | struct dtx_font *dtx_open_font_glyphmap(const char *fname) |
108 | { |
109 | struct dtx_font *fnt; |
110 | struct dtx_glyphmap *gmap; |
111 | |
112 | if(!(fnt = calloc(1, sizeof *fnt))) { |
113 | fperror("failed to allocate font structure" ); |
114 | return 0; |
115 | } |
116 | |
117 | if(fname) { |
118 | if(!(gmap = dtx_load_glyphmap(fname))) { |
119 | free(fnt); |
120 | return 0; |
121 | } |
122 | |
123 | dtx_add_glyphmap(fnt, gmap); |
124 | } |
125 | return fnt; |
126 | } |
127 | |
128 | void dtx_close_font(struct dtx_font *fnt) |
129 | { |
130 | if(!fnt) return; |
131 | |
132 | #ifdef USE_FREETYPE |
133 | FT_Done_Face(fnt->face); |
134 | #endif |
135 | |
136 | /* destroy the glyphmaps */ |
137 | while(fnt->gmaps) { |
138 | void *tmp = fnt->gmaps; |
139 | fnt->gmaps = fnt->gmaps->next; |
140 | dtx_free_glyphmap(tmp); |
141 | } |
142 | |
143 | free(fnt); |
144 | } |
145 | |
146 | void dtx_prepare(struct dtx_font *fnt, int sz) |
147 | { |
148 | if(!dtx_get_font_glyphmap_range(fnt, sz, 0, 256)) { |
149 | fprintf(stderr, "%s: failed (sz: %d, range: 0-255 [ascii])\n" , __FUNCTION__, sz); |
150 | } |
151 | } |
152 | |
153 | void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend) |
154 | { |
155 | if(!dtx_get_font_glyphmap_range(fnt, sz, cstart, cend)) { |
156 | fprintf(stderr, "%s: failed (sz: %d, range: %d-%d)\n" , __FUNCTION__, sz, cstart, cend); |
157 | } |
158 | } |
159 | |
160 | struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code) |
161 | { |
162 | struct dtx_glyphmap *gm; |
163 | |
164 | /* check to see if the last we've given out fits the bill */ |
165 | if(fnt->last_gmap && code >= fnt->last_gmap->cstart && code < fnt->last_gmap->cend && fnt->last_gmap->ptsize == sz) { |
166 | return fnt->last_gmap; |
167 | } |
168 | |
169 | /* otherwise search for the appropriate glyphmap */ |
170 | gm = fnt->gmaps; |
171 | while(gm) { |
172 | if(code >= gm->cstart && code < gm->cend && sz == gm->ptsize) { |
173 | fnt->last_gmap = gm; |
174 | return gm; |
175 | } |
176 | gm = gm->next; |
177 | } |
178 | return 0; |
179 | } |
180 | |
181 | struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend) |
182 | { |
183 | struct dtx_glyphmap *gm; |
184 | |
185 | /* search the available glyphmaps to see if we've got one that includes |
186 | * the requested range |
187 | */ |
188 | gm = fnt->gmaps; |
189 | while(gm) { |
190 | if(gm->cstart <= cstart && gm->cend >= cend && gm->ptsize == sz) { |
191 | return gm; |
192 | } |
193 | gm = gm->next; |
194 | } |
195 | |
196 | /* not found, create one and add it to the list */ |
197 | if(!(gm = dtx_create_glyphmap_range(fnt, sz, cstart, cend))) { |
198 | return 0; |
199 | } |
200 | return gm; |
201 | } |
202 | |
203 | struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend) |
204 | { |
205 | struct dtx_glyphmap *gmap = 0; |
206 | |
207 | #ifdef USE_FREETYPE |
208 | FT_Face face = fnt->face; |
209 | int i, j; |
210 | int gx, gy; |
211 | int padding = 4; |
212 | int total_width, max_width, max_height; |
213 | |
214 | FT_Set_Char_Size(fnt->face, 0, sz * 64, 72, 72); |
215 | |
216 | if(!(gmap = calloc(1, sizeof *gmap))) { |
217 | return 0; |
218 | } |
219 | |
220 | gmap->ptsize = sz; |
221 | gmap->cstart = cstart; |
222 | gmap->cend = cend; |
223 | gmap->crange = cend - cstart; |
224 | gmap->line_advance = FTSZ_TO_PIXELS((float)face->size->metrics.height); |
225 | |
226 | if(!(gmap->glyphs = malloc(gmap->crange * sizeof *gmap->glyphs))) { |
227 | free(gmap); |
228 | return 0; |
229 | } |
230 | |
231 | total_width = padding; |
232 | max_width = max_height = 0; |
233 | |
234 | for(i=0; i<gmap->crange; i++) { |
235 | int w, h; |
236 | |
237 | FT_Load_Char(face, i + cstart, 0); |
238 | w = FTSZ_TO_PIXELS(face->glyph->metrics.width); |
239 | h = FTSZ_TO_PIXELS(face->glyph->metrics.height); |
240 | |
241 | if(w > max_width) max_width = w; |
242 | if(h > max_height) max_height = h; |
243 | |
244 | total_width += w + padding; |
245 | } |
246 | |
247 | calc_best_size(total_width, max_width, max_height, padding, 1, &gmap->xsz, &gmap->ysz); |
248 | |
249 | if(!(gmap->pixels = malloc(gmap->xsz * gmap->ysz))) { |
250 | free(gmap->glyphs); |
251 | free(gmap); |
252 | return 0; |
253 | } |
254 | memset(gmap->pixels, 0, gmap->xsz * gmap->ysz); |
255 | |
256 | gx = padding; |
257 | gy = padding; |
258 | |
259 | for(i=0; i<gmap->crange; i++) { |
260 | float gwidth, gheight; |
261 | unsigned char *src, *dst; |
262 | FT_GlyphSlot glyph; |
263 | |
264 | FT_Load_Char(face, i + cstart, FT_LOAD_RENDER); |
265 | glyph = face->glyph; |
266 | gwidth = FTSZ_TO_PIXELS((float)glyph->metrics.width); |
267 | gheight = FTSZ_TO_PIXELS((float)glyph->metrics.height); |
268 | |
269 | if(gx > gmap->xsz - gwidth - padding) { |
270 | gx = padding; |
271 | gy += max_height + padding; |
272 | } |
273 | |
274 | src = glyph->bitmap.buffer; |
275 | dst = gmap->pixels + gy * gmap->xsz + gx; |
276 | |
277 | for(j=0; j<glyph->bitmap.rows; j++) { |
278 | memcpy(dst, src, glyph->bitmap.width); |
279 | dst += gmap->xsz; |
280 | src += glyph->bitmap.pitch; |
281 | } |
282 | |
283 | gmap->glyphs[i].code = i; |
284 | gmap->glyphs[i].x = (float)(gx - 1); |
285 | gmap->glyphs[i].y = (float)(gy - 1); |
286 | gmap->glyphs[i].width = gwidth + 2; |
287 | gmap->glyphs[i].height = gheight + 2; |
288 | gmap->glyphs[i].orig_x = -FTSZ_TO_PIXELS((float)glyph->metrics.horiBearingX) + 1; |
289 | gmap->glyphs[i].orig_y = FTSZ_TO_PIXELS((float)glyph->metrics.height - glyph->metrics.horiBearingY) + 1; |
290 | gmap->glyphs[i].advance = FTSZ_TO_PIXELS((float)glyph->metrics.horiAdvance); |
291 | /* also precalc normalized */ |
292 | gmap->glyphs[i].nx = (float)gmap->glyphs[i].x / (float)gmap->xsz; |
293 | gmap->glyphs[i].ny = (float)gmap->glyphs[i].y / (float)gmap->ysz; |
294 | gmap->glyphs[i].nwidth = (float)gmap->glyphs[i].width / (float)gmap->xsz; |
295 | gmap->glyphs[i].nheight = (float)gmap->glyphs[i].height / (float)gmap->ysz; |
296 | |
297 | gx += (int)(gwidth + padding); |
298 | } |
299 | |
300 | /* add it to the glyphmaps list of the font */ |
301 | dtx_add_glyphmap(fnt, gmap); |
302 | #endif /* USE_FREETYPE */ |
303 | |
304 | return gmap; |
305 | } |
306 | |
307 | void dtx_free_glyphmap(struct dtx_glyphmap *gmap) |
308 | { |
309 | if(gmap) { |
310 | gmap->tex = 0; |
311 | free(gmap->pixels); |
312 | free(gmap->glyphs); |
313 | free(gmap); |
314 | } |
315 | } |
316 | |
317 | unsigned char *dtx_get_glyphmap_pixels(struct dtx_glyphmap *gmap) |
318 | { |
319 | return gmap->pixels; |
320 | } |
321 | |
322 | int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap) |
323 | { |
324 | return gmap->xsz; |
325 | } |
326 | |
327 | int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap) |
328 | { |
329 | return gmap->ysz; |
330 | } |
331 | |
332 | struct dtx_glyphmap *dtx_load_glyphmap(const char *fname) |
333 | { |
334 | FILE *fp; |
335 | struct dtx_glyphmap *gmap; |
336 | |
337 | if(!(fp = fopen(fname, "r" ))) { |
338 | return 0; |
339 | } |
340 | gmap = dtx_load_glyphmap_stream(fp); |
341 | fclose(fp); |
342 | return gmap; |
343 | } |
344 | |
345 | struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp) |
346 | { |
347 | char buf[512]; |
348 | int hdr_lines = 0; |
349 | struct dtx_glyphmap *gmap; |
350 | struct glyph *glyphs = 0; |
351 | struct glyph *g; |
352 | int min_code = INT_MAX; |
353 | int max_code = INT_MIN; |
354 | int i; |
355 | int max_pixval = 0; |
356 | int num_pixels; |
357 | |
358 | if(!(gmap = calloc(1, sizeof *gmap))) { |
359 | fperror("failed to allocate glyphmap" ); |
360 | return 0; |
361 | } |
362 | gmap->ptsize = -1; |
363 | gmap->line_advance = FLT_MIN; |
364 | |
365 | while(hdr_lines < 3) { |
366 | char *line = buf; |
367 | if(!fgets(buf, sizeof buf, fp)) { |
368 | fperror("unexpected end of file" ); |
369 | goto err; |
370 | } |
371 | |
372 | while(isspace(*line)) { |
373 | line++; |
374 | } |
375 | |
376 | if(line[0] == '#') { |
377 | int c, res; |
378 | float x, y, xsz, ysz, orig_x, orig_y, adv, line_adv; |
379 | int ptsize; |
380 | |
381 | if((res = sscanf(line + 1, " size: %d\n" , &ptsize)) == 1) { |
382 | gmap->ptsize = ptsize; |
383 | |
384 | } else if((res = sscanf(line + 1, " advance: %f\n" , &line_adv)) == 1) { |
385 | gmap->line_advance = line_adv; |
386 | |
387 | } else if((res = sscanf(line + 1, " %d: %fx%f+%f+%f o:%f,%f adv:%f\n" , |
388 | &c, &xsz, &ysz, &x, &y, &orig_x, &orig_y, &adv)) == 8) { |
389 | if(!(g = malloc(sizeof *g))) { |
390 | fperror("failed to allocate glyph" ); |
391 | goto err; |
392 | } |
393 | g->code = c; |
394 | g->x = x; |
395 | g->y = y; |
396 | g->width = xsz; |
397 | g->height = ysz; |
398 | g->orig_x = orig_x; |
399 | g->orig_y = orig_y; |
400 | g->advance = adv; |
401 | /* normalized coordinates will be precalculated after everything is loaded */ |
402 | |
403 | g->next = glyphs; |
404 | glyphs = g; |
405 | |
406 | if(c < min_code) { |
407 | min_code = c; |
408 | } |
409 | if(c > max_code) { |
410 | max_code = c; |
411 | } |
412 | |
413 | } else { |
414 | fprintf(stderr, "%s: invalid glyph info line\n" , __FUNCTION__); |
415 | goto err; |
416 | } |
417 | |
418 | } else { |
419 | switch(hdr_lines) { |
420 | case 0: |
421 | if(0[line] != 'P' || 1[line] != '6') { |
422 | fprintf(stderr, "%s: invalid file format (magic)\n" , __FUNCTION__); |
423 | goto err; |
424 | } |
425 | break; |
426 | |
427 | case 1: |
428 | if(sscanf(line, "%d %d" , &gmap->xsz, &gmap->ysz) != 2) { |
429 | fprintf(stderr, "%s: invalid file format (dim)\n" , __FUNCTION__); |
430 | goto err; |
431 | } |
432 | break; |
433 | |
434 | case 2: |
435 | { |
436 | char *endp; |
437 | max_pixval = strtol(line, &endp, 10); |
438 | if(endp == line) { |
439 | fprintf(stderr, "%s: invalid file format (maxval)\n" , __FUNCTION__); |
440 | goto err; |
441 | } |
442 | } |
443 | break; |
444 | |
445 | default: |
446 | break; /* can't happen */ |
447 | } |
448 | hdr_lines++; |
449 | } |
450 | } |
451 | |
452 | if(gmap->ptsize == -1 || gmap->line_advance == FLT_MIN) { |
453 | fprintf(stderr, "%s: invalid glyphmap, insufficient information in ppm comments\n" , __FUNCTION__); |
454 | goto err; |
455 | } |
456 | |
457 | /* precalculate normalized glyph coordinates */ |
458 | g = glyphs; |
459 | while(g) { |
460 | g->nx = g->x / gmap->xsz; |
461 | g->ny = g->y / gmap->ysz; |
462 | g->nwidth = g->width / gmap->xsz; |
463 | g->nheight = g->height / gmap->ysz; |
464 | g = g->next; |
465 | } |
466 | |
467 | num_pixels = gmap->xsz * gmap->ysz; |
468 | if(!(gmap->pixels = malloc(num_pixels))) { |
469 | fperror("failed to allocate pixels" ); |
470 | goto err; |
471 | } |
472 | |
473 | for(i=0; i<num_pixels; i++) { |
474 | long c = fgetc(fp); |
475 | if(c == -1) { |
476 | fprintf(stderr, "unexpected end of file while reading pixels\n" ); |
477 | goto err; |
478 | } |
479 | gmap->pixels[i] = (unsigned char)(255 * c / max_pixval); |
480 | fseek(fp, 2, SEEK_CUR); |
481 | } |
482 | |
483 | gmap->cstart = min_code; |
484 | gmap->cend = max_code + 1; |
485 | gmap->crange = gmap->cend - gmap->cstart; |
486 | |
487 | if(!(gmap->glyphs = calloc(gmap->crange, sizeof *gmap->glyphs))) { |
488 | fperror("failed to allocate glyph info" ); |
489 | goto err; |
490 | } |
491 | |
492 | while(glyphs) { |
493 | struct glyph *g = glyphs; |
494 | glyphs = glyphs->next; |
495 | |
496 | gmap->glyphs[g->code - gmap->cstart] = *g; |
497 | free(g); |
498 | } |
499 | return gmap; |
500 | |
501 | err: |
502 | dtx_free_glyphmap(gmap); |
503 | while(glyphs) { |
504 | void *tmp = glyphs; |
505 | glyphs = glyphs->next; |
506 | free(tmp); |
507 | } |
508 | return 0; |
509 | } |
510 | |
511 | int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap) |
512 | { |
513 | FILE *fp; |
514 | int res; |
515 | |
516 | if(!(fp = fopen(fname, "wb" ))) { |
517 | fprintf(stderr, "%s: failed to open file: %s: %s\n" , __FUNCTION__, fname, strerror(errno)); |
518 | return -1; |
519 | } |
520 | res = dtx_save_glyphmap_stream(fp, gmap); |
521 | fclose(fp); |
522 | return res; |
523 | } |
524 | |
525 | int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap) |
526 | { |
527 | int i, num_pixels; |
528 | struct glyph *g = gmap->glyphs; |
529 | |
530 | fprintf(fp, "P6\n%d %d\n" , gmap->xsz, gmap->ysz); |
531 | fprintf(fp, "# size: %d\n" , gmap->ptsize); |
532 | fprintf(fp, "# advance: %g\n" , gmap->line_advance); |
533 | for(i=0; i<gmap->crange; i++) { |
534 | fprintf(fp, "# %d: %gx%g+%g+%g o:%g,%g adv:%g\n" , g->code, g->width, g->height, g->x, g->y, |
535 | g->orig_x, g->orig_y, g->advance); |
536 | g++; |
537 | } |
538 | fprintf(fp, "255\n" ); |
539 | |
540 | num_pixels = gmap->xsz * gmap->ysz; |
541 | for(i=0; i<num_pixels; i++) { |
542 | int c = gmap->pixels[i]; |
543 | fputc(c, fp); |
544 | fputc(c, fp); |
545 | fputc(c, fp); |
546 | } |
547 | return 0; |
548 | } |
549 | |
550 | void dtx_add_glyphmap(struct dtx_font *fnt, struct dtx_glyphmap *gmap) |
551 | { |
552 | gmap->next = fnt->gmaps; |
553 | fnt->gmaps = gmap; |
554 | } |
555 | |
556 | void dtx_use_interpolation(int interpolation) |
557 | { |
558 | dtx_interpolation = interpolation; |
559 | //gmap->tex = 0; |
560 | //dtx_update_texture_interpolation(); |
561 | } |
562 | |
563 | void dtx_use_font(struct dtx_font *fnt, int sz) |
564 | { |
565 | dtx_font = fnt; |
566 | dtx_font_sz = sz; |
567 | } |
568 | |
569 | float dtx_line_height(void) |
570 | { |
571 | struct dtx_glyphmap *gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, '\n'); |
572 | |
573 | return gmap->line_advance; |
574 | } |
575 | |
576 | void dtx_glyph_box(int code, struct dtx_box *box) |
577 | { |
578 | int cidx; |
579 | struct dtx_glyphmap *gmap; |
580 | gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); |
581 | |
582 | cidx = code - gmap->cstart; |
583 | |
584 | box->x = gmap->glyphs[cidx].orig_x; |
585 | box->y = gmap->glyphs[cidx].orig_y; |
586 | box->width = gmap->glyphs[cidx].width; |
587 | box->height = gmap->glyphs[cidx].height; |
588 | } |
589 | |
590 | float dtx_glyph_width(int code) |
591 | { |
592 | struct dtx_box box; |
593 | dtx_glyph_box(code, &box); |
594 | return box.width; |
595 | } |
596 | |
597 | float dtx_glyph_height(int code) |
598 | { |
599 | struct dtx_box box; |
600 | dtx_glyph_box(code, &box); |
601 | return box.height; |
602 | } |
603 | |
604 | void dtx_string_box(const char *str, struct dtx_box *box) |
605 | { |
606 | struct dtx_glyphmap *gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, *str); |
607 | |
608 | int code; |
609 | float pos_x = 0.0f, pos_y = 0.0f; |
610 | struct glyph *g = 0; |
611 | float x0, y0, x1, y1; |
612 | |
613 | x0 = y0 = FLT_MAX; |
614 | x1 = y1 = -FLT_MAX; |
615 | |
616 | while(*str) { |
617 | float px, py; |
618 | |
619 | // get the next character in the string and then advance to the next string character |
620 | code = *str;//dtx_utf8_char_code(str); |
621 | str++;// = dtx_utf8_next_char((char*)str); |
622 | |
623 | px = pos_x; |
624 | py = pos_y; |
625 | |
626 | // process special characters |
627 | if(code == '\n') |
628 | { |
629 | pos_y -= gmap->line_advance; |
630 | pos_x = 0.0; |
631 | } |
632 | else if(code == '\t') pos_x = (float)((fmod(pos_x, 4.0) + 4.0) * gmap->glyphs[0].advance); |
633 | else if(code == '\r') pos_x = 0.0f; |
634 | else |
635 | { |
636 | // advance the cursor normally and add the character |
637 | pos_x += gmap->glyphs[code - gmap->cstart].advance; |
638 | g = gmap->glyphs + code - gmap->cstart; |
639 | } |
640 | |
641 | if(px + g->orig_x < x0) { |
642 | x0 = px + g->orig_x; |
643 | } |
644 | if(py - g->orig_y < y0) { |
645 | y0 = py - g->orig_y; |
646 | } |
647 | if(px + g->orig_x + g->width > x1) { |
648 | x1 = px + g->orig_x + g->width; |
649 | } |
650 | if(py - g->orig_y + g->height > y1) { |
651 | y1 = py - g->orig_y + g->height; |
652 | } |
653 | } |
654 | |
655 | box->x = x0; |
656 | box->y = y0; |
657 | box->width = x1 - x0; |
658 | box->height = y1 - y0; |
659 | } |
660 | |
661 | float dtx_string_width(const char *str) |
662 | { |
663 | struct dtx_box box; |
664 | |
665 | dtx_string_box(str, &box); |
666 | return box.width; |
667 | } |
668 | |
669 | float dtx_string_height(const char *str) |
670 | { |
671 | struct dtx_box box; |
672 | |
673 | dtx_string_box(str, &box); |
674 | return box.height; |
675 | } |
676 | |
677 | float dtx_char_pos(const char *str, int n) |
678 | { |
679 | int i; |
680 | float pos = 0.0; |
681 | struct dtx_glyphmap *gmap; |
682 | |
683 | for(i=0; i<n; i++) { |
684 | int code = dtx_utf8_char_code(str); |
685 | str = dtx_utf8_next_char((char*)str); |
686 | |
687 | gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); |
688 | pos += gmap->glyphs[i].advance; |
689 | } |
690 | return pos; |
691 | } |
692 | |
693 | int dtx_char_at_pt(const char *str, float pt) |
694 | { |
695 | int i; |
696 | float prev_pos = 0.0f, pos = 0.0f; |
697 | struct dtx_glyphmap *gmap; |
698 | |
699 | for(i=0; *str; i++) { |
700 | int code = dtx_utf8_char_code(str); |
701 | str = dtx_utf8_next_char((char*)str); |
702 | |
703 | gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); |
704 | pos += gmap->glyphs[i].advance; |
705 | |
706 | if(fabs(pt - prev_pos) < fabs(pt - pos)) { |
707 | break; |
708 | } |
709 | prev_pos = pos; |
710 | } |
711 | return i; |
712 | } |
713 | |
714 | #ifdef USE_FREETYPE |
715 | static void calc_best_size(int total_width, int max_gwidth, int max_gheight, int padding, int pow2, int *imgw, int *imgh) |
716 | { |
717 | int xsz, ysz, num_rows; |
718 | float aspect; |
719 | |
720 | for(xsz=2; xsz<=MAX_IMG_WIDTH; xsz *= 2) { |
721 | num_rows = total_width / xsz + 1; |
722 | |
723 | /* assume worst case, all last glyphs will float to the next line |
724 | * so let's add extra rows for that. */ |
725 | num_rows += (padding + (max_gwidth + padding) * num_rows + xsz - 1) / xsz; |
726 | |
727 | ysz = num_rows * (max_gheight + padding) + padding; |
728 | if(pow2) { |
729 | ysz = next_pow2(ysz); |
730 | } |
731 | aspect = (float)xsz / (float)ysz; |
732 | |
733 | if(aspect >= 1.0) { |
734 | break; |
735 | } |
736 | } |
737 | |
738 | if(xsz > MAX_IMG_WIDTH) { |
739 | xsz = MAX_IMG_WIDTH; |
740 | } |
741 | |
742 | *imgw = xsz; |
743 | *imgh = ysz; |
744 | } |
745 | |
746 | |
747 | static int next_pow2(int x) |
748 | { |
749 | x--; |
750 | x = (x >> 1) | x; |
751 | x = (x >> 2) | x; |
752 | x = (x >> 4) | x; |
753 | x = (x >> 8) | x; |
754 | x = (x >> 16) | x; |
755 | return x + 1; |
756 | } |
757 | #endif |
758 | |