nuclear@7: /* nuclear@7: libdrawtext - a simple library for fast text rendering in OpenGL nuclear@7: Copyright (C) 2011-2014 John Tsiombikas nuclear@7: nuclear@7: This program is free software: you can redistribute it and/or modify nuclear@7: it under the terms of the GNU Lesser General Public License as published by nuclear@7: the Free Software Foundation, either version 3 of the License, or nuclear@7: (at your option) any later version. nuclear@7: nuclear@7: This program is distributed in the hope that it will be useful, nuclear@7: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@7: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@7: GNU Lesser General Public License for more details. nuclear@7: nuclear@7: You should have received a copy of the GNU Lesser General Public License nuclear@7: along with this program. If not, see . nuclear@7: */ nuclear@7: #ifndef NO_FREETYPE nuclear@7: #define USE_FREETYPE nuclear@7: #endif nuclear@7: nuclear@7: #include nuclear@7: #include nuclear@7: #include nuclear@7: #include nuclear@7: #include nuclear@7: #include nuclear@7: #include nuclear@7: #include nuclear@7: #ifdef USE_FREETYPE nuclear@7: #include nuclear@7: #include FT_FREETYPE_H nuclear@7: #endif nuclear@7: #include "assman.h" nuclear@7: #include "drawtext.h" nuclear@7: #include "drawtext_impl.h" nuclear@7: nuclear@7: #define FTSZ_TO_PIXELS(x) ((x) / 64) nuclear@7: #define MAX_IMG_WIDTH 4096 nuclear@7: nuclear@7: struct dtx_font *dtx_font; nuclear@7: int dtx_font_sz; nuclear@7: nuclear@7: nuclear@7: #ifdef USE_FREETYPE nuclear@7: static int init_freetype(void); nuclear@7: static void cleanup(void); nuclear@7: nuclear@7: static void calc_best_size(int total_width, int max_gwidth, int max_gheight, nuclear@7: int padding, int pow2, int *imgw, int *imgh); nuclear@7: static int next_pow2(int x); nuclear@7: nuclear@7: static FT_Library ft; nuclear@7: nuclear@7: nuclear@7: static int init_done; nuclear@7: nuclear@7: static int init_freetype(void) nuclear@7: { nuclear@7: if(!init_done) { nuclear@7: if(FT_Init_FreeType(&ft) != 0) { nuclear@7: return -1; nuclear@7: } nuclear@7: atexit(cleanup); nuclear@7: init_done = 1; nuclear@7: } nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: static void cleanup(void) nuclear@7: { nuclear@7: if(init_done) { nuclear@7: FT_Done_FreeType(ft); nuclear@7: } nuclear@7: } nuclear@7: #endif /* USE_FREETYPE */ nuclear@7: nuclear@7: struct dtx_font *dtx_open_font(const char *fname, int sz) nuclear@7: { nuclear@7: struct dtx_font *fnt = 0; nuclear@7: nuclear@7: #ifdef USE_FREETYPE nuclear@7: init_freetype(); nuclear@7: nuclear@7: if(!(fnt = calloc(1, sizeof *fnt))) { nuclear@7: fperror("failed to allocate font structure"); nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: if(FT_New_Face(ft, fname, 0, (FT_Face*)&fnt->face) != 0) { nuclear@7: fprintf(stderr, "failed to open font file: %s\n", fname); nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: /* pre-create the extended ASCII range glyphmap */ nuclear@7: if(sz) { nuclear@7: dtx_prepare_range(fnt, sz, 0, 256); nuclear@7: nuclear@7: if(!dtx_font) { nuclear@7: dtx_use_font(fnt, sz); nuclear@7: } nuclear@7: } nuclear@7: #else nuclear@7: fprintf(stderr, "ignoring call to dtx_open_font: not compiled with freetype support!\n"); nuclear@7: #endif nuclear@7: nuclear@7: return fnt; nuclear@7: } nuclear@7: nuclear@7: struct dtx_font *dtx_open_font_glyphmap(const char *fname) nuclear@7: { nuclear@7: struct dtx_font *fnt; nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: nuclear@7: if(!(fnt = calloc(1, sizeof *fnt))) { nuclear@7: fperror("failed to allocate font structure"); nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: if(fname) { nuclear@7: if(!(gmap = dtx_load_glyphmap(fname))) { nuclear@7: free(fnt); nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: dtx_add_glyphmap(fnt, gmap); nuclear@7: nuclear@7: if(!dtx_font) { nuclear@7: dtx_use_font(fnt, gmap->ptsize); nuclear@7: } nuclear@7: } nuclear@7: return fnt; nuclear@7: } nuclear@7: nuclear@7: void dtx_close_font(struct dtx_font *fnt) nuclear@7: { nuclear@7: if(!fnt) return; nuclear@7: nuclear@7: #ifdef USE_FREETYPE nuclear@7: FT_Done_Face(fnt->face); nuclear@7: #endif nuclear@7: nuclear@7: /* destroy the glyphmaps */ nuclear@7: while(fnt->gmaps) { nuclear@7: void *tmp = fnt->gmaps; nuclear@7: fnt->gmaps = fnt->gmaps->next; nuclear@7: dtx_free_glyphmap(tmp); nuclear@7: } nuclear@7: nuclear@7: free(fnt); nuclear@7: } nuclear@7: nuclear@7: void dtx_prepare(struct dtx_font *fnt, int sz) nuclear@7: { nuclear@7: if(!dtx_get_font_glyphmap_range(fnt, sz, 0, 256)) { nuclear@7: fprintf(stderr, "%s: failed (sz: %d, range: 0-255 [ascii])\n", __FUNCTION__, sz); nuclear@7: } nuclear@7: } nuclear@7: nuclear@7: void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend) nuclear@7: { nuclear@7: if(!dtx_get_font_glyphmap_range(fnt, sz, cstart, cend)) { nuclear@7: fprintf(stderr, "%s: failed (sz: %d, range: %d-%d)\n", __FUNCTION__, sz, cstart, cend); nuclear@7: } nuclear@7: } nuclear@7: nuclear@7: int dtx_get_font_glyphmap_count(struct dtx_font *fnt) nuclear@7: { nuclear@7: return fnt->num_gmaps; nuclear@7: } nuclear@7: nuclear@7: struct dtx_glyphmap *dtx_get_font_glyphmap_idx(struct dtx_font *fnt, int idx) nuclear@7: { nuclear@7: int i; nuclear@7: struct dtx_glyphmap *gm = fnt->gmaps; nuclear@7: nuclear@7: for(i=0; inext; nuclear@7: if(!gm) return 0; nuclear@7: } nuclear@7: return gm; nuclear@7: } nuclear@7: nuclear@7: struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code) nuclear@7: { nuclear@7: struct dtx_glyphmap *gm; nuclear@7: nuclear@7: /* check to see if the last we've given out fits the bill */ nuclear@7: if(fnt->last_gmap && code >= fnt->last_gmap->cstart && code < fnt->last_gmap->cend && fnt->last_gmap->ptsize == sz) { nuclear@7: return fnt->last_gmap; nuclear@7: } nuclear@7: nuclear@7: /* otherwise search for the appropriate glyphmap */ nuclear@7: gm = fnt->gmaps; nuclear@7: while(gm) { nuclear@7: if(code >= gm->cstart && code < gm->cend && sz == gm->ptsize) { nuclear@7: fnt->last_gmap = gm; nuclear@7: return gm; nuclear@7: } nuclear@7: gm = gm->next; nuclear@7: } nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend) nuclear@7: { nuclear@7: struct dtx_glyphmap *gm; nuclear@7: nuclear@7: /* search the available glyphmaps to see if we've got one that includes nuclear@7: * the requested range nuclear@7: */ nuclear@7: gm = fnt->gmaps; nuclear@7: while(gm) { nuclear@7: if(gm->cstart <= cstart && gm->cend >= cend && gm->ptsize == sz) { nuclear@7: return gm; nuclear@7: } nuclear@7: gm = gm->next; nuclear@7: } nuclear@7: nuclear@7: /* not found, create one and add it to the list */ nuclear@7: if(!(gm = dtx_create_glyphmap_range(fnt, sz, cstart, cend))) { nuclear@7: return 0; nuclear@7: } nuclear@7: return gm; nuclear@7: } nuclear@7: nuclear@7: struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend) nuclear@7: { nuclear@7: struct dtx_glyphmap *gmap = 0; nuclear@7: nuclear@7: #ifdef USE_FREETYPE nuclear@7: FT_Face face = fnt->face; nuclear@7: int i, j; nuclear@7: int gx, gy; nuclear@7: int padding = 4; nuclear@7: int total_width, max_width, max_height; nuclear@7: nuclear@7: FT_Set_Char_Size(fnt->face, 0, sz * 64, 72, 72); nuclear@7: nuclear@7: if(!(gmap = calloc(1, sizeof *gmap))) { nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: gmap->ptsize = sz; nuclear@7: gmap->cstart = cstart; nuclear@7: gmap->cend = cend; nuclear@7: gmap->crange = cend - cstart; nuclear@7: gmap->line_advance = FTSZ_TO_PIXELS((float)face->size->metrics.height); nuclear@7: nuclear@7: if(!(gmap->glyphs = malloc(gmap->crange * sizeof *gmap->glyphs))) { nuclear@7: free(gmap); nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: total_width = padding; nuclear@7: max_width = max_height = 0; nuclear@7: nuclear@7: for(i=0; icrange; i++) { nuclear@7: int w, h; nuclear@7: nuclear@7: FT_Load_Char(face, i + cstart, 0); nuclear@7: w = FTSZ_TO_PIXELS(face->glyph->metrics.width); nuclear@7: h = FTSZ_TO_PIXELS(face->glyph->metrics.height); nuclear@7: nuclear@7: if(w > max_width) max_width = w; nuclear@7: if(h > max_height) max_height = h; nuclear@7: nuclear@7: total_width += w + padding; nuclear@7: } nuclear@7: nuclear@7: calc_best_size(total_width, max_width, max_height, padding, 1, &gmap->xsz, &gmap->ysz); nuclear@7: nuclear@7: if(!(gmap->pixels = malloc(gmap->xsz * gmap->ysz))) { nuclear@7: free(gmap->glyphs); nuclear@7: free(gmap); nuclear@7: return 0; nuclear@7: } nuclear@7: memset(gmap->pixels, 0, gmap->xsz * gmap->ysz); nuclear@7: nuclear@7: gx = padding; nuclear@7: gy = padding; nuclear@7: nuclear@7: for(i=0; icrange; i++) { nuclear@7: float gwidth, gheight; nuclear@7: unsigned char *src, *dst; nuclear@7: FT_GlyphSlot glyph; nuclear@7: nuclear@7: FT_Load_Char(face, i + cstart, FT_LOAD_RENDER); nuclear@7: glyph = face->glyph; nuclear@7: gwidth = FTSZ_TO_PIXELS((float)glyph->metrics.width); nuclear@7: gheight = FTSZ_TO_PIXELS((float)glyph->metrics.height); nuclear@7: nuclear@7: if(gx > gmap->xsz - gwidth - padding) { nuclear@7: gx = padding; nuclear@7: gy += max_height + padding; nuclear@7: } nuclear@7: nuclear@7: src = glyph->bitmap.buffer; nuclear@7: dst = gmap->pixels + gy * gmap->xsz + gx; nuclear@7: nuclear@7: for(j=0; jbitmap.rows; j++) { nuclear@7: memcpy(dst, src, glyph->bitmap.width); nuclear@7: dst += gmap->xsz; nuclear@7: src += glyph->bitmap.pitch; nuclear@7: } nuclear@7: nuclear@7: gmap->glyphs[i].code = i; nuclear@7: gmap->glyphs[i].x = gx - 1; nuclear@7: gmap->glyphs[i].y = gy - 1; nuclear@7: gmap->glyphs[i].width = gwidth + 2; nuclear@7: gmap->glyphs[i].height = gheight + 2; nuclear@7: gmap->glyphs[i].orig_x = -FTSZ_TO_PIXELS((float)glyph->metrics.horiBearingX) + 1; nuclear@7: gmap->glyphs[i].orig_y = FTSZ_TO_PIXELS((float)glyph->metrics.height - glyph->metrics.horiBearingY) + 1; nuclear@7: gmap->glyphs[i].advance = FTSZ_TO_PIXELS((float)glyph->metrics.horiAdvance); nuclear@7: /* also precalc normalized */ nuclear@7: gmap->glyphs[i].nx = (float)gmap->glyphs[i].x / (float)gmap->xsz; nuclear@7: gmap->glyphs[i].ny = (float)gmap->glyphs[i].y / (float)gmap->ysz; nuclear@7: gmap->glyphs[i].nwidth = (float)gmap->glyphs[i].width / (float)gmap->xsz; nuclear@7: gmap->glyphs[i].nheight = (float)gmap->glyphs[i].height / (float)gmap->ysz; nuclear@7: nuclear@7: gx += gwidth + padding; nuclear@7: } nuclear@7: nuclear@7: /* add it to the glyphmaps list of the font */ nuclear@7: dtx_add_glyphmap(fnt, gmap); nuclear@7: #endif /* USE_FREETYPE */ nuclear@7: nuclear@7: return gmap; nuclear@7: } nuclear@7: nuclear@7: void dtx_free_glyphmap(struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: if(gmap) { nuclear@7: free(gmap->pixels); nuclear@7: free(gmap->glyphs); nuclear@7: free(gmap); nuclear@7: } nuclear@7: } nuclear@7: nuclear@7: unsigned char *dtx_get_glyphmap_pixels(struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: return gmap->pixels; nuclear@7: } nuclear@7: nuclear@7: int dtx_get_glyphmap_ptsize(struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: return gmap->ptsize; nuclear@7: } nuclear@7: nuclear@7: int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: return gmap->xsz; nuclear@7: } nuclear@7: nuclear@7: int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: return gmap->ysz; nuclear@7: } nuclear@7: nuclear@7: struct dtx_glyphmap *dtx_load_glyphmap(const char *fname) nuclear@7: { nuclear@7: ass_file *fp; nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: nuclear@7: if(!(fp = ass_fopen(fname, "rb"))) { nuclear@7: return 0; nuclear@7: } nuclear@7: gmap = dtx_load_glyphmap_asset(fp); nuclear@7: ass_fclose(fp); nuclear@7: return gmap; nuclear@7: } nuclear@7: nuclear@7: struct dtx_glyphmap *dtx_load_glyphmap_asset(ass_file *fp) nuclear@7: { nuclear@7: char buf[512]; nuclear@7: int hdr_lines = 0; nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: struct glyph *glyphs = 0; nuclear@7: struct glyph *g; nuclear@7: int min_code = INT_MAX; nuclear@7: int max_code = INT_MIN; nuclear@7: int i, max_pixval, num_pixels; nuclear@7: nuclear@7: if(!(gmap = calloc(1, sizeof *gmap))) { nuclear@7: fperror("failed to allocate glyphmap"); nuclear@7: return 0; nuclear@7: } nuclear@7: gmap->ptsize = -1; nuclear@7: gmap->line_advance = FLT_MIN; nuclear@7: nuclear@7: while(hdr_lines < 3) { nuclear@7: char *line = buf; nuclear@7: if(!ass_fgets(buf, sizeof buf, fp)) { nuclear@7: fperror("unexpected end of file"); nuclear@7: goto err; nuclear@7: } nuclear@7: nuclear@7: while(isspace(*line)) { nuclear@7: line++; nuclear@7: } nuclear@7: nuclear@7: if(line[0] == '#') { nuclear@7: int c, res; nuclear@7: float x, y, xsz, ysz, orig_x, orig_y, adv, line_adv; nuclear@7: int ptsize; nuclear@7: nuclear@7: if((res = sscanf(line + 1, " size: %d\n", &ptsize)) == 1) { nuclear@7: gmap->ptsize = ptsize; nuclear@7: nuclear@7: } else if((res = sscanf(line + 1, " advance: %f\n", &line_adv)) == 1) { nuclear@7: gmap->line_advance = line_adv; nuclear@7: nuclear@7: } else if((res = sscanf(line + 1, " %d: %fx%f+%f+%f o:%f,%f adv:%f\n", nuclear@7: &c, &xsz, &ysz, &x, &y, &orig_x, &orig_y, &adv)) == 8) { nuclear@7: if(!(g = malloc(sizeof *g))) { nuclear@7: fperror("failed to allocate glyph"); nuclear@7: goto err; nuclear@7: } nuclear@7: g->code = c; nuclear@7: g->x = x; nuclear@7: g->y = y; nuclear@7: g->width = xsz; nuclear@7: g->height = ysz; nuclear@7: g->orig_x = orig_x; nuclear@7: g->orig_y = orig_y; nuclear@7: g->advance = adv; nuclear@7: /* normalized coordinates will be precalculated after everything is loaded */ nuclear@7: nuclear@7: g->next = glyphs; nuclear@7: glyphs = g; nuclear@7: nuclear@7: if(c < min_code) { nuclear@7: min_code = c; nuclear@7: } nuclear@7: if(c > max_code) { nuclear@7: max_code = c; nuclear@7: } nuclear@7: nuclear@7: } else { nuclear@7: fprintf(stderr, "%s: invalid glyph info line\n", __FUNCTION__); nuclear@7: goto err; nuclear@7: } nuclear@7: nuclear@7: } else { nuclear@7: switch(hdr_lines) { nuclear@7: case 0: nuclear@7: if(0[line] != 'P' || 1[line] != '6') { nuclear@7: fprintf(stderr, "%s: invalid file format (magic)\n", __FUNCTION__); nuclear@7: goto err; nuclear@7: } nuclear@7: break; nuclear@7: nuclear@7: case 1: nuclear@7: if(sscanf(line, "%d %d", &gmap->xsz, &gmap->ysz) != 2) { nuclear@7: fprintf(stderr, "%s: invalid file format (dim)\n", __FUNCTION__); nuclear@7: goto err; nuclear@7: } nuclear@7: break; nuclear@7: nuclear@7: case 2: nuclear@7: { nuclear@7: char *endp; nuclear@7: max_pixval = strtol(line, &endp, 10); nuclear@7: if(endp == line) { nuclear@7: fprintf(stderr, "%s: invalid file format (maxval)\n", __FUNCTION__); nuclear@7: goto err; nuclear@7: } nuclear@7: } nuclear@7: break; nuclear@7: nuclear@7: default: nuclear@7: break; /* can't happen */ nuclear@7: } nuclear@7: hdr_lines++; nuclear@7: } nuclear@7: } nuclear@7: nuclear@7: if(gmap->ptsize == -1 || gmap->line_advance == FLT_MIN) { nuclear@7: fprintf(stderr, "%s: invalid glyphmap, insufficient information in ppm comments\n", __FUNCTION__); nuclear@7: goto err; nuclear@7: } nuclear@7: nuclear@7: /* precalculate normalized glyph coordinates */ nuclear@7: g = glyphs; nuclear@7: while(g) { nuclear@7: g->nx = g->x / gmap->xsz; nuclear@7: g->ny = g->y / gmap->ysz; nuclear@7: g->nwidth = g->width / gmap->xsz; nuclear@7: g->nheight = g->height / gmap->ysz; nuclear@7: g = g->next; nuclear@7: } nuclear@7: nuclear@7: num_pixels = gmap->xsz * gmap->ysz; nuclear@7: if(!(gmap->pixels = malloc(num_pixels))) { nuclear@7: fperror("failed to allocate pixels"); nuclear@7: goto err; nuclear@7: } nuclear@7: nuclear@7: for(i=0; ipixels[i] = 255 * c / max_pixval; nuclear@7: ass_fseek(fp, 2, SEEK_CUR); nuclear@7: } nuclear@7: nuclear@7: gmap->cstart = min_code; nuclear@7: gmap->cend = max_code + 1; nuclear@7: gmap->crange = gmap->cend - gmap->cstart; nuclear@7: nuclear@7: if(!(gmap->glyphs = calloc(gmap->crange, sizeof *gmap->glyphs))) { nuclear@7: fperror("failed to allocate glyph info"); nuclear@7: goto err; nuclear@7: } nuclear@7: nuclear@7: while(glyphs) { nuclear@7: struct glyph *g = glyphs; nuclear@7: glyphs = glyphs->next; nuclear@7: nuclear@7: gmap->glyphs[g->code - gmap->cstart] = *g; nuclear@7: free(g); nuclear@7: } nuclear@7: return gmap; nuclear@7: nuclear@7: err: nuclear@7: dtx_free_glyphmap(gmap); nuclear@7: while(glyphs) { nuclear@7: void *tmp = glyphs; nuclear@7: glyphs = glyphs->next; nuclear@7: free(tmp); nuclear@7: } nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: FILE *fp; nuclear@7: int res; nuclear@7: nuclear@7: if(!(fp = fopen(fname, "wb"))) { nuclear@7: fprintf(stderr, "%s: failed to open file: %s: %s\n", __FUNCTION__, fname, strerror(errno)); nuclear@7: return -1; nuclear@7: } nuclear@7: res = dtx_save_glyphmap_stream(fp, gmap); nuclear@7: fclose(fp); nuclear@7: return res; nuclear@7: } nuclear@7: nuclear@7: int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: int i, num_pixels; nuclear@7: struct glyph *g = gmap->glyphs; nuclear@7: nuclear@7: fprintf(fp, "P6\n%d %d\n", gmap->xsz, gmap->ysz); nuclear@7: fprintf(fp, "# size: %d\n", gmap->ptsize); nuclear@7: fprintf(fp, "# advance: %g\n", gmap->line_advance); nuclear@7: for(i=0; icrange; i++) { nuclear@7: 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: g->orig_x, g->orig_y, g->advance); nuclear@7: g++; nuclear@7: } nuclear@7: fprintf(fp, "255\n"); nuclear@7: nuclear@7: num_pixels = gmap->xsz * gmap->ysz; nuclear@7: for(i=0; ipixels[i]; nuclear@7: fputc(c, fp); nuclear@7: fputc(c, fp); nuclear@7: fputc(c, fp); nuclear@7: } nuclear@7: return 0; nuclear@7: } nuclear@7: nuclear@7: void dtx_add_glyphmap(struct dtx_font *fnt, struct dtx_glyphmap *gmap) nuclear@7: { nuclear@7: gmap->next = fnt->gmaps; nuclear@7: fnt->gmaps = gmap; nuclear@7: fnt->num_gmaps++; nuclear@7: } nuclear@7: nuclear@7: nuclear@7: void dtx_use_font(struct dtx_font *fnt, int sz) nuclear@7: { nuclear@7: dtx_gl_init(); nuclear@7: nuclear@7: dtx_font = fnt; nuclear@7: dtx_font_sz = sz; nuclear@7: } nuclear@7: nuclear@7: float dtx_line_height(void) nuclear@7: { nuclear@7: struct dtx_glyphmap *gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, '\n'); nuclear@7: nuclear@7: return gmap->line_advance; nuclear@7: } nuclear@7: nuclear@7: void dtx_glyph_box(int code, struct dtx_box *box) nuclear@7: { nuclear@7: int cidx; nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); nuclear@7: nuclear@7: cidx = code - gmap->cstart; nuclear@7: nuclear@7: box->x = gmap->glyphs[cidx].orig_x; nuclear@7: box->y = gmap->glyphs[cidx].orig_y; nuclear@7: box->width = gmap->glyphs[cidx].width; nuclear@7: box->height = gmap->glyphs[cidx].height; nuclear@7: } nuclear@7: nuclear@7: float dtx_glyph_width(int code) nuclear@7: { nuclear@7: struct dtx_box box; nuclear@7: dtx_glyph_box(code, &box); nuclear@7: return box.width; nuclear@7: } nuclear@7: nuclear@7: float dtx_glyph_height(int code) nuclear@7: { nuclear@7: struct dtx_box box; nuclear@7: dtx_glyph_box(code, &box); nuclear@7: return box.height; nuclear@7: } nuclear@7: nuclear@7: void dtx_string_box(const char *str, struct dtx_box *box) nuclear@7: { nuclear@7: int code; nuclear@7: float pos_x = 0.0f, pos_y = 0.0f; nuclear@7: struct glyph *g = 0; nuclear@7: float x0, y0, x1, y1; nuclear@7: nuclear@7: x0 = y0 = FLT_MAX; nuclear@7: x1 = y1 = -FLT_MAX; nuclear@7: nuclear@7: while(*str) { nuclear@7: float px, py; nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: nuclear@7: code = dtx_utf8_char_code(str); nuclear@7: str = dtx_utf8_next_char((char*)str); nuclear@7: nuclear@7: px = pos_x; nuclear@7: py = pos_y; nuclear@7: nuclear@7: if((gmap = dtx_proc_char(code, &pos_x, &pos_y))) { nuclear@7: g = gmap->glyphs + code - gmap->cstart; nuclear@7: nuclear@7: if(px + g->orig_x < x0) { nuclear@7: x0 = px + g->orig_x; nuclear@7: } nuclear@7: if(py - g->orig_y < y0) { nuclear@7: y0 = py - g->orig_y; nuclear@7: } nuclear@7: if(px + g->orig_x + g->width > x1) { nuclear@7: x1 = px + g->orig_x + g->width; nuclear@7: } nuclear@7: if(py - g->orig_y + g->height > y1) { nuclear@7: y1 = py - g->orig_y + g->height; nuclear@7: } nuclear@7: } nuclear@7: } nuclear@7: nuclear@7: box->x = x0; nuclear@7: box->y = y0; nuclear@7: box->width = x1 - x0; nuclear@7: box->height = y1 - y0; nuclear@7: } nuclear@7: nuclear@7: float dtx_string_width(const char *str) nuclear@7: { nuclear@7: struct dtx_box box; nuclear@7: nuclear@7: dtx_string_box(str, &box); nuclear@7: return box.width; nuclear@7: } nuclear@7: nuclear@7: float dtx_string_height(const char *str) nuclear@7: { nuclear@7: struct dtx_box box; nuclear@7: nuclear@7: dtx_string_box(str, &box); nuclear@7: return box.height; nuclear@7: } nuclear@7: nuclear@7: float dtx_char_pos(const char *str, int n) nuclear@7: { nuclear@7: int i; nuclear@7: float pos = 0.0; nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: nuclear@7: for(i=0; iglyphs[i].advance; nuclear@7: } nuclear@7: return pos; nuclear@7: } nuclear@7: nuclear@7: int dtx_char_at_pt(const char *str, float pt) nuclear@7: { nuclear@7: int i; nuclear@7: float prev_pos = 0.0f, pos = 0.0f; nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: nuclear@7: for(i=0; *str; i++) { nuclear@7: int code = dtx_utf8_char_code(str); nuclear@7: str = dtx_utf8_next_char((char*)str); nuclear@7: nuclear@7: gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); nuclear@7: pos += gmap->glyphs[i].advance; nuclear@7: nuclear@7: if(fabs(pt - prev_pos) < fabs(pt - pos)) { nuclear@7: break; nuclear@7: } nuclear@7: prev_pos = pos; nuclear@7: } nuclear@7: return i; nuclear@7: } nuclear@7: nuclear@7: struct dtx_glyphmap *dtx_proc_char(int code, float *xpos, float *ypos) nuclear@7: { nuclear@7: struct dtx_glyphmap *gmap; nuclear@7: gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code); nuclear@7: nuclear@7: switch(code) { nuclear@7: case '\n': nuclear@7: *xpos = 0.0; nuclear@7: if(gmap) { nuclear@7: *ypos -= gmap->line_advance; nuclear@7: } nuclear@7: return 0; nuclear@7: nuclear@7: case '\t': nuclear@7: if(gmap) { nuclear@7: *xpos = (fmod(*xpos, 4.0) + 4.0) * gmap->glyphs[0].advance; nuclear@7: } nuclear@7: return 0; nuclear@7: nuclear@7: case '\r': nuclear@7: *xpos = 0.0; nuclear@7: return 0; nuclear@7: nuclear@7: default: nuclear@7: break; nuclear@7: } nuclear@7: nuclear@7: if(gmap) { nuclear@7: *xpos += gmap->glyphs[code - gmap->cstart].advance; nuclear@7: } nuclear@7: return gmap; nuclear@7: } nuclear@7: nuclear@7: #ifdef USE_FREETYPE nuclear@7: static void calc_best_size(int total_width, int max_gwidth, int max_gheight, int padding, int pow2, int *imgw, int *imgh) nuclear@7: { nuclear@7: int xsz, ysz, num_rows; nuclear@7: float aspect; nuclear@7: nuclear@7: for(xsz=2; xsz<=MAX_IMG_WIDTH; xsz *= 2) { nuclear@7: num_rows = total_width / xsz + 1; nuclear@7: nuclear@7: /* assume worst case, all last glyphs will float to the next line nuclear@7: * so let's add extra rows for that. */ nuclear@7: num_rows += (padding + (max_gwidth + padding) * num_rows + xsz - 1) / xsz; nuclear@7: nuclear@7: ysz = num_rows * (max_gheight + padding) + padding; nuclear@7: if(pow2) { nuclear@7: ysz = next_pow2(ysz); nuclear@7: } nuclear@7: aspect = (float)xsz / (float)ysz; nuclear@7: nuclear@7: if(aspect >= 1.0) { nuclear@7: break; nuclear@7: } nuclear@7: } nuclear@7: nuclear@7: if(xsz > MAX_IMG_WIDTH) { nuclear@7: xsz = MAX_IMG_WIDTH; nuclear@7: } nuclear@7: nuclear@7: *imgw = xsz; nuclear@7: *imgh = ysz; nuclear@7: } nuclear@7: nuclear@7: nuclear@7: static int next_pow2(int x) nuclear@7: { nuclear@7: x--; nuclear@7: x = (x >> 1) | x; nuclear@7: x = (x >> 2) | x; nuclear@7: x = (x >> 4) | x; nuclear@7: x = (x >> 8) | x; nuclear@7: x = (x >> 16) | x; nuclear@7: return x + 1; nuclear@7: } nuclear@7: #endif