Mercurial > mplayer.hg
comparison libass/ass_shaper.c @ 34300:77976b68285b
Commit added forgotten in previous commit.
author | reimar |
---|---|
date | Sat, 03 Dec 2011 23:08:03 +0000 |
parents | |
children | 575ad51cc996 |
comparison
equal
deleted
inserted
replaced
34299:1237a0a79119 | 34300:77976b68285b |
---|---|
1 /* | |
2 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx> | |
3 * | |
4 * This file is part of libass. | |
5 * | |
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. | |
9 * | |
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. | |
17 */ | |
18 | |
19 #include "config.h" | |
20 | |
21 #include <fribidi/fribidi.h> | |
22 | |
23 #include "ass_shaper.h" | |
24 #include "ass_render.h" | |
25 #include "ass_font.h" | |
26 #include "ass_parse.h" | |
27 #include "ass_cache.h" | |
28 | |
29 #define MAX_RUNS 50 | |
30 | |
31 #ifdef CONFIG_HARFBUZZ | |
32 #include <hb-ft.h> | |
33 enum { | |
34 VERT = 0, | |
35 VKNA, | |
36 KERN | |
37 }; | |
38 #define NUM_FEATURES 3 | |
39 #endif | |
40 | |
41 struct ass_shaper { | |
42 ASS_ShapingLevel shaping_level; | |
43 | |
44 // FriBidi log2vis | |
45 int n_glyphs; | |
46 FriBidiChar *event_text; | |
47 FriBidiCharType *ctypes; | |
48 FriBidiLevel *emblevels; | |
49 FriBidiStrIndex *cmap; | |
50 FriBidiParType base_direction; | |
51 | |
52 #ifdef CONFIG_HARFBUZZ | |
53 // OpenType features | |
54 int n_features; | |
55 hb_feature_t *features; | |
56 hb_language_t language; | |
57 | |
58 // Glyph metrics cache, to speed up shaping | |
59 Cache *metrics_cache; | |
60 #endif | |
61 }; | |
62 | |
63 #ifdef CONFIG_HARFBUZZ | |
64 struct ass_shaper_metrics_data { | |
65 Cache *metrics_cache; | |
66 GlyphMetricsHashKey hash_key; | |
67 int vertical; | |
68 }; | |
69 | |
70 struct ass_shaper_font_data { | |
71 hb_font_t *fonts[ASS_FONT_MAX_FACES]; | |
72 hb_font_funcs_t *font_funcs[ASS_FONT_MAX_FACES]; | |
73 struct ass_shaper_metrics_data *metrics_data[ASS_FONT_MAX_FACES]; | |
74 }; | |
75 #endif | |
76 | |
77 /** | |
78 * \brief Print version information | |
79 */ | |
80 void ass_shaper_info(ASS_Library *lib) | |
81 { | |
82 ass_msg(lib, MSGL_V, "Shaper: FriBidi " | |
83 FRIBIDI_VERSION " (SIMPLE)" | |
84 #ifdef CONFIG_HARFBUZZ | |
85 " HarfBuzz-ng %s (COMPLEX)", hb_version_string() | |
86 #endif | |
87 ); | |
88 } | |
89 | |
90 /** | |
91 * \brief grow arrays, if needed | |
92 * \param new_size requested size | |
93 */ | |
94 static void check_allocations(ASS_Shaper *shaper, size_t new_size) | |
95 { | |
96 if (new_size > shaper->n_glyphs) { | |
97 shaper->event_text = realloc(shaper->event_text, sizeof(FriBidiChar) * new_size); | |
98 shaper->ctypes = realloc(shaper->ctypes, sizeof(FriBidiCharType) * new_size); | |
99 shaper->emblevels = realloc(shaper->emblevels, sizeof(FriBidiLevel) * new_size); | |
100 shaper->cmap = realloc(shaper->cmap, sizeof(FriBidiStrIndex) * new_size); | |
101 } | |
102 } | |
103 | |
104 /** | |
105 * \brief Free shaper and related data | |
106 */ | |
107 void ass_shaper_free(ASS_Shaper *shaper) | |
108 { | |
109 #ifdef CONFIG_HARFBUZZ | |
110 ass_cache_done(shaper->metrics_cache); | |
111 free(shaper->features); | |
112 #endif | |
113 free(shaper->event_text); | |
114 free(shaper->ctypes); | |
115 free(shaper->emblevels); | |
116 free(shaper->cmap); | |
117 free(shaper); | |
118 } | |
119 | |
120 void ass_shaper_font_data_free(ASS_ShaperFontData *priv) | |
121 { | |
122 #ifdef CONFIG_HARFBUZZ | |
123 int i; | |
124 for (i = 0; i < ASS_FONT_MAX_FACES; i++) | |
125 if (priv->fonts[i]) { | |
126 free(priv->metrics_data[i]); | |
127 hb_font_destroy(priv->fonts[i]); | |
128 hb_font_funcs_destroy(priv->font_funcs[i]); | |
129 } | |
130 free(priv); | |
131 #endif | |
132 } | |
133 | |
134 #ifdef CONFIG_HARFBUZZ | |
135 /** | |
136 * \brief set up the HarfBuzz OpenType feature list with some | |
137 * standard features. | |
138 */ | |
139 static void init_features(ASS_Shaper *shaper) | |
140 { | |
141 shaper->features = calloc(sizeof(hb_feature_t), NUM_FEATURES); | |
142 | |
143 shaper->n_features = NUM_FEATURES; | |
144 shaper->features[VERT].tag = HB_TAG('v', 'e', 'r', 't'); | |
145 shaper->features[VERT].end = INT_MAX; | |
146 shaper->features[VKNA].tag = HB_TAG('v', 'k', 'n', 'a'); | |
147 shaper->features[VKNA].end = INT_MAX; | |
148 shaper->features[KERN].tag = HB_TAG('k', 'e', 'r', 'n'); | |
149 shaper->features[KERN].end = INT_MAX; | |
150 } | |
151 | |
152 /** | |
153 * \brief Set features depending on properties of the run | |
154 */ | |
155 static void set_run_features(ASS_Shaper *shaper, GlyphInfo *info) | |
156 { | |
157 // enable vertical substitutions for @font runs | |
158 if (info->font->desc.vertical) | |
159 shaper->features[VERT].value = shaper->features[VKNA].value = 1; | |
160 else | |
161 shaper->features[VERT].value = shaper->features[VKNA].value = 0; | |
162 } | |
163 | |
164 /** | |
165 * \brief Update HarfBuzz's idea of font metrics | |
166 * \param hb_font HarfBuzz font | |
167 * \param face associated FreeType font face | |
168 */ | |
169 static void update_hb_size(hb_font_t *hb_font, FT_Face face) | |
170 { | |
171 hb_font_set_scale (hb_font, | |
172 ((uint64_t) face->size->metrics.x_scale * (uint64_t) face->units_per_EM) >> 16, | |
173 ((uint64_t) face->size->metrics.y_scale * (uint64_t) face->units_per_EM) >> 16); | |
174 hb_font_set_ppem (hb_font, face->size->metrics.x_ppem, | |
175 face->size->metrics.y_ppem); | |
176 } | |
177 | |
178 | |
179 /* | |
180 * Cached glyph metrics getters follow | |
181 * | |
182 * These functions replace HarfBuzz' standard FreeType font functions | |
183 * and provide cached access to essential glyph metrics. This usually | |
184 * speeds up shaping a lot. It also allows us to use custom load flags. | |
185 * | |
186 */ | |
187 | |
188 GlyphMetricsHashValue * | |
189 get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face, | |
190 hb_codepoint_t glyph) | |
191 { | |
192 GlyphMetricsHashValue *val; | |
193 | |
194 metrics->hash_key.glyph_index = glyph; | |
195 val = ass_cache_get(metrics->metrics_cache, &metrics->hash_key); | |
196 | |
197 if (!val) { | |
198 int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | |
199 | FT_LOAD_IGNORE_TRANSFORM; | |
200 GlyphMetricsHashValue new_val; | |
201 | |
202 if (FT_Load_Glyph(face, glyph, load_flags)) | |
203 return NULL; | |
204 | |
205 memcpy(&new_val.metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics)); | |
206 val = ass_cache_put(metrics->metrics_cache, &metrics->hash_key, &new_val); | |
207 } | |
208 | |
209 return val; | |
210 } | |
211 | |
212 static hb_bool_t | |
213 get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode, | |
214 hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data) | |
215 { | |
216 FT_Face face = font_data; | |
217 | |
218 if (variation) | |
219 *glyph = FT_Face_GetCharVariantIndex(face, unicode, variation); | |
220 else | |
221 *glyph = FT_Get_Char_Index(face, unicode); | |
222 | |
223 return *glyph != 0; | |
224 } | |
225 | |
226 static hb_position_t | |
227 cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, | |
228 void *user_data) | |
229 { | |
230 FT_Face face = font_data; | |
231 struct ass_shaper_metrics_data *metrics_priv = user_data; | |
232 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); | |
233 | |
234 if (!metrics) | |
235 return 0; | |
236 | |
237 if (metrics_priv->vertical && glyph > VERTICAL_LOWER_BOUND) | |
238 return metrics->metrics.vertAdvance; | |
239 | |
240 return metrics->metrics.horiAdvance; | |
241 } | |
242 | |
243 static hb_position_t | |
244 cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, | |
245 void *user_data) | |
246 { | |
247 FT_Face face = font_data; | |
248 struct ass_shaper_metrics_data *metrics_priv = user_data; | |
249 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); | |
250 | |
251 if (!metrics) | |
252 return 0; | |
253 | |
254 return metrics->metrics.vertAdvance; | |
255 | |
256 } | |
257 | |
258 static hb_bool_t | |
259 cached_h_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, | |
260 hb_position_t *x, hb_position_t *y, void *user_data) | |
261 { | |
262 return 1; | |
263 } | |
264 | |
265 static hb_bool_t | |
266 cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph, | |
267 hb_position_t *x, hb_position_t *y, void *user_data) | |
268 { | |
269 FT_Face face = font_data; | |
270 struct ass_shaper_metrics_data *metrics_priv = user_data; | |
271 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); | |
272 | |
273 if (!metrics) | |
274 return 0; | |
275 | |
276 *x = metrics->metrics.horiBearingX - metrics->metrics.vertBearingX; | |
277 *y = metrics->metrics.horiBearingY - (-metrics->metrics.vertBearingY); | |
278 | |
279 return 1; | |
280 } | |
281 | |
282 static hb_position_t | |
283 get_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first, | |
284 hb_codepoint_t second, void *user_data) | |
285 { | |
286 FT_Face face = font_data; | |
287 FT_Vector kern; | |
288 | |
289 if (FT_Get_Kerning (face, first, second, FT_KERNING_DEFAULT, &kern)) | |
290 return 0; | |
291 | |
292 return kern.x; | |
293 } | |
294 | |
295 static hb_position_t | |
296 get_v_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first, | |
297 hb_codepoint_t second, void *user_data) | |
298 { | |
299 return 0; | |
300 } | |
301 | |
302 static hb_bool_t | |
303 cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph, | |
304 hb_glyph_extents_t *extents, void *user_data) | |
305 { | |
306 FT_Face face = font_data; | |
307 struct ass_shaper_metrics_data *metrics_priv = user_data; | |
308 GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph); | |
309 | |
310 if (!metrics) | |
311 return 0; | |
312 | |
313 extents->x_bearing = metrics->metrics.horiBearingX; | |
314 extents->y_bearing = metrics->metrics.horiBearingY; | |
315 extents->width = metrics->metrics.width; | |
316 extents->height = metrics->metrics.height; | |
317 | |
318 return 1; | |
319 } | |
320 | |
321 static hb_bool_t | |
322 get_contour_point(hb_font_t *font, void *font_data, hb_codepoint_t glyph, | |
323 unsigned int point_index, hb_position_t *x, | |
324 hb_position_t *y, void *user_data) | |
325 { | |
326 FT_Face face = font_data; | |
327 int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | |
328 | FT_LOAD_IGNORE_TRANSFORM; | |
329 | |
330 if (FT_Load_Glyph(face, glyph, load_flags)) | |
331 return 0; | |
332 | |
333 if (point_index >= (unsigned)face->glyph->outline.n_points) | |
334 return 0; | |
335 | |
336 *x = face->glyph->outline.points[point_index].x; | |
337 *y = face->glyph->outline.points[point_index].y; | |
338 | |
339 return 1; | |
340 } | |
341 | |
342 /** | |
343 * \brief Retrieve HarfBuzz font from cache. | |
344 * Create it from FreeType font, if needed. | |
345 * \param info glyph cluster | |
346 * \return HarfBuzz font | |
347 */ | |
348 static hb_font_t *get_hb_font(ASS_Shaper *shaper, GlyphInfo *info) | |
349 { | |
350 ASS_Font *font = info->font; | |
351 hb_font_t **hb_fonts; | |
352 | |
353 if (!font->shaper_priv) | |
354 font->shaper_priv = calloc(sizeof(ASS_ShaperFontData), 1); | |
355 | |
356 | |
357 hb_fonts = font->shaper_priv->fonts; | |
358 if (!hb_fonts[info->face_index]) { | |
359 hb_fonts[info->face_index] = | |
360 hb_ft_font_create(font->faces[info->face_index], NULL); | |
361 | |
362 // set up cached metrics access | |
363 font->shaper_priv->metrics_data[info->face_index] = | |
364 calloc(sizeof(struct ass_shaper_metrics_data), 1); | |
365 struct ass_shaper_metrics_data *metrics = | |
366 font->shaper_priv->metrics_data[info->face_index]; | |
367 metrics->metrics_cache = shaper->metrics_cache; | |
368 metrics->vertical = info->font->desc.vertical; | |
369 | |
370 hb_font_funcs_t *funcs = hb_font_funcs_create(); | |
371 font->shaper_priv->font_funcs[info->face_index] = funcs; | |
372 hb_font_funcs_set_glyph_func(funcs, get_glyph, | |
373 metrics, NULL); | |
374 hb_font_funcs_set_glyph_h_advance_func(funcs, cached_h_advance, | |
375 metrics, NULL); | |
376 hb_font_funcs_set_glyph_v_advance_func(funcs, cached_v_advance, | |
377 metrics, NULL); | |
378 hb_font_funcs_set_glyph_h_origin_func(funcs, cached_h_origin, | |
379 metrics, NULL); | |
380 hb_font_funcs_set_glyph_v_origin_func(funcs, cached_v_origin, | |
381 metrics, NULL); | |
382 hb_font_funcs_set_glyph_h_kerning_func(funcs, get_h_kerning, | |
383 metrics, NULL); | |
384 hb_font_funcs_set_glyph_v_kerning_func(funcs, get_v_kerning, | |
385 metrics, NULL); | |
386 hb_font_funcs_set_glyph_extents_func(funcs, cached_extents, | |
387 metrics, NULL); | |
388 hb_font_funcs_set_glyph_contour_point_func(funcs, get_contour_point, | |
389 metrics, NULL); | |
390 hb_font_set_funcs(hb_fonts[info->face_index], funcs, | |
391 font->faces[info->face_index], NULL); | |
392 } | |
393 | |
394 ass_face_set_size(font->faces[info->face_index], info->font_size); | |
395 update_hb_size(hb_fonts[info->face_index], font->faces[info->face_index]); | |
396 | |
397 // update hash key for cached metrics | |
398 struct ass_shaper_metrics_data *metrics = | |
399 font->shaper_priv->metrics_data[info->face_index]; | |
400 metrics->hash_key.font = info->font; | |
401 metrics->hash_key.face_index = info->face_index; | |
402 metrics->hash_key.size = info->font_size; | |
403 metrics->hash_key.scale_x = double_to_d6(info->scale_x); | |
404 metrics->hash_key.scale_y = double_to_d6(info->scale_y); | |
405 | |
406 return hb_fonts[info->face_index]; | |
407 } | |
408 | |
409 /** | |
410 * \brief Shape event text with HarfBuzz. Full OpenType shaping. | |
411 * \param glyphs glyph clusters | |
412 * \param len number of clusters | |
413 */ | |
414 static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len) | |
415 { | |
416 int i, j; | |
417 int run = 0; | |
418 struct { | |
419 int offset; | |
420 int end; | |
421 hb_buffer_t *buf; | |
422 hb_font_t *font; | |
423 } runs[MAX_RUNS]; | |
424 | |
425 | |
426 for (i = 0; i < len && run < MAX_RUNS; i++, run++) { | |
427 // get length and level of the current run | |
428 int k = i; | |
429 int level = glyphs[i].shape_run_id; | |
430 int direction = shaper->emblevels[k] % 2; | |
431 while (i < (len - 1) && level == glyphs[i+1].shape_run_id) | |
432 i++; | |
433 runs[run].offset = k; | |
434 runs[run].end = i; | |
435 runs[run].buf = hb_buffer_create(); | |
436 runs[run].font = get_hb_font(shaper, glyphs + k); | |
437 set_run_features(shaper, glyphs + k); | |
438 hb_buffer_pre_allocate(runs[run].buf, i - k + 1); | |
439 hb_buffer_set_direction(runs[run].buf, direction ? HB_DIRECTION_RTL : | |
440 HB_DIRECTION_LTR); | |
441 hb_buffer_set_language(runs[run].buf, shaper->language); | |
442 hb_buffer_add_utf32(runs[run].buf, shaper->event_text + k, i - k + 1, | |
443 0, i - k + 1); | |
444 hb_shape(runs[run].font, runs[run].buf, shaper->features, | |
445 shaper->n_features); | |
446 } | |
447 | |
448 // Initialize: skip all glyphs, this is undone later as needed | |
449 for (i = 0; i < len; i++) | |
450 glyphs[i].skip = 1; | |
451 | |
452 // Update glyph indexes, positions and advances from the shaped runs | |
453 for (i = 0; i < run; i++) { | |
454 int num_glyphs = hb_buffer_get_length(runs[i].buf); | |
455 hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(runs[i].buf, NULL); | |
456 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(runs[i].buf, NULL); | |
457 | |
458 for (j = 0; j < num_glyphs; j++) { | |
459 int idx = glyph_info[j].cluster + runs[i].offset; | |
460 GlyphInfo *info = glyphs + idx; | |
461 GlyphInfo *root = info; | |
462 | |
463 // if we have more than one glyph per cluster, allocate a new one | |
464 // and attach to the root glyph | |
465 if (info->skip == 0) { | |
466 while (info->next) | |
467 info = info->next; | |
468 info->next = malloc(sizeof(GlyphInfo)); | |
469 memcpy(info->next, info, sizeof(GlyphInfo)); | |
470 info = info->next; | |
471 info->next = NULL; | |
472 } | |
473 | |
474 // set position and advance | |
475 info->skip = 0; | |
476 info->glyph_index = glyph_info[j].codepoint; | |
477 info->offset.x = pos[j].x_offset * info->scale_x; | |
478 info->offset.y = -pos[j].y_offset * info->scale_y; | |
479 info->advance.x = pos[j].x_advance * info->scale_x; | |
480 info->advance.y = -pos[j].y_advance * info->scale_y; | |
481 | |
482 // accumulate advance in the root glyph | |
483 root->cluster_advance.x += info->advance.x; | |
484 root->cluster_advance.y += info->advance.y; | |
485 } | |
486 } | |
487 | |
488 // Free runs and associated data | |
489 for (i = 0; i < run; i++) { | |
490 hb_buffer_destroy(runs[i].buf); | |
491 } | |
492 | |
493 } | |
494 #endif | |
495 | |
496 /** | |
497 * \brief Shape event text with FriBidi. Does mirroring and simple | |
498 * Arabic shaping. | |
499 * \param len number of clusters | |
500 */ | |
501 static void shape_fribidi(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len) | |
502 { | |
503 int i; | |
504 FriBidiJoiningType *joins = calloc(sizeof(*joins), len); | |
505 | |
506 // shape on codepoint level | |
507 fribidi_get_joining_types(shaper->event_text, len, joins); | |
508 fribidi_join_arabic(shaper->ctypes, len, shaper->emblevels, joins); | |
509 fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, | |
510 shaper->emblevels, len, joins, shaper->event_text); | |
511 | |
512 // update indexes | |
513 for (i = 0; i < len; i++) { | |
514 GlyphInfo *info = glyphs + i; | |
515 FT_Face face = info->font->faces[info->face_index]; | |
516 info->symbol = shaper->event_text[i]; | |
517 info->glyph_index = FT_Get_Char_Index(face, shaper->event_text[i]); | |
518 } | |
519 | |
520 free(joins); | |
521 } | |
522 | |
523 /** | |
524 * \brief Toggle kerning for HarfBuzz shaping. | |
525 * NOTE: currently only works with OpenType fonts, the TrueType fallback *always* | |
526 * kerns. It's a bug in HarfBuzz. | |
527 */ | |
528 void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern) | |
529 { | |
530 #ifdef CONFIG_HARFBUZZ | |
531 shaper->features[KERN].value = !!kern; | |
532 #endif | |
533 } | |
534 | |
535 /** | |
536 * \brief Find shape runs according to the event's selected fonts | |
537 */ | |
538 void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv, | |
539 GlyphInfo *glyphs, size_t len) | |
540 { | |
541 int i; | |
542 int shape_run = 0; | |
543 | |
544 for (i = 0; i < len; i++) { | |
545 GlyphInfo *last = glyphs + i - 1; | |
546 GlyphInfo *info = glyphs + i; | |
547 // skip drawings | |
548 if (info->symbol == 0xfffc) | |
549 continue; | |
550 // set size and get glyph index | |
551 ass_font_get_index(render_priv->fontconfig_priv, info->font, | |
552 info->symbol, &info->face_index, &info->glyph_index); | |
553 // shape runs share the same font face and size | |
554 if (i > 0 && (last->font != info->font || | |
555 last->font_size != info->font_size || | |
556 last->face_index != info->face_index)) | |
557 shape_run++; | |
558 info->shape_run_id = shape_run; | |
559 } | |
560 | |
561 } | |
562 | |
563 /** | |
564 * \brief Set base direction (paragraph direction) of the text. | |
565 * \param dir base direction | |
566 */ | |
567 void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir) | |
568 { | |
569 shaper->base_direction = dir; | |
570 } | |
571 | |
572 /** | |
573 * \brief Set language hint. Some languages have specific character variants, | |
574 * like Serbian Cyrillic. | |
575 * \param lang ISO 639-1 two-letter language code | |
576 */ | |
577 void ass_shaper_set_language(ASS_Shaper *shaper, const char *code) | |
578 { | |
579 #ifdef CONFIG_HARFBUZZ | |
580 shaper->language = hb_language_from_string(code, -1); | |
581 #endif | |
582 } | |
583 | |
584 /** | |
585 * Set shaping level. Essentially switches between FriBidi and HarfBuzz. | |
586 */ | |
587 void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level) | |
588 { | |
589 shaper->shaping_level = level; | |
590 } | |
591 | |
592 /** | |
593 * \brief Shape an event's text. Calculates directional runs and shapes them. | |
594 * \param text_info event's text | |
595 */ | |
596 void ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info) | |
597 { | |
598 int i, last_break; | |
599 FriBidiParType dir; | |
600 GlyphInfo *glyphs = text_info->glyphs; | |
601 | |
602 check_allocations(shaper, text_info->length); | |
603 | |
604 // Get bidi character types and embedding levels | |
605 last_break = 0; | |
606 for (i = 0; i < text_info->length; i++) { | |
607 shaper->event_text[i] = glyphs[i].symbol; | |
608 // embedding levels should be calculated paragraph by paragraph | |
609 if (glyphs[i].symbol == '\n' || i == text_info->length - 1) { | |
610 dir = shaper->base_direction; | |
611 fribidi_get_bidi_types(shaper->event_text + last_break, | |
612 i - last_break + 1, shaper->ctypes + last_break); | |
613 fribidi_get_par_embedding_levels(shaper->ctypes + last_break, | |
614 i - last_break + 1, &dir, shaper->emblevels + last_break); | |
615 last_break = i + 1; | |
616 } | |
617 } | |
618 | |
619 // add embedding levels to shape runs for final runs | |
620 for (i = 0; i < text_info->length; i++) { | |
621 glyphs[i].shape_run_id += shaper->emblevels[i]; | |
622 } | |
623 | |
624 #ifdef CONFIG_HARFBUZZ | |
625 switch (shaper->shaping_level) { | |
626 case ASS_SHAPING_SIMPLE: | |
627 shape_fribidi(shaper, glyphs, text_info->length); | |
628 break; | |
629 case ASS_SHAPING_COMPLEX: | |
630 shape_harfbuzz(shaper, glyphs, text_info->length); | |
631 break; | |
632 } | |
633 #else | |
634 shape_fribidi(shaper, glyphs, text_info->length); | |
635 #endif | |
636 | |
637 | |
638 // clean up | |
639 for (i = 0; i < text_info->length; i++) { | |
640 // Skip direction override control characters | |
641 // NOTE: Behdad said HarfBuzz is supposed to remove these, but this hasn't | |
642 // been implemented yet | |
643 if (glyphs[i].symbol <= 0x202F && glyphs[i].symbol >= 0x202a) { | |
644 glyphs[i].symbol = 0; | |
645 glyphs[i].skip++; | |
646 } | |
647 } | |
648 } | |
649 | |
650 /** | |
651 * \brief Create a new shaper instance and preallocate data structures | |
652 * \param prealloc preallocation size | |
653 */ | |
654 ASS_Shaper *ass_shaper_new(size_t prealloc) | |
655 { | |
656 ASS_Shaper *shaper = calloc(sizeof(*shaper), 1); | |
657 | |
658 shaper->base_direction = FRIBIDI_PAR_ON; | |
659 check_allocations(shaper, prealloc); | |
660 | |
661 #ifdef CONFIG_HARFBUZZ | |
662 init_features(shaper); | |
663 shaper->metrics_cache = ass_glyph_metrics_cache_create(); | |
664 #endif | |
665 | |
666 return shaper; | |
667 } | |
668 | |
669 | |
670 /** | |
671 * \brief clean up additional data temporarily needed for shaping and | |
672 * (e.g. additional glyphs allocated) | |
673 */ | |
674 void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info) | |
675 { | |
676 int i; | |
677 | |
678 for (i = 0; i < text_info->length; i++) { | |
679 GlyphInfo *info = text_info->glyphs + i; | |
680 info = info->next; | |
681 while (info) { | |
682 GlyphInfo *next = info->next; | |
683 free(info); | |
684 info = next; | |
685 } | |
686 } | |
687 } | |
688 | |
689 /** | |
690 * \brief Calculate reorder map to render glyphs in visual order | |
691 */ | |
692 FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info) | |
693 { | |
694 int i; | |
695 | |
696 // Initialize reorder map | |
697 for (i = 0; i < text_info->length; i++) | |
698 shaper->cmap[i] = i; | |
699 | |
700 // Create reorder map line-by-line | |
701 for (i = 0; i < text_info->n_lines; i++) { | |
702 LineInfo *line = text_info->lines + i; | |
703 int level; | |
704 FriBidiParType dir = FRIBIDI_PAR_ON; | |
705 | |
706 level = fribidi_reorder_line(0, | |
707 shaper->ctypes + line->offset, line->len, 0, dir, | |
708 shaper->emblevels + line->offset, NULL, | |
709 shaper->cmap + line->offset); | |
710 } | |
711 | |
712 return shaper->cmap; | |
713 } | |
714 | |
715 /** | |
716 * \brief Resolve a Windows font encoding number to a suitable | |
717 * base direction. 177 and 178 are Hebrew and Arabic respectively, and | |
718 * they map to RTL. 1 is autodetection and is mapped to just that. | |
719 * Everything else is mapped to LTR. | |
720 * \param enc Windows font encoding | |
721 */ | |
722 FriBidiParType resolve_base_direction(int enc) | |
723 { | |
724 switch (enc) { | |
725 case 1: | |
726 return FRIBIDI_PAR_ON; | |
727 case 177: | |
728 case 178: | |
729 return FRIBIDI_PAR_RTL; | |
730 default: | |
731 return FRIBIDI_PAR_LTR; | |
732 } | |
733 } |