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