Mercurial > mplayer.hg
annotate libass/ass_font.c @ 32313:ad6178f15dd1
Use a single svn:externals declaration for all of FFmpeg.
This avoids duplicating parts of the FFmpeg build system locally,
which is a brittle and maintenance-heavy process.
author | diego |
---|---|
date | Tue, 28 Sep 2010 20:46:19 +0000 |
parents | ac6e48baa03d |
children | 88eebbbbd6a0 |
rev | line source |
---|---|
21277 | 1 /* |
26723 | 2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> |
3 * | |
26738
588ce97b44f2
Speak of libass instead of MPlayer in the libass license headers.
diego
parents:
26723
diff
changeset
|
4 * This file is part of libass. |
26723 | 5 * |
26738
588ce97b44f2
Speak of libass instead of MPlayer in the libass license headers.
diego
parents:
26723
diff
changeset
|
6 * libass is free software; you can redistribute it and/or modify |
26723 | 7 * it under the terms of the GNU General Public License as published by |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
26738
588ce97b44f2
Speak of libass instead of MPlayer in the libass license headers.
diego
parents:
26723
diff
changeset
|
11 * libass is distributed in the hope that it will be useful, |
26723 | 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License along | |
26738
588ce97b44f2
Speak of libass instead of MPlayer in the libass license headers.
diego
parents:
26723
diff
changeset
|
17 * with libass; if not, write to the Free Software Foundation, Inc., |
26723 | 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 */ | |
21277 | 20 |
21 #include "config.h" | |
22 | |
23 #include <inttypes.h> | |
24 #include <ft2build.h> | |
25 #include FT_FREETYPE_H | |
26 #include FT_SYNTHESIS_H | |
27 #include FT_GLYPH_H | |
23328 | 28 #include FT_TRUETYPE_TABLES_H |
30200 | 29 #include FT_OUTLINE_H |
31875 | 30 #include <strings.h> |
21277 | 31 |
21458
7af6c25a0cfc
Keep embedded fonts in ass_library_t and perform actual disk write
eugeni
parents:
21351
diff
changeset
|
32 #include "ass.h" |
7af6c25a0cfc
Keep embedded fonts in ass_library_t and perform actual disk write
eugeni
parents:
21351
diff
changeset
|
33 #include "ass_library.h" |
21277 | 34 #include "ass_font.h" |
21321
7b7627ff1937
Move ass_font_desc_t and ass_font_t declarations to ass_font.h.
eugeni
parents:
21320
diff
changeset
|
35 #include "ass_bitmap.h" |
7b7627ff1937
Move ass_font_desc_t and ass_font_t declarations to ass_font.h.
eugeni
parents:
21320
diff
changeset
|
36 #include "ass_cache.h" |
21277 | 37 #include "ass_fontconfig.h" |
23299
0ee56ec36a40
Limit ass_font_set_transform to nonrotating transformations.
eugeni
parents:
23212
diff
changeset
|
38 #include "ass_utils.h" |
21277 | 39 |
31853 | 40 #define VERTICAL_LOWER_BOUND 0x02f1 |
41 | |
21277 | 42 /** |
31853 | 43 * Select a good charmap, prefer Microsoft Unicode charmaps. |
21277 | 44 * Otherwise, let FreeType decide. |
45 */ | |
30200 | 46 static void charmap_magic(ASS_Library *library, FT_Face face) |
21277 | 47 { |
30200 | 48 int i; |
31853 | 49 int ms_cmap = -1; |
50 | |
51 // Search for a Microsoft Unicode cmap | |
30200 | 52 for (i = 0; i < face->num_charmaps; ++i) { |
53 FT_CharMap cmap = face->charmaps[i]; | |
54 unsigned pid = cmap->platform_id; | |
55 unsigned eid = cmap->encoding_id; | |
56 if (pid == 3 /*microsoft */ | |
57 && (eid == 1 /*unicode bmp */ | |
58 || eid == 10 /*full unicode */ )) { | |
59 FT_Set_Charmap(face, cmap); | |
60 return; | |
31853 | 61 } else if (pid == 3 && ms_cmap < 0) |
62 ms_cmap = i; | |
63 } | |
64 | |
65 // Try the first Microsoft cmap if no Microsoft Unicode cmap was found | |
66 if (ms_cmap >= 0) { | |
67 FT_CharMap cmap = face->charmaps[ms_cmap]; | |
68 FT_Set_Charmap(face, cmap); | |
69 return; | |
30200 | 70 } |
22210
4a958bd08920
Select the first charmap in the font, if FreeType did not autoselect any.
eugeni
parents:
21630
diff
changeset
|
71 |
30200 | 72 if (!face->charmap) { |
73 if (face->num_charmaps == 0) { | |
74 ass_msg(library, MSGL_WARN, "Font face with no charmaps"); | |
75 return; | |
76 } | |
77 ass_msg(library, MSGL_WARN, | |
78 "No charmap autodetected, trying the first one"); | |
79 FT_Set_Charmap(face, face->charmaps[0]); | |
80 return; | |
81 } | |
21277 | 82 } |
83 | |
21630 | 84 /** |
85 * \brief find a memory font by name | |
86 */ | |
30200 | 87 static int find_font(ASS_Library *library, char *name) |
21460 | 88 { |
30200 | 89 int i; |
90 for (i = 0; i < library->num_fontdata; ++i) | |
91 if (strcasecmp(name, library->fontdata[i].name) == 0) | |
92 return i; | |
93 return -1; | |
21460 | 94 } |
95 | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
96 static void face_set_size(FT_Face face, double size); |
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
97 |
23982
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
98 static void buggy_font_workaround(FT_Face face) |
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
99 { |
30200 | 100 // Some fonts have zero Ascender/Descender fields in 'hhea' table. |
101 // In this case, get the information from 'os2' table or, as | |
102 // a last resort, from face.bbox. | |
103 if (face->ascender + face->descender == 0 || face->height == 0) { | |
104 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
105 if (os2) { | |
106 face->ascender = os2->sTypoAscender; | |
107 face->descender = os2->sTypoDescender; | |
108 face->height = face->ascender - face->descender; | |
109 } else { | |
110 face->ascender = face->bbox.yMax; | |
111 face->descender = face->bbox.yMin; | |
112 face->height = face->ascender - face->descender; | |
113 } | |
114 } | |
23982
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
115 } |
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
116 |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
117 /** |
30200 | 118 * \brief Select a face with the given charcode and add it to ASS_Font |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
119 * \return index of the new face in font->faces, -1 if failed |
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
120 */ |
30200 | 121 static int add_face(void *fc_priv, ASS_Font *font, uint32_t ch) |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
122 { |
30200 | 123 char *path; |
124 int index; | |
125 FT_Face face; | |
126 int error; | |
127 int mem_idx; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
128 |
30200 | 129 if (font->n_faces == ASS_FONT_MAX_FACES) |
130 return -1; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
131 |
30200 | 132 path = |
133 fontconfig_select(font->library, fc_priv, font->desc.family, | |
134 font->desc.treat_family_as_pattern, | |
135 font->desc.bold, font->desc.italic, &index, ch); | |
136 if (!path) | |
137 return -1; | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
138 |
30200 | 139 mem_idx = find_font(font->library, path); |
140 if (mem_idx >= 0) { | |
141 error = | |
142 FT_New_Memory_Face(font->ftlibrary, | |
143 (unsigned char *) font->library-> | |
144 fontdata[mem_idx].data, | |
31853 | 145 font->library->fontdata[mem_idx].size, index, |
30200 | 146 &face); |
147 if (error) { | |
148 ass_msg(font->library, MSGL_WARN, | |
149 "Error opening memory font: '%s'", path); | |
150 free(path); | |
151 return -1; | |
152 } | |
153 } else { | |
154 error = FT_New_Face(font->ftlibrary, path, index, &face); | |
155 if (error) { | |
156 ass_msg(font->library, MSGL_WARN, | |
157 "Error opening font: '%s', %d", path, index); | |
158 free(path); | |
159 return -1; | |
160 } | |
161 } | |
162 charmap_magic(font->library, face); | |
163 buggy_font_workaround(face); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
164 |
30200 | 165 font->faces[font->n_faces++] = face; |
166 face_set_size(face, font->size); | |
167 free(path); | |
168 return font->n_faces - 1; | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
169 } |
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
170 |
21630 | 171 /** |
30200 | 172 * \brief Create a new ASS_Font according to "desc" argument |
21630 | 173 */ |
30200 | 174 ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, |
175 FT_Library ftlibrary, void *fc_priv, | |
176 ASS_FontDesc *desc) | |
21277 | 177 { |
30200 | 178 int error; |
179 ASS_Font *fontp; | |
180 ASS_Font font; | |
21317 | 181 |
30200 | 182 fontp = ass_font_cache_find((Hashmap *) font_cache, desc); |
183 if (fontp) | |
184 return fontp; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
185 |
30200 | 186 font.library = library; |
187 font.ftlibrary = ftlibrary; | |
188 font.n_faces = 0; | |
189 font.desc.family = strdup(desc->family); | |
190 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; | |
191 font.desc.bold = desc->bold; | |
192 font.desc.italic = desc->italic; | |
31853 | 193 font.desc.vertical = desc->vertical; |
21277 | 194 |
30200 | 195 font.scale_x = font.scale_y = 1.; |
196 font.v.x = font.v.y = 0; | |
197 font.size = 0.; | |
21277 | 198 |
30200 | 199 error = add_face(fc_priv, &font, 0); |
200 if (error == -1) { | |
201 free(font.desc.family); | |
202 return 0; | |
203 } else | |
204 return ass_font_cache_add((Hashmap *) font_cache, &font); | |
21277 | 205 } |
206 | |
21630 | 207 /** |
208 * \brief Set font transformation matrix and shift vector | |
209 **/ | |
30200 | 210 void ass_font_set_transform(ASS_Font *font, double scale_x, |
211 double scale_y, FT_Vector *v) | |
21277 | 212 { |
30200 | 213 font->scale_x = scale_x; |
214 font->scale_y = scale_y; | |
215 if (v) { | |
216 font->v.x = v->x; | |
217 font->v.y = v->y; | |
218 } | |
21277 | 219 } |
220 | |
23328 | 221 static void face_set_size(FT_Face face, double size) |
222 { | |
30200 | 223 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); |
224 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
225 double mscale = 1.; | |
226 FT_Size_RequestRec rq; | |
227 FT_Size_Metrics *m = &face->size->metrics; | |
228 // VSFilter uses metrics from TrueType OS/2 table | |
229 // The idea was borrowed from asa (http://asa.diac24.net) | |
230 if (hori && os2) { | |
231 int hori_height = hori->Ascender - hori->Descender; | |
232 int os2_height = os2->usWinAscent + os2->usWinDescent; | |
233 if (hori_height && os2_height) | |
234 mscale = (double) hori_height / os2_height; | |
235 } | |
236 memset(&rq, 0, sizeof(rq)); | |
237 rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; | |
238 rq.width = 0; | |
239 rq.height = double_to_d6(size * mscale); | |
240 rq.horiResolution = rq.vertResolution = 0; | |
241 FT_Request_Size(face, &rq); | |
242 m->ascender /= mscale; | |
243 m->descender /= mscale; | |
244 m->height /= mscale; | |
23328 | 245 } |
246 | |
21630 | 247 /** |
248 * \brief Set font size | |
249 **/ | |
30200 | 250 void ass_font_set_size(ASS_Font *font, double size) |
21277 | 251 { |
30200 | 252 int i; |
253 if (font->size != size) { | |
254 font->size = size; | |
255 for (i = 0; i < font->n_faces; ++i) | |
256 face_set_size(font->faces[i], size); | |
257 } | |
21277 | 258 } |
259 | |
21630 | 260 /** |
261 * \brief Get maximal font ascender and descender. | |
262 * \param ch character code | |
263 * The values are extracted from the font face that provides glyphs for the given character | |
264 **/ | |
30200 | 265 void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, |
266 int *desc) | |
267 { | |
268 int i; | |
269 for (i = 0; i < font->n_faces; ++i) { | |
270 FT_Face face = font->faces[i]; | |
271 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
272 if (FT_Get_Char_Index(face, ch)) { | |
273 int y_scale = face->size->metrics.y_scale; | |
274 if (os2) { | |
275 *asc = FT_MulFix(os2->usWinAscent, y_scale); | |
276 *desc = FT_MulFix(os2->usWinDescent, y_scale); | |
277 } else { | |
278 *asc = FT_MulFix(face->ascender, y_scale); | |
279 *desc = FT_MulFix(-face->descender, y_scale); | |
280 } | |
31853 | 281 if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) { |
282 *asc = FT_MulFix(face->max_advance_width, y_scale); | |
283 } | |
30200 | 284 return; |
285 } | |
286 } | |
287 | |
288 *asc = *desc = 0; | |
289 } | |
290 | |
291 /* | |
292 * Strike a glyph with a horizontal line; it's possible to underline it | |
293 * and/or strike through it. For the line's position and size, truetype | |
294 * tables are consulted. Obviously this relies on the data in the tables | |
295 * being accurate. | |
296 * | |
297 */ | |
298 static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font, | |
299 FT_Glyph glyph, int under, int through) | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
300 { |
30200 | 301 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); |
302 TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post); | |
303 FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline; | |
304 int bear, advance, y_scale, i, dir; | |
305 | |
306 if (!under && !through) | |
307 return 0; | |
308 | |
309 // Grow outline | |
310 i = (under ? 4 : 0) + (through ? 4 : 0); | |
311 ol->points = realloc(ol->points, sizeof(FT_Vector) * | |
312 (ol->n_points + i)); | |
313 ol->tags = realloc(ol->tags, ol->n_points + i); | |
314 i = !!under + !!through; | |
315 ol->contours = realloc(ol->contours, sizeof(short) * | |
316 (ol->n_contours + i)); | |
317 | |
318 // If the bearing is negative, the glyph starts left of the current | |
319 // pen position | |
320 bear = FFMIN(face->glyph->metrics.horiBearingX, 0); | |
321 // We're adding half a pixel to avoid small gaps | |
322 advance = d16_to_d6(glyph->advance.x) + 32; | |
323 y_scale = face->size->metrics.y_scale; | |
324 | |
325 // Reverse drawing direction for non-truetype fonts | |
326 dir = FT_Outline_Get_Orientation(ol); | |
327 | |
328 // Add points to the outline | |
329 if (under && ps) { | |
330 int pos, size; | |
331 pos = FT_MulFix(ps->underlinePosition, y_scale * font->scale_y); | |
332 size = FT_MulFix(ps->underlineThickness, | |
333 y_scale * font->scale_y / 2); | |
334 | |
335 if (pos > 0 || size <= 0) | |
336 return 1; | |
337 | |
338 FT_Vector points[4] = { | |
339 {.x = bear, .y = pos + size}, | |
340 {.x = advance, .y = pos + size}, | |
341 {.x = advance, .y = pos - size}, | |
342 {.x = bear, .y = pos - size}, | |
343 }; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
344 |
30200 | 345 if (dir == FT_ORIENTATION_TRUETYPE) { |
346 for (i = 0; i < 4; i++) { | |
347 ol->points[ol->n_points] = points[i]; | |
348 ol->tags[ol->n_points++] = 1; | |
349 } | |
350 } else { | |
351 for (i = 3; i >= 0; i--) { | |
352 ol->points[ol->n_points] = points[i]; | |
353 ol->tags[ol->n_points++] = 1; | |
354 } | |
355 } | |
356 | |
357 ol->contours[ol->n_contours++] = ol->n_points - 1; | |
358 } | |
359 | |
360 if (through && os2) { | |
361 int pos, size; | |
362 pos = FT_MulFix(os2->yStrikeoutPosition, y_scale * font->scale_y); | |
363 size = FT_MulFix(os2->yStrikeoutSize, y_scale * font->scale_y / 2); | |
364 | |
365 if (pos < 0 || size <= 0) | |
366 return 1; | |
367 | |
368 FT_Vector points[4] = { | |
369 {.x = bear, .y = pos + size}, | |
370 {.x = advance, .y = pos + size}, | |
371 {.x = advance, .y = pos - size}, | |
372 {.x = bear, .y = pos - size}, | |
373 }; | |
374 | |
375 if (dir == FT_ORIENTATION_TRUETYPE) { | |
376 for (i = 0; i < 4; i++) { | |
377 ol->points[ol->n_points] = points[i]; | |
378 ol->tags[ol->n_points++] = 1; | |
379 } | |
380 } else { | |
381 for (i = 3; i >= 0; i--) { | |
382 ol->points[ol->n_points] = points[i]; | |
383 ol->tags[ol->n_points++] = 1; | |
384 } | |
385 } | |
386 | |
387 ol->contours[ol->n_contours++] = ol->n_points - 1; | |
388 } | |
389 | |
390 return 0; | |
391 } | |
392 | |
393 /** | |
394 * Slightly embold a glyph without touching its metrics | |
395 */ | |
396 static void ass_glyph_embolden(FT_GlyphSlot slot) | |
397 { | |
398 int str; | |
399 | |
400 if (slot->format != FT_GLYPH_FORMAT_OUTLINE) | |
401 return; | |
402 | |
403 str = FT_MulFix(slot->face->units_per_EM, | |
404 slot->face->size->metrics.y_scale) / 64; | |
405 | |
406 FT_Outline_Embolden(&slot->outline, str); | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
407 } |
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
408 |
21630 | 409 /** |
410 * \brief Get a glyph | |
411 * \param ch character code | |
412 **/ | |
30200 | 413 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, |
414 uint32_t ch, ASS_Hinting hinting, int deco) | |
21277 | 415 { |
30200 | 416 int error; |
417 int index = 0; | |
418 int i; | |
419 FT_Glyph glyph; | |
420 FT_Face face = 0; | |
421 int flags = 0; | |
31853 | 422 int vertical = font->desc.vertical; |
21350 | 423 |
30200 | 424 if (ch < 0x20) |
425 return 0; | |
426 // Handle NBSP like a regular space when rendering the glyph | |
427 if (ch == 0xa0) | |
428 ch = ' '; | |
429 if (font->n_faces == 0) | |
430 return 0; | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
431 |
30200 | 432 for (i = 0; i < font->n_faces; ++i) { |
433 face = font->faces[i]; | |
434 index = FT_Get_Char_Index(face, ch); | |
435 if (index) | |
436 break; | |
437 } | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
438 |
27393 | 439 #ifdef CONFIG_FONTCONFIG |
30200 | 440 if (index == 0) { |
441 int face_idx; | |
442 ass_msg(font->library, MSGL_INFO, | |
443 "Glyph 0x%X not found, selecting one more " | |
444 "font for (%s, %d, %d)", ch, font->desc.family, | |
445 font->desc.bold, font->desc.italic); | |
446 face_idx = add_face(fontconfig_priv, font, ch); | |
447 if (face_idx >= 0) { | |
448 face = font->faces[face_idx]; | |
449 index = FT_Get_Char_Index(face, ch); | |
31853 | 450 if (index == 0 && face->num_charmaps > 0) { |
451 ass_msg(font->library, MSGL_WARN, | |
452 "Glyph 0x%X not found, falling back to first charmap", ch); | |
453 FT_CharMap cur = face->charmap; | |
454 FT_Set_Charmap(face, face->charmaps[0]); | |
455 index = FT_Get_Char_Index(face, ch); | |
456 FT_Set_Charmap(face, cur); | |
457 } | |
30200 | 458 if (index == 0) { |
459 ass_msg(font->library, MSGL_ERR, | |
460 "Glyph 0x%X not found in font for (%s, %d, %d)", | |
461 ch, font->desc.family, font->desc.bold, | |
462 font->desc.italic); | |
463 } | |
464 } | |
465 } | |
21351
c611dfc4cb85
If a glyph is not found in the current font, switch to another one.
eugeni
parents:
21350
diff
changeset
|
466 #endif |
c611dfc4cb85
If a glyph is not found in the current font, switch to another one.
eugeni
parents:
21350
diff
changeset
|
467 |
31853 | 468 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH |
469 | FT_LOAD_IGNORE_TRANSFORM; | |
30200 | 470 switch (hinting) { |
471 case ASS_HINTING_NONE: | |
31853 | 472 flags |= FT_LOAD_NO_HINTING; |
30200 | 473 break; |
474 case ASS_HINTING_LIGHT: | |
31853 | 475 flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; |
30200 | 476 break; |
477 case ASS_HINTING_NORMAL: | |
31853 | 478 flags |= FT_LOAD_FORCE_AUTOHINT; |
30200 | 479 break; |
480 case ASS_HINTING_NATIVE: | |
481 break; | |
482 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
483 |
31853 | 484 error = FT_Load_Glyph(face, index, flags); |
30200 | 485 if (error) { |
486 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", | |
487 index); | |
488 return 0; | |
489 } | |
490 if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && | |
491 (font->desc.italic > 55)) { | |
492 FT_GlyphSlot_Oblique(face->glyph); | |
493 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
494 |
30200 | 495 if (!(face->style_flags & FT_STYLE_FLAG_BOLD) && |
496 (font->desc.bold > 80)) { | |
497 ass_glyph_embolden(face->glyph); | |
498 } | |
499 error = FT_Get_Glyph(face->glyph, &glyph); | |
500 if (error) { | |
501 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", | |
502 index); | |
503 return 0; | |
504 } | |
505 | |
31853 | 506 // Rotate glyph, if needed |
507 if (vertical && ch >= VERTICAL_LOWER_BOUND) { | |
508 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 }; | |
509 FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); | |
510 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, | |
511 face->glyph->metrics.vertAdvance, | |
512 0); | |
513 glyph->advance.x = face->glyph->linearVertAdvance; | |
514 } | |
515 | |
516 // Apply scaling and shift | |
517 FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0, | |
518 double_to_d16(font->scale_y) }; | |
519 FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline; | |
520 FT_Outline_Transform(outl, &scale); | |
521 FT_Outline_Translate(outl, font->v.x, font->v.y); | |
522 glyph->advance.x *= font->scale_x; | |
523 | |
30200 | 524 ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, |
525 deco & DECO_STRIKETHROUGH); | |
526 | |
527 return glyph; | |
21277 | 528 } |
529 | |
21630 | 530 /** |
531 * \brief Get kerning for the pair of glyphs. | |
532 **/ | |
30200 | 533 FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2) |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
534 { |
30200 | 535 FT_Vector v = { 0, 0 }; |
536 int i; | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
537 |
31853 | 538 if (font->desc.vertical) |
539 return v; | |
540 | |
30200 | 541 for (i = 0; i < font->n_faces; ++i) { |
542 FT_Face face = font->faces[i]; | |
543 int i1 = FT_Get_Char_Index(face, c1); | |
544 int i2 = FT_Get_Char_Index(face, c2); | |
545 if (i1 && i2) { | |
546 if (FT_HAS_KERNING(face)) | |
547 FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v); | |
548 return v; | |
549 } | |
550 if (i1 || i2) // these glyphs are from different font faces, no kerning information | |
551 return v; | |
552 } | |
553 return v; | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
554 } |
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
555 |
21630 | 556 /** |
30200 | 557 * \brief Deallocate ASS_Font |
21630 | 558 **/ |
30200 | 559 void ass_font_free(ASS_Font *font) |
21277 | 560 { |
30200 | 561 int i; |
562 for (i = 0; i < font->n_faces; ++i) | |
563 if (font->faces[i]) | |
564 FT_Done_Face(font->faces[i]); | |
31875 | 565 free(font->desc.family); |
30200 | 566 free(font); |
21277 | 567 } |
31853 | 568 |
569 /** | |
570 * \brief Calculate the cbox of a series of points | |
571 */ | |
572 static void | |
573 get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end) | |
574 { | |
575 box->xMin = box->yMin = INT_MAX; | |
576 box->xMax = box->yMax = INT_MIN; | |
577 int i; | |
578 | |
579 for (i = start; i <= end; i++) { | |
580 box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin; | |
581 box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax; | |
582 box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin; | |
583 box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax; | |
584 } | |
585 } | |
586 | |
587 /** | |
588 * \brief Determine winding direction of a contour | |
589 * \return direction; 0 = clockwise | |
590 */ | |
591 static int get_contour_direction(FT_Vector *points, int start, int end) | |
592 { | |
593 int i; | |
594 long long sum = 0; | |
595 int x = points[start].x; | |
596 int y = points[start].y; | |
597 for (i = start + 1; i <= end; i++) { | |
598 sum += x * (points[i].y - y) - y * (points[i].x - x); | |
599 x = points[i].x; | |
600 y = points[i].y; | |
601 } | |
602 sum += x * (points[start].y - y) - y * (points[start].x - x); | |
603 return sum > 0; | |
604 } | |
605 | |
606 /** | |
607 * \brief Fix-up stroker result for huge borders by removing inside contours | |
608 * that would reverse in size | |
609 */ | |
610 void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y) | |
611 { | |
612 int nc = glyph->outline.n_contours; | |
613 int begin, stop; | |
614 char modified = 0; | |
615 char *valid_cont = malloc(nc); | |
616 int start = 0; | |
617 int end = -1; | |
618 FT_BBox *boxes = malloc(nc * sizeof(FT_BBox)); | |
619 int i, j; | |
620 int inside_direction; | |
621 | |
622 inside_direction = FT_Outline_Get_Orientation(&glyph->outline) == | |
623 FT_ORIENTATION_TRUETYPE; | |
624 | |
625 // create a list of cboxes of the contours | |
626 for (i = 0; i < nc; i++) { | |
627 start = end + 1; | |
628 end = glyph->outline.contours[i]; | |
629 get_contour_cbox(&boxes[i], glyph->outline.points, start, end); | |
630 } | |
631 | |
632 // for each contour, check direction and whether it's "outside" | |
633 // or contained in another contour | |
634 end = -1; | |
635 for (i = 0; i < nc; i++) { | |
636 start = end + 1; | |
637 end = glyph->outline.contours[i]; | |
638 int dir = get_contour_direction(glyph->outline.points, start, end); | |
639 valid_cont[i] = 1; | |
640 if (dir == inside_direction) { | |
641 for (j = 0; j < nc; j++) { | |
642 if (i == j) | |
643 continue; | |
644 if (boxes[i].xMin >= boxes[j].xMin && | |
645 boxes[i].xMax <= boxes[j].xMax && | |
646 boxes[i].yMin >= boxes[j].yMin && | |
647 boxes[i].yMax <= boxes[j].yMax) | |
648 goto check_inside; | |
649 } | |
650 /* "inside" contour but we can't find anything it could be | |
651 * inside of - assume the font is buggy and it should be | |
652 * an "outside" contour, and reverse it */ | |
653 for (j = 0; j < (end + 1 - start) / 2; j++) { | |
654 FT_Vector temp = glyph->outline.points[start + j]; | |
655 char temp2 = glyph->outline.tags[start + j]; | |
656 glyph->outline.points[start + j] = glyph->outline.points[end - j]; | |
657 glyph->outline.points[end - j] = temp; | |
658 glyph->outline.tags[start + j] = glyph->outline.tags[end - j]; | |
659 glyph->outline.tags[end - j] = temp2; | |
660 } | |
661 dir ^= 1; | |
662 } | |
663 check_inside: | |
664 if (dir == inside_direction) { | |
665 FT_BBox box; | |
666 get_contour_cbox(&box, glyph->outline.points, start, end); | |
667 int width = box.xMax - box.xMin; | |
668 int height = box.yMax - box.yMin; | |
669 if (width < border_x * 2 || height < border_y * 2) { | |
670 valid_cont[i] = 0; | |
671 modified = 1; | |
672 } | |
673 } | |
674 } | |
675 | |
676 // zero-out contours that can be removed; much simpler than copying | |
677 if (modified) { | |
678 for (i = 0; i < nc; i++) { | |
679 if (valid_cont[i]) | |
680 continue; | |
681 begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1; | |
682 stop = glyph->outline.contours[i]; | |
683 for (j = begin; j <= stop; j++) { | |
684 glyph->outline.points[j].x = 0; | |
685 glyph->outline.points[j].y = 0; | |
686 glyph->outline.tags[j] = 0; | |
687 } | |
688 } | |
689 } | |
690 | |
691 free(boxes); | |
692 free(valid_cont); | |
693 } | |
694 |