istereo2

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