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/>.
32 #include FT_FREETYPE_H
35 #include "drawtext_impl.h"
40 int (*readchar)(struct io*);
41 void *(*readline)(void *buf, int bsz, struct io*);
44 #define FTSZ_TO_PIXELS(x) ((x) / 64)
45 #define MAX_IMG_SIZE 8192
47 static int opt_padding = 8;
48 static int opt_save_ppm;
50 static struct dtx_glyphmap *load_glyphmap(struct io *io);
53 static int init_freetype(void);
54 static void cleanup(void);
56 static int calc_best_size(int total_width, int max_gwidth, int max_gheight,
57 int padding, int pow2, int *imgw, int *imgh);
58 static int next_pow2(int x);
65 static int init_freetype(void)
68 if(FT_Init_FreeType(&ft) != 0) {
77 static void cleanup(void)
83 #endif /* USE_FREETYPE */
85 static int find_pow2(int x);
87 struct dtx_font *dtx_open_font(const char *fname, int sz)
89 struct dtx_font *fnt = 0;
94 if(!(fnt = calloc(1, sizeof *fnt))) {
95 fperror("failed to allocate font structure");
99 if(FT_New_Face(ft, fname, 0, (FT_Face*)&fnt->face) != 0) {
100 fprintf(stderr, "failed to open font file: %s\n", fname);
105 /* pre-create the extended ASCII range glyphmap */
107 dtx_prepare_range(fnt, sz, 0, 256);
110 dtx_use_font(fnt, sz);
114 fprintf(stderr, "ignoring call to dtx_open_font: not compiled with freetype support!\n");
120 struct dtx_font *dtx_open_font_mem(void *ptr, int memsz, int fontsz)
122 struct dtx_font *fnt = 0;
129 if(!(fnt = calloc(1, sizeof *fnt))) {
130 fperror("failed to allocate font structure");
134 memset(&args, 0, sizeof args);
135 args.flags = FT_OPEN_MEMORY;
136 args.memory_base = ptr;
137 args.memory_size = memsz;
139 if(FT_Open_Face(ft, &args, 0, (FT_Face*)&fnt->face) != 0) {
140 fprintf(stderr, "failed to open font from memory\n");
145 /* pre-create the extended ASCII range glyphmap */
147 dtx_prepare_range(fnt, fontsz, 0, 256);
150 dtx_use_font(fnt, fontsz);
154 fprintf(stderr, "ignoring call to dtx_open_font_mem: not compiled with freetype support!\n");
160 struct dtx_font *dtx_open_font_glyphmap(const char *fname)
162 struct dtx_font *fnt;
163 struct dtx_glyphmap *gmap;
165 if(!(fnt = calloc(1, sizeof *fnt))) {
166 fperror("failed to allocate font structure");
171 if(!(gmap = dtx_load_glyphmap(fname))) {
176 dtx_add_glyphmap(fnt, gmap);
179 dtx_use_font(fnt, gmap->ptsize);
185 struct dtx_font *dtx_open_font_glyphmap_mem(void *ptr, int memsz)
187 struct dtx_font *fnt;
188 struct dtx_glyphmap *gmap;
190 if(!(fnt = calloc(1, sizeof *fnt))) {
191 fperror("failed to allocate font structure");
195 if(!(gmap = dtx_load_glyphmap_mem(ptr, memsz))) {
200 dtx_add_glyphmap(fnt, gmap);
203 dtx_use_font(fnt, gmap->ptsize);
208 void dtx_close_font(struct dtx_font *fnt)
213 FT_Done_Face(fnt->face);
216 /* destroy the glyphmaps */
218 void *tmp = fnt->gmaps;
219 fnt->gmaps = fnt->gmaps->next;
220 dtx_free_glyphmap(tmp);
226 void dtx_prepare(struct dtx_font *fnt, int sz)
228 if(!dtx_get_font_glyphmap_range(fnt, sz, 0, 256)) {
229 fprintf(stderr, "%s: failed (sz: %d, range: 0-255 [ascii])\n", __func__, sz);
233 void dtx_prepare_range(struct dtx_font *fnt, int sz, int cstart, int cend)
235 if(!dtx_get_font_glyphmap_range(fnt, sz, cstart, cend)) {
236 fprintf(stderr, "%s: failed (sz: %d, range: %d-%d)\n", __func__, sz, cstart, cend);
240 int dtx_calc_font_distfield(struct dtx_font *fnt, int scale_numer, int scale_denom)
242 struct dtx_glyphmap *gm = fnt->gmaps;
244 if(dtx_calc_glyphmap_distfield(gm) == -1) {
245 fprintf(stderr, "%s failed to create distfield glyphmap\n", __func__);
249 if(dtx_resize_glyphmap(gm, scale_numer, scale_denom, DTX_LINEAR) == -1) {
250 fprintf(stderr, "%s: failed to resize glyhphmap during distfield conversion\n", __func__);
258 struct dtx_glyphmap *dtx_get_font_glyphmap(struct dtx_font *fnt, int sz, int code)
260 struct dtx_glyphmap *gm;
262 /* check to see if the last we've given out fits the bill */
263 if(fnt->last_gmap && code >= fnt->last_gmap->cstart && code < fnt->last_gmap->cend && fnt->last_gmap->ptsize == sz) {
264 return fnt->last_gmap;
267 /* otherwise search for the appropriate glyphmap */
270 if(code >= gm->cstart && code < gm->cend && sz == gm->ptsize) {
279 struct dtx_glyphmap *dtx_get_font_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend)
281 struct dtx_glyphmap *gm;
283 /* search the available glyphmaps to see if we've got one that includes
284 * the requested range
288 if(gm->cstart <= cstart && gm->cend >= cend && gm->ptsize == sz) {
294 /* not found, create one and add it to the list */
295 if(!(gm = dtx_create_glyphmap_range(fnt, sz, cstart, cend))) {
301 int dtx_get_num_glyphmaps(struct dtx_font *fnt)
304 struct dtx_glyphmap *gm = fnt->gmaps;
312 struct dtx_glyphmap *dtx_get_glyphmap(struct dtx_font *fnt, int idx)
314 struct dtx_glyphmap *gm = fnt->gmaps;
321 struct dtx_glyphmap *dtx_create_glyphmap_range(struct dtx_font *fnt, int sz, int cstart, int cend)
323 struct dtx_glyphmap *gmap = 0;
326 FT_Face face = fnt->face;
329 int total_width, max_width, max_height;
330 int half_pad = opt_padding / 2;
332 FT_Set_Char_Size(fnt->face, 0, sz * 64, 72, 72);
334 if(!(gmap = calloc(1, sizeof *gmap))) {
339 gmap->cstart = cstart;
341 gmap->crange = cend - cstart;
342 gmap->line_advance = FTSZ_TO_PIXELS((float)face->size->metrics.height);
343 gmap->baseline = -FTSZ_TO_PIXELS((float)face->size->metrics.descender);
345 if(!(gmap->glyphs = malloc(gmap->crange * sizeof *gmap->glyphs))) {
350 total_width = opt_padding;
351 max_width = max_height = 0;
353 for(i=0; i<gmap->crange; i++) {
356 FT_Load_Char(face, i + cstart, 0);
357 w = FTSZ_TO_PIXELS(face->glyph->metrics.width);
358 h = FTSZ_TO_PIXELS(face->glyph->metrics.height);
360 if(w > max_width) max_width = w;
361 if(h > max_height) max_height = h;
363 total_width += w + opt_padding;
366 if(calc_best_size(total_width, max_width, max_height, opt_padding, 1, &gmap->xsz, &gmap->ysz) == -1) {
371 gmap->xsz_shift = find_pow2(gmap->xsz);
373 if(!(gmap->pixels = malloc(gmap->xsz * gmap->ysz))) {
378 memset(gmap->pixels, 0, gmap->xsz * gmap->ysz);
383 for(i=0; i<gmap->crange; i++) {
384 float gwidth, gheight;
385 unsigned char *src, *dst;
388 FT_Load_Char(face, i + cstart, FT_LOAD_RENDER);
390 gwidth = FTSZ_TO_PIXELS((float)glyph->metrics.width);
391 gheight = FTSZ_TO_PIXELS((float)glyph->metrics.height);
393 if(gx > gmap->xsz - gwidth - opt_padding) {
395 gy += max_height + opt_padding;
398 src = glyph->bitmap.buffer;
399 dst = gmap->pixels + (gy << gmap->xsz_shift) + gx;
401 for(j=0; j<(int)glyph->bitmap.rows; j++) {
402 memcpy(dst, src, glyph->bitmap.width);
404 src += glyph->bitmap.pitch;
407 gmap->glyphs[i].code = i;
408 gmap->glyphs[i].x = gx - half_pad;
409 gmap->glyphs[i].y = gy - half_pad;
410 gmap->glyphs[i].width = gwidth + half_pad * 2;
411 gmap->glyphs[i].height = gheight + half_pad * 2;
412 gmap->glyphs[i].orig_x = -FTSZ_TO_PIXELS((float)glyph->metrics.horiBearingX) + 1;
413 gmap->glyphs[i].orig_y = FTSZ_TO_PIXELS((float)glyph->metrics.height - glyph->metrics.horiBearingY) + 1;
414 gmap->glyphs[i].advance = FTSZ_TO_PIXELS((float)glyph->metrics.horiAdvance);
415 /* also precalc normalized */
416 gmap->glyphs[i].nx = (float)gmap->glyphs[i].x / (float)gmap->xsz;
417 gmap->glyphs[i].ny = (float)gmap->glyphs[i].y / (float)gmap->ysz;
418 gmap->glyphs[i].nwidth = (float)gmap->glyphs[i].width / (float)gmap->xsz;
419 gmap->glyphs[i].nheight = (float)gmap->glyphs[i].height / (float)gmap->ysz;
421 gx += gwidth + opt_padding;
424 /* add it to the glyphmaps list of the font */
425 dtx_add_glyphmap(fnt, gmap);
426 #endif /* USE_FREETYPE */
431 void dtx_free_glyphmap(struct dtx_glyphmap *gmap)
440 #define CHECK_BOUNDS(gm, x, y) ((x) >= 0 && (x) < (gm)->xsz && (y) >= 0 && (y) < (gm)->ysz)
441 #define GET_PIXEL(gm, x, y) ((gm)->pixels[((y) << (gm)->xsz_shift) + (x)])
443 static int calc_distance(struct dtx_glyphmap *gmap, int x, int y, int max_dist)
445 int i, j, startx, starty, endx, endy, px, py;
447 int min_distsq = INT_MAX;
448 unsigned char cpix = GET_PIXEL(gmap, x, y);
451 if(max_dist > 128) max_dist = 128;
453 startx = x >= max_dist ? x - max_dist : 0;
454 starty = y >= max_dist ? y - max_dist : 0;
455 endx = x + max_dist < gmap->xsz ? x + max_dist : gmap->xsz - 1;
456 endy = y + max_dist < gmap->ysz ? y + max_dist : gmap->ysz - 1;
458 /* try the cardinal directions first to find the search bounding box */
460 int max_dist = x - startx;
461 for(j=0; j<max_dist; j++) {
462 if(GET_PIXEL(gmap, x - j, y) != cpix) {
467 max_dist = endx + 1 - x;
468 for(j=0; j<max_dist; j++) {
469 if(GET_PIXEL(gmap, x + j, y) != cpix) {
474 max_dist = y - starty;
475 for(j=0; j<max_dist; j++) {
476 if(GET_PIXEL(gmap, x, y - j) != cpix) {
481 max_dist = endy + 1 - y;
482 for(j=0; j<max_dist; j++) {
483 if(GET_PIXEL(gmap, x, y + j) != cpix) {
490 /* find the minimum squared distance inside the bounding box */
491 bwidth = endx + 1 - startx;
492 bheight = endy + 1 - starty;
495 for(i=0; i<bheight; i++) {
497 for(j=0; j<bwidth; j++) {
498 if(GET_PIXEL(gmap, px, py) != cpix) {
501 int distsq = dx * dx + dy * dy;
503 if(distsq < min_distsq) {
512 dist = (int)sqrt(min_distsq);
513 if(dist > 127) dist = 127;
515 return cpix ? dist + 128 : 127 - dist;
518 struct distcalc_data {
519 struct dtx_glyphmap *gmap;
521 unsigned char *pixels;
524 static void distcalc_func(void *cls)
527 struct distcalc_data *data = cls;
528 struct dtx_glyphmap *gmap = data->gmap;
530 printf("scanline %d of %d\n", data->scanline + 1, gmap->ysz);
531 for(i=0; i<gmap->xsz; i++) {
532 *data->pixels++ = calc_distance(gmap, i, data->scanline, 64);
536 int dtx_calc_glyphmap_distfield(struct dtx_glyphmap *gmap)
538 int i, num_pixels = gmap->xsz * gmap->ysz;
539 unsigned char *new_pixels;
542 /* first quantize the glyphmap to 1bit */
544 for(i=0; i<num_pixels; i++) {
545 unsigned char c = *dptr;
546 *dptr++ = c < 128 ? 0 : 255;
549 if(!(new_pixels = malloc(num_pixels))) {
550 fprintf(stderr, "%s: failed to allocate %dx%d pixel buffer\n", __func__, gmap->xsz, gmap->ysz);
555 for(i=0; i<gmap->ysz; i++) {
556 struct distcalc_data d;
559 d.pixels = new_pixels + (i << gmap->xsz_shift);
564 gmap->pixels = new_pixels;
568 static unsigned char sample_area(struct dtx_glyphmap *gm, float x, float y, float area)
571 int ksz = (int)(area + 0.5);
572 int half_ksz = ksz / 2;
574 int sum = 0, nsamples = 0;
576 for(i=0; i<ksz; i++) {
577 for(j=0; j<ksz; j++) {
578 int sx = x + j - half_ksz;
579 int sy = y + i - half_ksz;
581 if(sx < 0 || sx >= gm->xsz || sy < 0 || sy >= gm->ysz) {
585 sum += gm->pixels[(sy << gm->xsz_shift) + sx];
593 return sum > 255 ? 255 : sum;
596 static unsigned char sample_pixel(struct dtx_glyphmap *gm, int x, int y)
598 if(CHECK_BOUNDS(gm, x, y)) {
599 return gm->pixels[(y << gm->xsz_shift) + x];
604 static int count_bits(int x)
607 for(i=0; i<sizeof x * CHAR_BIT; i++) {
614 int dtx_resize_glyphmap(struct dtx_glyphmap *gmap, int snum, int sdenom, int filter)
616 int i, j, nxsz, nysz;
617 unsigned char *dptr, *new_pixels;
618 float scale, inv_scale, area;
620 if(snum == sdenom) return 0;
622 if((count_bits(snum) | count_bits(sdenom)) != 1) {
623 fprintf(stderr, "%s: invalid scale fraction %d/%d (not power of 2)\n", __func__, snum, sdenom);
627 /* normalize the fraction */
636 if(snum != 1 && sdenom != 1) {
637 fprintf(stderr, "%s: invalid scale fraction %d/%d (neither is 1)\n", __func__, snum, sdenom);
641 nxsz = snum * gmap->xsz / sdenom;
642 nysz = snum * gmap->ysz / sdenom;
644 if(nxsz < 1 || nysz < 1) {
648 new_pixels = malloc(nxsz * nysz);
650 fprintf(stderr, "%s: failed to allocate %dx%d pixel buffer\n", __func__, nxsz, nysz);
656 scale = (float)snum / (float)sdenom;
657 inv_scale = 1.0 / scale;
658 area = scale <= 1.0 ? inv_scale : 2.0;
660 if(filter == DTX_NEAREST) {
661 /* no filtering, nearest neighbor */
662 for(i=0; i<nysz; i++) {
663 for(j=0; j<nxsz; j++) {
664 *dptr++ = sample_pixel(gmap, j * inv_scale, i * inv_scale);
668 /* bilinear filtering */
669 for(i=0; i<nysz; i++) {
670 for(j=0; j<nxsz; j++) {
671 *dptr++ = sample_area(gmap, j * inv_scale, i * inv_scale, area);
677 gmap->pixels = new_pixels;
680 gmap->xsz_shift = find_pow2(nxsz);
682 /* also scale all the metrics accordingly */
683 for(i=0; i<gmap->crange; i++) {
684 struct glyph *g = gmap->glyphs + i;
693 gmap->ptsize = snum * gmap->ptsize / sdenom;
694 gmap->line_advance *= scale;
698 unsigned char *dtx_get_glyphmap_pixels(struct dtx_glyphmap *gmap)
703 int dtx_get_glyphmap_width(struct dtx_glyphmap *gmap)
708 int dtx_get_glyphmap_height(struct dtx_glyphmap *gmap)
713 int dtx_get_glyphmap_ptsize(struct dtx_glyphmap *gmap)
718 struct dtx_glyphmap *dtx_load_glyphmap(const char *fname)
721 struct dtx_glyphmap *gmap;
723 if(!(fp = fopen(fname, "rb"))) {
726 gmap = dtx_load_glyphmap_stream(fp);
732 static int file_readchar(struct io *io)
734 return fgetc(io->data);
737 static void *file_readline(void *buf, int bsz, struct io *io)
739 return fgets(buf, bsz, io->data);
742 static int mem_readchar(struct io *io)
746 if(io->size-- <= 0) {
753 static void *mem_readline(void *buf, int bsz, struct io *io)
758 while(--bsz > 0 && (c = mem_readchar(io)) != -1) {
767 struct dtx_glyphmap *dtx_load_glyphmap_stream(FILE *fp)
771 io.readchar = file_readchar;
772 io.readline = file_readline;
773 return load_glyphmap(&io);
776 struct dtx_glyphmap *dtx_load_glyphmap_mem(void *ptr, int memsz)
781 io.readchar = mem_readchar;
782 io.readline = mem_readline;
783 return load_glyphmap(&io);
786 static struct dtx_glyphmap *load_glyphmap(struct io *io)
790 struct dtx_glyphmap *gmap;
791 struct glyph *glyphs = 0;
793 int min_code = INT_MAX;
794 int max_code = INT_MIN;
795 int i, max_pixval = 255, num_pixels;
798 if(!(gmap = calloc(1, sizeof *gmap))) {
799 fperror("failed to allocate glyphmap");
803 gmap->line_advance = FLT_MIN;
805 while(hdr_lines < 3) {
807 if(!io->readline(buf, sizeof buf, io)) {
808 fperror("unexpected end of file");
812 while(isspace(*line)) {
818 float x, y, xsz, ysz, orig_x, orig_y, adv, line_adv;
821 if((res = sscanf(line + 1, " size: %d\n", &ptsize)) == 1) {
822 gmap->ptsize = ptsize;
824 } else if((res = sscanf(line + 1, " advance: %f\n", &line_adv)) == 1) {
825 gmap->line_advance = line_adv;
827 } else if((res = sscanf(line + 1, " %d: %fx%f+%f+%f o:%f,%f adv:%f\n",
828 &c, &xsz, &ysz, &x, &y, &orig_x, &orig_y, &adv)) == 8) {
829 if(!(g = malloc(sizeof *g))) {
830 fperror("failed to allocate glyph");
841 /* normalized coordinates will be precalculated after everything is loaded */
854 fprintf(stderr, "%s: invalid glyph info line\n", __func__);
861 if(line[0] != 'P' || !(line[1] == '6' || line[1] == '5')) {
862 fprintf(stderr, "%s: invalid file format (magic)\n", __func__);
865 greyscale = line[1] == '5';
869 if(sscanf(line, "%d %d", &gmap->xsz, &gmap->ysz) != 2) {
870 fprintf(stderr, "%s: invalid file format (dim)\n", __func__);
878 max_pixval = strtol(line, &endp, 10);
880 fprintf(stderr, "%s: invalid file format (maxval)\n", __func__);
887 break; /* can't happen */
893 if(gmap->ptsize == -1 || gmap->line_advance == FLT_MIN) {
894 fprintf(stderr, "%s: invalid glyphmap, insufficient information in ppm comments\n", __func__);
898 /* precalculate normalized glyph coordinates */
901 g->nx = g->x / gmap->xsz;
902 g->ny = g->y / gmap->ysz;
903 g->nwidth = g->width / gmap->xsz;
904 g->nheight = g->height / gmap->ysz;
908 num_pixels = gmap->xsz * gmap->ysz;
909 if(!(gmap->pixels = malloc(num_pixels))) {
910 fperror("failed to allocate pixels");
914 for(i=0; i<num_pixels; i++) {
915 long c = io->readchar(io);
917 fprintf(stderr, "unexpected end of file while reading pixels\n");
920 gmap->pixels[i] = 255 * c / max_pixval;
927 gmap->xsz_shift = find_pow2(gmap->xsz);
928 gmap->cstart = min_code;
929 gmap->cend = max_code + 1;
930 gmap->crange = gmap->cend - gmap->cstart;
932 if(!(gmap->glyphs = calloc(gmap->crange, sizeof *gmap->glyphs))) {
933 fperror("failed to allocate glyph info");
938 struct glyph *g = glyphs;
939 glyphs = glyphs->next;
941 gmap->glyphs[g->code - gmap->cstart] = *g;
947 dtx_free_glyphmap(gmap);
950 glyphs = glyphs->next;
956 int dtx_save_glyphmap(const char *fname, const struct dtx_glyphmap *gmap)
961 if(!(fp = fopen(fname, "wb"))) {
962 fprintf(stderr, "%s: failed to open file: %s: %s\n", __func__, fname, strerror(errno));
965 res = dtx_save_glyphmap_stream(fp, gmap);
970 int dtx_save_glyphmap_stream(FILE *fp, const struct dtx_glyphmap *gmap)
973 struct glyph *g = gmap->glyphs;
975 fprintf(fp, "P%d\n%d %d\n", opt_save_ppm ? 6 : 5, gmap->xsz, gmap->ysz);
976 fprintf(fp, "# size: %d\n", gmap->ptsize);
977 fprintf(fp, "# advance: %g\n", gmap->line_advance);
978 for(i=0; i<gmap->crange; i++) {
979 fprintf(fp, "# %d: %gx%g+%g+%g o:%g,%g adv:%g\n", g->code + gmap->cstart,
980 g->width, g->height, g->x, g->y, g->orig_x, g->orig_y, g->advance);
983 fprintf(fp, "255\n");
985 num_pixels = gmap->xsz * gmap->ysz;
986 for(i=0; i<num_pixels; i++) {
987 int c = gmap->pixels[i];
997 void dtx_add_glyphmap(struct dtx_font *fnt, struct dtx_glyphmap *gmap)
999 gmap->next = fnt->gmaps;
1004 void dtx_set(enum dtx_option opt, int val)
1016 dtx_gl_setopt(opt, val);
1017 dtx_rast_setopt(opt, val);
1021 int dtx_get(enum dtx_option opt)
1030 return opt_save_ppm;
1036 if(dtx_gl_getopt(opt, &val) != -1) {
1039 if(dtx_rast_getopt(opt, &val) != -1) {
1045 void dtx_use_font(struct dtx_font *fnt, int sz)
1048 dtx_target_opengl();
1055 float dtx_line_height(void)
1057 struct dtx_glyphmap *gmap = dtx_get_glyphmap(dtx_font, 0);
1059 return gmap->line_advance;
1062 float dtx_baseline(void)
1064 struct dtx_glyphmap *gmap = dtx_get_glyphmap(dtx_font, 0);
1066 return gmap->baseline;
1069 void dtx_glyph_box(int code, struct dtx_box *box)
1072 struct dtx_glyphmap *gmap;
1073 gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code);
1075 cidx = code - gmap->cstart;
1077 box->x = gmap->glyphs[cidx].orig_x;
1078 box->y = gmap->glyphs[cidx].orig_y;
1079 box->width = gmap->glyphs[cidx].width;
1080 box->height = gmap->glyphs[cidx].height;
1083 float dtx_glyph_width(int code)
1086 dtx_glyph_box(code, &box);
1090 float dtx_glyph_height(int code)
1093 dtx_glyph_box(code, &box);
1097 void dtx_string_box(const char *str, struct dtx_box *box)
1099 dtx_substring_box(str, 0, INT_MAX, box);
1102 void dtx_substring_box(const char *str, int start, int end, struct dtx_box *box)
1105 float pos_x = 0.0f, pos_y = 0.0f;
1106 struct glyph *g = 0;
1107 float x0, y0, x1, y1;
1112 /* skip start characters */
1113 while(*str && start > 0) {
1114 str = dtx_utf8_next_char((char*)str);
1119 while(*str && --end >= 0) {
1121 struct dtx_glyphmap *gmap;
1123 code = dtx_utf8_char_code(str);
1124 str = dtx_utf8_next_char((char*)str);
1129 if((gmap = dtx_proc_char(code, &pos_x, &pos_y))) {
1130 g = gmap->glyphs + code - gmap->cstart;
1132 if(px + g->orig_x < x0) {
1133 x0 = px + g->orig_x;
1135 if(py - g->orig_y < y0) {
1136 y0 = py - g->orig_y;
1138 if(px + g->orig_x + g->width > x1) {
1139 x1 = px + g->orig_x + g->width;
1141 if(py - g->orig_y + g->height > y1) {
1142 y1 = py - g->orig_y + g->height;
1149 box->width = x1 - x0;
1150 box->height = y1 - y0;
1153 float dtx_string_width(const char *str)
1157 dtx_string_box(str, &box);
1161 float dtx_string_height(const char *str)
1165 dtx_string_box(str, &box);
1169 float dtx_char_pos(const char *str, int n)
1173 struct dtx_glyphmap *gmap;
1175 for(i=0; i<n; i++) {
1178 code = dtx_utf8_char_code(str);
1179 str = dtx_utf8_next_char((char*)str);
1181 if((gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
1182 pos += gmap->glyphs[code - gmap->cstart].advance;
1188 int dtx_char_at_pt(const char *str, float pt)
1191 float prev_pos = 0.0f, pos = 0.0f;
1192 struct dtx_glyphmap *gmap;
1194 for(i=0; *str; i++) {
1195 int code = dtx_utf8_char_code(str);
1196 str = dtx_utf8_next_char((char*)str);
1198 if((gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code))) {
1199 pos += gmap->glyphs[code - gmap->cstart].advance;
1201 if(fabs(pt - prev_pos) < fabs(pt - pos)) {
1210 struct dtx_glyphmap *dtx_proc_char(int code, float *xpos, float *ypos)
1212 struct dtx_glyphmap *gmap;
1213 gmap = dtx_get_font_glyphmap(dtx_font, dtx_font_sz, code);
1219 *ypos -= gmap->line_advance;
1225 *xpos = (fmod(*xpos, 4.0) + 4.0) * gmap->glyphs[0].advance;
1238 *xpos += gmap->glyphs[code - gmap->cstart].advance;
1244 static int calc_best_size(int total_width, int max_gwidth, int max_gheight, int padding, int pow2, int *imgw, int *imgh)
1246 int xsz, ysz, num_rows;
1249 /* the widest glyph won't fit in the maximum image size */
1250 if(max_gwidth > MAX_IMG_SIZE) {
1254 for(xsz=2; xsz<=MAX_IMG_SIZE; xsz *= 2) {
1255 num_rows = total_width / xsz + 1;
1257 /* assume worst case, all last glyphs will float to the next line
1258 * so let's add extra rows for that. */
1259 num_rows += (padding + (max_gwidth + padding) * num_rows + xsz - 1) / xsz;
1261 ysz = num_rows * (max_gheight + padding) + padding;
1262 if(ysz <= 0 || ysz > MAX_IMG_SIZE) continue;
1265 ysz = next_pow2(ysz);
1267 aspect = (float)xsz / (float)ysz;
1274 if(xsz > MAX_IMG_SIZE || ysz > MAX_IMG_SIZE || ysz <= 0) {
1284 static int next_pow2(int x)
1296 static int find_pow2(int x)
1299 for(i=0; i<sizeof x * CHAR_BIT; i++) {