Mercurial > mplayer.hg
annotate libass/ass_font.c @ 33179:218edd8fc782
Cosmetic: Format to MPlayer coding style.
Additionally: remove needless includes, group and sort includes, group
and sort variables, rename gtkAOFakeSurround declaration gtkAOSurround,
add #ifdefs to variable declarations, group statements by adding or
removing new lines to ease reading, move assignments outside conditions,
add parentheses, avoid mixing declaration and code, revise comments and
add new ones.
author | ib |
---|---|
date | Fri, 15 Apr 2011 14:30:58 +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 |