Mercurial > mplayer.hg
annotate libass/ass_font.c @ 34421:407da2eec15e
Use check items for CD and VCD titles as well.
This way, the title being played will be emphasized.
(Check items are already being used in the DVD menus.)
author | ib |
---|---|
date | Thu, 05 Jan 2012 12:55:33 +0000 |
parents | 6e7f60f6f9d4 |
children | c3aaaf17c721 |
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 * |
34011 | 6 * Permission to use, copy, modify, and distribute this software for any |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
26723 | 9 * |
34011 | 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
26723 | 17 */ |
21277 | 18 |
19 #include "config.h" | |
20 | |
21 #include <inttypes.h> | |
22 #include <ft2build.h> | |
23 #include FT_FREETYPE_H | |
24 #include FT_SYNTHESIS_H | |
25 #include FT_GLYPH_H | |
23328 | 26 #include FT_TRUETYPE_TABLES_H |
30200 | 27 #include FT_OUTLINE_H |
31875 | 28 #include <strings.h> |
21277 | 29 |
21458
7af6c25a0cfc
Keep embedded fonts in ass_library_t and perform actual disk write
eugeni
parents:
21351
diff
changeset
|
30 #include "ass.h" |
7af6c25a0cfc
Keep embedded fonts in ass_library_t and perform actual disk write
eugeni
parents:
21351
diff
changeset
|
31 #include "ass_library.h" |
21277 | 32 #include "ass_font.h" |
33 #include "ass_fontconfig.h" | |
23299
0ee56ec36a40
Limit ass_font_set_transform to nonrotating transformations.
eugeni
parents:
23212
diff
changeset
|
34 #include "ass_utils.h" |
34295 | 35 #include "ass_shaper.h" |
31853 | 36 |
21277 | 37 /** |
31853 | 38 * Select a good charmap, prefer Microsoft Unicode charmaps. |
21277 | 39 * Otherwise, let FreeType decide. |
40 */ | |
30200 | 41 static void charmap_magic(ASS_Library *library, FT_Face face) |
21277 | 42 { |
30200 | 43 int i; |
31853 | 44 int ms_cmap = -1; |
45 | |
46 // Search for a Microsoft Unicode cmap | |
30200 | 47 for (i = 0; i < face->num_charmaps; ++i) { |
48 FT_CharMap cmap = face->charmaps[i]; | |
49 unsigned pid = cmap->platform_id; | |
50 unsigned eid = cmap->encoding_id; | |
51 if (pid == 3 /*microsoft */ | |
52 && (eid == 1 /*unicode bmp */ | |
53 || eid == 10 /*full unicode */ )) { | |
54 FT_Set_Charmap(face, cmap); | |
55 return; | |
31853 | 56 } else if (pid == 3 && ms_cmap < 0) |
57 ms_cmap = i; | |
58 } | |
59 | |
60 // Try the first Microsoft cmap if no Microsoft Unicode cmap was found | |
61 if (ms_cmap >= 0) { | |
62 FT_CharMap cmap = face->charmaps[ms_cmap]; | |
63 FT_Set_Charmap(face, cmap); | |
64 return; | |
30200 | 65 } |
22210
4a958bd08920
Select the first charmap in the font, if FreeType did not autoselect any.
eugeni
parents:
21630
diff
changeset
|
66 |
30200 | 67 if (!face->charmap) { |
68 if (face->num_charmaps == 0) { | |
69 ass_msg(library, MSGL_WARN, "Font face with no charmaps"); | |
70 return; | |
71 } | |
72 ass_msg(library, MSGL_WARN, | |
73 "No charmap autodetected, trying the first one"); | |
74 FT_Set_Charmap(face, face->charmaps[0]); | |
75 return; | |
76 } | |
21277 | 77 } |
78 | |
21630 | 79 /** |
80 * \brief find a memory font by name | |
81 */ | |
30200 | 82 static int find_font(ASS_Library *library, char *name) |
21460 | 83 { |
30200 | 84 int i; |
85 for (i = 0; i < library->num_fontdata; ++i) | |
86 if (strcasecmp(name, library->fontdata[i].name) == 0) | |
87 return i; | |
88 return -1; | |
21460 | 89 } |
90 | |
23982
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
91 static void buggy_font_workaround(FT_Face face) |
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
92 { |
30200 | 93 // Some fonts have zero Ascender/Descender fields in 'hhea' table. |
94 // In this case, get the information from 'os2' table or, as | |
95 // a last resort, from face.bbox. | |
96 if (face->ascender + face->descender == 0 || face->height == 0) { | |
97 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
98 if (os2) { | |
99 face->ascender = os2->sTypoAscender; | |
100 face->descender = os2->sTypoDescender; | |
101 face->height = face->ascender - face->descender; | |
102 } else { | |
103 face->ascender = face->bbox.yMax; | |
104 face->descender = face->bbox.yMin; | |
105 face->height = face->ascender - face->descender; | |
106 } | |
107 } | |
23982
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
108 } |
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
109 |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
110 /** |
30200 | 111 * \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
|
112 * \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
|
113 */ |
30200 | 114 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
|
115 { |
30200 | 116 char *path; |
117 int index; | |
118 FT_Face face; | |
119 int error; | |
120 int mem_idx; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
121 |
30200 | 122 if (font->n_faces == ASS_FONT_MAX_FACES) |
123 return -1; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
124 |
30200 | 125 path = |
126 fontconfig_select(font->library, fc_priv, font->desc.family, | |
127 font->desc.treat_family_as_pattern, | |
128 font->desc.bold, font->desc.italic, &index, ch); | |
129 if (!path) | |
130 return -1; | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
131 |
30200 | 132 mem_idx = find_font(font->library, path); |
133 if (mem_idx >= 0) { | |
134 error = | |
135 FT_New_Memory_Face(font->ftlibrary, | |
136 (unsigned char *) font->library-> | |
137 fontdata[mem_idx].data, | |
31853 | 138 font->library->fontdata[mem_idx].size, index, |
30200 | 139 &face); |
140 if (error) { | |
141 ass_msg(font->library, MSGL_WARN, | |
142 "Error opening memory font: '%s'", path); | |
143 free(path); | |
144 return -1; | |
145 } | |
146 } else { | |
147 error = FT_New_Face(font->ftlibrary, path, index, &face); | |
148 if (error) { | |
149 ass_msg(font->library, MSGL_WARN, | |
150 "Error opening font: '%s', %d", path, index); | |
151 free(path); | |
152 return -1; | |
153 } | |
154 } | |
155 charmap_magic(font->library, face); | |
156 buggy_font_workaround(face); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
157 |
30200 | 158 font->faces[font->n_faces++] = face; |
34295 | 159 ass_face_set_size(face, font->size); |
30200 | 160 free(path); |
161 return font->n_faces - 1; | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
162 } |
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
163 |
21630 | 164 /** |
30200 | 165 * \brief Create a new ASS_Font according to "desc" argument |
21630 | 166 */ |
34295 | 167 ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library, |
30200 | 168 FT_Library ftlibrary, void *fc_priv, |
169 ASS_FontDesc *desc) | |
21277 | 170 { |
30200 | 171 int error; |
172 ASS_Font *fontp; | |
173 ASS_Font font; | |
21317 | 174 |
34295 | 175 fontp = ass_cache_get(font_cache, desc); |
30200 | 176 if (fontp) |
177 return fontp; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
178 |
30200 | 179 font.library = library; |
180 font.ftlibrary = ftlibrary; | |
34295 | 181 font.shaper_priv = NULL; |
30200 | 182 font.n_faces = 0; |
183 font.desc.family = strdup(desc->family); | |
184 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; | |
185 font.desc.bold = desc->bold; | |
186 font.desc.italic = desc->italic; | |
31853 | 187 font.desc.vertical = desc->vertical; |
21277 | 188 |
30200 | 189 font.scale_x = font.scale_y = 1.; |
190 font.v.x = font.v.y = 0; | |
191 font.size = 0.; | |
21277 | 192 |
30200 | 193 error = add_face(fc_priv, &font, 0); |
194 if (error == -1) { | |
195 free(font.desc.family); | |
196 return 0; | |
197 } else | |
34295 | 198 return ass_cache_put(font_cache, &font.desc, &font); |
21277 | 199 } |
200 | |
21630 | 201 /** |
202 * \brief Set font transformation matrix and shift vector | |
203 **/ | |
30200 | 204 void ass_font_set_transform(ASS_Font *font, double scale_x, |
205 double scale_y, FT_Vector *v) | |
21277 | 206 { |
30200 | 207 font->scale_x = scale_x; |
208 font->scale_y = scale_y; | |
209 if (v) { | |
210 font->v.x = v->x; | |
211 font->v.y = v->y; | |
212 } | |
21277 | 213 } |
214 | |
34295 | 215 void ass_face_set_size(FT_Face face, double size) |
23328 | 216 { |
30200 | 217 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); |
218 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
219 double mscale = 1.; | |
220 FT_Size_RequestRec rq; | |
221 FT_Size_Metrics *m = &face->size->metrics; | |
222 // VSFilter uses metrics from TrueType OS/2 table | |
223 // The idea was borrowed from asa (http://asa.diac24.net) | |
224 if (hori && os2) { | |
225 int hori_height = hori->Ascender - hori->Descender; | |
226 int os2_height = os2->usWinAscent + os2->usWinDescent; | |
227 if (hori_height && os2_height) | |
228 mscale = (double) hori_height / os2_height; | |
229 } | |
230 memset(&rq, 0, sizeof(rq)); | |
231 rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; | |
232 rq.width = 0; | |
233 rq.height = double_to_d6(size * mscale); | |
234 rq.horiResolution = rq.vertResolution = 0; | |
235 FT_Request_Size(face, &rq); | |
236 m->ascender /= mscale; | |
237 m->descender /= mscale; | |
238 m->height /= mscale; | |
23328 | 239 } |
240 | |
21630 | 241 /** |
242 * \brief Set font size | |
243 **/ | |
30200 | 244 void ass_font_set_size(ASS_Font *font, double size) |
21277 | 245 { |
30200 | 246 int i; |
247 if (font->size != size) { | |
248 font->size = size; | |
249 for (i = 0; i < font->n_faces; ++i) | |
34295 | 250 ass_face_set_size(font->faces[i], size); |
30200 | 251 } |
21277 | 252 } |
253 | |
21630 | 254 /** |
255 * \brief Get maximal font ascender and descender. | |
256 * \param ch character code | |
257 * The values are extracted from the font face that provides glyphs for the given character | |
258 **/ | |
30200 | 259 void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, |
260 int *desc) | |
261 { | |
262 int i; | |
263 for (i = 0; i < font->n_faces; ++i) { | |
264 FT_Face face = font->faces[i]; | |
265 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
266 if (FT_Get_Char_Index(face, ch)) { | |
267 int y_scale = face->size->metrics.y_scale; | |
268 if (os2) { | |
269 *asc = FT_MulFix(os2->usWinAscent, y_scale); | |
270 *desc = FT_MulFix(os2->usWinDescent, y_scale); | |
271 } else { | |
272 *asc = FT_MulFix(face->ascender, y_scale); | |
273 *desc = FT_MulFix(-face->descender, y_scale); | |
274 } | |
275 return; | |
276 } | |
277 } | |
278 | |
279 *asc = *desc = 0; | |
280 } | |
281 | |
282 /* | |
283 * Strike a glyph with a horizontal line; it's possible to underline it | |
284 * and/or strike through it. For the line's position and size, truetype | |
285 * tables are consulted. Obviously this relies on the data in the tables | |
286 * being accurate. | |
287 * | |
288 */ | |
289 static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font, | |
290 FT_Glyph glyph, int under, int through) | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
291 { |
30200 | 292 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); |
293 TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post); | |
294 FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline; | |
295 int bear, advance, y_scale, i, dir; | |
296 | |
297 if (!under && !through) | |
298 return 0; | |
299 | |
300 // Grow outline | |
301 i = (under ? 4 : 0) + (through ? 4 : 0); | |
302 ol->points = realloc(ol->points, sizeof(FT_Vector) * | |
303 (ol->n_points + i)); | |
304 ol->tags = realloc(ol->tags, ol->n_points + i); | |
305 i = !!under + !!through; | |
306 ol->contours = realloc(ol->contours, sizeof(short) * | |
307 (ol->n_contours + i)); | |
308 | |
309 // If the bearing is negative, the glyph starts left of the current | |
310 // pen position | |
311 bear = FFMIN(face->glyph->metrics.horiBearingX, 0); | |
312 // We're adding half a pixel to avoid small gaps | |
313 advance = d16_to_d6(glyph->advance.x) + 32; | |
314 y_scale = face->size->metrics.y_scale; | |
315 | |
316 // Reverse drawing direction for non-truetype fonts | |
317 dir = FT_Outline_Get_Orientation(ol); | |
318 | |
319 // Add points to the outline | |
320 if (under && ps) { | |
321 int pos, size; | |
322 pos = FT_MulFix(ps->underlinePosition, y_scale * font->scale_y); | |
323 size = FT_MulFix(ps->underlineThickness, | |
324 y_scale * font->scale_y / 2); | |
325 | |
326 if (pos > 0 || size <= 0) | |
327 return 1; | |
328 | |
329 FT_Vector points[4] = { | |
330 {.x = bear, .y = pos + size}, | |
331 {.x = advance, .y = pos + size}, | |
332 {.x = advance, .y = pos - size}, | |
333 {.x = bear, .y = pos - size}, | |
334 }; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
335 |
30200 | 336 if (dir == FT_ORIENTATION_TRUETYPE) { |
337 for (i = 0; i < 4; i++) { | |
338 ol->points[ol->n_points] = points[i]; | |
339 ol->tags[ol->n_points++] = 1; | |
340 } | |
341 } else { | |
342 for (i = 3; i >= 0; i--) { | |
343 ol->points[ol->n_points] = points[i]; | |
344 ol->tags[ol->n_points++] = 1; | |
345 } | |
346 } | |
347 | |
348 ol->contours[ol->n_contours++] = ol->n_points - 1; | |
349 } | |
350 | |
351 if (through && os2) { | |
352 int pos, size; | |
353 pos = FT_MulFix(os2->yStrikeoutPosition, y_scale * font->scale_y); | |
354 size = FT_MulFix(os2->yStrikeoutSize, y_scale * font->scale_y / 2); | |
355 | |
356 if (pos < 0 || size <= 0) | |
357 return 1; | |
358 | |
359 FT_Vector points[4] = { | |
360 {.x = bear, .y = pos + size}, | |
361 {.x = advance, .y = pos + size}, | |
362 {.x = advance, .y = pos - size}, | |
363 {.x = bear, .y = pos - size}, | |
364 }; | |
365 | |
366 if (dir == FT_ORIENTATION_TRUETYPE) { | |
367 for (i = 0; i < 4; i++) { | |
368 ol->points[ol->n_points] = points[i]; | |
369 ol->tags[ol->n_points++] = 1; | |
370 } | |
371 } else { | |
372 for (i = 3; i >= 0; i--) { | |
373 ol->points[ol->n_points] = points[i]; | |
374 ol->tags[ol->n_points++] = 1; | |
375 } | |
376 } | |
377 | |
378 ol->contours[ol->n_contours++] = ol->n_points - 1; | |
379 } | |
380 | |
381 return 0; | |
382 } | |
383 | |
34295 | 384 void outline_copy(FT_Library lib, FT_Outline *source, FT_Outline **dest) |
385 { | |
386 if (source == NULL) { | |
387 *dest = NULL; | |
388 return; | |
389 } | |
390 *dest = calloc(1, sizeof(**dest)); | |
391 | |
392 FT_Outline_New(lib, source->n_points, source->n_contours, *dest); | |
393 FT_Outline_Copy(source, *dest); | |
394 } | |
395 | |
396 void outline_free(FT_Library lib, FT_Outline *outline) | |
397 { | |
398 if (outline) | |
399 FT_Outline_Done(lib, outline); | |
400 free(outline); | |
401 } | |
402 | |
30200 | 403 /** |
404 * Slightly embold a glyph without touching its metrics | |
405 */ | |
406 static void ass_glyph_embolden(FT_GlyphSlot slot) | |
407 { | |
408 int str; | |
409 | |
410 if (slot->format != FT_GLYPH_FORMAT_OUTLINE) | |
411 return; | |
412 | |
413 str = FT_MulFix(slot->face->units_per_EM, | |
414 slot->face->size->metrics.y_scale) / 64; | |
415 | |
416 FT_Outline_Embolden(&slot->outline, str); | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
417 } |
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
418 |
21630 | 419 /** |
34295 | 420 * \brief Get glyph and face index |
421 * Finds a face that has the requested codepoint and returns both face | |
422 * and glyph index. | |
423 */ | |
424 int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol, | |
425 int *face_index, int *glyph_index) | |
21277 | 426 { |
30200 | 427 int index = 0; |
428 int i; | |
429 FT_Face face = 0; | |
34295 | 430 |
431 *glyph_index = 0; | |
21350 | 432 |
34295 | 433 if (symbol < 0x20) { |
434 *face_index = 0; | |
30200 | 435 return 0; |
34295 | 436 } |
30200 | 437 // Handle NBSP like a regular space when rendering the glyph |
34295 | 438 if (symbol == 0xa0) |
439 symbol = ' '; | |
440 if (font->n_faces == 0) { | |
441 *face_index = 0; | |
30200 | 442 return 0; |
34295 | 443 } |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
444 |
34295 | 445 // try with the requested face |
446 if (*face_index < font->n_faces) { | |
447 face = font->faces[*face_index]; | |
448 index = FT_Get_Char_Index(face, symbol); | |
449 } | |
450 | |
451 // not found in requested face, try all others | |
452 for (i = 0; i < font->n_faces && index == 0; ++i) { | |
30200 | 453 face = font->faces[i]; |
34295 | 454 index = FT_Get_Char_Index(face, symbol); |
30200 | 455 if (index) |
34295 | 456 *face_index = i; |
30200 | 457 } |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
458 |
27393 | 459 #ifdef CONFIG_FONTCONFIG |
30200 | 460 if (index == 0) { |
461 int face_idx; | |
462 ass_msg(font->library, MSGL_INFO, | |
463 "Glyph 0x%X not found, selecting one more " | |
34295 | 464 "font for (%s, %d, %d)", symbol, font->desc.family, |
30200 | 465 font->desc.bold, font->desc.italic); |
34295 | 466 face_idx = *face_index = add_face(fcpriv, font, symbol); |
30200 | 467 if (face_idx >= 0) { |
468 face = font->faces[face_idx]; | |
34295 | 469 index = FT_Get_Char_Index(face, symbol); |
31853 | 470 if (index == 0 && face->num_charmaps > 0) { |
34011 | 471 int i; |
31853 | 472 ass_msg(font->library, MSGL_WARN, |
34295 | 473 "Glyph 0x%X not found, broken font? Trying all charmaps", symbol); |
34011 | 474 for (i = 0; i < face->num_charmaps; i++) { |
475 FT_Set_Charmap(face, face->charmaps[i]); | |
34295 | 476 if ((index = FT_Get_Char_Index(face, symbol)) != 0) break; |
34011 | 477 } |
31853 | 478 } |
30200 | 479 if (index == 0) { |
480 ass_msg(font->library, MSGL_ERR, | |
481 "Glyph 0x%X not found in font for (%s, %d, %d)", | |
34295 | 482 symbol, font->desc.family, font->desc.bold, |
30200 | 483 font->desc.italic); |
484 } | |
485 } | |
486 } | |
21351
c611dfc4cb85
If a glyph is not found in the current font, switch to another one.
eugeni
parents:
21350
diff
changeset
|
487 #endif |
34295 | 488 // FIXME: make sure we have a valid face_index. this is a HACK. |
489 *face_index = FFMAX(*face_index, 0); | |
490 *glyph_index = index; | |
491 | |
492 return 1; | |
493 } | |
494 | |
495 /** | |
496 * \brief Get a glyph | |
497 * \param ch character code | |
498 **/ | |
499 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, | |
500 uint32_t ch, int face_index, int index, | |
501 ASS_Hinting hinting, int deco) | |
502 { | |
503 int error; | |
504 FT_Glyph glyph; | |
505 FT_Face face = font->faces[face_index]; | |
506 int flags = 0; | |
507 int vertical = font->desc.vertical; | |
21351
c611dfc4cb85
If a glyph is not found in the current font, switch to another one.
eugeni
parents:
21350
diff
changeset
|
508 |
31853 | 509 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH |
510 | FT_LOAD_IGNORE_TRANSFORM; | |
30200 | 511 switch (hinting) { |
512 case ASS_HINTING_NONE: | |
31853 | 513 flags |= FT_LOAD_NO_HINTING; |
30200 | 514 break; |
515 case ASS_HINTING_LIGHT: | |
31853 | 516 flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; |
30200 | 517 break; |
518 case ASS_HINTING_NORMAL: | |
31853 | 519 flags |= FT_LOAD_FORCE_AUTOHINT; |
30200 | 520 break; |
521 case ASS_HINTING_NATIVE: | |
522 break; | |
523 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
524 |
31853 | 525 error = FT_Load_Glyph(face, index, flags); |
30200 | 526 if (error) { |
527 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", | |
528 index); | |
529 return 0; | |
530 } | |
531 if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && | |
532 (font->desc.italic > 55)) { | |
533 FT_GlyphSlot_Oblique(face->glyph); | |
534 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
535 |
30200 | 536 if (!(face->style_flags & FT_STYLE_FLAG_BOLD) && |
537 (font->desc.bold > 80)) { | |
538 ass_glyph_embolden(face->glyph); | |
539 } | |
540 error = FT_Get_Glyph(face->glyph, &glyph); | |
541 if (error) { | |
542 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", | |
543 index); | |
544 return 0; | |
545 } | |
546 | |
31853 | 547 // Rotate glyph, if needed |
548 if (vertical && ch >= VERTICAL_LOWER_BOUND) { | |
549 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 }; | |
34295 | 550 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); |
551 int desc = 0; | |
552 | |
553 if (os2) | |
554 desc = FT_MulFix(os2->sTypoDescender, face->size->metrics.y_scale); | |
555 | |
556 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, 0, -desc); | |
31853 | 557 FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); |
558 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, | |
34295 | 559 face->glyph->metrics.vertAdvance, desc); |
31853 | 560 glyph->advance.x = face->glyph->linearVertAdvance; |
561 } | |
562 | |
563 // Apply scaling and shift | |
564 FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0, | |
565 double_to_d16(font->scale_y) }; | |
566 FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline; | |
567 FT_Outline_Transform(outl, &scale); | |
568 FT_Outline_Translate(outl, font->v.x, font->v.y); | |
569 glyph->advance.x *= font->scale_x; | |
570 | |
30200 | 571 ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, |
572 deco & DECO_STRIKETHROUGH); | |
573 | |
574 return glyph; | |
21277 | 575 } |
576 | |
21630 | 577 /** |
578 * \brief Get kerning for the pair of glyphs. | |
579 **/ | |
30200 | 580 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
|
581 { |
30200 | 582 FT_Vector v = { 0, 0 }; |
583 int i; | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
584 |
31853 | 585 if (font->desc.vertical) |
586 return v; | |
587 | |
30200 | 588 for (i = 0; i < font->n_faces; ++i) { |
589 FT_Face face = font->faces[i]; | |
590 int i1 = FT_Get_Char_Index(face, c1); | |
591 int i2 = FT_Get_Char_Index(face, c2); | |
592 if (i1 && i2) { | |
593 if (FT_HAS_KERNING(face)) | |
594 FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v); | |
595 return v; | |
596 } | |
597 if (i1 || i2) // these glyphs are from different font faces, no kerning information | |
598 return v; | |
599 } | |
600 return v; | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
601 } |
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
602 |
21630 | 603 /** |
30200 | 604 * \brief Deallocate ASS_Font |
21630 | 605 **/ |
30200 | 606 void ass_font_free(ASS_Font *font) |
21277 | 607 { |
30200 | 608 int i; |
609 for (i = 0; i < font->n_faces; ++i) | |
610 if (font->faces[i]) | |
611 FT_Done_Face(font->faces[i]); | |
34295 | 612 if (font->shaper_priv) |
613 ass_shaper_font_data_free(font->shaper_priv); | |
31875 | 614 free(font->desc.family); |
30200 | 615 free(font); |
21277 | 616 } |
31853 | 617 |
618 /** | |
619 * \brief Calculate the cbox of a series of points | |
620 */ | |
621 static void | |
622 get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end) | |
623 { | |
624 box->xMin = box->yMin = INT_MAX; | |
625 box->xMax = box->yMax = INT_MIN; | |
626 int i; | |
627 | |
628 for (i = start; i <= end; i++) { | |
629 box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin; | |
630 box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax; | |
631 box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin; | |
632 box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax; | |
633 } | |
634 } | |
635 | |
636 /** | |
637 * \brief Determine winding direction of a contour | |
638 * \return direction; 0 = clockwise | |
639 */ | |
640 static int get_contour_direction(FT_Vector *points, int start, int end) | |
641 { | |
642 int i; | |
643 long long sum = 0; | |
644 int x = points[start].x; | |
645 int y = points[start].y; | |
646 for (i = start + 1; i <= end; i++) { | |
647 sum += x * (points[i].y - y) - y * (points[i].x - x); | |
648 x = points[i].x; | |
649 y = points[i].y; | |
650 } | |
651 sum += x * (points[start].y - y) - y * (points[start].x - x); | |
652 return sum > 0; | |
653 } | |
654 | |
655 /** | |
34011 | 656 * \brief Apply fixups to please the FreeType stroker and improve the |
657 * rendering result, especially in case the outline has some anomalies. | |
658 * At the moment, the following fixes are done: | |
659 * | |
660 * 1. Reverse contours that have "inside" winding direction but are not | |
661 * contained in any other contours' cbox. | |
662 * 2. Remove "inside" contours depending on border size, so that large | |
663 * borders do not reverse the winding direction, which leads to "holes" | |
664 * inside the border. The inside will be filled by the border of the | |
665 * outside contour anyway in this case. | |
666 * | |
667 * \param outline FreeType outline, modified in-place | |
668 * \param border_x border size, x direction, d6 format | |
669 * \param border_x border size, y direction, d6 format | |
31853 | 670 */ |
34295 | 671 void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y) |
31853 | 672 { |
34295 | 673 int nc = outline->n_contours; |
31853 | 674 int begin, stop; |
675 char modified = 0; | |
676 char *valid_cont = malloc(nc); | |
677 int start = 0; | |
678 int end = -1; | |
679 FT_BBox *boxes = malloc(nc * sizeof(FT_BBox)); | |
680 int i, j; | |
681 int inside_direction; | |
682 | |
34295 | 683 inside_direction = FT_Outline_Get_Orientation(outline) == |
31853 | 684 FT_ORIENTATION_TRUETYPE; |
685 | |
686 // create a list of cboxes of the contours | |
687 for (i = 0; i < nc; i++) { | |
688 start = end + 1; | |
34295 | 689 end = outline->contours[i]; |
690 get_contour_cbox(&boxes[i], outline->points, start, end); | |
31853 | 691 } |
692 | |
693 // for each contour, check direction and whether it's "outside" | |
694 // or contained in another contour | |
695 end = -1; | |
696 for (i = 0; i < nc; i++) { | |
697 start = end + 1; | |
34295 | 698 end = outline->contours[i]; |
699 int dir = get_contour_direction(outline->points, start, end); | |
31853 | 700 valid_cont[i] = 1; |
701 if (dir == inside_direction) { | |
702 for (j = 0; j < nc; j++) { | |
703 if (i == j) | |
704 continue; | |
705 if (boxes[i].xMin >= boxes[j].xMin && | |
706 boxes[i].xMax <= boxes[j].xMax && | |
707 boxes[i].yMin >= boxes[j].yMin && | |
708 boxes[i].yMax <= boxes[j].yMax) | |
709 goto check_inside; | |
710 } | |
711 /* "inside" contour but we can't find anything it could be | |
712 * inside of - assume the font is buggy and it should be | |
713 * an "outside" contour, and reverse it */ | |
714 for (j = 0; j < (end + 1 - start) / 2; j++) { | |
34295 | 715 FT_Vector temp = outline->points[start + j]; |
716 char temp2 = outline->tags[start + j]; | |
717 outline->points[start + j] = outline->points[end - j]; | |
718 outline->points[end - j] = temp; | |
719 outline->tags[start + j] = outline->tags[end - j]; | |
720 outline->tags[end - j] = temp2; | |
31853 | 721 } |
722 dir ^= 1; | |
723 } | |
724 check_inside: | |
725 if (dir == inside_direction) { | |
726 FT_BBox box; | |
34295 | 727 get_contour_cbox(&box, outline->points, start, end); |
31853 | 728 int width = box.xMax - box.xMin; |
729 int height = box.yMax - box.yMin; | |
730 if (width < border_x * 2 || height < border_y * 2) { | |
731 valid_cont[i] = 0; | |
732 modified = 1; | |
733 } | |
734 } | |
735 } | |
736 | |
34011 | 737 // if we need to modify the outline, rewrite it and skip |
738 // the contours that we determined should be removed. | |
31853 | 739 if (modified) { |
34011 | 740 int p = 0, c = 0; |
31853 | 741 for (i = 0; i < nc; i++) { |
34011 | 742 if (!valid_cont[i]) |
31853 | 743 continue; |
34295 | 744 begin = (i == 0) ? 0 : outline->contours[i - 1] + 1; |
745 stop = outline->contours[i]; | |
31853 | 746 for (j = begin; j <= stop; j++) { |
34011 | 747 outline->points[p].x = outline->points[j].x; |
748 outline->points[p].y = outline->points[j].y; | |
749 outline->tags[p] = outline->tags[j]; | |
750 p++; | |
31853 | 751 } |
34011 | 752 outline->contours[c] = p - 1; |
753 c++; | |
31853 | 754 } |
34011 | 755 outline->n_points = p; |
756 outline->n_contours = c; | |
31853 | 757 } |
758 | |
759 free(boxes); | |
760 free(valid_cont); | |
761 } | |
762 |