2 libdrawtext - a simple library for fast text rendering in OpenGL
3 Copyright (C) 2011-2018 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #define DTX_DEFINE_COMMON
34 #include FT_FREETYPE_H
42 int (*readchar)(struct io*);
43 void *(*readline)(void *buf, int bsz, struct io*);
46 #define FTSZ_TO_PIXELS(x) ((x) / 64)
47 #define MAX_IMG_SIZE 8192
49 static int opt_padding = 8;
50 static int opt_save_ppm;
52 static struct dtx_glyphmap *load_glyphmap(struct io *io);
55 static int init_freetype(void);
56 static void cleanup(void);
58 static int calc_best_size(int total_width, int max_gwidth, int max_gheight,
59 int padding, int pow2, int *imgw, int *imgh);
60 static int next_pow2(int x);
67 static int init_freetype(void)
70 if(FT_Init_FreeType(&ft) != 0) {
79 static void cleanup(void)
85 #endif /* USE_FREETYPE */
87 static int find_pow2(int x);
89 struct dtx_font *dtx_open_font(const char *fname, int sz)
91 struct dtx_font *fnt = 0;
96 if(!(fnt = calloc(1, sizeof *fnt))) {
97 fperror("dtx_open_font: failed to allocate font structure");
101 if(FT_New_Face(ft, fname, 0, (FT_Face*)&fnt->face) != 0) {
102 fprintf(stderr, "failed to open font file: %s\n", fname);
107 /* pre-create the extended ASCII range glyphmap */
109 dtx_prepare_range(fnt, sz, 0, 256);
112 dtx_use_font(fnt, sz);
116 fprintf(stderr, "ignoring call to dtx_open_font: not compiled with freetype support!\n");
122 struct dtx_font *dtx_open_font_mem(void *ptr, int memsz, int fontsz)
124 struct dtx_font *fnt = 0;
131 if(!(fnt = calloc(1, sizeof *fnt))) {
132 fperror("dtx_open_font_mem: failed to allocate font structure");
136 memset(&args, 0, sizeof args);
137 args.flags = FT_OPEN_MEMORY;
138 args.memory_base = ptr;
139 args.memory_size = memsz;
141 if(FT_Open_Face(ft, &args, 0, (FT_Face*)&fnt->face) != 0) {
142 fprintf(stderr, "failed to open font from memory\n");
147 /* pre-create the extended ASCII range glyphmap */
149 dtx_prepare_range(fnt, fontsz, 0, 256);
152 dtx_use_font(fnt, fontsz);
156 fprintf(stderr, "ignoring call to dtx_open_font_mem: not compiled with freetype support!\n");
162 struct dtx_font *dtx_open_font_glyphmap(const char *fname)
164 struct dtx_font *fnt;
165 struct dtx_glyphmap *gmap;
167 if(!(fnt = calloc(1, sizeof *fnt))) {
168 fperror("dtx_open_font_glyphmap: failed to allocate font structure");
173 if(!(gmap = dtx_load_glyphmap(fname))) {
178 dtx_add_glyphmap(fnt, gmap);
181 dtx_use_font(fnt, gmap->ptsize);
187 struct dtx_font *dtx_open_font_glyphmap_mem(void *ptr, int memsz)
189 struct dtx_font *fnt;
190 struct dtx_glyphmap *gmap;
192 if(!(fnt = calloc(1, sizeof *fnt))) {
193 fperror("dtx_open_font_glyphmap_mem: failed to allocate font structure");
197 if(!(gmap = dtx_load_glyphmap_mem(ptr, memsz))) {
202 dtx_add_glyphmap(fnt, gmap);
205 dtx_use_font(fnt, gmap->ptsize);
210 void dtx_close_font(struct dtx_font *fnt)
215 FT_Done_Face(fnt->face);
218 /* destroy the glyphmaps */
220 void *tmp = fnt->gmaps;
221 fnt->gmaps = fnt->gmaps->next;
222 dtx_free_glyphmap(tmp);
228 void dtx_prepare(struct dtx_font *fnt, int sz)
230 if(!dtx_get_font_glyphmap_range(fnt, sz, 0, 256)) {
231 fprintf(stderr, "dtx_prepare: failed (sz: %d, range: 0-255 [ascii])\n", sz);
235 void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend)
237 if(!dtx_get_font_glyphmap_range(fnt, sz, cstart, cend)) {
238 fprintf(stderr, "dtx_prepare_range: failed (sz: %d, range: %d-%d)\n", sz, cstart, cend);
242 int dtx_calc_font_distfield(struct dtx_font *fnt, int scale_numer, int scale_denom)
244 struct dtx_glyphmap *gm = fnt->gmaps;
246 if(dtx_calc_glyphmap_distfield(gm) == -1) {
247 fprintf(stderr, "dtx_calc_glyphmap_distfield: failed to create distfield glyphmap\n");
251 if(dtx_resize_glyphmap(gm, scale_numer, scale_denom, DTX_LINEAR) == -1) {
252 fprintf(stderr, "dtx_calc_glyphmap_distfield: failed to resize glyhphmap during distfield conversion\n");
260 struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code)
262 struct dtx_glyphmap *gm;
264 /* check to see if the last we've given out fits the bill */
265 if(fnt->last_gmap && code >= fnt->last_gmap->cstart && code < fnt->last_gmap->cend && fnt->last_gmap->ptsize == sz) {
266 return fnt->last_gmap;
269 /* otherwise search for the appropriate glyphmap */
272 if(code >= gm->cstart && code < gm->cend && sz == gm->ptsize) {
281 struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend)
283 struct dtx_glyphmap *gm;
285 /* search the available glyphmaps to see if we've got one that includes
286 * the requested range
290 if(gm->cstart <= cstart && gm->cend >= cend && gm->ptsize == sz) {
296 /* not found, create one and add it to the list */
297 if(!(gm = dtx_create_glyphmap_range(fnt, sz, cstart, cend))) {
303 int dtx_get_num_glyphmaps(struct dtx_font *fnt)
306 struct dtx_glyphmap *gm = fnt->gmaps;
314 struct dtx_glyphmap *dtx_get_glyphmap(struct dtx_font *fnt, int idx)
316 struct dtx_glyphmap *gm = fnt->gmaps;
323 struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend)
325 struct dtx_glyphmap *gmap = 0;
328 FT_Face face = fnt->face;
331 int total_width, max_width, max_height;
332 int half_pad = opt_padding / 2;
334 FT_Set_Char_Size(fnt->face, 0, sz * 64, 72, 72);
336 if(!(gmap = calloc(1, sizeof *gmap))) {
341 gmap->cstart = cstart;
343 gmap->crange = cend - cstart;
344 gmap->line_advance = FTSZ_TO_PIXELS((float)face->size->metrics.height);
345 gmap->baseline = -FTSZ_TO_PIXELS((float)face->size->metrics.descender);
347 if(!(gmap->glyphs = malloc(gmap->crange * sizeof *gmap->glyphs))) {
352 total_width = opt_padding;
353 max_width = max_height = 0;
355 for(i=0; i<gmap->crange; i++) {
358 FT_Load_Char(face, i + cstart, 0);
359 w = FTSZ_TO_PIXELS(face->glyph->metrics.width);
360 h = FTSZ_TO_PIXELS(face->glyph->metrics.height);
362 if(w > max_width) max_width = w;
363 if(h > max_height) max_height = h;
365 total_width += w + opt_padding;
368 if(calc_best_size(total_width, max_width, max_height, opt_padding, 1, &gmap->xsz, &gmap->ysz) == -1) {
373 gmap->xsz_shift = find_pow2(gmap->xsz);
375 if(!(gmap->pixels = malloc(gmap->xsz * gmap->ysz))) {
380 memset(gmap->pixels, 0, gmap->xsz * gmap->ysz);
385 for(i=0; i<gmap->crange; i++) {
386 float gwidth, gheight;
387 unsigned char *src, *dst;
390 FT_Load_Char(face, i + cstart, FT_LOAD_RENDER);
392 gwidth = FTSZ_TO_PIXELS((float)glyph->metrics.width);
393 gheight = FTSZ_TO_PIXELS((float)glyph->metrics.height);
395 if(gx > gmap->xsz - gwidth - opt_padding) {
397 gy += max_height + opt_padding;
400 src = glyph->bitmap.buffer;
401 dst = gmap->pixels + (gy << gmap->xsz_shift) + gx;
403 for(j=0; j<(int)glyph->bitmap.rows; j++) {
404 memcpy(dst, src, glyph->bitmap.width);
406 src += glyph->bitmap.pitch;
409 gmap->glyphs[i].code = i;
410 gmap->glyphs[i].x = gx - half_pad;
411 gmap->glyphs[i].y = gy - half_pad;
412 gmap->glyphs[i].width = gwidth + half_pad * 2;
413 gmap->glyphs[i].height = gheight + half_pad * 2;
414 gmap->glyphs[i].orig_x = -FTSZ_TO_PIXELS((float)glyph->metrics.horiBearingX) + 1;
415 gmap->glyphs[i].orig_y = FTSZ_TO_PIXELS((float)glyph->metrics.height - glyph->metrics.horiBearingY) + 1;
416 gmap->glyphs[i].advance = FTSZ_TO_PIXELS((float)glyph->metrics.horiAdvance);
417 /* also precalc normalized */
418 gmap->glyphs[i].nx = (float)gmap->glyphs[i].x / (float)gmap->xsz;
419 gmap->glyphs[i].ny = (float)gmap->glyphs[i].y / (float)gmap->ysz;
420 gmap->glyphs[i].nwidth = (float)gmap->glyphs[i].width / (float)gmap->xsz;
421 gmap->glyphs[i].nheight = (float)gmap->glyphs[i].height / (float)gmap->ysz;
423 gx += gwidth + opt_padding;
426 /* add it to the glyphmaps list of the font */
427 dtx_add_glyphmap(fnt, gmap);
428 #endif /* USE_FREETYPE */
433 void dtx_free_glyphmap(struct dtx_glyphmap *gmap)
442 #define CHECK_BOUNDS(gm, x, y) ((x) >= 0 && (x) < (gm)->xsz && (y) >= 0 && (y) < (gm)->ysz)
443 #define GET_PIXEL(gm, x, y) ((gm)->pixels[((y) << (gm)->xsz_shift) + (x)])
445 static int calc_distance(struct dtx_glyphmap *gmap, int x, int y, int max_dist)
447 int i, j, startx, starty, endx, endy, px, py;
449 int min_distsq = INT_MAX;
450 unsigned char cpix = GET_PIXEL(gmap, x, y);
453 if(max_dist > 128) max_dist = 128;
455 startx = x >= max_dist ? x - max_dist : 0;
456 starty = y >= max_dist ? y - max_dist : 0;
457 endx = x + max_dist < gmap->xsz ? x + max_dist : gmap->xsz - 1;
458 endy = y + max_dist < gmap->ysz ? y + max_dist : gmap->ysz - 1;
460 /* try the cardinal directions first to find the search bounding box */
462 int max_dist = x - startx;
463 for(j=0; j<max_dist; j++) {
464 if(GET_PIXEL(gmap, x - j, y) != cpix) {
469 max_dist = endx + 1 - x;
470 for(j=0; j<max_dist; j++) {
471 if(GET_PIXEL(gmap, x + j, y) != cpix) {
476 max_dist = y - starty;
477 for(j=0; j<max_dist; j++) {
478 if(GET_PIXEL(gmap, x, y - j) != cpix) {
483 max_dist = endy + 1 - y;
484 for(j=0; j<max_dist; j++) {
485 if(GET_PIXEL(gmap, x, y + j) != cpix) {
492 /* find the minimum squared distance inside the bounding box */
493 bwidth = endx + 1 - startx;
494 bheight = endy + 1 - starty;
497 for(i=0; i<bheight; i++) {
499 for(j=0; j<bwidth; j++) {
500 if(GET_PIXEL(gmap, px, py) != cpix) {
503 int distsq = dx * dx + dy * dy;
505 if(distsq < min_distsq) {
514 dist = (int)sqrt(min_distsq);
515 if(dist > 127) dist = 127;
517 return cpix ? dist + 128 : 127 - dist;
520 struct distcalc_data {
521 struct dtx_glyphmap *gmap;
523 unsigned char *pixels;
526 static void distcalc_func(void *cls)
529 struct distcalc_data *data = cls;
530 struct dtx_glyphmap *gmap = data->gmap;
532 printf("scanline %d of %d\n", data->scanline + 1, gmap->ysz);
533 for(i=0; i<gmap->xsz; i++) {
534 *data->pixels++ = calc_distance(gmap, i, data->scanline, 64);
538 int dtx_calc_glyphmap_distfield(struct dtx_glyphmap *gmap)
540 int i, num_pixels = gmap->xsz * gmap->ysz;
541 unsigned char *new_pixels;
544 /* first quantize the glyphmap to 1bit */
546 for(i=0; i<num_pixels; i++) {
547 unsigned char c = *dptr;
548 *dptr++ = c < 128 ? 0 : 255;
551 if(!(new_pixels = malloc(num_pixels))) {
552 fprintf(stderr, "dtx_calc_glyphmap_distfield: failed to allocate %dx%d pixel buffer\n", gmap->xsz, gmap->ysz);
557 for(i=0; i<gmap->ysz; i++) {
558 struct distcalc_data d;
561 d.pixels = new_pixels + (i << gmap->xsz_shift);
566 gmap->pixels = new_pixels;
570 static unsigned char sample_area(struct dtx_glyphmap *gm, float x, float y, float area)
573 int ksz = (int)(area + 0.5);
574 int half_ksz = ksz / 2;
576 int sum = 0, nsamples = 0;
578 for(i=0; i<ksz; i++) {
579 for(j=0; j<ksz; j++) {
580 int sx = x + j - half_ksz;
581 int sy = y + i - half_ksz;
583 if(sx < 0 || sx >= gm->xsz || sy < 0 || sy >= gm->ysz) {
587 sum += gm->pixels[(sy << gm->xsz_shift) + sx];
595 return sum > 255 ? 255 : sum;
598 static unsigned char sample_pixel(struct dtx_glyphmap *gm, int x, int y)
600 if(CHECK_BOUNDS(gm, x, y)) {
601 return gm->pixels[(y << gm->xsz_shift) + x];
606 static int count_bits(int x)
609 for(i=0; i<sizeof x * CHAR_BIT; i++) {
616 int dtx_resize_glyphmap(struct dtx_glyphmap *gmap, int snum, int sdenom, int filter)
618 int i, j, nxsz, nysz;
619 unsigned char *dptr, *new_pixels;
620 float scale, inv_scale, area;
622 if(snum == sdenom) return 0;
624 if((count_bits(snum) | count_bits(sdenom)) != 1) {
625 fprintf(stderr, "dtx_resize_glyphmap: invalid scale fraction %d/%d (not power of 2)\n", snum, sdenom);
629 /* normalize the fraction */
638 if(snum != 1 && sdenom != 1) {
639 fprintf(stderr, "dtx_resize_glyphmap: invalid scale fraction %d/%d (neither is 1)\n", snum, sdenom);
643 nxsz = snum * gmap->xsz / sdenom;
644 nysz = snum * gmap->ysz / sdenom;
646 if(nxsz < 1 || nysz < 1) {
650 new_pixels = malloc(nxsz * nysz);
652 fprintf(stderr, "dtx_resize_glyphmap: failed to allocate %dx%d pixel buffer\n", nxsz, nysz);
658 scale = (float)snum / (float)sdenom;
659 inv_scale = 1.0 / scale;
660 area = scale <= 1.0 ? inv_scale : 2.0;
662 if(filter == DTX_NEAREST) {
663 /* no filtering, nearest neighbor */
664 for(i=0; i<nysz; i++) {
665 for(j=0; j<nxsz; j++) {
666 *dptr++ = sample_pixel(gmap, j * inv_scale, i * inv_scale);
670 /* bilinear filtering */
671 for(i=0; i<nysz; i++) {
672 for(j=0; j<nxsz; j++) {
673 *dptr++ = sample_area(gmap, j * inv_scale, i * inv_scale, area);
679 gmap->pixels = new_pixels;
682 gmap->xsz_shift = find_pow2(nxsz);
684 /* also scale all the metrics accordingly */
685 for(i=0; i<gmap->crange; i++) {
686 struct glyph *g = gmap->glyphs + i;
695 gmap->ptsize = snum * gmap->ptsize / sdenom;
696 gmap->line_advance *= scale;
700 unsigned char *dtx_get_glyphmap_pixels(struct dtx_glyphmap *gmap)
705 int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap)
710 int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap)
715 int dtx_get_glyphmap_ptsize(struct dtx_glyphmap *gmap)
720 struct dtx_glyphmap *dtx_load_glyphmap(const char *fname)
723 struct dtx_glyphmap *gmap;
725 if(!(fp = fopen(fname, "rb"))) {
728 gmap = dtx_load_glyphmap_stream(fp);
734 static int file_readchar(struct io *io)
736 return fgetc(io->data);
739 static void *file_readline(void *buf, int bsz, struct io *io)
741 return fgets(buf, bsz, io->data);
744 static int mem_readchar(struct io *io)
748 if(io->size-- <= 0) {
755 static void *mem_readline(void *buf, int bsz, struct io *io)
760 while(--bsz > 0 && (c = mem_readchar(io)) != -1) {
769 struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp)
773 io.readchar = file_readchar;
774 io.readline = file_readline;
775 return load_glyphmap(&io);
778 struct dtx_glyphmap *dtx_load_glyphmap_mem(void *ptr, int memsz)
783 io.readchar = mem_readchar;
784 io.readline = mem_readline;
785 return load_glyphmap(&io);
788 static struct dtx_glyphmap *load_glyphmap(struct io *io)
792 struct dtx_glyphmap *gmap;
793 struct glyph *glyphs = 0;
795 int min_code = INT_MAX;
796 int max_code = INT_MIN;
797 int i, max_pixval = 255, num_pixels;
800 if(!(gmap = calloc(1, sizeof *gmap))) {
801 fperror("load_glyphmap: failed to allocate glyphmap");
805 gmap->line_advance = FLT_MIN;
807 while(hdr_lines < 3) {
809 if(!io->readline(buf, sizeof buf, io)) {
810 fperror("load_glyphmap: unexpected end of file");
814 while(isspace(*line)) {
820 float x, y, xsz, ysz, orig_x, orig_y, adv, line_adv;
823 if((res = sscanf(line + 1, " size: %d\n", &ptsize)) == 1) {
824 gmap->ptsize = ptsize;
826 } else if((res = sscanf(line + 1, " advance: %f\n", &line_adv)) == 1) {
827 gmap->line_advance = line_adv;
829 } else if((res = sscanf(line + 1, " %d: %fx%f+%f+%f o:%f,%f adv:%f\n",
830 &c, &xsz, &ysz, &x, &y, &orig_x, &orig_y, &adv)) == 8) {
831 if(!(g = malloc(sizeof *g))) {
832 fperror("load_glyphmap: failed to allocate glyph");
843 /* normalized coordinates will be precalculated after everything is loaded */
856 fprintf(stderr, "load_glyphmap: invalid glyph info line\n");
863 if(line[0] != 'P' || !(line[1] == '6' || line[1] == '5')) {
864 fprintf(stderr, "load_glyphmap: invalid file format (magic)\n");
867 greyscale = line[1] == '5';
871 if(sscanf(line, "%d %d", &gmap->xsz, &gmap->ysz) != 2) {
872 fprintf(stderr, "load_glyphmap: invalid file format (dim)\n");
880 max_pixval = strtol(line, &endp, 10);
882 fprintf(stderr, "load_glyphmap: invalid file format (maxval)\n");
889 break; /* can't happen */
895 if(gmap->ptsize == -1 || gmap->line_advance == FLT_MIN) {
896 fprintf(stderr, "load_glyphmap: invalid glyphmap, insufficient information in ppm comments\n");
900 /* precalculate normalized glyph coordinates */
903 g->nx = g->x / gmap->xsz;
904 g->ny = g->y / gmap->ysz;
905 g->nwidth = g->width / gmap->xsz;
906 g->nheight = g->height / gmap->ysz;
910 num_pixels = gmap->xsz * gmap->ysz;
911 if(!(gmap->pixels = malloc(num_pixels))) {
912 fperror("load_glyphmap: failed to allocate pixels");
916 for(i=0; i<num_pixels; i++) {
917 long c = io->readchar(io);
919 fprintf(stderr, "unexpected end of file while reading pixels\n");
922 gmap->pixels[i] = 255 * c / max_pixval;
929 gmap->xsz_shift = find_pow2(gmap->xsz);
930 gmap->cstart = min_code;
931 gmap->cend = max_code + 1;
932 gmap->crange = gmap->cend - gmap->cstart;
934 if(!(gmap->glyphs = calloc(gmap->crange, sizeof *gmap->glyphs))) {
935 fperror("load_glyphmap: failed to allocate glyph info");
940 struct glyph *g = glyphs;
941 glyphs = glyphs->next;
943 gmap->glyphs[g->code - gmap->cstart] = *g;
949 dtx_free_glyphmap(gmap);
952 glyphs = glyphs->next;
958 int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap)
963 if(!(fp = fopen(fname, "wb"))) {
964 fprintf(stderr, "dtx_save_glyphmap: failed to open file: %s: %s\n", fname, strerror(errno));
967 res = dtx_save_glyphmap_stream(fp, gmap);
972 int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap)
975 struct glyph *g = gmap->glyphs;
977 fprintf(fp, "P%d\n%d %d\n", opt_save_ppm ? 6 : 5, gmap->xsz, gmap->ysz);
978 fprintf(fp, "# size: %d\n", gmap->ptsize);
979 fprintf(fp, "# advance: %g\n", gmap->line_advance);
980 for(i=0; i<gmap->crange; i++) {
981 fprintf(fp, "# %d: %gx%g+%g+%g o:%g,%g adv:%g\n", g->code + gmap->cstart,
982 g->width, g->height, g->x, g->y, g->orig_x, g->orig_y, g->advance);
985 fprintf(fp, "255\n");
987 num_pixels = gmap->xsz * gmap->ysz;
988 for(i=0; i<num_pixels; i++) {
989 int c = gmap->pixels[i];
999 void dtx_add_glyphmap(struct dtx_font *fnt, struct dtx_glyphmap *gmap)
1001 gmap->next = fnt->gmaps;
1006 void dtx_set(enum dtx_option opt, int val)
1018 dtx_gl_setopt(opt, val);
1019 dtx_rast_setopt(opt, val);
1023 int dtx_get(enum dtx_option opt)
1032 return opt_save_ppm;
1038 if(dtx_gl_getopt(opt, &val) != -1) {
1041 if(dtx_rast_getopt(opt, &val) != -1) {
1047 void dtx_use_font(struct dtx_font *fnt, int sz)
1050 dtx_target_opengl();
1057 float dtx_line_height(void)
1059 struct dtx_glyphmap *gmap = dtx_get_glyphmap(dtx_font, 0);
1061 return gmap->line_advance;
1064 float dtx_baseline(void)
1066 struct dtx_glyphmap *gmap = dtx_get_glyphmap(dtx_font, 0);
1068 return gmap->baseline;
1071 void dtx_glyph_box(int code, struct dtx_box *box)
1074 struct dtx_glyphmap *gmap;
1075 gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code);
1077 cidx = code - gmap->cstart;
1079 box->x = gmap->glyphs[cidx].orig_x;
1080 box->y = gmap->glyphs[cidx].orig_y;
1081 box->width = gmap->glyphs[cidx].width;
1082 box->height = gmap->glyphs[cidx].height;
1085 float dtx_glyph_width(int code)
1088 dtx_glyph_box(code, &box);
1092 float dtx_glyph_height(int code)
1095 dtx_glyph_box(code, &box);
1099 void dtx_string_box(const char *str, struct dtx_box *box)
1101 dtx_substring_box(str, 0, INT_MAX, box);
1104 void dtx_substring_box(const char *str, int start, int end, struct dtx_box *box)
1107 float pos_x = 0.0f, pos_y = 0.0f;
1108 struct glyph *g = 0;
1109 float x0, y0, x1, y1;
1114 /* skip start characters */
1115 while(*str && start > 0) {
1116 str = dtx_utf8_next_char((char*)str);
1121 while(*str && --end >= 0) {
1123 struct dtx_glyphmap *gmap;
1125 code = dtx_utf8_char_code(str);
1126 str = dtx_utf8_next_char((char*)str);
1131 if((gmap = dtx_proc_char(code, &pos_x, &pos_y))) {
1132 g = gmap->glyphs + code - gmap->cstart;
1134 if(px + g->orig_x < x0) {
1135 x0 = px + g->orig_x;
1137 if(py - g->orig_y < y0) {
1138 y0 = py - g->orig_y;
1140 if(px + g->orig_x + g->width > x1) {
1141 x1 = px + g->orig_x + g->width;
1143 if(py - g->orig_y + g->height > y1) {
1144 y1 = py - g->orig_y + g->height;
1151 box->width = x1 - x0;
1152 box->height = y1 - y0;
1155 float dtx_string_width(const char *str)
1159 dtx_string_box(str, &box);
1163 float dtx_string_height(const char *str)
1167 dtx_string_box(str, &box);
1171 float dtx_char_pos(const char *str, int n)
1175 struct dtx_glyphmap *gmap;
1177 for(i=0; i<n; i++) {
1180 code = dtx_utf8_char_code(str);
1181 str = dtx_utf8_next_char((char*)str);
1183 if((gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
1184 pos += gmap->glyphs[code - gmap->cstart].advance;
1190 int dtx_char_at_pt(const char *str, float pt)
1193 float prev_pos = 0.0f, pos = 0.0f;
1194 struct dtx_glyphmap *gmap;
1196 for(i=0; *str; i++) {
1197 int code = dtx_utf8_char_code(str);
1198 str = dtx_utf8_next_char((char*)str);
1200 if((gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
1201 pos += gmap->glyphs[code - gmap->cstart].advance;
1203 if(fabs(pt - prev_pos) < fabs(pt - pos)) {
1212 struct dtx_glyphmap *dtx_proc_char(int code, float *xpos, float *ypos)
1214 struct dtx_glyphmap *gmap;
1215 gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code);
1221 *ypos -= gmap->line_advance;
1227 *xpos = (fmod(*xpos, 4.0) + 4.0) * gmap->glyphs[0].advance;
1240 *xpos += gmap->glyphs[code - gmap->cstart].advance;
1246 static int calc_best_size(int total_width, int max_gwidth, int max_gheight, int padding, int pow2, int *imgw, int *imgh)
1248 int xsz, ysz, num_rows;
1251 /* the widest glyph won't fit in the maximum image size */
1252 if(max_gwidth > MAX_IMG_SIZE) {
1256 for(xsz=2; xsz<=MAX_IMG_SIZE; xsz *= 2) {
1257 num_rows = total_width / xsz + 1;
1259 /* assume worst case, all last glyphs will float to the next line
1260 * so let's add extra rows for that. */
1261 num_rows += (padding + (max_gwidth + padding) * num_rows + xsz - 1) / xsz;
1263 ysz = num_rows * (max_gheight + padding) + padding;
1264 if(ysz <= 0 || ysz > MAX_IMG_SIZE) continue;
1267 ysz = next_pow2(ysz);
1269 aspect = (float)xsz / (float)ysz;
1276 if(xsz > MAX_IMG_SIZE || ysz > MAX_IMG_SIZE || ysz <= 0) {
1286 static int next_pow2(int x)
1298 static int find_pow2(int x)
1301 for(i=0; i<sizeof x * CHAR_BIT; i++) {