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