dungeon_crawler

annotate prototype/drawtext/font.c @ 24:e122ba214ee1

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