summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDr.Smile <vabnick@gmail.com>2015-01-23 06:32:16 +0300
committerDr.Smile <vabnick@gmail.com>2015-01-28 02:43:47 +0300
commitb63d8931845af256523d1142abea5caf2f65685d (patch)
treebc71c7904e56d23dfa6e8022a6eee29437d1c799
parent1c4459b7d88f231fb832682d0a4e25888e8c12ee (diff)
downloadlibass-b63d8931845af256523d1142abea5caf2f65685d.tar.bz2
libass-b63d8931845af256523d1142abea5caf2f65685d.tar.xz
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.
-rw-r--r--libass/ass_bitmap.c21
-rw-r--r--libass/ass_bitmap.h16
-rw-r--r--libass/ass_cache.c51
-rw-r--r--libass/ass_cache.h20
-rw-r--r--libass/ass_cache_template.h39
-rw-r--r--libass/ass_render.c508
-rw-r--r--libass/ass_render.h55
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(&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);
}
}
}
- }
<