summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libass/ass_cache_template.h3
-rw-r--r--libass/ass_font.c24
-rw-r--r--libass/ass_render.c65
-rw-r--r--libass/ass_render.h4
-rw-r--r--libass/ass_shaper.c83
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) {