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