Mercurial > mplayer.hg
annotate libass/ass_font.c @ 31853:e64df5862cea
Import libass 0.9.10
author | greg |
---|---|
date | Fri, 06 Aug 2010 21:13:41 +0000 |
parents | 48d020c5ceca |
children | ac6e48baa03d |
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 |
21277 | 30 |
21458
7af6c25a0cfc
Keep embedded fonts in ass_library_t and perform actual disk write
eugeni
parents:
21351
diff
changeset
|
31 #include "ass.h" |
7af6c25a0cfc
Keep embedded fonts in ass_library_t and perform actual disk write
eugeni
parents:
21351
diff
changeset
|
32 #include "ass_library.h" |
21277 | 33 #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
|
34 #include "ass_bitmap.h" |
7b7627ff1937
Move ass_font_desc_t and ass_font_t declarations to ass_font.h.
eugeni
parents:
21320
diff
changeset
|
35 #include "ass_cache.h" |
21277 | 36 #include "ass_fontconfig.h" |
23299
0ee56ec36a40
Limit ass_font_set_transform to nonrotating transformations.
eugeni
parents:
23212
diff
changeset
|
37 #include "ass_utils.h" |
21277 | 38 |
31853 | 39 #define VERTICAL_LOWER_BOUND 0x02f1 |
40 | |
21277 | 41 /** |
31853 | 42 * Select a good charmap, prefer Microsoft Unicode charmaps. |
21277 | 43 * Otherwise, let FreeType decide. |
44 */ | |
30200 | 45 static void charmap_magic(ASS_Library *library, FT_Face face) |
21277 | 46 { |
30200 | 47 int i; |
31853 | 48 int ms_cmap = -1; |
49 | |
50 // Search for a Microsoft Unicode cmap | |
30200 | 51 for (i = 0; i < face->num_charmaps; ++i) { |
52 FT_CharMap cmap = face->charmaps[i]; | |
53 unsigned pid = cmap->platform_id; | |
54 unsigned eid = cmap->encoding_id; | |
55 if (pid == 3 /*microsoft */ | |
56 && (eid == 1 /*unicode bmp */ | |
57 || eid == 10 /*full unicode */ )) { | |
58 FT_Set_Charmap(face, cmap); | |
59 return; | |
31853 | 60 } else if (pid == 3 && ms_cmap < 0) |
61 ms_cmap = i; | |
62 } | |
63 | |
64 // Try the first Microsoft cmap if no Microsoft Unicode cmap was found | |
65 if (ms_cmap >= 0) { | |
66 FT_CharMap cmap = face->charmaps[ms_cmap]; | |
67 FT_Set_Charmap(face, cmap); | |
68 return; | |
30200 | 69 } |
22210
4a958bd08920
Select the first charmap in the font, if FreeType did not autoselect any.
eugeni
parents:
21630
diff
changeset
|
70 |
30200 | 71 if (!face->charmap) { |
72 if (face->num_charmaps == 0) { | |
73 ass_msg(library, MSGL_WARN, "Font face with no charmaps"); | |
74 return; | |
75 } | |
76 ass_msg(library, MSGL_WARN, | |
77 "No charmap autodetected, trying the first one"); | |
78 FT_Set_Charmap(face, face->charmaps[0]); | |
79 return; | |
80 } | |
21277 | 81 } |
82 | |
21630 | 83 /** |
84 * \brief find a memory font by name | |
85 */ | |
30200 | 86 static int find_font(ASS_Library *library, char *name) |
21460 | 87 { |
30200 | 88 int i; |
89 for (i = 0; i < library->num_fontdata; ++i) | |
90 if (strcasecmp(name, library->fontdata[i].name) == 0) | |
91 return i; | |
92 return -1; | |
21460 | 93 } |
94 | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
95 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
|
96 |
23982
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
97 static void buggy_font_workaround(FT_Face face) |
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
98 { |
30200 | 99 // Some fonts have zero Ascender/Descender fields in 'hhea' table. |
100 // In this case, get the information from 'os2' table or, as | |
101 // a last resort, from face.bbox. | |
102 if (face->ascender + face->descender == 0 || face->height == 0) { | |
103 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
104 if (os2) { | |
105 face->ascender = os2->sTypoAscender; | |
106 face->descender = os2->sTypoDescender; | |
107 face->height = face->ascender - face->descender; | |
108 } else { | |
109 face->ascender = face->bbox.yMax; | |
110 face->descender = face->bbox.yMin; | |
111 face->height = face->ascender - face->descender; | |
112 } | |
113 } | |
23982
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
114 } |
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
115 |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
116 /** |
30200 | 117 * \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
|
118 * \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
|
119 */ |
30200 | 120 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
|
121 { |
30200 | 122 char *path; |
123 int index; | |
124 FT_Face face; | |
125 int error; | |
126 int mem_idx; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
127 |
30200 | 128 if (font->n_faces == ASS_FONT_MAX_FACES) |
129 return -1; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
130 |
30200 | 131 path = |
132 fontconfig_select(font->library, fc_priv, font->desc.family, | |
133 font->desc.treat_family_as_pattern, | |
134 font->desc.bold, font->desc.italic, &index, ch); | |
135 if (!path) | |
136 return -1; | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
137 |
30200 | 138 mem_idx = find_font(font->library, path); |
139 if (mem_idx >= 0) { | |
140 error = | |
141 FT_New_Memory_Face(font->ftlibrary, | |
142 (unsigned char *) font->library-> | |
143 fontdata[mem_idx].data, | |
31853 | 144 font->library->fontdata[mem_idx].size, index, |
30200 | 145 &face); |
146 if (error) { | |
147 ass_msg(font->library, MSGL_WARN, | |
148 "Error opening memory font: '%s'", path); | |
149 free(path); | |
150 return -1; | |
151 } | |
152 } else { | |
153 error = FT_New_Face(font->ftlibrary, path, index, &face); | |
154 if (error) { | |
155 ass_msg(font->library, MSGL_WARN, | |
156 "Error opening font: '%s', %d", path, index); | |
157 free(path); | |
158 return -1; | |
159 } | |
160 } | |
161 charmap_magic(font->library, face); | |
162 buggy_font_workaround(face); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
163 |
30200 | 164 font->faces[font->n_faces++] = face; |
165 face_set_size(face, font->size); | |
166 free(path); | |
167 return font->n_faces - 1; | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
168 } |
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
169 |
21630 | 170 /** |
30200 | 171 * \brief Create a new ASS_Font according to "desc" argument |
21630 | 172 */ |
30200 | 173 ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, |
174 FT_Library ftlibrary, void *fc_priv, | |
175 ASS_FontDesc *desc) | |
21277 | 176 { |
30200 | 177 int error; |
178 ASS_Font *fontp; | |
179 ASS_Font font; | |
21317 | 180 |
30200 | 181 fontp = ass_font_cache_find((Hashmap *) font_cache, desc); |
182 if (fontp) | |
183 return fontp; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
184 |
30200 | 185 font.library = library; |
186 font.ftlibrary = ftlibrary; | |
187 font.n_faces = 0; | |
188 font.desc.family = strdup(desc->family); | |
189 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; | |
190 font.desc.bold = desc->bold; | |
191 font.desc.italic = desc->italic; | |
31853 | 192 font.desc.vertical = desc->vertical; |
21277 | 193 |
30200 | 194 font.scale_x = font.scale_y = 1.; |
195 font.v.x = font.v.y = 0; | |
196 font.size = 0.; | |
21277 | 197 |
30200 | 198 error = add_face(fc_priv, &font, 0); |
199 if (error == -1) { | |
200 free(font.desc.family); | |
201 return 0; | |
202 } else | |
203 return ass_font_cache_add((Hashmap *) font_cache, &font); | |
21277 | 204 } |
205 | |
21630 | 206 /** |
207 * \brief Set font transformation matrix and shift vector | |
208 **/ | |
30200 | 209 void ass_font_set_transform(ASS_Font *font, double scale_x, |
210 double scale_y, FT_Vector *v) | |
21277 | 211 { |
30200 | 212 font->scale_x = scale_x; |
213 font->scale_y = scale_y; | |
214 if (v) { | |
215 font->v.x = v->x; | |
216 font->v.y = v->y; | |
217 } | |
21277 | 218 } |
219 | |
23328 | 220 static void face_set_size(FT_Face face, double size) |
221 { | |
30200 | 222 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); |
223 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
224 double mscale = 1.; | |
225 FT_Size_RequestRec rq; | |
226 FT_Size_Metrics *m = &face->size->metrics; | |
227 // VSFilter uses metrics from TrueType OS/2 table | |
228 // The idea was borrowed from asa (http://asa.diac24.net) | |
229 if (hori && os2) { | |
230 int hori_height = hori->Ascender - hori->Descender; | |
231 int os2_height = os2->usWinAscent + os2->usWinDescent; | |
232 if (hori_height && os2_height) | |
233 mscale = (double) hori_height / os2_height; | |
234 } | |
235 memset(&rq, 0, sizeof(rq)); | |
236 rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; | |
237 rq.width = 0; | |
238 rq.height = double_to_d6(size * mscale); | |
239 rq.horiResolution = rq.vertResolution = 0; | |
240 FT_Request_Size(face, &rq); | |
241 m->ascender /= mscale; | |
242 m->descender /= mscale; | |
243 m->height /= mscale; | |
23328 | 244 } |
245 | |
21630 | 246 /** |
247 * \brief Set font size | |
248 **/ | |
30200 | 249 void ass_font_set_size(ASS_Font *font, double size) |
21277 | 250 { |
30200 | 251 int i; |
252 if (font->size != size) { | |
253 font->size = size; | |
254 for (i = 0; i < font->n_faces; ++i) | |
255 face_set_size(font->faces[i], size); | |
256 } | |
21277 | 257 } |
258 | |
21630 | 259 /** |
260 * \brief Get maximal font ascender and descender. | |
261 * \param ch character code | |
262 * The values are extracted from the font face that provides glyphs for the given character | |
263 **/ | |
30200 | 264 void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, |
265 int *desc) | |
266 { | |
267 int i; | |
268 for (i = 0; i < font->n_faces; ++i) { | |
269 FT_Face face = font->faces[i]; | |
270 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
271 if (FT_Get_Char_Index(face, ch)) { | |
272 int y_scale = face->size->metrics.y_scale; | |
273 if (os2) { | |
274 *asc = FT_MulFix(os2->usWinAscent, y_scale); | |
275 *desc = FT_MulFix(os2->usWinDescent, y_scale); | |
276 } else { | |
277 *asc = FT_MulFix(face->ascender, y_scale); | |
278 *desc = FT_MulFix(-face->descender, y_scale); | |
279 } | |
31853 | 280 if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) { |
281 *asc = FT_MulFix(face->max_advance_width, y_scale); | |
282 } | |
30200 | 283 return; |
284 } | |
285 } | |
286 | |
287 *asc = *desc = 0; | |
288 } | |
289 | |
290 /* | |
291 * Strike a glyph with a horizontal line; it's possible to underline it | |
292 * and/or strike through it. For the line's position and size, truetype | |
293 * tables are consulted. Obviously this relies on the data in the tables | |
294 * being accurate. | |
295 * | |
296 */ | |
297 static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font, | |
298 FT_Glyph glyph, int under, int through) | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
299 { |
30200 | 300 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); |
301 TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post); | |
302 FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline; | |
303 int bear, advance, y_scale, i, dir; | |
304 | |
305 if (!under && !through) | |
306 return 0; | |
307 | |
308 // Grow outline | |
309 i = (under ? 4 : 0) + (through ? 4 : 0); | |
310 ol->points = realloc(ol->points, sizeof(FT_Vector) * | |
311 (ol->n_points + i)); | |
312 ol->tags = realloc(ol->tags, ol->n_points + i); | |
313 i = !!under + !!through; | |
314 ol->contours = realloc(ol->contours, sizeof(short) * | |
315 (ol->n_contours + i)); | |
316 | |
317 // If the bearing is negative, the glyph starts left of the current | |
318 // pen position | |
319 bear = FFMIN(face->glyph->metrics.horiBearingX, 0); | |
320 // We're adding half a pixel to avoid small gaps | |
321 advance = d16_to_d6(glyph->advance.x) + 32; | |
322 y_scale = face->size->metrics.y_scale; | |
323 | |
324 // Reverse drawing direction for non-truetype fonts | |
325 dir = FT_Outline_Get_Orientation(ol); | |
326 | |
327 // Add points to the outline | |
328 if (under && ps) { | |
329 int pos, size; | |
330 pos = FT_MulFix(ps->underlinePosition, y_scale * font->scale_y); | |
331 size = FT_MulFix(ps->underlineThickness, | |
332 y_scale * font->scale_y / 2); | |
333 | |
334 if (pos > 0 || size <= 0) | |
335 return 1; | |
336 | |
337 FT_Vector points[4] = { | |
338 {.x = bear, .y = pos + size}, | |
339 {.x = advance, .y = pos + size}, | |
340 {.x = advance, .y = pos - size}, | |
341 {.x = bear, .y = pos - size}, | |
342 }; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
343 |
30200 | 344 if (dir == FT_ORIENTATION_TRUETYPE) { |
345 for (i = 0; i < 4; i++) { | |
346 ol->points[ol->n_points] = points[i]; | |
347 ol->tags[ol->n_points++] = 1; | |
348 } | |
349 } else { | |
350 for (i = 3; i >= 0; i--) { | |
351 ol->points[ol->n_points] = points[i]; | |
352 ol->tags[ol->n_points++] = 1; | |
353 } | |
354 } | |
355 | |
356 ol->contours[ol->n_contours++] = ol->n_points - 1; | |
357 } | |
358 | |
359 if (through && os2) { | |
360 int pos, size; | |
361 pos = FT_MulFix(os2->yStrikeoutPosition, y_scale * font->scale_y); | |
362 size = FT_MulFix(os2->yStrikeoutSize, y_scale * font->scale_y / 2); | |
363 | |
364 if (pos < 0 || size <= 0) | |
365 return 1; | |
366 | |
367 FT_Vector points[4] = { | |
368 {.x = bear, .y = pos + size}, | |
369 {.x = advance, .y = pos + size}, | |
370 {.x = advance, .y = pos - size}, | |
371 {.x = bear, .y = pos - size}, | |
372 }; | |
373 | |
374 if (dir == FT_ORIENTATION_TRUETYPE) { | |
375 for (i = 0; i < 4; i++) { | |
376 ol->points[ol->n_points] = points[i]; | |
377 ol->tags[ol->n_points++] = 1; | |
378 } | |
379 } else { | |
380 for (i = 3; i >= 0; i--) { | |
381 ol->points[ol->n_points] = points[i]; | |
382 ol->tags[ol->n_points++] = 1; | |
383 } | |
384 } | |
385 | |
386 ol->contours[ol->n_contours++] = ol->n_points - 1; | |
387 } | |
388 | |
389 return 0; | |
390 } | |
391 | |
392 /** | |
393 * Slightly embold a glyph without touching its metrics | |
394 */ | |
395 static void ass_glyph_embolden(FT_GlyphSlot slot) | |
396 { | |
397 int str; | |
398 | |
399 if (slot->format != FT_GLYPH_FORMAT_OUTLINE) | |
400 return; | |
401 | |
402 str = FT_MulFix(slot->face->units_per_EM, | |
403 slot->face->size->metrics.y_scale) / 64; | |
404 | |
405 FT_Outline_Embolden(&slot->outline, str); | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
406 } |
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
407 |
21630 | 408 /** |
409 * \brief Get a glyph | |
410 * \param ch character code | |
411 **/ | |
30200 | 412 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, |
413 uint32_t ch, ASS_Hinting hinting, int deco) | |
21277 | 414 { |
30200 | 415 int error; |
416 int index = 0; | |
417 int i; | |
418 FT_Glyph glyph; | |
419 FT_Face face = 0; | |
420 int flags = 0; | |
31853 | 421 int vertical = font->desc.vertical; |
21350 | 422 |
30200 | 423 if (ch < 0x20) |
424 return 0; | |
425 // Handle NBSP like a regular space when rendering the glyph | |
426 if (ch == 0xa0) | |
427 ch = ' '; | |
428 if (font->n_faces == 0) | |
429 return 0; | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
430 |
30200 | 431 for (i = 0; i < font->n_faces; ++i) { |
432 face = font->faces[i]; | |
433 index = FT_Get_Char_Index(face, ch); | |
434 if (index) | |
435 break; | |
436 } | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
437 |
27393 | 438 #ifdef CONFIG_FONTCONFIG |
30200 | 439 if (index == 0) { |
440 int face_idx; | |
441 ass_msg(font->library, MSGL_INFO, | |
442 "Glyph 0x%X not found, selecting one more " | |
443 "font for (%s, %d, %d)", ch, font->desc.family, | |
444 font->desc.bold, font->desc.italic); | |
445 face_idx = add_face(fontconfig_priv, font, ch); | |
446 if (face_idx >= 0) { | |
447 face = font->faces[face_idx]; | |
448 index = FT_Get_Char_Index(face, ch); | |
31853 | 449 if (index == 0 && face->num_charmaps > 0) { |
450 ass_msg(font->library, MSGL_WARN, | |
451 "Glyph 0x%X not found, falling back to first charmap", ch); | |
452 FT_CharMap cur = face->charmap; | |
453 FT_Set_Charmap(face, face->charmaps[0]); | |
454 index = FT_Get_Char_Index(face, ch); | |
455 FT_Set_Charmap(face, cur); | |
456 } | |
30200 | 457 if (index == 0) { |
458 ass_msg(font->library, MSGL_ERR, | |
459 "Glyph 0x%X not found in font for (%s, %d, %d)", | |
460 ch, font->desc.family, font->desc.bold, | |
461 font->desc.italic); | |
462 } | |
463 } | |
464 } | |
21351
c611dfc4cb85
If a glyph is not found in the current font, switch to another one.
eugeni
parents:
21350
diff
changeset
|
465 #endif |
c611dfc4cb85
If a glyph is not found in the current font, switch to another one.
eugeni
parents:
21350
diff
changeset
|
466 |
31853 | 467 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH |
468 | FT_LOAD_IGNORE_TRANSFORM; | |
30200 | 469 switch (hinting) { |
470 case ASS_HINTING_NONE: | |
31853 | 471 flags |= FT_LOAD_NO_HINTING; |
30200 | 472 break; |
473 case ASS_HINTING_LIGHT: | |
31853 | 474 flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; |
30200 | 475 break; |
476 case ASS_HINTING_NORMAL: | |
31853 | 477 flags |= FT_LOAD_FORCE_AUTOHINT; |
30200 | 478 break; |
479 case ASS_HINTING_NATIVE: | |
480 break; | |
481 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
482 |
31853 | 483 error = FT_Load_Glyph(face, index, flags); |
30200 | 484 if (error) { |
485 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", | |
486 index); | |
487 return 0; | |
488 } | |
489 if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && | |
490 (font->desc.italic > 55)) { | |
491 FT_GlyphSlot_Oblique(face->glyph); | |
492 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
493 |
30200 | 494 if (!(face->style_flags & FT_STYLE_FLAG_BOLD) && |
495 (font->desc.bold > 80)) { | |
496 ass_glyph_embolden(face->glyph); | |
497 } | |
498 error = FT_Get_Glyph(face->glyph, &glyph); | |
499 if (error) { | |
500 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", | |
501 index); | |
502 return 0; | |
503 } | |
504 | |
31853 | 505 // Rotate glyph, if needed |
506 if (vertical && ch >= VERTICAL_LOWER_BOUND) { | |
507 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 }; | |
508 FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); | |
509 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, | |
510 face->glyph->metrics.vertAdvance, | |
511 0); | |
512 glyph->advance.x = face->glyph->linearVertAdvance; | |
513 } | |
514 | |
515 // Apply scaling and shift | |
516 FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0, | |
517 double_to_d16(font->scale_y) }; | |
518 FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline; | |
519 FT_Outline_Transform(outl, &scale); | |
520 FT_Outline_Translate(outl, font->v.x, font->v.y); | |
521 glyph->advance.x *= font->scale_x; | |
522 | |
30200 | 523 ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, |
524 deco & DECO_STRIKETHROUGH); | |
525 | |
526 return glyph; | |
21277 | 527 } |
528 | |
21630 | 529 /** |
530 * \brief Get kerning for the pair of glyphs. | |
531 **/ | |
30200 | 532 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
|
533 { |
30200 | 534 FT_Vector v = { 0, 0 }; |
535 int i; | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
536 |
31853 | 537 if (font->desc.vertical) |
538 return v; | |
539 | |
30200 | 540 for (i = 0; i < font->n_faces; ++i) { |
541 FT_Face face = font->faces[i]; | |
542 int i1 = FT_Get_Char_Index(face, c1); | |
543 int i2 = FT_Get_Char_Index(face, c2); | |
544 if (i1 && i2) { | |
545 if (FT_HAS_KERNING(face)) | |
546 FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v); | |
547 return v; | |
548 } | |
549 if (i1 || i2) // these glyphs are from different font faces, no kerning information | |
550 return v; | |
551 } | |
552 return v; | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
553 } |
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
554 |
21630 | 555 /** |
30200 | 556 * \brief Deallocate ASS_Font |
21630 | 557 **/ |
30200 | 558 void ass_font_free(ASS_Font *font) |
21277 | 559 { |
30200 | 560 int i; |
561 for (i = 0; i < font->n_faces; ++i) | |
562 if (font->faces[i]) | |
563 FT_Done_Face(font->faces[i]); | |
564 if (font->desc.family) | |
565 free(font->desc.family); | |
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 |