Mercurial > mplayer.hg
annotate libass/ass_font.c @ 34038:422eb76aee70
stream_bluray: switch to new libbluray API
The last libbluray API change is
commit 17896a40e6ddbe12473db77549ff0207ef748a64
Date: Mon Jun 13 20:36:16 2011 +0300
previous libbluray versions are no longer supported.
author | diego |
---|---|
date | Fri, 23 Sep 2011 14:26:20 +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 |