3dphotoshoot
diff libs/drawtext/font.c @ 10:c71c477521ca
converting to GLES2 and C++
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 31 May 2015 00:40:26 +0300 |
parents | |
children | d7fe157c402d |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/drawtext/font.c Sun May 31 00:40:26 2015 +0300 1.3 @@ -0,0 +1,773 @@ 1.4 +/* 1.5 +libdrawtext - a simple library for fast text rendering in OpenGL 1.6 +Copyright (C) 2011-2014 John Tsiombikas <nuclear@member.fsf.org> 1.7 + 1.8 +This program is free software: you can redistribute it and/or modify 1.9 +it under the terms of the GNU Lesser General Public License as published by 1.10 +the Free Software Foundation, either version 3 of the License, or 1.11 +(at your option) any later version. 1.12 + 1.13 +This program is distributed in the hope that it will be useful, 1.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 1.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.16 +GNU Lesser General Public License for more details. 1.17 + 1.18 +You should have received a copy of the GNU Lesser General Public License 1.19 +along with this program. If not, see <http://www.gnu.org/licenses/>. 1.20 +*/ 1.21 +#ifndef NO_FREETYPE 1.22 +#define USE_FREETYPE 1.23 +#endif 1.24 + 1.25 +#include <stdio.h> 1.26 +#include <stdlib.h> 1.27 +#include <string.h> 1.28 +#include <math.h> 1.29 +#include <limits.h> 1.30 +#include <ctype.h> 1.31 +#include <float.h> 1.32 +#include <errno.h> 1.33 +#ifdef USE_FREETYPE 1.34 +#include <ft2build.h> 1.35 +#include FT_FREETYPE_H 1.36 +#endif 1.37 +#include "drawtext.h" 1.38 +#include "drawtext_impl.h" 1.39 + 1.40 +#define FTSZ_TO_PIXELS(x) ((x) / 64) 1.41 +#define MAX_IMG_WIDTH 4096 1.42 + 1.43 + 1.44 +#ifdef USE_FREETYPE 1.45 +static int init_freetype(void); 1.46 +static void cleanup(void); 1.47 + 1.48 +static void calc_best_size(int total_width, int max_gwidth, int max_gheight, 1.49 + int padding, int pow2, int *imgw, int *imgh); 1.50 +static int next_pow2(int x); 1.51 + 1.52 +static FT_Library ft; 1.53 + 1.54 + 1.55 +static int init_done; 1.56 + 1.57 +static int init_freetype(void) 1.58 +{ 1.59 + if(!init_done) { 1.60 + if(FT_Init_FreeType(&ft) != 0) { 1.61 + return -1; 1.62 + } 1.63 + atexit(cleanup); 1.64 + init_done = 1; 1.65 + } 1.66 + return 0; 1.67 +} 1.68 + 1.69 +static void cleanup(void) 1.70 +{ 1.71 + if(init_done) { 1.72 + FT_Done_FreeType(ft); 1.73 + } 1.74 +} 1.75 +#endif /* USE_FREETYPE */ 1.76 + 1.77 +struct dtx_font *dtx_open_font(const char *fname, int sz) 1.78 +{ 1.79 + struct dtx_font *fnt = 0; 1.80 + 1.81 +#ifdef USE_FREETYPE 1.82 + init_freetype(); 1.83 + 1.84 + if(!(fnt = calloc(1, sizeof *fnt))) { 1.85 + fperror("failed to allocate font structure"); 1.86 + return 0; 1.87 + } 1.88 + 1.89 + if(FT_New_Face(ft, fname, 0, (FT_Face*)&fnt->face) != 0) { 1.90 + fprintf(stderr, "failed to open font file: %s\n", fname); 1.91 + return 0; 1.92 + } 1.93 + 1.94 + /* pre-create the extended ASCII range glyphmap */ 1.95 + if(sz) { 1.96 + dtx_prepare_range(fnt, sz, 0, 256); 1.97 + 1.98 + if(!dtx_font) { 1.99 + dtx_use_font(fnt, sz); 1.100 + } 1.101 + } 1.102 +#else 1.103 + fprintf(stderr, "ignoring call to dtx_open_font: not compiled with freetype support!\n"); 1.104 +#endif 1.105 + 1.106 + return fnt; 1.107 +} 1.108 + 1.109 +struct dtx_font *dtx_open_font_glyphmap(const char *fname) 1.110 +{ 1.111 + struct dtx_font *fnt; 1.112 + struct dtx_glyphmap *gmap; 1.113 + 1.114 + if(!(fnt = calloc(1, sizeof *fnt))) { 1.115 + fperror("failed to allocate font structure"); 1.116 + return 0; 1.117 + } 1.118 + 1.119 + if(fname) { 1.120 + if(!(gmap = dtx_load_glyphmap(fname))) { 1.121 + free(fnt); 1.122 + return 0; 1.123 + } 1.124 + 1.125 + dtx_add_glyphmap(fnt, gmap); 1.126 + 1.127 + if(!dtx_font) { 1.128 + dtx_use_font(fnt, gmap->ptsize); 1.129 + } 1.130 + } 1.131 + return fnt; 1.132 +} 1.133 + 1.134 +void dtx_close_font(struct dtx_font *fnt) 1.135 +{ 1.136 + if(!fnt) return; 1.137 + 1.138 +#ifdef USE_FREETYPE 1.139 + FT_Done_Face(fnt->face); 1.140 +#endif 1.141 + 1.142 + /* destroy the glyphmaps */ 1.143 + while(fnt->gmaps) { 1.144 + void *tmp = fnt->gmaps; 1.145 + fnt->gmaps = fnt->gmaps->next; 1.146 + dtx_free_glyphmap(tmp); 1.147 + } 1.148 + 1.149 + free(fnt); 1.150 +} 1.151 + 1.152 +void dtx_prepare(struct dtx_font *fnt, int sz) 1.153 +{ 1.154 + if(!dtx_get_font_glyphmap_range(fnt, sz, 0, 256)) { 1.155 + fprintf(stderr, "%s: failed (sz: %d, range: 0-255 [ascii])\n", __FUNCTION__, sz); 1.156 + } 1.157 +} 1.158 + 1.159 +void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend) 1.160 +{ 1.161 + if(!dtx_get_font_glyphmap_range(fnt, sz, cstart, cend)) { 1.162 + fprintf(stderr, "%s: failed (sz: %d, range: %d-%d)\n", __FUNCTION__, sz, cstart, cend); 1.163 + } 1.164 +} 1.165 + 1.166 +struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code) 1.167 +{ 1.168 + struct dtx_glyphmap *gm; 1.169 + 1.170 + /* check to see if the last we've given out fits the bill */ 1.171 + if(fnt->last_gmap && code >= fnt->last_gmap->cstart && code < fnt->last_gmap->cend && fnt->last_gmap->ptsize == sz) { 1.172 + return fnt->last_gmap; 1.173 + } 1.174 + 1.175 + /* otherwise search for the appropriate glyphmap */ 1.176 + gm = fnt->gmaps; 1.177 + while(gm) { 1.178 + if(code >= gm->cstart && code < gm->cend && sz == gm->ptsize) { 1.179 + fnt->last_gmap = gm; 1.180 + return gm; 1.181 + } 1.182 + gm = gm->next; 1.183 + } 1.184 + return 0; 1.185 +} 1.186 + 1.187 +struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend) 1.188 +{ 1.189 + struct dtx_glyphmap *gm; 1.190 + 1.191 + /* search the available glyphmaps to see if we've got one that includes 1.192 + * the requested range 1.193 + */ 1.194 + gm = fnt->gmaps; 1.195 + while(gm) { 1.196 + if(gm->cstart <= cstart && gm->cend >= cend && gm->ptsize == sz) { 1.197 + return gm; 1.198 + } 1.199 + gm = gm->next; 1.200 + } 1.201 + 1.202 + /* not found, create one and add it to the list */ 1.203 + if(!(gm = dtx_create_glyphmap_range(fnt, sz, cstart, cend))) { 1.204 + return 0; 1.205 + } 1.206 + return gm; 1.207 +} 1.208 + 1.209 +struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend) 1.210 +{ 1.211 + struct dtx_glyphmap *gmap = 0; 1.212 + 1.213 +#ifdef USE_FREETYPE 1.214 + FT_Face face = fnt->face; 1.215 + int i, j; 1.216 + int gx, gy; 1.217 + int padding = 4; 1.218 + int total_width, max_width, max_height; 1.219 + 1.220 + FT_Set_Char_Size(fnt->face, 0, sz * 64, 72, 72); 1.221 + 1.222 + if(!(gmap = calloc(1, sizeof *gmap))) { 1.223 + return 0; 1.224 + } 1.225 + 1.226 + gmap->ptsize = sz; 1.227 + gmap->cstart = cstart; 1.228 + gmap->cend = cend; 1.229 + gmap->crange = cend - cstart; 1.230 + gmap->line_advance = FTSZ_TO_PIXELS((float)face->size->metrics.height); 1.231 + 1.232 + if(!(gmap->glyphs = malloc(gmap->crange * sizeof *gmap->glyphs))) { 1.233 + free(gmap); 1.234 + return 0; 1.235 + } 1.236 + 1.237 + total_width = padding; 1.238 + max_width = max_height = 0; 1.239 + 1.240 + for(i=0; i<gmap->crange; i++) { 1.241 + int w, h; 1.242 + 1.243 + FT_Load_Char(face, i + cstart, 0); 1.244 + w = FTSZ_TO_PIXELS(face->glyph->metrics.width); 1.245 + h = FTSZ_TO_PIXELS(face->glyph->metrics.height); 1.246 + 1.247 + if(w > max_width) max_width = w; 1.248 + if(h > max_height) max_height = h; 1.249 + 1.250 + total_width += w + padding; 1.251 + } 1.252 + 1.253 + calc_best_size(total_width, max_width, max_height, padding, 1, &gmap->xsz, &gmap->ysz); 1.254 + 1.255 + if(!(gmap->pixels = malloc(gmap->xsz * gmap->ysz))) { 1.256 + free(gmap->glyphs); 1.257 + free(gmap); 1.258 + return 0; 1.259 + } 1.260 + memset(gmap->pixels, 0, gmap->xsz * gmap->ysz); 1.261 + 1.262 + gx = padding; 1.263 + gy = padding; 1.264 + 1.265 + for(i=0; i<gmap->crange; i++) { 1.266 + float gwidth, gheight; 1.267 + unsigned char *src, *dst; 1.268 + FT_GlyphSlot glyph; 1.269 + 1.270 + FT_Load_Char(face, i + cstart, FT_LOAD_RENDER); 1.271 + glyph = face->glyph; 1.272 + gwidth = FTSZ_TO_PIXELS((float)glyph->metrics.width); 1.273 + gheight = FTSZ_TO_PIXELS((float)glyph->metrics.height); 1.274 + 1.275 + if(gx > gmap->xsz - gwidth - padding) { 1.276 + gx = padding; 1.277 + gy += max_height + padding; 1.278 + } 1.279 + 1.280 + src = glyph->bitmap.buffer; 1.281 + dst = gmap->pixels + gy * gmap->xsz + gx; 1.282 + 1.283 + for(j=0; j<glyph->bitmap.rows; j++) { 1.284 + memcpy(dst, src, glyph->bitmap.width); 1.285 + dst += gmap->xsz; 1.286 + src += glyph->bitmap.pitch; 1.287 + } 1.288 + 1.289 + gmap->glyphs[i].code = i; 1.290 + gmap->glyphs[i].x = gx - 1; 1.291 + gmap->glyphs[i].y = gy - 1; 1.292 + gmap->glyphs[i].width = gwidth + 2; 1.293 + gmap->glyphs[i].height = gheight + 2; 1.294 + gmap->glyphs[i].orig_x = -FTSZ_TO_PIXELS((float)glyph->metrics.horiBearingX) + 1; 1.295 + gmap->glyphs[i].orig_y = FTSZ_TO_PIXELS((float)glyph->metrics.height - glyph->metrics.horiBearingY) + 1; 1.296 + gmap->glyphs[i].advance = FTSZ_TO_PIXELS((float)glyph->metrics.horiAdvance); 1.297 + /* also precalc normalized */ 1.298 + gmap->glyphs[i].nx = (float)gmap->glyphs[i].x / (float)gmap->xsz; 1.299 + gmap->glyphs[i].ny = (float)gmap->glyphs[i].y / (float)gmap->ysz; 1.300 + gmap->glyphs[i].nwidth = (float)gmap->glyphs[i].width / (float)gmap->xsz; 1.301 + gmap->glyphs[i].nheight = (float)gmap->glyphs[i].height / (float)gmap->ysz; 1.302 + 1.303 + gx += gwidth + padding; 1.304 + } 1.305 + 1.306 + /* add it to the glyphmaps list of the font */ 1.307 + dtx_add_glyphmap(fnt, gmap); 1.308 +#endif /* USE_FREETYPE */ 1.309 + 1.310 + return gmap; 1.311 +} 1.312 + 1.313 +void dtx_free_glyphmap(struct dtx_glyphmap *gmap) 1.314 +{ 1.315 + if(gmap) { 1.316 + free(gmap->pixels); 1.317 + free(gmap->glyphs); 1.318 + free(gmap); 1.319 + } 1.320 +} 1.321 + 1.322 +unsigned char *dtx_get_glyphmap_pixels(struct dtx_glyphmap *gmap) 1.323 +{ 1.324 + return gmap->pixels; 1.325 +} 1.326 + 1.327 +int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap) 1.328 +{ 1.329 + return gmap->xsz; 1.330 +} 1.331 + 1.332 +int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap) 1.333 +{ 1.334 + return gmap->ysz; 1.335 +} 1.336 + 1.337 +struct dtx_glyphmap *dtx_load_glyphmap(const char *fname) 1.338 +{ 1.339 + FILE *fp; 1.340 + struct dtx_glyphmap *gmap; 1.341 + 1.342 + if(!(fp = fopen(fname, "rb"))) { 1.343 + return 0; 1.344 + } 1.345 + gmap = dtx_load_glyphmap_stream(fp); 1.346 + fclose(fp); 1.347 + return gmap; 1.348 +} 1.349 + 1.350 +struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp) 1.351 +{ 1.352 + char buf[512]; 1.353 + int hdr_lines = 0; 1.354 + struct dtx_glyphmap *gmap; 1.355 + struct glyph *glyphs = 0; 1.356 + struct glyph *g; 1.357 + int min_code = INT_MAX; 1.358 + int max_code = INT_MIN; 1.359 + int i, max_pixval, num_pixels; 1.360 + 1.361 + if(!(gmap = calloc(1, sizeof *gmap))) { 1.362 + fperror("failed to allocate glyphmap"); 1.363 + return 0; 1.364 + } 1.365 + gmap->ptsize = -1; 1.366 + gmap->line_advance = FLT_MIN; 1.367 + 1.368 + while(hdr_lines < 3) { 1.369 + char *line = buf; 1.370 + if(!fgets(buf, sizeof buf, fp)) { 1.371 + fperror("unexpected end of file"); 1.372 + goto err; 1.373 + } 1.374 + 1.375 + while(isspace(*line)) { 1.376 + line++; 1.377 + } 1.378 + 1.379 + if(line[0] == '#') { 1.380 + int c, res; 1.381 + float x, y, xsz, ysz, orig_x, orig_y, adv, line_adv; 1.382 + int ptsize; 1.383 + 1.384 + if((res = sscanf(line + 1, " size: %d\n", &ptsize)) == 1) { 1.385 + gmap->ptsize = ptsize; 1.386 + 1.387 + } else if((res = sscanf(line + 1, " advance: %f\n", &line_adv)) == 1) { 1.388 + gmap->line_advance = line_adv; 1.389 + 1.390 + } else if((res = sscanf(line + 1, " %d: %fx%f+%f+%f o:%f,%f adv:%f\n", 1.391 + &c, &xsz, &ysz, &x, &y, &orig_x, &orig_y, &adv)) == 8) { 1.392 + if(!(g = malloc(sizeof *g))) { 1.393 + fperror("failed to allocate glyph"); 1.394 + goto err; 1.395 + } 1.396 + g->code = c; 1.397 + g->x = x; 1.398 + g->y = y; 1.399 + g->width = xsz; 1.400 + g->height = ysz; 1.401 + g->orig_x = orig_x; 1.402 + g->orig_y = orig_y; 1.403 + g->advance = adv; 1.404 + /* normalized coordinates will be precalculated after everything is loaded */ 1.405 + 1.406 + g->next = glyphs; 1.407 + glyphs = g; 1.408 + 1.409 + if(c < min_code) { 1.410 + min_code = c; 1.411 + } 1.412 + if(c > max_code) { 1.413 + max_code = c; 1.414 + } 1.415 + 1.416 + } else { 1.417 + fprintf(stderr, "%s: invalid glyph info line\n", __FUNCTION__); 1.418 + goto err; 1.419 + } 1.420 + 1.421 + } else { 1.422 + switch(hdr_lines) { 1.423 + case 0: 1.424 + if(0[line] != 'P' || 1[line] != '6') { 1.425 + fprintf(stderr, "%s: invalid file format (magic)\n", __FUNCTION__); 1.426 + goto err; 1.427 + } 1.428 + break; 1.429 + 1.430 + case 1: 1.431 + if(sscanf(line, "%d %d", &gmap->xsz, &gmap->ysz) != 2) { 1.432 + fprintf(stderr, "%s: invalid file format (dim)\n", __FUNCTION__); 1.433 + goto err; 1.434 + } 1.435 + break; 1.436 + 1.437 + case 2: 1.438 + { 1.439 + char *endp; 1.440 + max_pixval = strtol(line, &endp, 10); 1.441 + if(endp == line) { 1.442 + fprintf(stderr, "%s: invalid file format (maxval)\n", __FUNCTION__); 1.443 + goto err; 1.444 + } 1.445 + } 1.446 + break; 1.447 + 1.448 + default: 1.449 + break; /* can't happen */ 1.450 + } 1.451 + hdr_lines++; 1.452 + } 1.453 + } 1.454 + 1.455 + if(gmap->ptsize == -1 || gmap->line_advance == FLT_MIN) { 1.456 + fprintf(stderr, "%s: invalid glyphmap, insufficient information in ppm comments\n", __FUNCTION__); 1.457 + goto err; 1.458 + } 1.459 + 1.460 + /* precalculate normalized glyph coordinates */ 1.461 + g = glyphs; 1.462 + while(g) { 1.463 + g->nx = g->x / gmap->xsz; 1.464 + g->ny = g->y / gmap->ysz; 1.465 + g->nwidth = g->width / gmap->xsz; 1.466 + g->nheight = g->height / gmap->ysz; 1.467 + g = g->next; 1.468 + } 1.469 + 1.470 + num_pixels = gmap->xsz * gmap->ysz; 1.471 + if(!(gmap->pixels = malloc(num_pixels))) { 1.472 + fperror("failed to allocate pixels"); 1.473 + goto err; 1.474 + } 1.475 + 1.476 + for(i=0; i<num_pixels; i++) { 1.477 + long c = fgetc(fp); 1.478 + if(c == -1) { 1.479 + fprintf(stderr, "unexpected end of file while reading pixels\n"); 1.480 + goto err; 1.481 + } 1.482 + gmap->pixels[i] = 255 * c / max_pixval; 1.483 + fseek(fp, 2, SEEK_CUR); 1.484 + } 1.485 + 1.486 + gmap->cstart = min_code; 1.487 + gmap->cend = max_code + 1; 1.488 + gmap->crange = gmap->cend - gmap->cstart; 1.489 + 1.490 + if(!(gmap->glyphs = calloc(gmap->crange, sizeof *gmap->glyphs))) { 1.491 + fperror("failed to allocate glyph info"); 1.492 + goto err; 1.493 + } 1.494 + 1.495 + while(glyphs) { 1.496 + struct glyph *g = glyphs; 1.497 + glyphs = glyphs->next; 1.498 + 1.499 + gmap->glyphs[g->code - gmap->cstart] = *g; 1.500 + free(g); 1.501 + } 1.502 + return gmap; 1.503 + 1.504 +err: 1.505 + dtx_free_glyphmap(gmap); 1.506 + while(glyphs) { 1.507 + void *tmp = glyphs; 1.508 + glyphs = glyphs->next; 1.509 + free(tmp); 1.510 + } 1.511 + return 0; 1.512 +} 1.513 + 1.514 +int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap) 1.515 +{ 1.516 + FILE *fp; 1.517 + int res; 1.518 + 1.519 + if(!(fp = fopen(fname, "wb"))) { 1.520 + fprintf(stderr, "%s: failed to open file: %s: %s\n", __FUNCTION__, fname, strerror(errno)); 1.521 + return -1; 1.522 + } 1.523 + res = dtx_save_glyphmap_stream(fp, gmap); 1.524 + fclose(fp); 1.525 + return res; 1.526 +} 1.527 + 1.528 +int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap) 1.529 +{ 1.530 + int i, num_pixels; 1.531 + struct glyph *g = gmap->glyphs; 1.532 + 1.533 + fprintf(fp, "P6\n%d %d\n", gmap->xsz, gmap->ysz); 1.534 + fprintf(fp, "# size: %d\n", gmap->ptsize); 1.535 + fprintf(fp, "# advance: %g\n", gmap->line_advance); 1.536 + for(i=0; i<gmap->crange; i++) { 1.537 + fprintf(fp, "# %d: %gx%g+%g+%g o:%g,%g adv:%g\n", g->code, g->width, g->height, g->x, g->y, 1.538 + g->orig_x, g->orig_y, g->advance); 1.539 + g++; 1.540 + } 1.541 + fprintf(fp, "255\n"); 1.542 + 1.543 + num_pixels = gmap->xsz * gmap->ysz; 1.544 + for(i=0; i<num_pixels; i++) { 1.545 + int c = gmap->pixels[i]; 1.546 + fputc(c, fp); 1.547 + fputc(c, fp); 1.548 + fputc(c, fp); 1.549 + } 1.550 + return 0; 1.551 +} 1.552 + 1.553 +void dtx_add_glyphmap(struct dtx_font *fnt, struct dtx_glyphmap *gmap) 1.554 +{ 1.555 + gmap->next = fnt->gmaps; 1.556 + fnt->gmaps = gmap; 1.557 +} 1.558 + 1.559 + 1.560 +void dtx_use_font(struct dtx_font *fnt, int sz) 1.561 +{ 1.562 + dtx_gl_init(); 1.563 + 1.564 + dtx_font = fnt; 1.565 + dtx_font_sz = sz; 1.566 +} 1.567 + 1.568 +float dtx_line_height(void) 1.569 +{ 1.570 + struct dtx_glyphmap *gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, '\n'); 1.571 + 1.572 + return gmap->line_advance; 1.573 +} 1.574 + 1.575 +void dtx_glyph_box(int code, struct dtx_box *box) 1.576 +{ 1.577 + int cidx; 1.578 + struct dtx_glyphmap *gmap; 1.579 + gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); 1.580 + 1.581 + cidx = code - gmap->cstart; 1.582 + 1.583 + box->x = gmap->glyphs[cidx].orig_x; 1.584 + box->y = gmap->glyphs[cidx].orig_y; 1.585 + box->width = gmap->glyphs[cidx].width; 1.586 + box->height = gmap->glyphs[cidx].height; 1.587 +} 1.588 + 1.589 +float dtx_glyph_width(int code) 1.590 +{ 1.591 + struct dtx_box box; 1.592 + dtx_glyph_box(code, &box); 1.593 + return box.width; 1.594 +} 1.595 + 1.596 +float dtx_glyph_height(int code) 1.597 +{ 1.598 + struct dtx_box box; 1.599 + dtx_glyph_box(code, &box); 1.600 + return box.height; 1.601 +} 1.602 + 1.603 +void dtx_string_box(const char *str, struct dtx_box *box) 1.604 +{ 1.605 + int code; 1.606 + float pos_x = 0.0f, pos_y = 0.0f; 1.607 + struct glyph *g = 0; 1.608 + float x0, y0, x1, y1; 1.609 + 1.610 + x0 = y0 = FLT_MAX; 1.611 + x1 = y1 = -FLT_MAX; 1.612 + 1.613 + while(*str) { 1.614 + float px, py; 1.615 + struct dtx_glyphmap *gmap; 1.616 + 1.617 + code = dtx_utf8_char_code(str); 1.618 + str = dtx_utf8_next_char((char*)str); 1.619 + 1.620 + px = pos_x; 1.621 + py = pos_y; 1.622 + 1.623 + if((gmap = dtx_proc_char(code, &pos_x, &pos_y))) { 1.624 + g = gmap->glyphs + code - gmap->cstart; 1.625 + 1.626 + if(px + g->orig_x < x0) { 1.627 + x0 = px + g->orig_x; 1.628 + } 1.629 + if(py - g->orig_y < y0) { 1.630 + y0 = py - g->orig_y; 1.631 + } 1.632 + if(px + g->orig_x + g->width > x1) { 1.633 + x1 = px + g->orig_x + g->width; 1.634 + } 1.635 + if(py - g->orig_y + g->height > y1) { 1.636 + y1 = py - g->orig_y + g->height; 1.637 + } 1.638 + } 1.639 + } 1.640 + 1.641 + box->x = x0; 1.642 + box->y = y0; 1.643 + box->width = x1 - x0; 1.644 + box->height = y1 - y0; 1.645 +} 1.646 + 1.647 +float dtx_string_width(const char *str) 1.648 +{ 1.649 + struct dtx_box box; 1.650 + 1.651 + dtx_string_box(str, &box); 1.652 + return box.width; 1.653 +} 1.654 + 1.655 +float dtx_string_height(const char *str) 1.656 +{ 1.657 + struct dtx_box box; 1.658 + 1.659 + dtx_string_box(str, &box); 1.660 + return box.height; 1.661 +} 1.662 + 1.663 +float dtx_char_pos(const char *str, int n) 1.664 +{ 1.665 + int i; 1.666 + float pos = 0.0; 1.667 + struct dtx_glyphmap *gmap; 1.668 + 1.669 + for(i=0; i<n; i++) { 1.670 + int code = dtx_utf8_char_code(str); 1.671 + str = dtx_utf8_next_char((char*)str); 1.672 + 1.673 + gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); 1.674 + pos += gmap->glyphs[i].advance; 1.675 + } 1.676 + return pos; 1.677 +} 1.678 + 1.679 +int dtx_char_at_pt(const char *str, float pt) 1.680 +{ 1.681 + int i; 1.682 + float prev_pos = 0.0f, pos = 0.0f; 1.683 + struct dtx_glyphmap *gmap; 1.684 + 1.685 + for(i=0; *str; i++) { 1.686 + int code = dtx_utf8_char_code(str); 1.687 + str = dtx_utf8_next_char((char*)str); 1.688 + 1.689 + gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); 1.690 + pos += gmap->glyphs[i].advance; 1.691 + 1.692 + if(fabs(pt - prev_pos) < fabs(pt - pos)) { 1.693 + break; 1.694 + } 1.695 + prev_pos = pos; 1.696 + } 1.697 + return i; 1.698 +} 1.699 + 1.700 +struct dtx_glyphmap *dtx_proc_char(int code, float *xpos, float *ypos) 1.701 +{ 1.702 + struct dtx_glyphmap *gmap; 1.703 + gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); 1.704 + 1.705 + switch(code) { 1.706 + case '\n': 1.707 + *xpos = 0.0; 1.708 + if(gmap) { 1.709 + *ypos -= gmap->line_advance; 1.710 + } 1.711 + return 0; 1.712 + 1.713 + case '\t': 1.714 + if(gmap) { 1.715 + *xpos = (fmod(*xpos, 4.0) + 4.0) * gmap->glyphs[0].advance; 1.716 + } 1.717 + return 0; 1.718 + 1.719 + case '\r': 1.720 + *xpos = 0.0; 1.721 + return 0; 1.722 + 1.723 + default: 1.724 + break; 1.725 + } 1.726 + 1.727 + if(gmap) { 1.728 + *xpos += gmap->glyphs[code - gmap->cstart].advance; 1.729 + } 1.730 + return gmap; 1.731 +} 1.732 + 1.733 +#ifdef USE_FREETYPE 1.734 +static void calc_best_size(int total_width, int max_gwidth, int max_gheight, int padding, int pow2, int *imgw, int *imgh) 1.735 +{ 1.736 + int xsz, ysz, num_rows; 1.737 + float aspect; 1.738 + 1.739 + for(xsz=2; xsz<=MAX_IMG_WIDTH; xsz *= 2) { 1.740 + num_rows = total_width / xsz + 1; 1.741 + 1.742 + /* assume worst case, all last glyphs will float to the next line 1.743 + * so let's add extra rows for that. */ 1.744 + num_rows += (padding + (max_gwidth + padding) * num_rows + xsz - 1) / xsz; 1.745 + 1.746 + ysz = num_rows * (max_gheight + padding) + padding; 1.747 + if(pow2) { 1.748 + ysz = next_pow2(ysz); 1.749 + } 1.750 + aspect = (float)xsz / (float)ysz; 1.751 + 1.752 + if(aspect >= 1.0) { 1.753 + break; 1.754 + } 1.755 + } 1.756 + 1.757 + if(xsz > MAX_IMG_WIDTH) { 1.758 + xsz = MAX_IMG_WIDTH; 1.759 + } 1.760 + 1.761 + *imgw = xsz; 1.762 + *imgh = ysz; 1.763 +} 1.764 + 1.765 + 1.766 +static int next_pow2(int x) 1.767 +{ 1.768 + x--; 1.769 + x = (x >> 1) | x; 1.770 + x = (x >> 2) | x; 1.771 + x = (x >> 4) | x; 1.772 + x = (x >> 8) | x; 1.773 + x = (x >> 16) | x; 1.774 + return x + 1; 1.775 +} 1.776 +#endif