diff options
-rw-r--r-- | libass/ass_cache_template.h | 3 | ||||
-rw-r--r-- | libass/ass_font.c | 24 | ||||
-rw-r--r-- | libass/ass_render.c | 65 | ||||
-rw-r--r-- | libass/ass_render.h | 4 | ||||
-rw-r--r-- | libass/ass_shaper.c | 83 |
5 files changed, 145 insertions, 34 deletions
diff --git a/libass/ass_cache_template.h b/libass/ass_cache_template.h index 2339e2b..d6f7d1f 100644 --- a/libass/ass_cache_template.h +++ b/libass/ass_cache_template.h @@ -87,7 +87,8 @@ END(ClipMaskHashKey) START(glyph, glyph_hash_key) GENERIC(ASS_Font *, font) GENERIC(double, size) // font size - GENERIC(uint32_t, ch) // character code + GENERIC(int, face_index) + GENERIC(int, glyph_index) GENERIC(int, bold) GENERIC(int, italic) GENERIC(unsigned, scale_x) // 16.16 diff --git a/libass/ass_font.c b/libass/ass_font.c index af1f350..14790b4 100644 --- a/libass/ass_font.c +++ b/libass/ass_font.c @@ -433,24 +433,32 @@ int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol, int i; FT_Face face = 0; - *face_index = 0; - *face_index = 0; + *glyph_index = 0; - if (symbol < 0x20) + if (symbol < 0x20) { + *face_index = 0; return 0; + } // Handle NBSP like a regular space when rendering the glyph if (symbol == 0xa0) symbol = ' '; - if (font->n_faces == 0) + if (font->n_faces == 0) { + *face_index = 0; return 0; + } - for (i = 0; i < font->n_faces; ++i) { + // try with the requested face + if (*face_index < font->n_faces) { + face = font->faces[i]; + index = FT_Get_Char_Index(face, symbol); + } + + // not found in requested face, try all others + for (i = 0; i < font->n_faces && index == 0; ++i) { face = font->faces[i]; index = FT_Get_Char_Index(face, symbol); - if (index) { + if (index) *face_index = i; - break; - } } #ifdef CONFIG_FONTCONFIG diff --git a/libass/ass_render.c b/libass/ass_render.c index 3aaf943..b634577 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -1050,7 +1050,8 @@ fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key, outline_key->type = OUTLINE_GLYPH; key->font = info->font; key->size = info->font_size; - key->ch = info->symbol; + key->face_index = info->face_index; + key->glyph_index = info->glyph_index; key->bold = info->bold; key->italic = info->italic; key->scale_x = double_to_d16(info->scale_x); @@ -1085,8 +1086,11 @@ get_outline_glyph(ASS_Renderer *render_priv, GlyphInfo *info) info->outline = val->outline; info->border = val->border; info->bbox = val->bbox_scaled; - info->advance.x = val->advance.x; - info->advance.y = val->advance.y; + // XXX: more elegant solution? + if (info->drawing) { + info->advance.x = info->drawing->advance.x; + info->advance.y = info->drawing->advance.y; + } info->asc = val->asc; info->desc = val->desc; } else { @@ -1106,22 +1110,22 @@ get_outline_glyph(ASS_Renderer *render_priv, GlyphInfo *info) } else { double size_scaled = ensure_font_size(render_priv, info->font_size * render_priv->font_scale); - int face_index = 0; - int index = 0; ass_font_set_size(info->font, size_scaled); ass_font_set_transform(info->font, info->scale_x, info->scale_y, NULL); - ass_font_get_index(render_priv->fontconfig_priv, info->font, - info->symbol, &face_index, &index); + // symbol might have been changed. re-get it. + //if (info->face_index < 0) + // ass_font_get_index(render_priv->fontconfig_priv, info->font, + // info->symbol, &info->face_index, &info->glyph_index); FT_Glyph glyph = ass_font_get_glyph(render_priv->fontconfig_priv, info->font, - info->symbol, face_index, index, + info->symbol, info->face_index, info->glyph_index, render_priv->settings.hinting, info->flags); if (glyph != NULL) { outline_copy(render_priv->ftlibrary, &((FT_OutlineGlyph)glyph)->outline, &info->outline); - info->advance.x = d16_to_d6(glyph->advance.x); - info->advance.y = d16_to_d6(glyph->advance.y); + //info->advance.x = d16_to_d6(glyph->advance.x); + //info->advance.y = d16_to_d6(glyph->advance.y); FT_Done_Glyph(glyph); ass_font_get_asc_desc(info->font, info->symbol, &info->asc, &info->desc); @@ -1795,6 +1799,35 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event, } + // Determine shape runs + int shape_run = 0; + for (i = 0; i < text_info->length; i++) { + GlyphInfo *last = glyphs + i - 1; + GlyphInfo *info = glyphs + i; + // skip drawings + if (info->symbol == 0xfffc) + continue; + // initialize face_index to continue with the same face, if possible + // XXX: can be problematic in some cases, for example if a font misses + // a single glyph, like space (U+0020) + if (i > 0) + info->face_index = last->face_index; + // set size and get glyph index + double size_scaled = ensure_font_size(render_priv, + info->font_size * render_priv->font_scale); + ass_font_set_size(info->font, size_scaled); + ass_font_get_index(render_priv->fontconfig_priv, info->font, + info->symbol, &info->face_index, &info->glyph_index); + // shape runs share the same font face and size + if (i > 0 && (last->font != info->font || + last->font_size != info->font_size || + last->face_index != info->face_index)) + shape_run++; + info->shape_run_id = shape_run; + //printf("glyph '%c' shape run id %d face %d\n", info->symbol, info->shape_run_id, + // info->face_index); + } + if (text_info->length == 0) { // no valid symbols in the event; this can be smth like {comment} free_render_context(render_priv); @@ -1831,14 +1864,6 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event, GlyphInfo *info = glyphs + i; #if 0 - // Add kerning to pen - if (kern && previous && info->symbol && !info->drawing) { - FT_Vector delta; - delta = ass_font_get_kerning(info->font, previous, info->symbol); - pen.x += delta.x * info->scale_x; - pen.y += delta.y * info->scale_y; - } - // Add additional space after italic to non-italic style changes if (i && glyphs[i - 1].italic && !info->italic) { int back = i - 1; @@ -1916,8 +1941,8 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event, lineno++; } if (info->skip) continue; - info->pos.x = pen.x; - info->pos.y = pen.y; + info->pos.x = info->offset.x + pen.x; + info->pos.y = info->offset.y + pen.y; pen.x += info->advance.x; pen.y += info->advance.y; } diff --git a/libass/ass_render.h b/libass/ass_render.h index 8b446e6..ea29c79 100644 --- a/libass/ass_render.h +++ b/libass/ass_render.h @@ -100,6 +100,8 @@ typedef struct { unsigned symbol; unsigned skip; // skip glyph when layouting text ASS_Font *font; + int face_index; + int glyph_index; double font_size; ASS_Drawing *drawing; FT_Outline *outline; @@ -109,6 +111,7 @@ typedef struct { Bitmap *bm_s; // shadow bitmap FT_BBox bbox; FT_Vector pos; + FT_Vector offset; char linebreak; // the first (leading) glyph of some line ? uint32_t c[4]; // colors FT_Vector advance; // 26.6 @@ -131,6 +134,7 @@ typedef struct { int flags; int bm_run_id; + int shape_run_id; BitmapHashKey hash_key; } GlyphInfo; 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) { |