diff options
-rw-r--r-- | libass/ass_cache.c | 65 | ||||
-rw-r--r-- | libass/ass_cache.h | 6 | ||||
-rw-r--r-- | libass/ass_cache_template.h | 49 | ||||
-rw-r--r-- | libass/ass_font.c | 41 | ||||
-rw-r--r-- | libass/ass_font.h | 4 | ||||
-rw-r--r-- | libass/ass_parse.c | 5 | ||||
-rw-r--r-- | libass/ass_render.c | 631 | ||||
-rw-r--r-- | libass/ass_render.h | 8 | ||||
-rw-r--r-- | libass/ass_shaper.c | 73 |
9 files changed, 448 insertions, 434 deletions
diff --git a/libass/ass_cache.c b/libass/ass_cache.c index 2e8d7d7..f33babc 100644 --- a/libass/ass_cache.c +++ b/libass/ass_cache.c @@ -79,10 +79,13 @@ static void font_destruct(void *key, void *value) ass_font_clear(value); } +size_t ass_font_construct(void *key, void *value, void *priv); + const CacheDesc font_cache_desc = { .hash_func = font_hash, .compare_func = font_compare, .key_move_func = font_key_move, + .construct_func = ass_font_construct, .destruct_func = font_destruct, .key_size = sizeof(ASS_FontDesc), .value_size = sizeof(ASS_Font) @@ -141,10 +144,13 @@ static void bitmap_destruct(void *key, void *value) } } +size_t ass_bitmap_construct(void *key, void *value, void *priv); + const CacheDesc bitmap_cache_desc = { .hash_func = bitmap_hash, .compare_func = bitmap_compare, .key_move_func = bitmap_key_move, + .construct_func = ass_bitmap_construct, .destruct_func = bitmap_destruct, .key_size = sizeof(BitmapHashKey), .value_size = sizeof(BitmapHashValue) @@ -207,10 +213,13 @@ static void composite_destruct(void *key, void *value) free(k->bitmaps); } +size_t ass_composite_construct(void *key, void *value, void *priv); + const CacheDesc composite_cache_desc = { .hash_func = composite_hash, .compare_func = composite_compare, .key_move_func = composite_key_move, + .construct_func = ass_composite_construct, .destruct_func = composite_destruct, .key_size = sizeof(CompositeHashKey), .value_size = sizeof(CompositeHashValue) @@ -224,7 +233,7 @@ static unsigned outline_hash(void *key, size_t key_size) switch (k->type) { case OUTLINE_GLYPH: return glyph_hash(&k->u, key_size); case OUTLINE_DRAWING: return drawing_hash(&k->u, key_size); - default: return 0; + default: return outline_common_hash(&k->u, key_size); } } @@ -236,7 +245,7 @@ static unsigned outline_compare(void *a, void *b, size_t key_size) switch (ak->type) { case OUTLINE_GLYPH: return glyph_compare(&ak->u, &bk->u, key_size); case OUTLINE_DRAWING: return drawing_compare(&ak->u, &bk->u, key_size); - default: return 0; + default: return outline_common_compare(&ak->u, &bk->u, key_size); } } @@ -268,10 +277,13 @@ static void outline_destruct(void *key, void *value) } } +size_t ass_outline_construct(void *key, void *value, void *priv); + const CacheDesc outline_cache_desc = { .hash_func = outline_hash, .compare_func = outline_compare, .key_move_func = outline_key_move, + .construct_func = ass_outline_construct, .destruct_func = outline_destruct, .key_size = sizeof(OutlineHashKey), .value_size = sizeof(OutlineHashValue) @@ -295,10 +307,13 @@ static void glyph_metrics_destruct(void *key, void *value) ass_cache_dec_ref(k->font); } +size_t ass_glyph_metrics_construct(void *key, void *value, void *priv); + const CacheDesc glyph_metrics_cache_desc = { .hash_func = glyph_metrics_hash, .compare_func = glyph_metrics_compare, .key_move_func = glyph_metrics_key_move, + .construct_func = ass_glyph_metrics_construct, .destruct_func = glyph_metrics_destruct, .key_size = sizeof(GlyphMetricsHashKey), .value_size = sizeof(GlyphMetricsHashValue) @@ -360,9 +375,8 @@ Cache *ass_cache_create(const CacheDesc *desc) return cache; } -bool ass_cache_get(Cache *cache, void *key, void *value_ptr) +void *ass_cache_get(Cache *cache, void *key, void *priv) { - char **value = (char **) value_ptr; const CacheDesc *desc = cache->desc; size_t key_offs = CACHE_ITEM_SIZE + align_cache(desc->value_size); unsigned bucket = desc->hash_func(key, desc->key_size) % cache->buckets; @@ -383,9 +397,8 @@ bool ass_cache_get(Cache *cache, void *key, void *value_ptr) } cache->hits++; desc->key_move_func(NULL, key, desc->key_size); - *value = (char *) item + CACHE_ITEM_SIZE; item->ref_count++; - return true; + return (char *) item + CACHE_ITEM_SIZE; } item = item->next; } @@ -394,18 +407,18 @@ bool ass_cache_get(Cache *cache, void *key, void *value_ptr) item = malloc(key_offs + desc->key_size); if (!item) { desc->key_move_func(NULL, key, desc->key_size); - *value = NULL; - return false; + return NULL; } - item->size = 0; item->cache = cache; item->desc = desc; - if (!desc->key_move_func((char *) item + key_offs, key, desc->key_size)) { + void *new_key = (char *) item + key_offs; + if (!desc->key_move_func(new_key, key, desc->key_size)) { free(item); - *value = NULL; - return false; + return NULL; } - *value = (char *) item + CACHE_ITEM_SIZE; + void *value = (char *) item + CACHE_ITEM_SIZE; + item->size = desc->construct_func(new_key, value, priv); + assert(item->size); CacheItem **bucketptr = &cache->map[bucket]; if (*bucketptr) @@ -414,10 +427,15 @@ bool ass_cache_get(Cache *cache, void *key, void *value_ptr) item->next = *bucketptr; *bucketptr = item; - item->queue_prev = NULL; + *cache->queue_last = item; + item->queue_prev = cache->queue_last; + cache->queue_last = &item->queue_next; item->queue_next = NULL; - item->ref_count = 1; - return false; + item->ref_count = 2; + + cache->cache_size += item->size; + cache->items++; + return value; } void *ass_cache_key(void *value) @@ -426,21 +444,6 @@ void *ass_cache_key(void *value) return (char *) value + align_cache(item->desc->value_size); } -void ass_cache_commit(void *value, size_t item_size) -{ - CacheItem *item = value_to_item(value); - assert(!item->size && item_size); - item->size = item_size; - Cache *cache = item->cache; - cache->cache_size += item_size; - cache->items++; - - *cache->queue_last = item; - item->queue_prev = cache->queue_last; - cache->queue_last = &item->queue_next; - item->ref_count++; -} - static inline void destroy_item(const CacheDesc *desc, CacheItem *item) { assert(item->desc == desc); diff --git a/libass/ass_cache.h b/libass/ass_cache.h index 6c07370..acfa4bb 100644 --- a/libass/ass_cache.h +++ b/libass/ass_cache.h @@ -62,6 +62,7 @@ typedef struct { typedef unsigned(*HashFunction)(void *key, size_t key_size); typedef unsigned(*HashCompare)(void *a, void *b, size_t key_size); typedef bool(*CacheKeyMove)(void *dst, void *src, size_t key_size); +typedef size_t(*CacheValueConstructor)(void *key, void *value, void *priv); typedef void(*CacheItemDestructor)(void *key, void *value); // cache hash keys @@ -74,6 +75,7 @@ typedef struct outline_hash_key { union { GlyphHashKey glyph; DrawingHashKey drawing; + OutlineCommonKey common; } u; } OutlineHashKey; @@ -111,15 +113,15 @@ typedef struct HashFunction hash_func; HashCompare compare_func; CacheKeyMove key_move_func; + CacheValueConstructor construct_func; CacheItemDestructor destruct_func; size_t key_size; size_t value_size; } CacheDesc; Cache *ass_cache_create(const CacheDesc *desc); -bool ass_cache_get(Cache *cache, void *key, void *value_ptr); +void *ass_cache_get(Cache *cache, void *key, void *priv); void *ass_cache_key(void *value); -void ass_cache_commit(void *value, size_t item_size); void ass_cache_inc_ref(void *value); void ass_cache_dec_ref(void *value); void ass_cache_cut(Cache *cache, size_t max_size); diff --git a/libass/ass_cache_template.h b/libass/ass_cache_template.h index 2cb2df4..b4a6864 100644 --- a/libass/ass_cache_template.h +++ b/libass/ass_cache_template.h @@ -77,42 +77,55 @@ END(OutlineBitmapHashKey) // describe a clip mask bitmap START(clip_bitmap, clip_bitmap_hash_key) + GENERIC(int, scale) STRING(text) END(ClipMaskHashKey) -// describes an outline glyph -START(glyph, glyph_hash_key) +START(glyph_metrics, glyph_metrics_hash_key) GENERIC(ASS_Font *, font) - GENERIC(double, size) // font size + GENERIC(double, size) GENERIC(int, face_index) GENERIC(int, glyph_index) - GENERIC(int, bold) - GENERIC(int, italic) +END(GlyphMetricsHashKey) + +// common outline data +START(outline_common, outline_common_hash_key) GENERIC(unsigned, scale_x) // 16.16 GENERIC(unsigned, scale_y) // 16.16 VECTOR(outline) // border width, 26.6 - GENERIC(unsigned, flags) // glyph decoration flags GENERIC(unsigned, border_style) - GENERIC(int, hspacing) // 16.16 -END(GlyphHashKey) + GENERIC(int, scale_fix) // 16.16 + GENERIC(int, advance) // 26.6 +END(OutlineCommonKey) + +// describes an outline glyph +START(glyph, glyph_hash_key) + GENERIC(unsigned, scale_x) // 16.16 + GENERIC(unsigned, scale_y) // 16.16 + VECTOR(outline) // border width, 26.6 + GENERIC(unsigned, border_style) + GENERIC(int, scale_fix) // 16.16 + GENERIC(int, advance) // 26.6 -START(glyph_metrics, glyph_metrics_hash_key) GENERIC(ASS_Font *, font) - GENERIC(double, size) + GENERIC(double, size) // font size GENERIC(int, face_index) GENERIC(int, glyph_index) - GENERIC(unsigned, scale_x) - GENERIC(unsigned, scale_y) -END(GlyphMetricsHashKey) + GENERIC(int, bold) + GENERIC(int, italic) + GENERIC(unsigned, flags) // glyph decoration flags +END(GlyphHashKey) // describes an outline drawing START(drawing, drawing_hash_key) - GENERIC(unsigned, scale_x) - GENERIC(unsigned, scale_y) - GENERIC(int, pbo) - VECTOR(outline) + GENERIC(unsigned, scale_x) // 16.16 + GENERIC(unsigned, scale_y) // 16.16 + VECTOR(outline) // border width, 26.6 GENERIC(unsigned, border_style) - GENERIC(int, hspacing) + GENERIC(int, scale_fix) // 16.16 + GENERIC(int, advance) // 26.6 + + GENERIC(int, pbo) GENERIC(int, scale) STRING(text) END(DrawingHashKey) diff --git a/libass/ass_font.c b/libass/ass_font.c index 6b369aa..2c488cc 100644 --- a/libass/ass_font.c +++ b/libass/ass_font.c @@ -224,26 +224,28 @@ static int add_face(ASS_FontSelector *fontsel, ASS_Font *font, uint32_t ch) /** * \brief Create a new ASS_Font according to "desc" argument */ -ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library, - FT_Library ftlibrary, ASS_FontSelector *fontsel, - ASS_FontDesc *desc) +ASS_Font *ass_font_new(ASS_Renderer *render_priv, ASS_FontDesc *desc) { - ASS_Font *font; - if (ass_cache_get(font_cache, desc, &font)) { - if (font->desc.family) - return font; - ass_cache_dec_ref(font); - return NULL; - } + ASS_Font *font = ass_cache_get(render_priv->cache.font_cache, desc, render_priv); if (!font) return NULL; + if (font->desc.family) + return font; + ass_cache_dec_ref(font); + return NULL; +} + +size_t ass_font_construct(void *key, void *value, void *priv) +{ + ASS_Renderer *render_priv = priv; + ASS_FontDesc *desc = key; + ASS_Font *font = value; - font->library = library; - font->ftlibrary = ftlibrary; + font->library = render_priv->library; + font->ftlibrary = render_priv->ftlibrary; font->shaper_priv = NULL; font->n_faces = 0; - ASS_FontDesc *new_desc = ass_cache_key(font); - font->desc.family = new_desc->family; + font->desc.family = desc->family; font->desc.bold = desc->bold; font->desc.italic = desc->italic; font->desc.vertical = desc->vertical; @@ -251,15 +253,10 @@ ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library, font->scale_x = font->scale_y = 1.; font->size = 0.; - int error = add_face(fontsel, font, 0); - if (error == -1) { + int error = add_face(render_priv->fontselect, font, 0); + if (error == -1) font->desc.family = NULL; - ass_cache_commit(font, 1); - ass_cache_dec_ref(font); - return NULL; - } - ass_cache_commit(font, 1); - return font; + return 1; } /** diff --git a/libass/ass_font.h b/libass/ass_font.h index dd0c11a..6aa76ef 100644 --- a/libass/ass_font.h +++ b/libass/ass_font.h @@ -59,9 +59,7 @@ struct ass_font { }; void charmap_magic(ASS_Library *library, FT_Face face); -ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library, - FT_Library ftlibrary, ASS_FontSelector *fontsel, - ASS_FontDesc *desc); +ASS_Font *ass_font_new(ASS_Renderer *render_priv, ASS_FontDesc *desc); void ass_font_set_transform(ASS_Font *font, double scale_x, double scale_y); void ass_face_set_size(FT_Face face, double size); void ass_font_set_size(ASS_Font *font, double size); diff --git a/libass/ass_parse.c b/libass/ass_parse.c index 9397971..a3a1bdb 100644 --- a/libass/ass_parse.c +++ b/libass/ass_parse.c @@ -128,10 +128,7 @@ void update_font(ASS_Renderer *render_priv) desc.italic = val; ass_cache_dec_ref(render_priv->state.font); - render_priv->state.font = - ass_font_new(render_priv->cache.font_cache, render_priv->library, - render_priv->ftlibrary, render_priv->fontselect, - &desc); + render_priv->state.font = ass_font_new(render_priv, &desc); if (render_priv->state.font) change_font_size(render_priv, render_priv->state.font_size); diff --git a/libass/ass_render.c b/libass/ass_render.c index e1cdb4f..5b790c9 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -459,48 +459,15 @@ static void blend_vector_clip(ASS_Renderer *render_priv, if (!render_priv->state.clip_drawing_text) return; - // Try to get mask from cache + // Get mask from cache BitmapHashKey key; - memset(&key, 0, sizeof(key)); key.type = BITMAP_CLIP; + key.u.clip.scale = render_priv->state.clip_drawing_scale; key.u.clip.text = render_priv->state.clip_drawing_text; - BitmapHashValue *val; - if (!ass_cache_get(render_priv->cache.bitmap_cache, &key, &val)) { - if (!val) - return; - val->bm = val->bm_o = NULL; - - // Not found in cache, parse and rasterize it - ASS_Drawing drawing; - drawing.text = render_priv->state.clip_drawing_text; - drawing.scale = render_priv->state.clip_drawing_scale; - drawing.pbo = 0; - drawing.scale_x = render_priv->font_scale_x * render_priv->font_scale; - drawing.scale_y = render_priv->font_scale; - if (!ass_drawing_parse(&drawing, render_priv->library, true)) { - ass_msg(render_priv->library, MSGL_WARN, - "Clip vector parsing failed. Skipping."); - ass_cache_commit(val, sizeof(BitmapHashKey) + sizeof(BitmapHashValue)); - ass_cache_dec_ref(val); - return; - } - - // We need to translate the clip according to screen borders - if (render_priv->settings.left_margin != 0 || - render_priv->settings.top_margin != 0) { - ASS_Vector trans = { - .x = int_to_d6(render_priv->settings.left_margin), - .y = int_to_d6(render_priv->settings.top_margin), - }; - outline_translate(&drawing.outline, trans.x, trans.y); - } - - val->bm = outline_to_bitmap(render_priv, &drawing.outline, NULL, 1); - ass_cache_commit(val, bitmap_size(val->bm) + - sizeof(BitmapHashKey) + sizeof(BitmapHashValue)); - outline_free(&drawing.outline); - } + BitmapHashValue *val = ass_cache_get(render_priv->cache.bitmap_cache, &key, render_priv); + if (!val) + return; Bitmap *clip_bm = val->bm; if (!clip_bm) { @@ -917,20 +884,17 @@ static void free_render_context(ASS_Renderer *render_priv) * Replace the outline of a glyph by a contour which makes up a simple * opaque rectangle. */ -static void draw_opaque_box(ASS_Renderer *render_priv, GlyphInfo *info, +static void draw_opaque_box(ASS_Renderer *render_priv, + double scale_x, double scale_y, int asc, int desc, ASS_Outline *ol, int adv, int sx, int sy) { - double scale_y = info->orig_scale_y; - double scale_x = info->orig_scale_x; - // to avoid gaps sx = FFMAX(64, sx); sy = FFMAX(64, sy); // Emulate the WTFish behavior of VSFilter, i.e. double-scale // the sizes of the opaque box. - adv += double_to_d6(info->hspacing * render_priv->font_scale * scale_x); adv *= scale_x; sx *= scale_x; sy *= scale_y; @@ -967,38 +931,40 @@ static void fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key, GlyphInfo *info) { + OutlineCommonKey *common = &outline_key->u.common; + common->scale_x = double_to_d16(info->scale_x); + common->scale_y = double_to_d16(info->scale_y); + common->outline.x = double_to_d6(info->border_x * priv->border_scale); + common->outline.y = double_to_d6(info->border_y * priv->border_scale); + common->border_style = info->border_style; + // following fields only matter for opaque box borders (see draw_opaque_box), + // so for normal borders, maximize cache utility by ignoring them + if (info->border_style == 3) { + common->scale_fix = double_to_d16(info->scale_fix); + common->advance = info->hspacing_scaled; + if (priv->settings.shaper != ASS_SHAPING_SIMPLE && !info->drawing_text) + common->advance += info->advance.x; + } else { + common->scale_fix = 0; + common->advance = 0; + } + if (info->drawing_text) { - DrawingHashKey *key = &outline_key->u.drawing; outline_key->type = OUTLINE_DRAWING; - key->scale_x = double_to_d16(info->scale_x); - key->scale_y = double_to_d16(info->scale_y); - key->outline.x = double_to_d6(info->border_x * priv->border_scale); - key->outline.y = double_to_d6(info->border_y * priv->border_scale); - key->border_style = info->border_style; - // hpacing only matters for opaque box borders (see draw_opaque_box), - // so for normal borders, maximize cache utility by ignoring it - key->hspacing = - info->border_style == 3 ? double_to_d16(info->hspacing) : 0; + DrawingHashKey *key = &outline_key->u.drawing; key->text = info->drawing_text; key->pbo = info->drawing_pbo; key->scale = info->drawing_scale; } else { - GlyphHashKey *key = &outline_key->u.glyph; outline_key->type = OUTLINE_GLYPH; + GlyphHashKey *key = &outline_key->u.glyph; key->font = info->font; key->size = info->font_size; 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); - key->scale_y = double_to_d16(info->scale_y); - key->outline.x = double_to_d6(info->border_x * priv->border_scale); - key->outline.y = double_to_d6(info->border_y * priv->border_scale); key->flags = info->flags; - key->border_style = info->border_style; - key->hspacing = - info->border_style == 3 ? double_to_d16(info->hspacing) : 0; } } @@ -1018,7 +984,6 @@ static void fill_composite_hash(CompositeHashKey *hk, CombinedBitmapInfo *info) * Tries to get both glyphs from cache. * If they can't be found, gets a glyph from font face, generates outline, * and add them to cache. - * The glyphs are returned in info->glyph and info->outline_glyph */ static void get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) @@ -1026,88 +991,9 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) memset(&info->hash_key, 0, sizeof(info->hash_key)); OutlineHashKey key; - OutlineHashValue *val; fill_glyph_hash(priv, &key, info); - if (!ass_cache_get(priv->cache.outline_cache, &key, &val)) { - if (!val) - return; - memset(val, 0, sizeof(*val)); - - if (info->drawing_text) { - ASS_Drawing drawing; - drawing.text = info->drawing_text; - drawing.scale = info->drawing_scale; - drawing.pbo = info->drawing_pbo; - drawing.scale_x = info->scale_x * priv->font_scale; - drawing.scale_y = info->scale_y * priv->font_scale; - if (!ass_drawing_parse(&drawing, priv->library, false)) { - ass_cache_commit(val, 1); - ass_cache_dec_ref(val); - return; - } - outline_move(&val->outline, &drawing.outline); - val->advance = drawing.advance; - val->asc = drawing.asc; - val->desc = drawing.desc; - } else { - ass_face_set_size(info->font->faces[info->face_index], - info->font_size); - ass_font_set_transform(info->font, info->scale_x, info->scale_y); - FT_Glyph glyph = - ass_font_get_glyph(info->font, - info->face_index, info->glyph_index, - priv->settings.hinting, info->flags); - if (glyph != NULL) { - FT_Outline *src = &((FT_OutlineGlyph) glyph)->outline; - if (!outline_convert(&val->outline, src)) { - ass_cache_commit(val, 1); - ass_cache_dec_ref(val); - return; - } - if (priv->settings.shaper == ASS_SHAPING_SIMPLE) - val->advance = d16_to_d6(glyph->advance.x); - FT_Done_Glyph(glyph); - ass_font_get_asc_desc(info->font, info->face_index, - &val->asc, &val->desc); - val->asc *= info->scale_y; - val->desc *= info->scale_y; - } - } - val->valid = true; - - outline_get_cbox(&val->outline, &val->bbox_scaled); - - if (info->border_style == 3) { - int advance; - if (priv->settings.shaper == ASS_SHAPING_SIMPLE || info->drawing_text) - advance = val->advance; - else - advance = info->advance.x; - - draw_opaque_box(priv, info, val->asc, val->desc, &val->border[0], advance, - double_to_d6(info->border_x * priv->border_scale), - double_to_d6(info->border_y * priv->border_scale)); - - } else if (val->outline.n_points && (info->border_x > 0 || info->border_y > 0) - && double_to_d6(info->scale_x) && double_to_d6(info->scale_y)) { - const int eps = 16; - int xbord = double_to_d6(info->border_x * priv->border_scale); - int ybord = double_to_d6(info->border_y * priv->border_scale); - if(xbord >= eps || ybord >= eps) { - outline_alloc(&val->border[0], 2 * val->outline.n_points, 2 * val->outline.n_segments); - outline_alloc(&val->border[1], 2 * val->outline.n_points, 2 * val->outline.n_segments); - if (!val->border[0].max_points || !val->border[1].max_points || - !outline_stroke(&val->border[0], &val->border[1], - &val->outline, xbord, ybord, eps)) { - ass_msg(priv->library, MSGL_WARN, "Cannot stoke outline"); - outline_free(&val->border[0]); - outline_free(&val->border[1]); - } - } - } - - ass_cache_commit(val, 1); - } else if (!val->valid) { + OutlineHashValue *val = ass_cache_get(priv->cache.outline_cache, &key, priv); + if (!val || !val->valid) { ass_cache_dec_ref(val); return; } @@ -1125,6 +1011,84 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info) info->desc = val->desc; } +size_t ass_outline_construct(void *key, void *value, void *priv) +{ + ASS_Renderer *render_priv = priv; + OutlineHashKey *outline_key = key; + OutlineHashValue *v = value; + memset(v, 0, sizeof(*v)); + + OutlineCommonKey *common = &outline_key->u.common; + double scale_x = d16_to_double(common->scale_x); + double scale_y = d16_to_double(common->scale_y); + + if (outline_key->type == OUTLINE_DRAWING) { + DrawingHashKey *k = &outline_key->u.drawing; + + ASS_Drawing drawing; + drawing.text = k->text; + drawing.scale = k->scale; + drawing.pbo = k->pbo; + drawing.scale_x = scale_x * render_priv->font_scale; + drawing.scale_y = scale_y * render_priv->font_scale; + if (!ass_drawing_parse(&drawing, render_priv->library, false)) + return 1; + outline_move(&v->outline, &drawing.outline); + v->advance = drawing.advance; + v->asc = drawing.asc; + v->desc = drawing.desc; + } else { + GlyphHashKey *k = &outline_key->u.glyph; + ass_face_set_size(k->font->faces[k->face_index], k->size); + ass_font_set_transform(k->font, scale_x, scale_y); + FT_Glyph glyph = + ass_font_get_glyph(k->font, k->face_index, k->glyph_index, + render_priv->settings.hinting, k->flags); + if (glyph != NULL) { + FT_Outline *src = &((FT_OutlineGlyph) glyph)->outline; + if (!outline_convert(&v->outline, src)) + return 1; + if (render_priv->settings.shaper == ASS_SHAPING_SIMPLE) + v->advance = d16_to_d6(glyph->advance.x); + FT_Done_Glyph(glyph); + ass_font_get_asc_desc(k->font, k->face_index, + &v->asc, &v->desc); + v->asc *= scale_y; + v->desc *= scale_y; + } + } + v->valid = true; + + outline_get_cbox(&v->outline, &v->bbox_scaled); + + if (common->border_style == 3) { + int advance = common->advance; + if (render_priv->settings.shaper == ASS_SHAPING_SIMPLE || + outline_key->type == OUTLINE_DRAWING) + advance += v->advance; + + double scale_fix = d16_to_double(common->scale_fix); + draw_opaque_box(priv, scale_x * scale_fix, scale_y * scale_fix, + v->asc, v->desc, &v->border[0], advance, + common->outline.x, common->outline.y); + + } else if (v->outline.n_points && common->scale_x && common->scale_y) { + const int eps = 16; + if (common->outline.x >= eps || common->outline.y >= eps) { + outline_alloc(&v->border[0], 2 * v->outline.n_points, 2 * v->outline.n_segments); + outline_alloc(&v->border[1], 2 * v->outline.n_points, 2 * v->outline.n_segments); + if (!v->border[0].max_points || !v->border[1].max_points || + !outline_stroke(&v->border[0], &v->border[1], + &v->outline, common->outline.x, common->outline.y, eps)) { + ass_msg(render_priv->library, MSGL_WARN, "Cannot stroke outline"); + outline_free(&v->border[0]); + outline_free(&v->border[1]); + } + } + } + return 1; +} + /** * \brief Calculate transform matrix for transform_3d() */ @@ -1217,61 +1181,86 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) if (!info->outline || info->symbol == '\n' || info->symbol == 0 || info->skip) return; - BitmapHashValue *val; - OutlineBitmapHashKey *key = &info->hash_key.u.outline; - if (ass_cache_get(render_priv->cache.bitmap_cache, &info->hash_key, &val)) { - info->image = val; - if (!val->valid) - info->symbol = 0; - return; - } - if (!val) { - info->symbol = 0; - return; - } - if (!info->outline) { - memset(val, 0, sizeof(*val)); - ass_cache_commit(val, sizeof(BitmapHashKey) + sizeof(BitmapHashValue)); - info->image = val; + BitmapHashValue *val = ass_cache_get(render_priv->cache.bitmap_cache, &info->hash_key, render_priv); + if (!val || !val->valid) info->symbol = 0; - return; + info->image = val; +} + +size_t ass_bitmap_construct(void *key, void *value, void *priv) +{ + ASS_Renderer *render_priv = priv; + BitmapHashKey *bitmap_key = key; + BitmapHashValue *v = value; + v->bm = v->bm_o = NULL; + v->valid = false; + + const size_t hdr = sizeof(BitmapHashKey) + sizeof(BitmapHashValue); + + if (bitmap_key->type == BITMAP_CLIP) { + ASS_Drawing drawing; + drawing.text = bitmap_key->u.clip.text; + drawing.scale = bitmap_key->u.clip.scale; + drawing.pbo = 0; + drawing.scale_x = render_priv->font_scale_x * render_priv->font_scale; + drawing.scale_y = render_priv->font_scale; + if (!ass_drawing_parse(&drawing, render_priv->library, true)) { + ass_msg(render_priv->library, MSGL_WARN, + "Clip vector parsing failed. Skipping."); + return hdr; + } + + // We need to translate the clip according to screen borders + if (render_priv->settings.left_margin != 0 || + render_priv->settings.top_margin != 0) { + ASS_Vector trans = { + .x = int_to_d6(render_priv->settings.left_margin), + .y = int_to_d6(render_priv->settings.top_margin), + }; + outline_translate(&drawing.outline, trans.x, trans.y); + } + + v->bm = outline_to_bitmap(render_priv, &drawing.outline, NULL, 1); + outline_free(&drawing.outline); + v->valid = !!v->bm; + + return bitmap_size(v->bm) + hdr; } + OutlineBitmapHashKey *k = &bitmap_key->u.outline; + if (!k->outline->valid) + return hdr; + const int n_outlines = 3; ASS_Outline outline[n_outlines]; - outline_copy(&outline[0], info->outline); - outline_copy(&outline[1], info->border[0]); - outline_copy(&outline[2], info->border[1]); + outline_copy(&outline[0], &k->outline->outline); + outline_copy(&outline[1], &k->outline->border[0]); + outline_copy(&outline[2], &k->outline->border[1]); // calculating rotation shift vector (from rotation origin to the glyph basepoint) - ASS_Vector shift = { key->shift_x, key->shift_y }; + ASS_Vector shift = { k->shift_x, k->shift_y }; double scale_x = render_priv->font_scale_x; - double fax_scaled = info->fax / info->scale_y * info->scale_x; - double fay_scaled = info->fay / info->scale_x * info->scale_y; + double fax_scaled = d16_to_double(k->fax); + double fay_scaled = d16_to_double(k->fay); // apply rotation // use blur_scale because, like blurs, VSFilter forgets to scale this transform_3d(shift, outline, n_outlines, - info->frx, info->fry, info->frz, fax_scaled, - fay_scaled, render_priv->blur_scale, info->asc); + d22_to_double(k->frx), d22_to_double(k->fry), d22_to_double(k->frz), + fax_scaled, fay_scaled, render_priv->blur_scale, k->outline->asc); // PAR correction scaling + subpixel shift for (int i = 0; i < n_outlines; i++) - outline_adjust(&outline[i], scale_x, key->advance.x, key->advance.y); + outline_adjust(&outline[i], scale_x, k->advance.x, k->advance.y); // render glyph - val->valid = outline_to_bitmap2(render_priv, - &outline[0], &outline[1], &outline[2], - &val->bm, &val->bm_o); - if (!val->valid) - info->symbol = 0; - - ass_cache_commit(val, bitmap_size(val->bm) + bitmap_size(val->bm_o) + - sizeof(BitmapHashKey) + sizeof(BitmapHashValue)); - info->image = val; - + v->valid = outline_to_bitmap2(render_priv, + &outline[0], &outline[1], &outline[2], + &v->bm, &v->bm_o); for (int i = 0; i < n_outlines; i++) outline_free(&outline[i]); + + return bitmap_size(v->bm) + bitmap_size(v->bm_o) + hdr; } /** @@ -1584,8 +1573,8 @@ fill_bitmap_hash(ASS_Renderer *priv, GlyphInfo *info, hash_key->frx = rot_key(info->frx); hash_key->fry = rot_key(info->fry); hash_key->frz = rot_key(info->frz); - hash_key->fax = double_to_d16(info->fax); - hash_key->fay = double_to_d16(info->fay); + hash_key->fax = double_to_d16(info->fax * info->scale_x / info->scale_y); + hash_key->fay = double_to_d16(info->fay * info->scale_y / info->scale_x); } /** @@ -1610,8 +1599,10 @@ fix_glyph_scaling(ASS_Renderer *priv, GlyphInfo *glyph) // to freetype. Normalize scale_y to 1.0. ft_size = glyph->scale_y * glyph->font_size; } - glyph->scale_x = glyph->scale_x * glyph->font_size / ft_size; - glyph->scale_y = glyph->scale_y * glyph->font_size / ft_size; + double mul = glyph->font_size / ft_size; + glyph->scale_fix = 1 / mul; + glyph->scale_x *= mul; + glyph->scale_y *= mul; glyph->font_size = ft_size; } @@ -1653,38 +1644,39 @@ static bool is_new_bm_run(GlyphInfo *info, GlyphInfo *last) ((last->flags ^ info->flags) & ~DECO_ROTATE); } -static void make_shadow_bitmap(CombinedBitmapInfo *info, ASS_Renderer *render_priv) +static void make_shadow_bitmap(ASS_Renderer *render_priv, + CompositeHashValue *val, const FilterDesc *filter) { - if (!(info->filter.flags & FILTER_NONZERO_SHADOW)) { - if (info->bm && info->bm_o && !(info->filter.flags & FILTER_BORDER_STYLE_3)) { - fix_outline(info->bm, info->bm_o); - } else if (info->bm_o && !(info->filter.flags & FILTER_NONZERO_BORDER)) { - ass_free_bitmap(info->bm_o); - info->bm_o = 0; + if (!(filter->flags & FILTER_NONZERO_SHADOW)) { + if (val->bm && val->bm_o && !(filter->flags & FILTER_BORDER_STYLE_3)) { + fix_outline(val->bm, val->bm_o); + } else if (val->bm_o && !(filter->flags & FILTER_NONZERO_BORDER)) { + ass_free_bitmap(val->bm_o); + val->bm_o = NULL; } return; } // Create shadow and fix outline as needed - if (info->bm && info->bm_o && !(info->filter.flags & FILTER_BORDER_STYLE_3)) { - info->bm_s = copy_bitmap(render_priv->engine, info->b |