Mercurial > mplayer.hg
annotate libass/ass_font.c @ 34114:113156bc1137
Ensure that filename related config will always be loaded.
Currently, filename related config will only be loaded if MPlayer knows
the filename when it gets called, which isn't the case if either MPlayer
is called in slave mode or the GUI is called without file argument. In
either case, the file to be played is known only later.
If filename related config hasn't yet been read, do it after leaving
the idle mode loop.
author | ib |
---|---|
date | Sat, 15 Oct 2011 11:07:29 +0000 |
parents | 88eebbbbd6a0 |
children | 6e7f60f6f9d4 |
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" |
21321
7b7627ff1937
Move ass_font_desc_t and ass_font_t declarations to ass_font.h.
eugeni
parents:
21320
diff
changeset
|
33 #include "ass_bitmap.h" |
7b7627ff1937
Move ass_font_desc_t and ass_font_t declarations to ass_font.h.
eugeni
parents:
21320
diff
changeset
|
34 #include "ass_cache.h" |
21277 | 35 #include "ass_fontconfig.h" |
23299
0ee56ec36a40
Limit ass_font_set_transform to nonrotating transformations.
eugeni
parents:
23212
diff
changeset
|
36 #include "ass_utils.h" |
21277 | 37 |
31853 | 38 #define VERTICAL_LOWER_BOUND 0x02f1 |
39 | |
21277 | 40 /** |
31853 | 41 * Select a good charmap, prefer Microsoft Unicode charmaps. |
21277 | 42 * Otherwise, let FreeType decide. |
43 */ | |
30200 | 44 static void charmap_magic(ASS_Library *library, FT_Face face) |
21277 | 45 { |
30200 | 46 int i; |
31853 | 47 int ms_cmap = -1; |
48 | |
49 // Search for a Microsoft Unicode cmap | |
30200 | 50 for (i = 0; i < face->num_charmaps; ++i) { |
51 FT_CharMap cmap = face->charmaps[i]; | |
52 unsigned pid = cmap->platform_id; | |
53 unsigned eid = cmap->encoding_id; | |
54 if (pid == 3 /*microsoft */ | |
55 && (eid == 1 /*unicode bmp */ | |
56 || eid == 10 /*full unicode */ )) { | |
57 FT_Set_Charmap(face, cmap); | |
58 return; | |
31853 | 59 } else if (pid == 3 && ms_cmap < 0) |
60 ms_cmap = i; | |
61 } | |
62 | |
63 // Try the first Microsoft cmap if no Microsoft Unicode cmap was found | |
64 if (ms_cmap >= 0) { | |
65 FT_CharMap cmap = face->charmaps[ms_cmap]; | |
66 FT_Set_Charmap(face, cmap); | |
67 return; | |
30200 | 68 } |
22210
4a958bd08920
Select the first charmap in the font, if FreeType did not autoselect any.
eugeni
parents:
21630
diff
changeset
|
69 |
30200 | 70 if (!face->charmap) { |
71 if (face->num_charmaps == 0) { | |
72 ass_msg(library, MSGL_WARN, "Font face with no charmaps"); | |
73 return; | |
74 } | |
75 ass_msg(library, MSGL_WARN, | |
76 "No charmap autodetected, trying the first one"); | |
77 FT_Set_Charmap(face, face->charmaps[0]); | |
78 return; | |
79 } | |
21277 | 80 } |
81 | |
21630 | 82 /** |
83 * \brief find a memory font by name | |
84 */ | |
30200 | 85 static int find_font(ASS_Library *library, char *name) |
21460 | 86 { |
30200 | 87 int i; |
88 for (i = 0; i < library->num_fontdata; ++i) | |
89 if (strcasecmp(name, library->fontdata[i].name) == 0) | |
90 return i; | |
91 return -1; | |
21460 | 92 } |
93 | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
94 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
|
95 |
23982
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
96 static void buggy_font_workaround(FT_Face face) |
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
97 { |
30200 | 98 // Some fonts have zero Ascender/Descender fields in 'hhea' table. |
99 // In this case, get the information from 'os2' table or, as | |
100 // a last resort, from face.bbox. | |
101 if (face->ascender + face->descender == 0 || face->height == 0) { | |
102 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
103 if (os2) { | |
104 face->ascender = os2->sTypoAscender; | |
105 face->descender = os2->sTypoDescender; | |
106 face->height = face->ascender - face->descender; | |
107 } else { | |
108 face->ascender = face->bbox.yMax; | |
109 face->descender = face->bbox.yMin; | |
110 face->height = face->ascender - face->descender; | |
111 } | |
112 } | |
23982
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
113 } |
17b5fa69243c
Workaround for fonts with zero ascender/descender in horizontal header.
eugeni
parents:
23981
diff
changeset
|
114 |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
115 /** |
30200 | 116 * \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
|
117 * \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
|
118 */ |
30200 | 119 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
|
120 { |
30200 | 121 char *path; |
122 int index; | |
123 FT_Face face; | |
124 int error; | |
125 int mem_idx; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
126 |
30200 | 127 if (font->n_faces == ASS_FONT_MAX_FACES) |
128 return -1; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
129 |
30200 | 130 path = |
131 fontconfig_select(font->library, fc_priv, font->desc.family, | |
132 font->desc.treat_family_as_pattern, | |
133 font->desc.bold, font->desc.italic, &index, ch); | |
134 if (!path) | |
135 return -1; | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
136 |
30200 | 137 mem_idx = find_font(font->library, path); |
138 if (mem_idx >= 0) { | |
139 error = | |
140 FT_New_Memory_Face(font->ftlibrary, | |
141 (unsigned char *) font->library-> | |
142 fontdata[mem_idx].data, | |
31853 | 143 font->library->fontdata[mem_idx].size, index, |
30200 | 144 &face); |
145 if (error) { | |
146 ass_msg(font->library, MSGL_WARN, | |
147 "Error opening memory font: '%s'", path); | |
148 free(path); | |
149 return -1; | |
150 } | |
151 } else { | |
152 error = FT_New_Face(font->ftlibrary, path, index, &face); | |
153 if (error) { | |
154 ass_msg(font->library, MSGL_WARN, | |
155 "Error opening font: '%s', %d", path, index); | |
156 free(path); | |
157 return -1; | |
158 } | |
159 } | |
160 charmap_magic(font->library, face); | |
161 buggy_font_workaround(face); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
162 |
30200 | 163 font->faces[font->n_faces++] = face; |
164 face_set_size(face, font->size); | |
165 free(path); | |
166 return font->n_faces - 1; | |
23981
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
167 } |
705628816d98
Factor out common code from ass_font_new and ass_font_reselect.
eugeni
parents:
23980
diff
changeset
|
168 |
21630 | 169 /** |
30200 | 170 * \brief Create a new ASS_Font according to "desc" argument |
21630 | 171 */ |
30200 | 172 ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, |
173 FT_Library ftlibrary, void *fc_priv, | |
174 ASS_FontDesc *desc) | |
21277 | 175 { |
30200 | 176 int error; |
177 ASS_Font *fontp; | |
178 ASS_Font font; | |
21317 | 179 |
30200 | 180 fontp = ass_font_cache_find((Hashmap *) font_cache, desc); |
181 if (fontp) | |
182 return fontp; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
183 |
30200 | 184 font.library = library; |
185 font.ftlibrary = ftlibrary; | |
186 font.n_faces = 0; | |
187 font.desc.family = strdup(desc->family); | |
188 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; | |
189 font.desc.bold = desc->bold; | |
190 font.desc.italic = desc->italic; | |
31853 | 191 font.desc.vertical = desc->vertical; |
21277 | 192 |
30200 | 193 font.scale_x = font.scale_y = 1.; |
194 font.v.x = font.v.y = 0; | |
195 font.size = 0.; | |
21277 | 196 |
30200 | 197 error = add_face(fc_priv, &font, 0); |
198 if (error == -1) { | |
199 free(font.desc.family); | |
200 return 0; | |
201 } else | |
202 return ass_font_cache_add((Hashmap *) font_cache, &font); | |
21277 | 203 } |
204 | |
21630 | 205 /** |
206 * \brief Set font transformation matrix and shift vector | |
207 **/ | |
30200 | 208 void ass_font_set_transform(ASS_Font *font, double scale_x, |
209 double scale_y, FT_Vector *v) | |
21277 | 210 { |
30200 | 211 font->scale_x = scale_x; |
212 font->scale_y = scale_y; | |
213 if (v) { | |
214 font->v.x = v->x; | |
215 font->v.y = v->y; | |
216 } | |
21277 | 217 } |
218 | |
23328 | 219 static void face_set_size(FT_Face face, double size) |
220 { | |
30200 | 221 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); |
222 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
223 double mscale = 1.; | |
224 FT_Size_RequestRec rq; | |
225 FT_Size_Metrics *m = &face->size->metrics; | |
226 // VSFilter uses metrics from TrueType OS/2 table | |
227 // The idea was borrowed from asa (http://asa.diac24.net) | |
228 if (hori && os2) { | |
229 int hori_height = hori->Ascender - hori->Descender; | |
230 int os2_height = os2->usWinAscent + os2->usWinDescent; | |
231 if (hori_height && os2_height) | |
232 mscale = (double) hori_height / os2_height; | |
233 } | |
234 memset(&rq, 0, sizeof(rq)); | |
235 rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; | |
236 rq.width = 0; | |
237 rq.height = double_to_d6(size * mscale); | |
238 rq.horiResolution = rq.vertResolution = 0; | |
239 FT_Request_Size(face, &rq); | |
240 m->ascender /= mscale; | |
241 m->descender /= mscale; | |
242 m->height /= mscale; | |
23328 | 243 } |
244 | |
21630 | 245 /** |
246 * \brief Set font size | |
247 **/ | |
30200 | 248 void ass_font_set_size(ASS_Font *font, double size) |
21277 | 249 { |
30200 | 250 int i; |
251 if (font->size != size) { | |
252 font->size = size; | |
253 for (i = 0; i < font->n_faces; ++i) | |
254 face_set_size(font->faces[i], size); | |
255 } | |
21277 | 256 } |
257 | |
21630 | 258 /** |
259 * \brief Get maximal font ascender and descender. | |
260 * \param ch character code | |
261 * The values are extracted from the font face that provides glyphs for the given character | |
262 **/ | |
30200 | 263 void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, |
264 int *desc) | |
265 { | |
266 int i; | |
267 for (i = 0; i < font->n_faces; ++i) { | |
268 FT_Face face = font->faces[i]; | |
269 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); | |
270 if (FT_Get_Char_Index(face, ch)) { | |
271 int y_scale = face->size->metrics.y_scale; | |
272 if (os2) { | |
273 *asc = FT_MulFix(os2->usWinAscent, y_scale); | |
274 *desc = FT_MulFix(os2->usWinDescent, y_scale); | |
275 } else { | |
276 *asc = FT_MulFix(face->ascender, y_scale); | |
277 *desc = FT_MulFix(-face->descender, y_scale); | |
278 } | |
31853 | 279 if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) { |
280 *asc = FT_MulFix(face->max_advance_width, y_scale); | |
281 } | |
30200 | 282 return; |
283 } | |
284 } | |
285 | |
286 *asc = *desc = 0; | |
287 } | |
288 | |
289 /* | |
290 * Strike a glyph with a horizontal line; it's possible to underline it | |
291 * and/or strike through it. For the line's position and size, truetype | |
292 * tables are consulted. Obviously this relies on the data in the tables | |
293 * being accurate. | |
294 * | |
295 */ | |
296 static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font, | |
297 FT_Glyph glyph, int under, int through) | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
298 { |
30200 | 299 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); |
300 TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post); | |
301 FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline; | |
302 int bear, advance, y_scale, i, dir; | |
303 | |
304 if (!under && !through) | |
305 return 0; | |
306 | |
307 // Grow outline | |
308 i = (under ? 4 : 0) + (through ? 4 : 0); | |
309 ol->points = realloc(ol->points, sizeof(FT_Vector) * | |
310 (ol->n_points + i)); | |
311 ol->tags = realloc(ol->tags, ol->n_points + i); | |
312 i = !!under + !!through; | |
313 ol->contours = realloc(ol->contours, sizeof(short) * | |
314 (ol->n_contours + i)); | |
315 | |
316 // If the bearing is negative, the glyph starts left of the current | |
317 // pen position | |
318 bear = FFMIN(face->glyph->metrics.horiBearingX, 0); | |
319 // We're adding half a pixel to avoid small gaps | |
320 advance = d16_to_d6(glyph->advance.x) + 32; | |
321 y_scale = face->size->metrics.y_scale; | |
322 | |
323 // Reverse drawing direction for non-truetype fonts | |
324 dir = FT_Outline_Get_Orientation(ol); | |
325 | |
326 // Add points to the outline | |
327 if (under && ps) { | |
328 int pos, size; | |
329 pos = FT_MulFix(ps->underlinePosition, y_scale * font->scale_y); | |
330 size = FT_MulFix(ps->underlineThickness, | |
331 y_scale * font->scale_y / 2); | |
332 | |
333 if (pos > 0 || size <= 0) | |
334 return 1; | |
335 | |
336 FT_Vector points[4] = { | |
337 {.x = bear, .y = pos + size}, | |
338 {.x = advance, .y = pos + size}, | |
339 {.x = advance, .y = pos - size}, | |
340 {.x = bear, .y = pos - size}, | |
341 }; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
28860
diff
changeset
|
342 |
30200 | 343 if (dir == FT_ORIENTATION_TRUETYPE) { |
344 for (i = 0; i < 4; i++) { | |
345 ol->points[ol->n_points] = points[i]; | |
346 ol->tags[ol->n_points++] = 1; | |
347 } | |
348 } else { | |
349 for (i = 3; i >= 0; i--) { | |
350 ol->points[ol->n_points] = points[i]; | |
351 ol->tags[ol->n_points++] = 1; | |
352 } | |
353 } | |
354 | |
355 ol->contours[ol->n_contours++] = ol->n_points - 1; | |
356 } | |
357 | |
358 if (through && os2) { | |
359 int pos, size; | |
360 pos = FT_MulFix(os2->yStrikeoutPosition, y_scale * font->scale_y); | |
361 size = FT_MulFix(os2->yStrikeoutSize, y_scale * font->scale_y / 2); | |
362 | |
363 if (pos < 0 || size <= 0) | |
364 return 1; | |
365 | |
366 FT_Vector points[4] = { | |
367 {.x = bear, .y = pos + size}, | |
368 {.x = advance, .y = pos + size}, | |
369 {.x = advance, .y = pos - size}, | |
370 {.x = bear, .y = pos - size}, | |
371 }; | |
372 | |
373 if (dir == FT_ORIENTATION_TRUETYPE) { | |
374 for (i = 0; i < 4; i++) { | |
375 ol->points[ol->n_points] = points[i]; | |
376 ol->tags[ol->n_points++] = 1; | |
377 } | |
378 } else { | |
379 for (i = 3; i >= 0; i--) { | |
380 ol->points[ol->n_points] = points[i]; | |
381 ol->tags[ol->n_points++] = 1; | |
382 } | |
383 } | |
384 | |
385 ol->contours[ol->n_contours++] = ol->n_points - 1; | |
386 } | |
387 | |
388 return 0; | |
389 } | |
390 | |
391 /** | |
392 * Slightly embold a glyph without touching its metrics | |
393 */ | |
394 static void ass_glyph_embolden(FT_GlyphSlot slot) | |
395 { | |
396 int str; | |
397 | |
398 if (slot->format != FT_GLYPH_FORMAT_OUTLINE) | |
399 return; | |
400 | |
401 str = FT_MulFix(slot->face->units_per_EM, | |
402 slot->face->size->metrics.y_scale) / 64; | |
403 | |
404 FT_Outline_Embolden(&slot->outline, str); | |
21614
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
405 } |
5d2ca7ca18b5
Move ascender, descender, and kerning computation to ass_font.c.
eugeni
parents:
21460
diff
changeset
|
406 |
21630 | 407 /** |
408 * \brief Get a glyph | |
409 * \param ch character code | |
410 **/ | |
30200 | 411 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, |
412 uint32_t ch, ASS_Hinting hinting, int deco) | |
21277 | 413 { |
30200 | 414 int error; |
415 int index = 0; | |
416 int i; | |
417 FT_Glyph glyph; | |
418 FT_Face face = 0; | |
419 int flags = 0; | |
31853 | 420 int vertical = font->desc.vertical; |
21350 | 421 |
30200 | 422 if (ch < 0x20) |
423 return 0; | |
424 // Handle NBSP like a regular space when rendering the glyph | |
425 if (ch == 0xa0) | |
426 ch = ' '; | |
427 if (font->n_faces == 0) | |
428 return 0; | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
429 |
30200 | 430 for (i = 0; i < font->n_faces; ++i) { |
431 face = font->faces[i]; | |
432 index = FT_Get_Char_Index(face, ch); | |
433 if (index) | |
434 break; | |
435 } | |
21619
b4b51eb2904f
Keep reselected fonts in an array, adding new ones to the end. Glyph
eugeni
parents:
21618
diff
changeset
|
436 |
27393 | 437 #ifdef CONFIG_FONTCONFIG |
30200 | 438 if (index == 0) { |
439 int face_idx; | |
440 ass_msg(font->library, MSGL_INFO, | |
441 "Glyph 0x%X not found, selecting one more " | |
442 "font for (%s, %d, %d)", ch, font->desc.family, | |
443 font->desc.bold, font->desc.italic); | |
444 face_idx = add_face(fontconfig_priv, font, ch); | |
445 if (face_idx >= 0) { | |
446 face = font->faces[face_idx]; | |
447 index = FT_Get_Char_Index(face, ch); | |
31853 | 448 if (index == 0 && face->num_charmaps > 0) { |
34011 | 449 int i; |
31853 | 450 ass_msg(font->library, MSGL_WARN, |
34011 | 451 "Glyph 0x%X not found, broken font? Trying all charmaps", ch); |
452 for (i = 0; i < face->num_charmaps; i++) { | |
453 FT_Set_Charmap(face, face->charmaps[i]); | |
454 if ((index = FT_Get_Char_Index(face, ch)) != 0) break; | |
455 } | |
31853 | 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]); | |
31875 | 564 free(font->desc.family); |
30200 | 565 free(font); |
21277 | 566 } |
31853 | 567 |
568 /** | |
569 * \brief Calculate the cbox of a series of points | |
570 */ | |
571 static void | |
572 get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end) | |
573 { | |
574 box->xMin = box->yMin = INT_MAX; | |
575 box->xMax = box->yMax = INT_MIN; | |
576 int i; | |
577 | |
578 for (i = start; i <= end; i++) { | |
579 box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin; | |
580 box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax; | |
581 box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin; | |
582 box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax; | |
583 } | |
584 } | |
585 | |
586 /** | |
587 * \brief Determine winding direction of a contour | |
588 * \return direction; 0 = clockwise | |
589 */ | |
590 static int get_contour_direction(FT_Vector *points, int start, int end) | |
591 { | |
592 int i; | |
593 long long sum = 0; | |
594 int x = points[start].x; | |
595 int y = points[start].y; | |
596 for (i = start + 1; i <= end; i++) { | |
597 sum += x * (points[i].y - y) - y * (points[i].x - x); | |
598 x = points[i].x; | |
599 y = points[i].y; | |
600 } | |
601 sum += x * (points[start].y - y) - y * (points[start].x - x); | |
602 return sum > 0; | |
603 } | |
604 | |
605 /** | |
34011 | 606 * \brief Apply fixups to please the FreeType stroker and improve the |
607 * rendering result, especially in case the outline has some anomalies. | |
608 * At the moment, the following fixes are done: | |
609 * | |
610 * 1. Reverse contours that have "inside" winding direction but are not | |
611 * contained in any other contours' cbox. | |
612 * 2. Remove "inside" contours depending on border size, so that large | |
613 * borders do not reverse the winding direction, which leads to "holes" | |
614 * inside the border. The inside will be filled by the border of the | |
615 * outside contour anyway in this case. | |
616 * | |
617 * \param outline FreeType outline, modified in-place | |
618 * \param border_x border size, x direction, d6 format | |
619 * \param border_x border size, y direction, d6 format | |
31853 | 620 */ |
621 void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y) | |
622 { | |
623 int nc = glyph->outline.n_contours; | |
624 int begin, stop; | |
625 char modified = 0; | |
626 char *valid_cont = malloc(nc); | |
627 int start = 0; | |
628 int end = -1; | |
629 FT_BBox *boxes = malloc(nc * sizeof(FT_BBox)); | |
630 int i, j; | |
631 int inside_direction; | |
632 | |
633 inside_direction = FT_Outline_Get_Orientation(&glyph->outline) == | |
634 FT_ORIENTATION_TRUETYPE; | |
635 | |
636 // create a list of cboxes of the contours | |
637 for (i = 0; i < nc; i++) { | |
638 start = end + 1; | |
639 end = glyph->outline.contours[i]; | |
640 get_contour_cbox(&boxes[i], glyph->outline.points, start, end); | |
641 } | |
642 | |
643 // for each contour, check direction and whether it's "outside" | |
644 // or contained in another contour | |
645 end = -1; | |
646 for (i = 0; i < nc; i++) { | |
647 start = end + 1; | |
648 end = glyph->outline.contours[i]; | |
649 int dir = get_contour_direction(glyph->outline.points, start, end); | |
650 valid_cont[i] = 1; | |
651 if (dir == inside_direction) { | |
652 for (j = 0; j < nc; j++) { | |
653 if (i == j) | |
654 continue; | |
655 if (boxes[i].xMin >= boxes[j].xMin && | |
656 boxes[i].xMax <= boxes[j].xMax && | |
657 boxes[i].yMin >= boxes[j].yMin && | |
658 boxes[i].yMax <= boxes[j].yMax) | |
659 goto check_inside; | |
660 } | |
661 /* "inside" contour but we can't find anything it could be | |
662 * inside of - assume the font is buggy and it should be | |
663 * an "outside" contour, and reverse it */ | |
664 for (j = 0; j < (end + 1 - start) / 2; j++) { | |
665 FT_Vector temp = glyph->outline.points[start + j]; | |
666 char temp2 = glyph->outline.tags[start + j]; | |
667 glyph->outline.points[start + j] = glyph->outline.points[end - j]; | |
668 glyph->outline.points[end - j] = temp; | |
669 glyph->outline.tags[start + j] = glyph->outline.tags[end - j]; | |
670 glyph->outline.tags[end - j] = temp2; | |
671 } | |
672 dir ^= 1; | |
673 } | |
674 check_inside: | |
675 if (dir == inside_direction) { | |
676 FT_BBox box; | |
677 get_contour_cbox(&box, glyph->outline.points, start, end); | |
678 int width = box.xMax - box.xMin; | |
679 int height = box.yMax - box.yMin; | |
680 if (width < border_x * 2 || height < border_y * 2) { | |
681 valid_cont[i] = 0; | |
682 modified = 1; | |
683 } | |
684 } | |
685 } | |
686 | |
34011 | 687 // if we need to modify the outline, rewrite it and skip |
688 // the contours that we determined should be removed. | |
31853 | 689 if (modified) { |
34011 | 690 FT_Outline *outline = &glyph->outline; |
691 int p = 0, c = 0; | |
31853 | 692 for (i = 0; i < nc; i++) { |
34011 | 693 if (!valid_cont[i]) |
31853 | 694 continue; |
695 begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1; | |
696 stop = glyph->outline.contours[i]; | |
697 for (j = begin; j <= stop; j++) { | |
34011 | 698 outline->points[p].x = outline->points[j].x; |
699 outline->points[p].y = outline->points[j].y; | |
700 outline->tags[p] = outline->tags[j]; | |
701 p++; | |
31853 | 702 } |
34011 | 703 outline->contours[c] = p - 1; |
704 c++; | |
31853 | 705 } |
34011 | 706 outline->n_points = p; |
707 outline->n_contours = c; | |
31853 | 708 } |
709 | |
710 free(boxes); | |
711 free(valid_cont); | |
712 } | |
713 |