From b63d8931845af256523d1142abea5caf2f65685d Mon Sep 17 00:00:00 2001 From: "Dr.Smile" Date: Fri, 23 Jan 2015 06:32:16 +0300 Subject: Clean up bitmap combining Now pre- and post-combining operations are clearly separated, many parameters in cache keys are no longer necessary due to that. Also an ambiguous (in case of multiple fonts) text string is replaced with a list of direct bitmap references in composite cache key. Fixes #153. Fixes #82. --- libass/ass_bitmap.c | 21 +- libass/ass_bitmap.h | 16 +- libass/ass_cache.c | 51 ++++- libass/ass_cache.h | 20 +- libass/ass_cache_template.h | 39 +--- libass/ass_render.c | 508 ++++++++++++++++++-------------------------- libass/ass_render.h | 55 ++--- 7 files changed, 292 insertions(+), 418 deletions(-) diff --git a/libass/ass_bitmap.c b/libass/ass_bitmap.c index 01bf013..6f77a17 100644 --- a/libass/ass_bitmap.c +++ b/libass/ass_bitmap.c @@ -707,30 +707,21 @@ void be_blur_c(uint8_t *buf, intptr_t w, } } -int outline_to_bitmap3(ASS_Renderer *render_priv, +int outline_to_bitmap2(ASS_Renderer *render_priv, ASS_Outline *outline, ASS_Outline *border, - Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, - int be, double blur_radius, FT_Vector shadow_offset, - int border_style, int border_visible) + Bitmap **bm_g, Bitmap **bm_o) { - blur_radius *= 2; - int bbord = be > 0 ? sqrt(2 * be) : 0; - int gbord = blur_radius > 0.0 ? FFMIN(blur_radius + 1, INT_MAX) : 0; - int bord = FFMAX(bbord, gbord); - if (bord == 0 && (shadow_offset.x || shadow_offset.y)) - bord = 1; + assert(bm_g && bm_o); - assert(bm_g && bm_o && bm_s); - - *bm_g = *bm_o = *bm_s = 0; + *bm_g = *bm_o = NULL; if (outline) - *bm_g = outline_to_bitmap(render_priv, outline, bord); + *bm_g = outline_to_bitmap(render_priv, outline, 1); if (!*bm_g) return 1; if (border) { - *bm_o = outline_to_bitmap(render_priv, border, bord); + *bm_o = outline_to_bitmap(render_priv, border, 1); if (!*bm_o) { return 1; } diff --git a/libass/ass_bitmap.h b/libass/ass_bitmap.h index 4a070ba..9a36a3e 100644 --- a/libass/ass_bitmap.h +++ b/libass/ass_bitmap.h @@ -56,18 +56,14 @@ void ass_synth_blur(ASS_SynthPriv *priv_blur, int opaque_box, int be, /** * \brief perform glyph rendering - * \param glyph original glyph - * \param outline_glyph "border" glyph, produced from original by FreeType's glyph stroker + * \param outline original glyph + * \param border "border" glyph, produced from outline by FreeType's glyph stroker * \param bm_g out: pointer to the bitmap of original glyph is returned here - * \param bm_o out: pointer to the bitmap of outline (border) glyph is returned here - * \param bm_g out: pointer to the bitmap of glyph shadow is returned here - * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps - * \param border_visible whether border is visible if border_style is 3 + * \param bm_o out: pointer to the bitmap of border glyph is returned here */ -int outline_to_bitmap3(ASS_Renderer *render_priv, ASS_Outline *outline, ASS_Outline *border, - Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, - int be, double blur_radius, FT_Vector shadow_offset, - int border_style, int border_visible); +int outline_to_bitmap2(ASS_Renderer *render_priv, + ASS_Outline *outline, ASS_Outline *border, + Bitmap **bm_g, Bitmap **bm_o); void ass_free_bitmap(Bitmap *bm); void ass_gauss_blur(unsigned char *buffer, unsigned *tmp2, diff --git a/libass/ass_cache.c b/libass/ass_cache.c index 062ddfa..2381e88 100644 --- a/libass/ass_cache.c +++ b/libass/ass_cache.c @@ -81,8 +81,6 @@ static void bitmap_destruct(void *key, void *value) ass_free_bitmap(v->bm); if (v->bm_o) ass_free_bitmap(v->bm_o); - if (v->bm_s) - ass_free_bitmap(v->bm_s); if (k->type == BITMAP_CLIP) free(k->u.clip.text); free(key); @@ -92,11 +90,12 @@ static void bitmap_destruct(void *key, void *value) static size_t bitmap_size(void *value, size_t value_size) { BitmapHashValue *val = value; + size_t size = sizeof(BitmapHashKey) + sizeof(BitmapHashValue); + if (val->bm) + size += sizeof(Bitmap) + val->bm->stride * val->bm->h; if (val->bm_o) - return val->bm_o->w * val->bm_o->h * 3; - else if (val->bm) - return val->bm->w * val->bm->h * 3; - return 0; + size += sizeof(Bitmap) + val->bm_o->stride * val->bm_o->h; + return size; } static unsigned bitmap_hash(void *key, size_t key_size) @@ -132,7 +131,7 @@ static void composite_destruct(void *key, void *value) ass_free_bitmap(v->bm_o); if (v->bm_s) ass_free_bitmap(v->bm_s); - free(k->str.data); + free(k->bitmaps); free(key); free(value); } @@ -140,11 +139,41 @@ static void composite_destruct(void *key, void *value) static size_t composite_size(void *value, size_t value_size) { CompositeHashValue *val = value; + size_t size = sizeof(CompositeHashKey) + sizeof(CompositeHashValue); + if (val->bm) + size += sizeof(Bitmap) + val->bm->stride * val->bm->h; if (val->bm_o) - return val->bm_o->w * val->bm_o->h * 3; - else if (val->bm) - return val->bm->w * val->bm->h * 3; - return 0; + size += sizeof(Bitmap) + val->bm_o->stride * val->bm_o->h; + if (val->bm_s) + size += sizeof(Bitmap) + val->bm_s->stride * val->bm_s->h; + return size; +} + +static unsigned composite_hash(void *key, size_t key_size) +{ + CompositeHashKey *k = key; + unsigned hval = filter_hash(&k->filter, key_size); + for (size_t i = 0; i < k->bitmap_count; ++i) { + hval = fnv_32a_buf(&k->bitmaps[i].image, sizeof(k->bitmaps[i].image), hval); + hval = fnv_32a_buf(&k->bitmaps[i].x, sizeof(k->bitmaps[i].x), hval); + hval = fnv_32a_buf(&k->bitmaps[i].y, sizeof(k->bitmaps[i].y), hval); + } + return hval; +} + +static unsigned composite_compare(void *a, void *b, size_t key_size) +{ + CompositeHashKey *ak = a; + CompositeHashKey *bk = b; + if (ak->bitmap_count != bk->bitmap_count) + return 0; + for (size_t i = 0; i < ak->bitmap_count; ++i) { + if (ak->bitmaps[i].image != bk->bitmaps[i].image || + ak->bitmaps[i].x != bk->bitmaps[i].x || + ak->bitmaps[i].y != bk->bitmaps[i].y) + return 0; + } + return filter_compare(&ak->filter, &bk->filter, key_size); } // outline cache diff --git a/libass/ass_cache.h b/libass/ass_cache.h index 0600142..af814ac 100644 --- a/libass/ass_cache.h +++ b/libass/ass_cache.h @@ -31,14 +31,12 @@ typedef struct cache Cache; typedef struct { Bitmap *bm; // the actual bitmaps Bitmap *bm_o; - Bitmap *bm_s; } BitmapHashValue; typedef struct { Bitmap *bm; Bitmap *bm_o; Bitmap *bm_s; - FT_Vector pos; } CompositeHashValue; typedef struct { @@ -87,6 +85,24 @@ typedef struct bitmap_hash_key { } u; } BitmapHashKey; +typedef struct { + BitmapHashValue *image; + int x, y; +} BitmapRef; + +enum { + FILTER_BORDER_STYLE_3 = 1, + FILTER_NONZERO_BORDER = 2, + FILTER_NONZERO_SHADOW = 4, + FILTER_DRAW_SHADOW = 8, // VSFilter compatibility +}; + +typedef struct { + FilterDesc filter; + size_t bitmap_count; + BitmapRef *bitmaps; +} CompositeHashKey; + Cache *ass_cache_create(HashFunction hash_func, HashCompare compare_func, CacheItemDestructor destruct_func, ItemSize size_func, size_t key_size, size_t value_size); diff --git a/libass/ass_cache_template.h b/libass/ass_cache_template.h index 1458284..dbf724c 100644 --- a/libass/ass_cache_template.h +++ b/libass/ass_cache_template.h @@ -70,8 +70,6 @@ // describes an outline bitmap START(outline_bitmap, outline_bitmap_hash_key) GENERIC(OutlineHashValue *, outline) - GENERIC(char, be) // blur edges - GENERIC(double, blur) // gaussian blur GENERIC(int, frx) // signed 16.16 GENERIC(int, fry) // signed 16.16 GENERIC(int, frz) // signed 16.16 @@ -83,7 +81,6 @@ START(outline_bitmap, outline_bitmap_hash_key) GENERIC(int, shift_x) GENERIC(int, shift_y) FTVECTOR(advance) // subpixel shift vector - FTVECTOR(shadow_offset) // shadow subpixel shift END(OutlineBitmapHashKey) // describe a clip mask bitmap @@ -129,39 +126,13 @@ START(drawing, drawing_hash_key) STRING(text) END(DrawingHashKey) -// Cache for composited bitmaps -START(composite, composite_hash_key) - GENERIC(unsigned, w) - GENERIC(unsigned, h) - GENERIC(unsigned, o_w) - GENERIC(unsigned, o_h) - GENERIC(int, is_drawing) - GENERIC(unsigned, chars) +// describes post-combining effects +START(filter, filter_desc) + GENERIC(int, flags) GENERIC(int, be) GENERIC(double, blur) - GENERIC(int, border_style) - GENERIC(int, has_border) - GENERIC(double, border_x) - GENERIC(double, border_y) - GENERIC(double, shadow_x) - GENERIC(double, shadow_y) - GENERIC(double, frx) - GENERIC(double, fry) - GENERIC(double, frz) - GENERIC(double, fax) - GENERIC(double, fay) - GENERIC(double, scale_x) - GENERIC(double, scale_y) - GENERIC(double, hspacing) - GENERIC(unsigned, italic) - GENERIC(unsigned, bold) - GENERIC(int, flags) - GENERIC(unsigned, has_outline) - GENERIC(int, shift_x) - GENERIC(int, shift_y) - FTVECTOR(advance) - BINSTRING(str) -END(CompositeHashKey) + FTVECTOR(shadow) +END(FilterDesc) #undef START #undef GENERIC diff --git a/libass/ass_render.c b/libass/ass_render.c index 4371aa3..105aa45 100644 --- a/libass/ass_render.c +++ b/libass/ass_render.c @@ -30,7 +30,7 @@ #define MAX_GLYPHS_INITIAL 1024 #define MAX_LINES_INITIAL 64 #define MAX_BITMAPS_INITIAL 16 -#define MAX_STR_LENGTH_INITIAL 64 +#define MAX_SUB_BITMAPS_INITIAL 64 #define SUBPIXEL_MASK 63 #define SUBPIXEL_ACCURACY 7 @@ -665,77 +665,60 @@ static inline int is_skip_symbol(uint32_t x) * \brief Convert TextInfo struct to ASS_Image list * Splits glyphs in halves when needed (for \kf karaoke). */ -static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x, int dst_y) +static ASS_Image *render_text(ASS_Renderer *render_priv) { - int pen_x, pen_y; - int i; - Bitmap *bm; ASS_Image *head; ASS_Image **tail = &head; TextInfo *text_info = &render_priv->text_info; - for (i = 0; i < text_info->n_bitmaps; ++i) { + for (int i = 0; i < text_info->n_bitmaps; ++i) { CombinedBitmapInfo *info = &text_info->combined_bitmaps[i]; - if (!info->bm_s || (info->shadow_x == 0 && info->shadow_y == 0)) - continue; - if (render_priv->state.border_style == 4) + if (!info->bm_s || render_priv->state.border_style == 4) continue; - pen_x = dst_x + info->pos.x; - pen_y = dst_y + info->pos.y; - bm = info->bm_s; - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, - 1000000, tail, IMAGE_TYPE_SHADOW); + render_glyph(render_priv, info->bm_s, info->x, info->y, info->c[3], 0, + 1000000, tail, IMAGE_TYPE_SHADOW); } - for (i = 0; i < text_info->n_bitmaps; ++i) { + for (int i = 0; i < text_info->n_bitmaps; ++i) { CombinedBitmapInfo *info = &text_info->combined_bitmaps[i]; if (!info->bm_o) continue; - pen_x = dst_x + info->pos.x; - pen_y = dst_y + info->pos.y; - bm = info->bm_o; - if ((info->effect_type == EF_KARAOKE_KO) && (info->effect_timing <= info->first_pos_x)) { // do nothing } else { tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], - 0, 1000000, tail, IMAGE_TYPE_OUTLINE); + render_glyph(render_priv, info->bm_o, info->x, info->y, info->c[2], + 0, 1000000, tail, IMAGE_TYPE_OUTLINE); } } - for (i = 0; i < text_info->n_bitmaps; ++i) { + for (int i = 0; i < text_info->n_bitmaps; ++i) { CombinedBitmapInfo *info = &text_info->combined_bitmaps[i]; if (!info->bm) continue; - pen_x = dst_x + info->pos.x; - pen_y = dst_y + info->pos.y; - bm = info->bm; - if ((info->effect_type == EF_KARAOKE) || (info->effect_type == EF_KARAOKE_KO)) { if (info->effect_timing > info->first_pos_x) tail = - render_glyph(render_priv, bm, pen_x, pen_y, - info->c[0], 0, 1000000, tail, IMAGE_TYPE_CHARACTER); + render_glyph(render_priv, info->bm, info->x, info->y, + info->c[0], 0, 1000000, tail, IMAGE_TYPE_CHARACTER); else tail = - render_glyph(render_priv, bm, pen_x, pen_y, - info->c[1], 0, 1000000, tail, IMAGE_TYPE_CHARACTER); + render_glyph(render_priv, info->bm, info->x, info->y, + info->c[1], 0, 1000000, tail, IMAGE_TYPE_CHARACTER); } else if (info->effect_type == EF_KARAOKE_KF) { tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - info->c[1], info->effect_timing, tail, IMAGE_TYPE_CHARACTER); + render_glyph(render_priv, info->bm, info->x, info->y, info->c[0], + info->c[1], info->effect_timing, tail, IMAGE_TYPE_CHARACTER); } else tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - 0, 1000000, tail, IMAGE_TYPE_CHARACTER); + render_glyph(render_priv, info->bm, info->x, info->y, info->c[0], + 0, 1000000, tail, IMAGE_TYPE_CHARACTER); } *tail = 0; @@ -928,7 +911,6 @@ init_render_context(ASS_Renderer *render_priv, ASS_Event *event) render_priv->state.effect_type = EF_NONE; render_priv->state.effect_timing = 0; render_priv->state.effect_skip_timing = 0; - render_priv->state.bm_run_id = 0; ass_drawing_free(render_priv->state.drawing); render_priv->state.drawing = ass_drawing_new(render_priv->library, render_priv->ftlibrary); @@ -1151,37 +1133,9 @@ fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key, */ static void fill_composite_hash(CompositeHashKey *hk, CombinedBitmapInfo *info) { - hk->w = info->w; - hk->h = info->h; - hk->o_w = info->o_w; - hk->o_h = info->o_h; - hk->be = info->be; - hk->blur = info->blur; - hk->border_style = info->border_style; - hk->has_outline = info->has_outline; - hk->is_drawing = info->is_drawing; - hk->str.size = info->str_length; - hk->str.data = info->str; - hk->chars = info->chars; - hk->shadow_x = info->shadow_x; - hk->shadow_y = info->shadow_y; - hk->flags = info->flags; - hk->bold = info->bold; - hk->italic = info->italic; - hk->hspacing = info->hspacing; - hk->scale_x = info->scale_x; - hk->scale_y = info->scale_y; - hk->has_border = info->has_border; - hk->border_x = info->border_x; - hk->border_y = info->border_y; - hk->frx = info->frx; - hk->fry = info->fry; - hk->frz = info->frz; - hk->fax = info->fax; - hk->fay = info->fay; - hk->shift_x = info->shift_x; - hk->shift_y = info->shift_y; - hk->advance = info->advance; + hk->filter = info->filter; + hk->bitmap_count = info->bitmap_count; + hk->bitmaps = info->bitmaps; } /** @@ -1386,7 +1340,7 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) double fax_scaled, fay_scaled; double scale_x = render_priv->font_scale_x; - hash_val.bm = hash_val.bm_o = hash_val.bm_s = 0; + hash_val.bm = hash_val.bm_o = NULL; ASS_Outline *outline = outline_copy(info->outline); ASS_Outline *border = outline_copy(info->border); @@ -1420,14 +1374,8 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) } // render glyph - error = outline_to_bitmap3(render_priv, - outline, border, - &hash_val.bm, &hash_val.bm_o, - &hash_val.bm_s, info->be, - info->blur * render_priv->blur_scale, - key->shadow_offset, - info->border_style, - info->border_x || info->border_y); + error = outline_to_bitmap2(render_priv, outline, border, + &hash_val.bm, &hash_val.bm_o); if (error) info->symbol = 0; @@ -1440,8 +1388,7 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) free(border); } - info->bm = val->bm; - info->bm_o = val->bm_o; + info->image = val; } /** @@ -1767,12 +1714,6 @@ fill_bitmap_hash(ASS_Renderer *priv, GlyphInfo *info, 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->be = info->be; - hash_key->blur = info->blur; - hash_key->shadow_offset.x = - double_to_d6(info->shadow_x * priv->border_scale) & SUBPIXEL_MASK; - hash_key->shadow_offset.y = - double_to_d6(info->shadow_y * priv->border_scale) & SUBPIXEL_MASK; } /** @@ -1810,11 +1751,9 @@ fix_glyph_scaling(ASS_Renderer *priv, GlyphInfo *glyph) */ static int is_new_bm_run(GlyphInfo *info, GlyphInfo *last) { - if (!last || info->effect || info->drawing || last->drawing) { - return 1; - } // FIXME: Don't break on glyph substitutions - if (strcmp(last->font->desc.family, info->font->desc.family) || + return !last || info->effect || info->drawing || last->drawing || + strcmp(last->font->desc.family, info->font->desc.family) || last->font->desc.vertical != info->font->desc.vertical || last->face_index != info->face_index || last->font_size != info->font_size || @@ -1839,25 +1778,22 @@ static int is_new_bm_run(GlyphInfo *info, GlyphInfo *last) last->hspacing != info->hspacing || last->italic != info->italic || last->bold != info->bold || - last->flags != info->flags){ - return 1; - } - return 0; + last->flags != info->flags; } static void make_shadow_bitmap(CombinedBitmapInfo *info, ASS_Renderer *render_priv) { - // VSFilter compatibility: invisible fill and no border? - // In this case no shadow is supposed to be rendered. - if (!info->has_border && (info->c[0] & 0xFF) == 0xFF) { + if (!(info->filter.flags & FILTER_NONZERO_SHADOW)) { + if (info->bm_o && !(info->filter.flags & FILTER_BORDER_STYLE_3)) + fix_outline(info->bm, info->bm_o); return; } // Create shadow and fix outline as needed - if (info->bm_o && info->border_style != 3) { + if (info->bm_o && !(info->filter.flags & FILTER_BORDER_STYLE_3)) { info->bm_s = copy_bitmap(info->bm_o); fix_outline(info->bm, info->bm_o); - } else if (info->bm_o && (info->border_x || info->border_y)) { + } else if (info->bm_o && (info->filter.flags & FILTER_NONZERO_BORDER)) { info->bm_s = copy_bitmap(info->bm_o); } else if (info->bm_o) { info->bm_s = info->bm_o; @@ -1870,11 +1806,9 @@ static void make_shadow_bitmap(CombinedBitmapInfo *info, ASS_Renderer *render_pr // Works right even for negative offsets // '>>' rounds toward negative infinity, '&' returns correct remainder - int offset_x = double_to_d6(info->shadow_x * render_priv->border_scale); - int offset_y = double_to_d6(info->shadow_y * render_priv->border_scale); - info->bm_s->left += offset_x >> 6; - info->bm_s->top += offset_y >> 6; - shift_bitmap(info->bm_s, offset_x & SUBPIXEL_MASK, offset_y & SUBPIXEL_MASK); + info->bm_s->left += info->filter.shadow.x >> 6; + info->bm_s->top += info->filter.shadow.y >> 6; + shift_bitmap(info->bm_s, info->filter.shadow.x & SUBPIXEL_MASK, info->filter.shadow.y & SUBPIXEL_MASK); } // Parse event text. @@ -2190,11 +2124,24 @@ static void calculate_rotation_params(ASS_Renderer *render_priv, DBBox *bbox, } +static inline void rectangle_reset(Rectangle *rect) +{ + rect->x_min = rect->y_min = INT_MAX; + rect->x_max = rect->y_max = INT_MIN; +} + +static inline void rectangle_combine(Rectangle *rect, const Bitmap *bm, int x, int y) +{ + rect->x_min = FFMIN(rect->x_min, x + bm->left); + rect->y_min = FFMIN(rect->y_min, y + bm->top); + rect->x_max = FFMAX(rect->x_max, x + bm->left + bm->w); + rect->y_max = FFMAX(rect->y_max, y + bm->top + bm->h); +} + // Convert glyphs to bitmaps, combine them, apply blur, generate shadows. static void render_and_combine_glyphs(ASS_Renderer *render_priv, double device_x, double device_y) { - int i; TextInfo *text_info = &render_priv->text_info; int left = render_priv->settings.left_margin; device_x = (device_x - left) * render_priv->font_scale_x + left; @@ -2203,254 +2150,201 @@ static void render_and_combine_glyphs(ASS_Renderer *render_priv, CombinedBitmapInfo *combined_info = text_info->combined_bitmaps; CombinedBitmapInfo *current_info = NULL; GlyphInfo *last_info = NULL; - for (i = 0; i < text_info->length; ++i) { + for (int i = 0; i < text_info->length; ++i) { GlyphInfo *info = text_info->glyphs + i; if (info->linebreak) linebreak = 1; if (info->skip) continue; - while (info) { + for (; info; info = info->next) { OutlineBitmapHashKey *key = &info->hash_key.u.outline; - info->pos.x *= render_priv->font_scale_x; - key->advance.x = - double_to_d6(device_x - (int) device_x + - d6_to_double(info->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; - key->advance.y = - double_to_d6(device_y - (int) device_y + - d6_to_double(info->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; - get_bitmap_glyph(render_priv, info); - - int bm_x = info->pos.x >> 6, - bm_y = info->pos.y >> 6, - bm_o_x = bm_x, bm_o_y = bm_y, min_bm_x = bm_x, min_bm_y = bm_y; - if(info->bm){ - bm_x += info->bm->left; - bm_y += info->bm->top; - min_bm_x = bm_x; - min_bm_y = bm_y; - } - - if(info->bm_o){ - bm_o_x += info->bm_o->left; - bm_o_y += info->bm_o->top; - min_bm_x = FFMIN(min_bm_x, bm_o_x); - min_bm_y = FFMIN(min_bm_y, bm_o_y); - } + info->pos.x = double_to_d6(device_x + d6_to_double(info->pos.x) * render_priv->font_scale_x); + info->pos.y = double_to_d6(device_y) + info->pos.y; + key->advance.x = info->pos.x & (SUBPIXEL_MASK & ~SUBPIXEL_ACCURACY); + key->advance.y = info->pos.y & (SUBPIXEL_MASK & ~SUBPIXEL_ACCURACY); + int x = info->pos.x >> 6, y = info->pos.y >> 6; + get_bitmap_glyph(render_priv, info); - if(linebreak || is_new_bm_run(info, last_info)){ + if(linebreak || is_new_bm_run(info, last_info)) { linebreak = 0; - ++nb_bitmaps; + last_info = NULL; if (nb_bitmaps >= text_info->max_bitmaps) { - // Raise maximum number of bitmaps - text_info->max_bitmaps *= 2; - text_info->combined_bitmaps = combined_info = - realloc(combined_info, - sizeof(CombinedBitmapInfo) * text_info->max_bitmaps); + size_t new_size = 2 * text_info->max_bitmaps; + if (!ASS_REALLOC_ARRAY(text_info->combined_bitmaps, new_size)) + continue; + text_info->max_bitmaps = new_size; + combined_info = text_info->combined_bitmaps; } - - current_info = &combined_info[nb_bitmaps - 1]; - - current_info->pos.x = min_bm_x; - current_info->pos.y = min_bm_y; - - current_info->first_pos_x = info->bbox.xMax >> 6; + current_info = &combined_info[nb_bitmaps]; memcpy(¤t_info->c, &info->c, sizeof(info->c)); current_info->effect_type = info->effect_type; current_info->effect_timing = info->effect_timing; - current_info->be = info->be; - current_info->blur = info->blur; - current_info->shadow_x = info->shadow_x; - current_info->shadow_y = info->shadow_y; - current_info->frx = info->frx; - current_info->fry = info->fry; - current_info->frz = info->frz; - current_info->fax = info->fax; - current_info->fay = info->fay; - current_info->scale_x = info->scale_x; - current_info->scale_y = info->scale_y; - current_info->border_style = info->border_style; - current_info->border_x = info->border_x; - current_info->border_y = info->border_y; - current_info->hspacing = info->hspacing; - current_info->italic = info->italic; - current_info->bold = info->bold; - current_info->flags = info->flags; - - current_info->shift_x = key->shift_x; - current_info->shift_y = key->shift_y; - current_info->advance = key->advance; - - current_info->has_border = !!info->border; - - current_info->has_outline = 0; - current_info->cached = 0; - current_info->is_drawing = 0; + current_info->first_pos_x = info->bbox.xMax >> 6; + current_info->filter.flags = 0; + if (info->border_style == 3) + current_info->filter.flags |= FILTER_BORDER_STYLE_3; + if (info->border_x || info->border_y) + current_info->filter.flags |= FILTER_NONZERO_BORDER; + if (info->shadow_x || info->shadow_y) + current_info->filter.flags |= FILTER_NONZERO_SHADOW; + // VSFilter compatibility: invisible fill and no border? + // In this case no shadow is supposed to be rendered. + if (info->border || (info->c[0] & 0xFF) != 0xFF) + current_info->filter.flags |= FILTER_DRAW_SHADOW; + + current_info->filter.be = info->be; + current_info->filter.blur = 2 * info->blur * render_priv->blur_scale; + current_info->filter.shadow.x = double_to_d6(info->shadow_x * render_priv->border_scale); + current_info->filter.shadow.y = double_to_d6(info->shadow_y * render_priv->border_scale); + + current_info->x = current_info->y = INT_MAX; + rectangle_reset(¤t_info->rect); + rectangle_reset(¤t_info->rect_o); current_info->bm = current_info->bm_o = current_info->bm_s = NULL; + current_info->n_bm = current_info->n_bm_o = 0; - current_info->max_str_length = - MAX_STR_LENGTH_INITIAL * sizeof(info->glyph_index); - current_info->str_length = 0; - current_info->str = malloc(current_info->max_str_length); - current_info->chars = 0; + current_info->bitmap_count = current_info->max_bitmap_count = 0; + current_info->bitmaps = malloc(MAX_SUB_BITMAPS_INITIAL * sizeof(BitmapRef)); + if (!current_info->bitmaps) + continue; + current_info->max_bitmap_count = MAX_SUB_BITMAPS_INITIAL; - current_info->w = current_info->h = current_info->o_w = current_info->o_h = 0; - - } - - if(info->drawing){ - free(current_info->str); - current_info->str = strdup(info->drawing->text); - current_info->str_length = strlen(info->drawing->text); - current_info->is_drawing = 1; - ass_drawing_free(info->drawing); - }else{ - int length = current_info->str_length; - current_info->chars++; - current_info->str_length += sizeof(info->glyph_index); - if(current_info->str_length > current_info->max_str_length){ - current_info->max_str_length *= 2; - current_info->str = realloc(current_info->str, - current_info->max_str_length); - } - *(int *)(current_info->str + length) = info->glyph_index; + ++nb_bitmaps; } + last_info = info; - current_info->has_outline = current_info->has_outline || !!info->bm_o; - - if(min_bm_y < current_info->pos.y){ - current_info->h += current_info->pos.y - min_bm_y; - current_info->o_h += current_info->pos.y - min_bm_y; - current_info->pos.y = min_bm_y; - } + if (!info->image) + continue; - if(min_bm_x < current_info->pos.x){ - current_info->w += current_info->pos.x - min_bm_x; - current_info->o_w += current_info->pos.x - min_bm_x; - current_info->pos.x = min_bm_x; + if (current_info->bitmap_count >= current_info->max_bitmap_count) { + size_t new_size = 2 * current_info->max_bitmap_count; + if (!ASS_REALLOC_ARRAY(current_info->bitmaps, new_size)) + continue; + current_info->max_bitmap_count = new_size; } - - if(info->bm){ - current_info->w = - FFMAX(current_info->w, info->bm->w + bm_x - current_info->pos.x); - current_info->h = - FFMAX(current_info->h, info->bm->h + bm_y - current_info->pos.y); + current_info->bitmaps[current_info->bitmap_count].image = info->image; + current_info->bitmaps[current_info->bitmap_count].x = x; + current_info->bitmaps[current_info->bitmap_count].y = y; + ++current_info->bitmap_count; + + current_info->x = FFMIN(current_info->x, x); + current_info->y = FFMIN(current_info->y, y); + if (info->image->bm) { + rectangle_combine(¤t_info->rect, info->image->bm, x, y); + ++current_info->n_bm; } - - if(info->bm_o){ - current_info->o_w = - FFMAX(current_info->o_w, info->bm_o->w + bm_o_x - current_info->pos.x); - current_info->o_h = - FFMAX(current_info->o_h, info->bm_o->h + bm_o_y - current_info->pos.y); + if (info->image->bm_o) { + rectangle_combine(¤t_info->rect_o, info->image->bm_o, x, y); + ++current_info->n_bm_o; } - - info->bm_run_id = nb_bitmaps - 1; - - last_info = info; - info = info->next; } } CompositeHashKey hk; CompositeHashValue *hv; - for (i = 0; i < nb_bitmaps; ++i) { + for (int i = 0; i < nb_bitmaps; ++i) { CombinedBitmapInfo *info = &combined_info[i]; + for (int j = 0; j < info->bitmap_count; ++j) { + info->bitmaps[j].x -= info->x; + info->bitmaps[j].y -= info->y; + } fill_composite_hash(&hk, info); hv = ass_cache_get(render_priv->cache.composite_cache, &hk); - - if(hv){ + if (hv) { info->bm = hv->bm; info->bm_o = hv->bm_o; info->bm_s = hv->bm_s; - info->cached = 1; - free(info->str); - }else{ - if(info->chars != 1 && !info->is_drawing){ - info->bm = alloc_bitmap(info->w, info->h); - if(info->has_outline){ - info->bm_o = alloc_bitmap(info->o_w, info->o_h); + free(info->bitmaps); + continue; + } + + int bbord = info->filter.be > 0 ? sqrt(2 * info->filter.be) : 0; + int gbord = info->filter.blur > 0.0 ? FFMIN(info->filter.blur + 1, INT_MAX) : 0; + int bord = FFMAX(bbord, gbord); + + if (!bord && info->n_bm == 1) { + for (int j = 0; j < info->bitmap_count; ++j) { + if (!info->bitmaps[j].image->bm) + continue; + info->bm = copy_bitmap(info->bitmaps[j].image->bm); + if (info->bm) { + info->bm->left += info->bitmaps[j].x; + info->bm->top += info->bitmaps[j].y; + } + break; + } + } else if (info->n_bm) { + info->bm = alloc_bitmap(info->rect.x_max - info->rect.x_min + 2 * bord, + info->rect.y_max - info->rect.y_min + 2 * bord); + Bitmap *dst = info->bm; + if (dst) { + dst->left = info->rect.x_min - info->x - bord; + dst->top = info->rect.y_min - info->y - bord; + for (int j = 0; j < info->bitmap_count; ++j) { + Bitmap *src = info->bitmaps[j].image->bm; + if (!src) + continue; + int x = info->bitmaps[j].x + src->left - dst->left; + int y = info->bitmaps[j].y + src->top - dst->top; + assert(x >= 0 && x + src->w <= dst->w); + assert(y >= 0 && y + src->h <= dst->h); + unsigned char *buf = dst->buffer + y * dst->stride + x; + render_priv->add_bitmaps_func(buf, dst->stride, + src->buffer, src->stride, + src->h, src->w); } } } - } - - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = text_info->glyphs + i; - if (info->skip) continue; - while (info) { - current_info = &combined_info[info->bm_run_id]; - if(!current_info->cached && !is_skip_symbol(info->symbol)){ - if(current_info->chars == 1 || current_info->is_drawing){ - int offset_x = (info->pos.x >> 6) - current_info->pos.x; - int offset_y = (info->pos.y >> 6) - current_info->pos.y; - if(info->bm){ - current_info->bm = copy_bitmap(info->bm); - if (!current_info->bm) - goto cleanup; - current_info->bm->left += offset_x; - current_info->bm->top += offset_y; - } - if(info->bm_o){ - current_info->bm_o = copy_bitmap(info->bm_o); - if (!current_info->bm_o) - goto cleanup; - current_info->bm_o->left += offset_x; - current_info->bm_o->top += offset_y; - } - }else{ - unsigned offset_x, offset_y; - if(info->bm && info->bm->w && info->bm->h){ - offset_x = (info->pos.x >> 6) - current_info->pos.x + info->bm->left; - offset_y = (info->pos.y >> 6) - current_info->pos.y + info->bm->top; - render_priv->add_bitmaps_func( - ¤t_info->bm->buffer[offset_y * current_info->bm->stride + offset_x], - current_info->bm->stride, - info->bm->buffer, - info->bm->stride, - info->bm->h, - info->bm->w - ); - } - if(info->bm_o && info->bm_o->w && info->bm_o->h){ - offset_x = (info->pos.x >> 6) - current_info->pos.x + info->bm_o->left; - offset_y = (info->pos.y >> 6) - current_info->pos.y + info->bm_o->top; - render_priv->add_bitmaps_func( - ¤t_info->bm_o->buffer[offset_y * current_info->bm_o->stride + offset_x], - current_info->bm_o->stride, - info->bm_o->buffer, - info->bm_o->stride, - info->bm_o->h, - info->bm_o->w - ); - } + if (!bord && info->n_bm_o == 1) { + for (int j = 0; j < info->bitmap_count; ++j) { + if (!info->bitmaps[j].image->bm_o) + continue; + info->bm_o = copy_bitmap(info->bitmaps[j].image->bm_o); + if (info->bm_o) { + info->bm_o->left += info->bitmaps[j].x; + info->bm_o->top += info->bitmaps[j].y; + } + break; + } + } else if (info->n_bm_o) { + info->bm_o = alloc_bitmap(info->rect_o.x_max - info->rect_o.x_min + 2 * bord, + info->rect_o.y_max - info->rect_o.y_min + 2 * bord); + Bitmap *dst = info->bm_o; + if (dst) { + dst->left = info->rect_o.x_min - info->x - bord; + dst->top = info->rect_o.y_min - info->y - bord; + for (int j = 0; j < info->bitmap_count; ++j) { + Bitmap *src = info->bitmaps[j].image->bm_o; + if (!src) + continue; + int x = info->bitmaps[j].x + src->left - dst->left; + int y = info->bitmaps[j].y + src->top - dst->top; + assert(x >= 0 && x + src->w <= dst->w); + assert(y >= 0 && y + src->h <= dst->h); + unsigned char *buf = dst->buffer + y * dst->stride + x; + render_priv->add_bitmaps_func(buf, dst->stride, + src->buffer, src->stride, + src->h, src->w); } } - cleanup: - info = info->next; } - } - for (i = 0; i < nb_bitmaps; ++i) { - if(!combined_info[i].cached){ - CompositeHashValue chv; - CombinedBitmapInfo *info = &combined_info[i]; - if(info->bm || info->bm_o){ - ass_synth_blur(render_priv->synth_priv, info->border_style == 3, - info->be, info->blur * render_priv->blur_scale * 2, - info->bm, info->bm_o); + if(info->bm || info->bm_o){ + ass_synth_blur(render_priv->synth_priv, info->filter.flags & FILTER_BORDER_STYLE_3, + info->filter.be, info->filter.blur, info->bm, info->bm_o); + if (info->filter.flags & FILTER_DRAW_SHADOW) make_shadow_bitmap(info, render_priv); - } - - fill_composite_hash(&hk, info); + } - chv.bm = info->bm; - chv.bm_o = info->bm_o; - chv.bm_s = info->bm_s; + CompositeHashValue chv; + chv.bm = info->bm; + chv.bm_o = info->bm_o; + chv.bm_s = info->bm_s; - ass_cache_put(render_priv->cache.composite_cache, &hk, &chv); - } + ass_cache_put(render_priv->cache.composite_cache, &hk, &chv); } text_info->n_bitmaps = nb_bitmaps; @@ -2690,7 +2584,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event, event_images->detect_collisions = render_priv->state.detect_collisions; event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1; event_images->event = event; - event_images->imgs = render_text(render_priv, (int) device_x, (int) device_y); + event_images->imgs = render_text(render_priv); if (render_priv->state.border_style == 4) add_background(render_priv, event_images); @@ -2720,12 +2614,14 @@ void ass_free_images(ASS_Image *img) static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache) { if (ass_cache_empty(cache->bitmap_cache, cache->bitmap_max_size)) { + ass_cache_empty(cache->composite_cache, 0); ass_free_images(priv->prev_images_root); priv->prev_images_root = 0; priv->cache_cleared = 1; } if (ass_cache_empty(cache->outline_cache, cache->glyph_max)) { ass_cache_empty(cache->bitmap_cache, 0); + ass_cache_empty(cache->composite_cache, 0); ass_free_images(priv->prev_images_root); priv->prev_images_root = 0; priv->cache_cleared = 1; diff --git a/libass/ass_render.h b/libass/ass_render.h index e99b0fa..b0892ce 100644 --- a/libass/ass_render.h +++ b/libass/ass_render.h @@ -107,48 +107,29 @@ typedef enum { EF_KARAOKE_KO } Effect; +typedef struct +{ + int x_min, y_min, x_max, y_max; +} Rectangle; + // describes a combined bitmap typedef struct { - Bitmap *bm; // glyphs bitmap - unsigned w; - unsigned h; - Bitmap *bm_o; // outline bitmap - unsigned o_w; - unsigned o_h; - Bitmap *bm_s; // shadow bitmap - FT_Vector pos; + FilterDesc filter; uint32_t c[4]; // colors - FT_Vector advance; // 26.6 Effect effect_type; int effect_timing; // time duration of current karaoke word // after process_karaoke_effects: distance in pixels from the glyph origin. // part of the glyph to the left of it is displayed in a different color. - int be; // blur edges - double blur; // gaussian blur - double shadow_x; - double shadow_y; - double frx, fry, frz; // rotation - double fax, fay; // text shearing - double scale_x, scale_y; - int border_style; - int has_border; - double border_x, border_y; - double hspacing; - unsigned italic; - unsigned bold; - int flags; - int shift_x, shift_y; - - unsigned has_outline; - unsigned is_drawing; - int max_str_length; - int str_length; - unsigned chars; - char *str; - int cached; - FT_Vector pos_orig; int first_pos_x; + + size_t bitmap_count, max_bitmap_count; + BitmapRef *bitmaps; + + int x, y; + Rectangle rect, rect_o; + Bitmap *bm, *bm_o, *bm_s; // glyphs, outline, shadow bitmaps + size_t n_bm, n_bm_o; } CombinedBitmapInfo; // describes a glyph @@ -168,9 +149,6 @@ typedef struct glyph_info { ASS_Drawing *drawing; ASS_Outline *outline; ASS_Outline *border; - Bitmap *bm; // glyph bitmap - Bitmap *bm_o; // outline bitmap - Bitmap *bm_s; // shadow bitmap FT_BBox bbox; FT_Vector pos; FT_Vector offset; @@ -200,10 +178,10 @@ typedef struct glyph_info { unsigned bold; int flags; - int bm_run_id; int shape_run_id; BitmapHashKey hash_key; + BitmapHashValue *image; // next glyph in this cluster struct glyph_info *next; @@ -277,9 +255,6 @@ typedef struct { int effect_timing; int effect_skip_timing; - // bitmap run id (used for final bitmap rendering) - int bm_run_id; - enum { SCROLL_LR, // left-to-right SCROLL_RL, -- cgit v1.2.3