]> granicus.if.org Git - libass/commitdiff
Clean up bitmap combining
authorDr.Smile <vabnick@gmail.com>
Fri, 23 Jan 2015 03:32:16 +0000 (06:32 +0300)
committerDr.Smile <vabnick@gmail.com>
Tue, 27 Jan 2015 23:43:47 +0000 (02:43 +0300)
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
libass/ass_bitmap.h
libass/ass_cache.c
libass/ass_cache.h
libass/ass_cache_template.h
libass/ass_render.c
libass/ass_render.h

index 01bf013213f68ad38828edf0cab3c8ba55f4d7e3..6f77a173c09921f46ac216cdada952b8ebe03148 100644 (file)
@@ -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;
         }
index 4a070baac4c50af7caccb38b0b6d38a88f04d7c0..9a36a3ec305147f4a1e6e4984152652ed7643e2f 100644 (file)
@@ -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,
index 062ddfa4d771e48563b767e76cbe447d4d2f7415..2381e88c32dde624c980f0f0c374a9872bb20e22 100644 (file)
@@ -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
index 060014231bdaac6ae6fddf505cf1f8b5af9bc27b..af814ac3ce222f05c1d66d276956562fb8ae678c 100644 (file)
@@ -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);
index 145828412cbd870c066961fb8f5cb7e720c23af2..dbf724c151674a257262ad626bd47a40a953be21 100644 (file)
@@ -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
index 4371aa3e41adb7dd8bc58d0837c012c51d843f15..105aa45285b819edabe620119d2ff66a1d89f8eb 100644 (file)
@@ -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(&current_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(&current_info->rect);
+                rectangle_reset(&current_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(&current_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(&current_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(
-                            &current_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(
-                            &current_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;
index e99b0fa655d169e642cb4cc386f460d42e197a19..b0892ce5b9da541ee4da2ea3334929f379015639 100644 (file)
@@ -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,