diff options
author | Grigori Goronzy <greg@blackbox> | 2011-07-11 13:00:08 +0200 |
---|---|---|
committer | Grigori Goronzy <greg@blackbox> | 2011-07-11 13:05:52 +0200 |
commit | db6ccb3634db5ccbce1a2fdaa383085242d52c82 (patch) | |
tree | 33cf84b60230d8a6f2188af87581580cad3d8102 /libass/ass_shaper.c | |
parent | 778c5cebe439598bf3a6f0277ed7516928f0ff00 (diff) | |
download | libass-db6ccb3634db5ccbce1a2fdaa383085242d52c82.tar.bz2 libass-db6ccb3634db5ccbce1a2fdaa383085242d52c82.tar.xz |
HarfBuzz shaping support
Split up text into runs with the same direction, font face and font
size, shape these runs with HarfBuzz and reorder accordingly.
This noticeably improves Arabic shaping and should make shaping for many
other scripts work. HarfBuzz also does kerning for Latin text.
Diffstat (limited to 'libass/ass_shaper.c')
-rw-r--r-- | libass/ass_shaper.c | 83 |
1 files changed, 78 insertions, 5 deletions
diff --git a/libass/ass_shaper.c b/libass/ass_shaper.c index 6efc177..911732c 100644 --- a/libass/ass_shaper.c +++ b/libass/ass_shaper.c @@ -17,17 +17,20 @@ */ #include <fribidi/fribidi.h> +#include <hb-ft.h> #include "ass_render.h" #include "ass_shaper.h" +#define MAX_RUNS 30 + /** * \brief Print version information */ void ass_shaper_info(ASS_Library *lib) { ass_msg(lib, MSGL_V, "Complex text layout enabled, using FriBidi " - FRIBIDI_VERSION); + FRIBIDI_VERSION " HarfBuzz-ng %s", hb_version_string()); } /** @@ -39,11 +42,18 @@ void ass_shaper_info(ASS_Library *lib) void ass_shaper_shape(TextInfo *text_info, FriBidiCharType *ctypes, FriBidiLevel *emblevels) { - int i, last_break; + int i, j, last_break; FriBidiParType dir; FriBidiChar *event_text = calloc(sizeof(*event_text), text_info->length); FriBidiJoiningType *joins = calloc(sizeof(*joins), text_info->length); GlyphInfo *glyphs = text_info->glyphs; + // XXX: dynamically allocate + struct { + int offset; + int end; + hb_buffer_t *buf; + hb_font_t *font; + } runs[MAX_RUNS]; // Get bidi character types and embedding levels last_break = 0; @@ -61,26 +71,89 @@ void ass_shaper_shape(TextInfo *text_info, FriBidiCharType *ctypes, } } + // add embedding levels to shape runs for final runs + for (i = 0; i < text_info->length; i++) { + glyphs[i].shape_run_id += emblevels[i]; + } + #if 0 printf("levels "); for (i = 0; i < text_info->length; i++) { - printf("%d:%d ", ctypes[i], emblevels[i]); + printf("%d ", glyphs[i].shape_run_id); } printf("\n"); #endif +#if 0 // Use FriBidi's shaper for mirroring and simple Arabic shaping fribidi_get_joining_types(event_text, text_info->length, joins); fribidi_join_arabic(ctypes, text_info->length, emblevels, joins); fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, emblevels, text_info->length, joins, event_text); +#endif + + // Shape runs with HarfBuzz-ng + int run = 0; + for (i = 0; i < text_info->length && run < MAX_RUNS; i++, run++) { + // get length and level of the current run + int k = i; + int level = glyphs[i].shape_run_id; + while (i < (text_info->length - 1) && level == glyphs[i+1].shape_run_id) + i++; + //printf("run %d from %d to %d with level %d\n", run, k, i, level); + FT_Face run_font = glyphs[k].font->faces[glyphs[k].face_index]; + runs[run].offset = k; + runs[run].end = i; + runs[run].buf = hb_buffer_create(i - k + 1); + runs[run].font = hb_ft_font_create(run_font, NULL); + hb_buffer_set_direction(runs[run].buf, (level % 2) ? HB_DIRECTION_RTL : + HB_DIRECTION_LTR); + hb_buffer_add_utf32(runs[run].buf, event_text + k, i - k + 1, + 0, i - k + 1); + hb_shape(runs[run].font, runs[run].buf, NULL, 0); + } + //printf("shaped %d runs\n", run); - // XXX: insert HarfBuzz shaper here + // Initialize: skip all glyphs, this is undone later as needed + for (i = 0; i < text_info->length; i++) + glyphs[i].skip = 1; + + // Update glyph indexes, positions and advances from the shaped runs + for (i = 0; i < run; i++) { + int num_glyphs = hb_buffer_get_length(runs[i].buf); + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(runs[i].buf, NULL); + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(runs[i].buf, NULL); + //printf("run text len %d num_glyphs %d\n", runs[i].end - runs[i].offset + 1, + // num_glyphs); + // Update glyphs + for (j = 0; j < num_glyphs; j++) { + int idx = glyph_info[j].cluster + runs[i].offset; +#if 0 + printf("run %d cluster %d codepoint %d -> '%c'\n", i, idx, + glyph_info[j].codepoint, event_text[idx]); + printf("position %d %d advance %d %d\n", + pos[j].x_offset, pos[j].y_offset, + pos[j].x_advance, pos[j].y_advance); +#endif + glyphs[idx].skip = 0; + glyphs[idx].glyph_index = glyph_info[j].codepoint; + glyphs[idx].offset.x = pos[j].x_offset * glyphs[idx].scale_x; + glyphs[idx].offset.y = pos[j].y_offset * glyphs[idx].scale_y; + glyphs[idx].advance.x = pos[j].x_advance * glyphs[idx].scale_x; + glyphs[idx].advance.y = pos[j].y_advance * glyphs[idx].scale_y; + } + } + + // Free runs and associated data + for (i = 0; i < run; i++) { + hb_buffer_destroy(runs[i].buf); + hb_font_destroy(runs[i].font); + } // Update glyphs for (i = 0; i < text_info->length; i++) { glyphs[i].symbol = event_text[i]; - // Skip direction override characters + // Skip direction override control characters // NOTE: Behdad said HarfBuzz is supposed to remove these, but this hasn't // been implemented yet if (glyphs[i].symbol <= 0x202F && glyphs[i].symbol >= 0x202a) { |