summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author11rcombs <rodger.combs@gmail.com>2014-01-25 19:06:12 -0600
committer11rcombs <rodger.combs@gmail.com>2014-01-25 19:09:15 -0600
commit5dd56af2f97419ce5a72d1dab2e9c19b34c1dd57 (patch)
tree1997f519ae3d5f75a76a3cee3dd95034e513210a
parentfa3b6fa3ee1de42232f0513539af2464a3c9203a (diff)
downloadlibass-5dd56af2f97419ce5a72d1dab2e9c19b34c1dd57.tar.bz2
libass-5dd56af2f97419ce5a72d1dab2e9c19b34c1dd57.tar.xz
Combine bitmaps before applying blur and shadow
-rw-r--r--libass/ass_bitmap.c225
-rw-r--r--libass/ass_bitmap.h43
-rw-r--r--libass/ass_cache.c22
-rw-r--r--libass/ass_cache.h6
-rw-r--r--libass/ass_cache_template.h41
-rw-r--r--libass/ass_parse.c1
-rw-r--r--libass/ass_render.c745
-rw-r--r--libass/ass_render.h76
-rw-r--r--libass/ass_shaper.c29
-rw-r--r--libass/ass_utils.c32
-rw-r--r--libass/ass_utils.h1
11 files changed, 885 insertions, 336 deletions
diff --git a/libass/ass_bitmap.c b/libass/ass_bitmap.c
index dcc2f2b..ad00df2 100644
--- a/libass/ass_bitmap.c
+++ b/libass/ass_bitmap.c
@@ -27,24 +27,11 @@
#include "ass_utils.h"
#include "ass_bitmap.h"
-
-struct ass_synth_priv {
- int tmp_w, tmp_h;
- unsigned *tmp;
-
- int g_r;
- int g_w;
-
- double *g0;
- unsigned *g;
- unsigned *gt2;
-
- double radius;
-};
+#include "ass_render.h"
static const unsigned base = 256;
-static int generate_tables(ASS_SynthPriv *priv, double radius)
+int generate_tables(ASS_SynthPriv *priv, double radius)
{
double A = log(1.0 / base) / (radius * radius * 2);
int mx, i;
@@ -103,7 +90,7 @@ static int generate_tables(ASS_SynthPriv *priv, double radius)
return 0;
}
-static void resize_tmp(ASS_SynthPriv *priv, int w, int h)
+void resize_tmp(ASS_SynthPriv *priv, int w, int h)
{
if (priv->tmp_w >= w && priv->tmp_h >= h)
return;
@@ -135,12 +122,17 @@ void ass_synth_done(ASS_SynthPriv *priv)
free(priv);
}
-static Bitmap *alloc_bitmap(int w, int h)
+Bitmap *alloc_bitmap(int w, int h)
{
Bitmap *bm;
- unsigned s = w; // XXX: alignment
+
+ uintptr_t alignment_offset = (w > 31) ? 31 : ((w > 15) ? 15 : 0);
+ unsigned s = (w + alignment_offset) & ~alignment_offset;
bm = malloc(sizeof(Bitmap));
- bm->buffer = calloc(s, h);
+ bm->buffer_ptr = malloc(s * h + alignment_offset + 32);
+ bm->buffer = (unsigned char*)
+ (((uintptr_t)bm->buffer_ptr + alignment_offset) & ~alignment_offset);
+ memset(bm->buffer, 0, s * h + 32);
bm->w = w;
bm->h = h;
bm->stride = s;
@@ -151,11 +143,11 @@ static Bitmap *alloc_bitmap(int w, int h)
void ass_free_bitmap(Bitmap *bm)
{
if (bm)
- free(bm->buffer);
+ free(bm->buffer_ptr);
free(bm);
}
-static Bitmap *copy_bitmap(const Bitmap *src)
+Bitmap *copy_bitmap(const Bitmap *src)
{
Bitmap *dst = alloc_bitmap(src->w, src->h);
dst->left = src->left;
@@ -220,7 +212,7 @@ Bitmap *outline_to_bitmap(ASS_Library *library, FT_Library ftlib,
* The glyph bitmap is subtracted from outline bitmap. This way looks much
* better in some cases.
*/
-static void fix_outline(Bitmap *bm_g, Bitmap *bm_o)
+void fix_outline(Bitmap *bm_g, Bitmap *bm_o)
{
int x, y;
const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left;
@@ -253,7 +245,7 @@ static void fix_outline(Bitmap *bm_g, Bitmap *bm_o)
* \brief Shift a bitmap by the fraction of a pixel in x and y direction
* expressed in 26.6 fixed point
*/
-static void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
+void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
{
int x, y, b;
int w = bm->w;
@@ -305,9 +297,9 @@ static void shift_bitmap(Bitmap *bm, int shift_x, int shift_y)
/*
* Gaussian blur. An fast pure C implementation from MPlayer.
*/
-static void ass_gauss_blur(unsigned char *buffer, unsigned *tmp2,
- int width, int height, int stride,
- unsigned *m2, int r, int mwidth)
+void ass_gauss_blur(unsigned char *buffer, unsigned *tmp2,
+ int width, int height, int stride,
+ unsigned *m2, int r, int mwidth)
{
int x, y;
@@ -427,36 +419,72 @@ static void ass_gauss_blur(unsigned char *buffer, unsigned *tmp2,
/**
* \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
* This blur is the same as the one employed by vsfilter.
+ * Pure C implementation.
*/
-static void be_blur(Bitmap *bm)
+void be_blur_c(uint8_t *buf, intptr_t w,
+ intptr_t h, intptr_t stride,
+ uint16_t *tmp)
{
- int w = bm->w;
- int h = bm->h;
- int s = bm->stride;
- unsigned char *buf = bm->buffer;
- unsigned int x, y;
- unsigned int old_sum, new_sum;
-
- for (y = 0; y < h; y++) {
- old_sum = 2 * buf[y * s];
- for (x = 0; x < w - 1; x++) {
- new_sum = buf[y * s + x] + buf[y * s + x + 1];
- buf[y * s + x] = (old_sum + new_sum) >> 2;
- old_sum = new_sum;
+ unsigned short *col_pix_buf = tmp;
+ unsigned short *col_sum_buf = tmp + w * sizeof(unsigned short);
+ unsigned x, y, old_pix, old_sum, new_sum, temp1, temp2;
+ unsigned char *src, *dst;
+ memset(col_pix_buf, 0, w * sizeof(unsigned short));
+ memset(col_sum_buf, 0, w * sizeof(unsigned short));
+ {
+ y = 0;
+ src=buf+y*stride;
+
+ x = 2;
+ old_pix = src[x-1];
+ old_sum = old_pix + src[x-2];
+ for ( ; x < w; x++) {
+ temp1 = src[x];
+ temp2 = old_pix + temp1;
+ old_pix = temp1;
+ temp1 = old_sum + temp2;
+ old_sum = temp2;
+ col_pix_buf[x] = temp1;
+ }
+ }
+ new_sum = 2 * buf[y * stride + w - 1];
+ buf[y * stride + w - 1] = (old_sum + new_sum) >> 2;
+ {
+ x = 2;
+ old_pix = src[x-1];
+ old_sum = old_pix + src[x-2];
+ for ( ; x < w; x++) {
+ temp1 = src[x];
+ temp2 = old_pix + temp1;
+ old_pix = temp1;
+ temp1 = old_sum + temp2;
+ old_sum = temp2;
+
+ temp2 = col_pix_buf[x] + temp1;
+ col_pix_buf[x] = temp1;
+ col_sum_buf[x] = temp2;
}
- new_sum = 2 * buf[y * s + w - 1];
- buf[y * s + w - 1] = (old_sum + new_sum) >> 2;
}
- for (x = 0; x < w; x++) {
- old_sum = 2 * buf[x];
- for (y = 0; y < h - 1; y++) {
- new_sum = buf[y * s + x] + buf[(y + 1) * s + x];
- buf[y * s + x] = (old_sum + new_sum) >> 2;
- old_sum = new_sum;
+ for (y = 2; y < h; y++) {
+ src=buf+y*stride;
+ dst=buf+(y-1)*stride;
+
+ x = 2;
+ old_pix = src[x-1];
+ old_sum = old_pix + src[x-2];
+ for ( ; x < w; x++) {
+ temp1 = src[x];
+ temp2 = old_pix + temp1;
+ old_pix = temp1;
+ temp1 = old_sum + temp2;
+ old_sum = temp2;
+
+ temp2 = col_pix_buf[x] + temp1;
+ col_pix_buf[x] = temp1;
+ dst[x-1] = (col_sum_buf[x] + temp2) >> 4;
+ col_sum_buf[x] = temp2;
}
- new_sum = 2 * buf[(h - 1) * s + x];
- buf[(h - 1) * s + x] = (old_sum + new_sum) >> 2;
}
}
@@ -489,46 +517,69 @@ int outline_to_bitmap3(ASS_Library *library, ASS_SynthPriv *priv_blur,
}
}
- // Apply box blur (multiple passes, if requested)
- while (be--) {
- if (*bm_o)
- be_blur(*bm_o);
- if (!*bm_o || border_style == 3)
- be_blur(*bm_g);
- }
+ return 0;
+}
- // Apply gaussian blur
- if (blur_radius > 0.0) {
- if (*bm_o)
- resize_tmp(priv_blur, (*bm_o)->w, (*bm_o)->h);
- if (!*bm_o || border_style == 3)
- resize_tmp(priv_blur, (*bm_g)->w, (*bm_g)->h);
- generate_tables(priv_blur, blur_radius);
- if (*bm_o)
- ass_gauss_blur((*bm_o)->buffer, priv_blur->tmp,
- (*bm_o)->w, (*bm_o)->h, (*bm_o)->stride,
- priv_blur->gt2, priv_blur->g_r, priv_blur->g_w);
- if (!*bm_o || border_style == 3)
- ass_gauss_blur((*bm_g)->buffer, priv_blur->tmp,
- (*bm_g)->w, (*bm_g)->h, (*bm_g)->stride,
- priv_blur->gt2, priv_blur->g_r, priv_blur->g_w);
+/**
+ * \brief Add two bitmaps together at a given position
+ * Uses additive blending, clipped to [0,255]. Pure C implementation.
+ */
+void add_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src, intptr_t src_stride,
+ intptr_t height, intptr_t width)
+{
+ unsigned out;
+ uint8_t* end = dst + dst_stride * height;
+ while (dst < end) {
+ for (unsigned j = 0; j < width; ++j) {
+ out = dst[j] + src[j];
+ dst[j] = FFMIN(out, 255);
+ }
+ dst += dst_stride;
+ src += src_stride;
}
+}
- // Create shadow and fix outline as needed
- if (*bm_o && border_style != 3) {
- *bm_s = copy_bitmap(*bm_o);
- fix_outline(*bm_g, *bm_o);
- } else if (*bm_o && border_visible) {
- *bm_s = copy_bitmap(*bm_o);
- } else if (*bm_o) {
- *bm_s = *bm_o;
- *bm_o = 0;
- } else
- *bm_s = copy_bitmap(*bm_g);
-
- assert(bm_s);
+void sub_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src, intptr_t src_stride,
+ intptr_t height, intptr_t width)
+{
+ unsigned out;
+ uint8_t* end = dst + dst_stride * height;
+ while (dst < end) {
+ for (unsigned j = 0; j < width; ++j) {
+ out = dst[j] - src[j];
+ dst[j] = FFMAX(out, 0);
+ }
+ dst += dst_stride;
+ src += src_stride;
+ }
+}
- shift_bitmap(*bm_s, shadow_offset.x, shadow_offset.y);
+void restride_bitmap_c(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src, intptr_t src_stride,
+ intptr_t width, intptr_t height)
+{
+ uint8_t* end = dst + dst_stride * height;
+ while (dst < end) {
+ memcpy(dst, src, width);
+ dst += dst_stride;
+ src += src_stride;
+ }
+}
- return 0;
+void mul_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src1, intptr_t src1_stride,
+ uint8_t *src2, intptr_t src2_stride,
+ intptr_t w, intptr_t h)
+{
+ uint8_t* end = src1 + src1_stride * h;
+ while (src1 < end) {
+ for (unsigned x = 0; x < w; ++x) {
+ dst[x] = (src1[x] * src2[x] + 255) >> 8;
+ }
+ dst += dst_stride;
+ src1 += src1_stride;
+ src2 += src2_stride;
+ }
}
diff --git a/libass/ass_bitmap.h b/libass/ass_bitmap.h
index 53be7af..b51c1bf 100644
--- a/libass/ass_bitmap.h
+++ b/libass/ass_bitmap.h
@@ -24,7 +24,19 @@
#include "ass.h"
-typedef struct ass_synth_priv ASS_SynthPriv;
+typedef struct ass_synth_priv {
+ int tmp_w, tmp_h;
+ unsigned *tmp;
+
+ int g_r;
+ int g_w;
+
+ double *g0;
+ unsigned *g;
+ unsigned *gt2;
+
+ double radius;
+} ASS_SynthPriv;
ASS_SynthPriv *ass_synth_init(double);
void ass_synth_done(ASS_SynthPriv *priv);
@@ -33,11 +45,14 @@ typedef struct {
int left, top;
int w, h; // width, height
int stride;
- unsigned char *buffer; // w x h buffer
+ unsigned char *buffer; // h * stride buffer
+ unsigned char *buffer_ptr; // unaligned pointer (for free())
} Bitmap;
Bitmap *outline_to_bitmap(ASS_Library *library, FT_Library ftlib,
FT_Outline *outline, int bord);
+
+Bitmap *alloc_bitmap(int w, int h);
/**
* \brief perform glyph rendering
* \param glyph original glyph
@@ -55,5 +70,29 @@ int outline_to_bitmap3(ASS_Library *library, ASS_SynthPriv *priv_blur,
int border_style, int border_visible);
void ass_free_bitmap(Bitmap *bm);
+void ass_gauss_blur(unsigned char *buffer, unsigned *tmp2,
+ int width, int height, int stride,
+ unsigned *m2, int r, int mwidth);
+void be_blur_c(uint8_t *buf, intptr_t w,
+ intptr_t h, intptr_t stride,
+ uint16_t *tmp);
+void add_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src, intptr_t src_stride,
+ intptr_t height, intptr_t width);
+void sub_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src, intptr_t src_stride,
+ intptr_t height, intptr_t width);
+void restride_bitmap_c(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src, intptr_t src_stride,
+ intptr_t width, intptr_t height);
+void mul_bitmaps_c(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src1, intptr_t src1_stride,
+ uint8_t *src2, intptr_t src2_stride,
+ intptr_t w, intptr_t h);
+void shift_bitmap(Bitmap *bm, int shift_x, int shift_y);
+void fix_outline(Bitmap *bm_g, Bitmap *bm_o);
+void resize_tmp(ASS_SynthPriv *priv, int w, int h);
+int generate_tables(ASS_SynthPriv *priv, double radius);
+Bitmap *copy_bitmap(const Bitmap *src);
#endif /* LIBASS_BITMAP_H */
diff --git a/libass/ass_cache.c b/libass/ass_cache.c
index 8234e5f..6baa924 100644
--- a/libass/ass_cache.c
+++ b/libass/ass_cache.c
@@ -125,12 +125,28 @@ static unsigned bitmap_compare (void *a, void *b, size_t key_size)
static void composite_destruct(void *key, void *value)
{
CompositeHashValue *v = value;
- free(v->a);
- free(v->b);
+ CompositeHashKey *k = key;
+ if (v->bm)
+ 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);
+ free(k->str);
free(key);
free(value);
}
+static size_t composite_size(void *value, size_t value_size)
+{
+ CompositeHashValue *val = value;
+ 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;
+}
+
// outline cache
static unsigned outline_hash(void *key, size_t key_size)
@@ -349,6 +365,6 @@ Cache *ass_bitmap_cache_create(void)
Cache *ass_composite_cache_create(void)
{
return ass_cache_create(composite_hash, composite_compare,
- composite_destruct, (ItemSize)NULL, sizeof(CompositeHashKey),
+ composite_destruct, composite_size, sizeof(CompositeHashKey),
sizeof(CompositeHashValue));
}
diff --git a/libass/ass_cache.h b/libass/ass_cache.h
index 7375f04..677b705 100644
--- a/libass/ass_cache.h
+++ b/libass/ass_cache.h
@@ -35,8 +35,10 @@ typedef struct {
} BitmapHashValue;
typedef struct {
- unsigned char *a;
- unsigned char *b;
+ Bitmap *bm;
+ Bitmap *bm_o;
+ Bitmap *bm_s;
+ FT_Vector pos;
} CompositeHashValue;
typedef struct {
diff --git a/libass/ass_cache_template.h b/libass/ass_cache_template.h
index 3d8185f..8f7f2af 100644
--- a/libass/ass_cache_template.h
+++ b/libass/ass_cache_template.h
@@ -123,21 +123,36 @@ END(DrawingHashKey)
// Cache for composited bitmaps
START(composite, composite_hash_key)
- GENERIC(int, aw)
- GENERIC(int, ah)
- GENERIC(int, bw)
- GENERIC(int, bh)
- GENERIC(int, ax)
- GENERIC(int, ay)
- GENERIC(int, bx)
- GENERIC(int, by)
- GENERIC(int, as)
- GENERIC(int, bs)
- GENERIC(unsigned char *, a)
- GENERIC(unsigned char *, b)
+ GENERIC(unsigned, w)
+ GENERIC(unsigned, h)
+ GENERIC(unsigned, o_w)
+ GENERIC(unsigned, o_h)
+ GENERIC(int, is_drawing)
+ GENERIC(unsigned, chars)
+ 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)
+ FTVECTOR(advance)
+ STRING(str)
END(CompositeHashKey)
-
#undef START
#undef GENERIC
#undef STRING
diff --git a/libass/ass_parse.c b/libass/ass_parse.c
index c3292c9..fe40e69 100644
--- a/libass/ass_parse.c
+++ b/libass/ass_parse.c
@@ -983,6 +983,7 @@ void process_karaoke_effects(ASS_Renderer *render_priv)
cur2->effect_type = s1->effect_type;
cur2->effect_timing = x - d6_to_int(cur2->pos.x);
}
+ s1->effect = 1;
}
}
}
diff --git a/libass/ass_render.c b/libass/ass_render.c
index 12f5701..e6e6052 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -20,6 +20,7 @@
#include <assert.h>
#include <math.h>
+#include <string.h>
#include "ass_render.h"
#include "ass_parse.h"
@@ -27,9 +28,12 @@
#define MAX_GLYPHS_INITIAL 1024
#define MAX_LINES_INITIAL 64
+#define MAX_BITMAPS_INITIAL 16
+#define MAX_STR_LENGTH_INITIAL 64
#define SUBPIXEL_MASK 63
#define SUBPIXEL_ACCURACY 7
+
ASS_Renderer *ass_renderer_init(ASS_Library *library)
{
int error;
@@ -59,15 +63,25 @@ ASS_Renderer *ass_renderer_init(ASS_Library *library)
priv->ftlibrary = ft;
// images_root and related stuff is zero-filled in calloc
+ priv->add_bitmaps_func = add_bitmaps_c;
+ priv->sub_bitmaps_func = sub_bitmaps_c;
+ priv->mul_bitmaps_func = mul_bitmaps_c;
+ priv->be_blur_func = be_blur_c;
+ priv->restride_bitmap_func = restride_bitmap_c;
+
priv->cache.font_cache = ass_font_cache_create();
priv->cache.bitmap_cache = ass_bitmap_cache_create();
priv->cache.composite_cache = ass_composite_cache_create();
priv->cache.outline_cache = ass_outline_cache_create();
priv->cache.glyph_max = GLYPH_CACHE_MAX;
priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE;
+ priv->cache.composite_max_size = COMPOSITE_CACHE_MAX_SIZE;
+ priv->text_info.max_bitmaps = MAX_BITMAPS_INITIAL;
priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL;
priv->text_info.max_lines = MAX_LINES_INITIAL;
+ priv->text_info.n_bitmaps = 0;
+ priv->text_info.combined_bitmaps = calloc(MAX_BITMAPS_INITIAL, sizeof(CombinedBitmapInfo));
priv->text_info.glyphs = calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo));
priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo));
@@ -129,6 +143,8 @@ void ass_renderer_done(ASS_Renderer *render_priv)
free(render_priv->text_info.glyphs);
free(render_priv->text_info.lines);
+ free(render_priv->text_info.combined_bitmaps);
+
free(render_priv->settings.default_font);
free(render_priv->settings.default_family);
@@ -415,109 +431,6 @@ render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y,
return tail;
}
-/**
- * \brief Replace the bitmap buffer in ASS_Image with a copy
- * \param img ASS_Image to operate on
- * \return pointer to old bitmap buffer
- */
-static unsigned char *clone_bitmap_buffer(ASS_Image *img)
-{
- unsigned char *old_bitmap = img->bitmap;
- int size = img->stride * (img->h - 1) + img->w;
- img->bitmap = malloc(size);
- memcpy(img->bitmap, old_bitmap, size);
- return old_bitmap;
-}
-
-/**
- * \brief Calculate overlapping area of two consecutive bitmaps and in case they
- * overlap, blend them together
- * Mainly useful for translucent glyphs and especially borders, to avoid the
- * luminance adding up where they overlap (which looks ugly)
- */
-static void
-render_overlap(ASS_Renderer *render_priv, ASS_Image **last_tail,
- ASS_Image **tail)
-{
- int left, top, bottom, right;
- int old_left, old_top, w, h, cur_left, cur_top;
- int x, y, opos, cpos;
- char m;
- CompositeHashKey hk;
- CompositeHashValue *hv;
- CompositeHashValue chv;
- int ax = (*last_tail)->dst_x;
- int ay = (*last_tail)->dst_y;
- int aw = (*last_tail)->w;
- int as = (*last_tail)->stride;
- int ah = (*last_tail)->h;
- int bx = (*tail)->dst_x;
- int by = (*tail)->dst_y;
- int bw = (*tail)->w;
- int bs = (*tail)->stride;
- int bh = (*tail)->h;
- unsigned char *a;
- unsigned char *b;
-
- if ((*last_tail)->bitmap == (*tail)->bitmap)
- return;
-
- if ((*last_tail)->color != (*tail)->color)
- return;
-
- // Calculate overlap coordinates
- left = (ax > bx) ? ax : bx;
- top = (ay > by) ? ay : by;
- right = ((ax + aw) < (bx + bw)) ? (ax + aw) : (bx + bw);
- bottom = ((ay + ah) < (by + bh)) ? (ay + ah) : (by + bh);
- if ((right <= left) || (bottom <= top))
- return;
- old_left = left - ax;
- old_top = top - ay;
- w = right - left;
- h = bottom - top;
- cur_left = left - bx;
- cur_top = top - by;
-
- // Query cache
- hk.a = (*last_tail)->bitmap;
- hk.b = (*tail)->bitmap;
- hk.aw = aw;
- hk.ah = ah;
- hk.bw = bw;
- hk.bh = bh;
- hk.ax = ax;
- hk.ay = ay;
- hk.bx = bx;
- hk.by = by;
- hk.as = as;
- hk.bs = bs;
- hv = ass_cache_get(render_priv->cache.composite_cache, &hk);
- if (hv) {
- (*last_tail)->bitmap = hv->a;
- (*tail)->bitmap = hv->b;
- return;
- }
- // Allocate new bitmaps and copy over data
- a = clone_bitmap_buffer(*last_tail);
- b = clone_bitmap_buffer(*tail);
-
- // Blend overlapping area
- for (y = 0; y < h; y++)
- for (x = 0; x < w; x++) {
- opos = (old_top + y) * (as) + (old_left + x);
- cpos = (cur_top + y) * (bs) + (cur_left + x);
- m = FFMIN(a[opos] + b[cpos], 0xff);
- (*last_tail)->bitmap[opos] = 0;
- (*tail)->bitmap[cpos] = m;
- }
-
- // Insert bitmaps into the cache
- chv.a = (*last_tail)->bitmap;
- chv.b = (*tail)->bitmap;
- ass_cache_put(render_priv->cache.composite_cache, &hk, &chv);
-}
-
static void free_list_add(ASS_Renderer *render_priv, void *object)
{
if (!render_priv->free_head) {
@@ -594,7 +507,7 @@ blend_vector_error:
// Iterate through bitmaps and blend/clip them
for (cur = head; cur; cur = cur->next) {
- int left, top, right, bottom, apos, bpos, y, x, w, h;
+ int left, top, right, bottom, w, h;
int ax, ay, aw, ah, as;
int bx, by, bw, bh, bs;
int aleft, atop, bleft, btop;
@@ -628,43 +541,48 @@ blend_vector_error:
if (render_priv->state.clip_drawing_mode) {
// Inverse clip
if (ax + aw < bx || ay + ah < by || ax > bx + bw ||
- ay > by + bh) {
+ ay > by + bh || !h || !w) {
continue;
}
// Allocate new buffer and add to free list
- nbuffer = malloc(as * ah);
+ nbuffer = malloc(as * ah + 0x1F);
if (!nbuffer) goto blend_vector_exit;
free_list_add(render_priv, nbuffer);
+ nbuffer = (unsigned char*)(((uintptr_t)nbuffer + 0x1F) & ~0x1F);
// Blend together
- memcpy(nbuffer, abuffer, as * (ah - 1) + aw);
- for (y = 0; y < h; y++)
- for (x = 0; x < w; x++) {
- apos = (atop + y) * as + aleft + x;
- bpos = (btop + y) * bs + bleft + x;
- nbuffer[apos] = FFMAX(0, abuffer[apos] - bbuffer[bpos]);
- }
+ memcpy(nbuffer, abuffer, ((ah - 1) * as) + aw);
+ render_priv->sub_bitmaps_func(nbuffer + atop * as + aleft, as,
+ bbuffer + btop * bs + bleft, bs,
+ h, w);
} else {
// Regular clip
if (ax + aw < bx || ay + ah < by || ax > bx + bw ||
- ay > by + bh) {
- cur->w = cur->h = 0;
+ ay > by + bh || !h || !w) {
+ cur->w = cur->h = cur->stride = 0;
continue;
}
// Allocate new buffer and add to free list
- nbuffer = calloc(as, ah);
+ uintptr_t alignment_offset = (w > 15) ? 15 : ((w > 7) ? 7 : 0);
+ unsigned ns = (w + alignment_offset) & ~alignment_offset;
+ nbuffer = malloc(ns * h + alignment_offset);
if (!nbuffer) goto blend_vector_exit;
free_list_add(render_priv, nbuffer);
+ nbuffer = (unsigned char*)
+ (((uintptr_t)nbuffer + alignment_offset) & ~alignment_offset);
// Blend together
- for (y = 0; y < h; y++)
- for (x = 0; x < w; x++) {
- apos = (atop + y) * as + aleft + x;
- bpos = (btop + y) * bs + bleft + x;
- nbuffer[apos] = (abuffer[apos] * bbuffer[bpos] + 255) >> 8;
- }
+ render_priv->mul_bitmaps_func(nbuffer, ns,
+ abuffer + atop * as + aleft, as,
+ bbuffer + btop * bs + bleft, bs,
+ w, h);
+ cur->dst_x += aleft;
+ cur->dst_y += atop;
+ cur->w = w;
+ cur->h = h;
+ cur->stride = ns;
}
cur->bitmap = nbuffer;
}
@@ -674,8 +592,10 @@ blend_vector_exit:
render_priv->state.clip_drawing = 0;
}
-#define IS_SKIP_SYMBOL(x) ((x) == 0 || (x) == '\n' || (x) == '\r')
-
+static inline int is_skip_symbol(uint32_t x)
+{
+ return (x == 0 || x == '\n' || x == '\r');
+}
/**
* \brief Convert TextInfo struct to ASS_Image list
* Splits glyphs in halves when needed (for \kf karaoke).
@@ -687,118 +607,102 @@ static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x, int dst_y)
Bitmap *bm;
ASS_Image *head;
ASS_Image **tail = &head;
- ASS_Image **last_tail = 0;
- ASS_Image **here_tail = 0;
TextInfo *text_info = &render_priv->text_info;
- for (i = 0; i < text_info->length; ++i) {
- GlyphInfo *info = text_info->glyphs + i;
- if (IS_SKIP_SYMBOL(info->symbol) || !info->bm_s
- || (info->shadow_x == 0 && info->shadow_y == 0) || info->skip)
+ for (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;
- while (info) {
- if (!info->bm_s) {
- info = info->next;
- continue;
- }
-
- pen_x =
- dst_x + (info->pos.x >> 6) +
- (int) (info->shadow_x * render_priv->border_scale);
- pen_y =
- dst_y + (info->pos.y >> 6) +
- (int) (info->shadow_y * render_priv->border_scale);
- bm = info->bm_s;
-
- here_tail = tail;
- tail =
- render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0,
- 1000000, tail, IMAGE_TYPE_SHADOW);
-
- if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0))
- render_overlap(render_priv, last_tail, here_tail);
- last_tail = here_tail;
+ pen_x =
+ dst_x + info->pos.x +
+ (int) (info->shadow_x * render_priv->border_scale);
+ pen_y =
+ dst_y + info->pos.y +
+ (int) (info->shadow_y * render_priv->border_scale);
+ bm = info->bm_s;
- info = info->next;
- }
+ tail =
+ render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0,
+ 1000000, tail, IMAGE_TYPE_SHADOW);
}
- last_tail = 0;
- for (i = 0; i < text_info->length; ++i) {
- GlyphInfo *info = text_info->glyphs + i;
- if (IS_SKIP_SYMBOL(info->symbol) || !info->bm_o
- || info->skip)
+ for (i = 0; i < text_info->n_bitmaps; ++i) {
+ CombinedBitmapInfo *info = &text_info->combined_bitmaps[i];
+ if (!info->bm_o)
continue;
- while (info) {
- if (!info->bm_o) {
- info = info->next;
- continue;
- }
-
- pen_x = dst_x + (info->pos.x >> 6);
- pen_y = dst_y + (info->pos.y >> 6);
- bm = info->bm_o;
-
- if ((info->effect_type == EF_KARAOKE_KO)
- && (info->effect_timing <= (info->bbox.xMax >> 6))) {
- // do nothing
- } else {
- here_tail = tail;
- tail =
- render_glyph(render_priv, bm, pen_x, pen_y, info->c[2],
- 0, 1000000, tail, IMAGE_TYPE_OUTLINE);
- if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0))
- render_overlap(render_priv, last_tail, here_tail);
+ pen_x = dst_x + info->pos.x;
+ pen_y = dst_y + info->pos.y;
+ bm = info->bm_o;
- last_tail = here_tail;
- }
- info = info->next;
+ 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);
}
}
- for (i = 0; i < text_info->length; ++i) {
- GlyphInfo *info = text_info->glyphs + i;
- if (IS_SKIP_SYMBOL(info->symbol) || !info->bm
- || info->skip)
+ for (i = 0; i < text_info->n_bitmaps; ++i) {
+ CombinedBitmapInfo *info = &text_info->combined_bitmaps[i];
+ if (!info->bm)
continue;
- while (info) {
- if (!info->bm) {
- info = info->next;
- continue;
- }
+ pen_x = dst_x + info->pos.x;
+ pen_y = dst_y + info->pos.y;
+ bm = info->bm;
- pen_x = dst_x + (info->pos.x >> 6);
- pen_y = dst_y + (info->pos.y >> 6);
- bm = info->bm;
-
- if ((info->effect_type == EF_KARAOKE)
- || (info->effect_type == EF_KARAOKE_KO)) {
- if (info->effect_timing > (info->bbox.xMax >> 6))
- tail =
- render_glyph(render_priv, bm, pen_x, pen_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);
- } else if (info->effect_type == EF_KARAOKE_KF) {
+ 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],
- info->c[1], info->effect_timing, tail, IMAGE_TYPE_CHARACTER);
- } else
+ render_glyph(render_priv, bm, pen_x, pen_y,
+ info->c[0], 0, 1000000, tail, IMAGE_TYPE_CHARACTER);
+ else
tail =
- render_glyph(render_priv, bm, pen_x, pen_y, info->c[0],
- 0, 1000000, tail, IMAGE_TYPE_CHARACTER);
- info = info->next;
- }
+ render_glyph(render_priv, bm, pen_x, pen_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);
+ } else
+ tail =
+ render_glyph(render_priv, bm, pen_x, pen_y, info->c[0],
+ 0, 1000000, tail, IMAGE_TYPE_CHARACTER);
}
*tail = 0;
blend_vector_clip(render_priv, head);
+ for (ASS_Image* cur = head; cur; cur = cur->next) {
+ unsigned w = cur->w,
+ h = cur->h,
+ s = cur->stride;
+ if(w + 31 < (unsigned)cur->stride){ // Larger value? Play with this.
+ // Allocate new buffer and add to free list
+ uintptr_t alignment_offset = (w > 31) ? 31 : ((w > 15) ? 15 : 0);
+ unsigned ns = (w + alignment_offset) & ~alignment_offset;
+ uint8_t* nbuffer = malloc(ns * cur->h + alignment_offset);
+ if (!nbuffer) continue;
+ free_list_add(render_priv, nbuffer);
+ nbuffer = (unsigned char*)
+ (((uintptr_t)nbuffer + alignment_offset) & ~alignment_offset);
+
+ // Copy
+ render_priv->restride_bitmap_func(nbuffer, ns,
+ cur->bitmap, s,
+ w, h);
+ cur->w = w;
+ cur->h = h;
+ cur->stride = ns;
+ cur->bitmap = nbuffer;
+ }
+ }
+
return head;
}
@@ -1063,6 +967,41 @@ fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key,
}
/**
+ * \brief Prepare combined-bitmap hash
+ */
+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 = 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->advance = info->advance;
+}
+
+/**
* \brief Get normal and outline (border) glyphs
* \param info out: struct filled with extracted data
* Tries to get both glyphs from cache.
@@ -1164,8 +1103,6 @@ get_outline_glyph(ASS_Renderer *priv, GlyphInfo *info)
}
info->asc = val->asc;
info->desc = val->desc;
-
- ass_drawing_free(info->drawing);
}
/**
@@ -1326,12 +1263,6 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
info->bm = val->bm;
info->bm_o = val->bm_o;
- info->bm_s = val->bm_s;
-
- // 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)
- info->bm_s = 0;
}
/**
@@ -1594,7 +1525,6 @@ wrap_lines_smart(ASS_Renderer *render_priv, double max_text_width)
pen_shift_x = d6_to_double(-cur->pos.x);
pen_shift_y += height + render_priv->settings.line_spacing;
}
- cur->bm_run_id += run_offset;
cur->pos.x += double_to_d6(pen_shift_x);
cur->pos.y += double_to_d6(pen_shift_y);
}
@@ -1695,6 +1625,50 @@ fix_glyph_scaling(ASS_Renderer *priv, GlyphInfo *glyph)
glyph->font_size = ft_size;
}
+ /**
+ * \brief Checks whether a glyph should start a new bitmap run
+ * \param info Pointer to new GlyphInfo to check
+ * \param current_info Pointer to CombinedBitmapInfo for current run (may be NULL)
+ * \return 1 if a new run should be started
+ */
+static int is_new_bm_run(GlyphInfo *info, GlyphInfo *last)
+{
+ if (!last || info->linebreak || 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) ||
+ last->font->desc.vertical != info->font->desc.vertical ||
+ last->face_index != info->face_index ||
+ last->font_size != info->font_size ||
+ last->c[0] != info->c[0] ||
+ last->c[1] != info->c[1] ||
+ last->c[2] != info->c[2] ||
+ last->c[3] != info->c[3] ||
+ last->be != info->be ||
+ last->blur != info->blur ||
+ last->shadow_x != info->shadow_x ||
+ last->shadow_y != info->shadow_y ||
+ last->frx != info->frx ||
+ last->fry != info->fry ||
+ last->frz != info->frz ||
+ last->fax != info->fax ||
+ last->fay != info->fay ||
+ last->scale_x != info->scale_x ||
+ last->scale_y != info->scale_y ||
+ last->border_style != info->border_style ||
+ last->border_x != info->border_x ||
+ last->border_y != info->border_y ||
+ last->hspacing != info->hspacing ||
+ last->italic != info->italic ||
+ last->bold != info->bold ||
+ last->flags != info->flags){
+ return 1;
+ }
+ return 0;
+}
+
/**
* \brief Main ass rendering function, glues everything together
* \param event event to render
@@ -2173,6 +2147,10 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
// convert glyphs to bitmaps
int left = render_priv->settings.left_margin;
device_x = (device_x - left) * render_priv->font_scale_x + left;
+ unsigned nb_bitmaps = 0;
+ CombinedBitmapInfo *combined_info = text_info->combined_bitmaps;
+ CombinedBitmapInfo *current_info = NULL;
+ GlyphInfo *last_info = NULL;
for (i = 0; i < text_info->length; ++i) {
GlyphInfo *info = glyphs + i;
while (info) {
@@ -2185,10 +2163,232 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
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);
+ }
+
+ if(is_new_bm_run(info, last_info)){
+ ++nb_bitmaps;
+ 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);
+ }
+
+ 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;
+
+ 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->advance = info->hash_key.u.outline.advance;
+
+ current_info->has_border = !!info->border;
+
+ current_info->has_outline = 0;
+ current_info->cached = 0;
+ current_info->is_drawing = 0;
+
+ current_info->bm = current_info->bm_o = current_info->bm_s = NULL;
+
+ current_info->max_str_length = MAX_STR_LENGTH_INITIAL;
+ current_info->str_length = 0;
+ current_info->str = malloc(MAX_STR_LENGTH_INITIAL);
+ current_info->chars = 0;
+
+ 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->is_drawing = 1;
+ ass_drawing_free(info->drawing);
+ }else{
+ current_info->str_length +=
+ ass_utf8_put_char(
+ current_info->str + current_info->str_length,
+ info->symbol);
+ current_info->chars++;
+ if(current_info->str_length > current_info->max_str_length - 5){
+ current_info->max_str_length *= 2;
+ current_info->str = realloc(current_info->str,
+ current_info->max_str_length);
+ }
+ }
+
+ 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(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(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);
+ }
+
+ 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);
+ }
+
+ info->bm_run_id = nb_bitmaps - 1;
+
+ last_info = info;
+ info = info->next;
+ }
+ }
+
+ CompositeHashKey hk;
+ CompositeHashValue *hv;
+ for (i = 0; i < nb_bitmaps; ++i) {
+ CombinedBitmapInfo *info = &combined_info[i];
+
+ fill_composite_hash(&hk, info);
+
+ hv = ass_cache_get(render_priv->cache.composite_cache, &hk);
+
+ 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);
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < text_info->length; ++i) {
+ GlyphInfo *info = glyphs + i;
+ 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);
+ 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);
+ 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
+ );
+ }
+ }
+ }
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){
+ apply_blur(info, render_priv);
+ make_shadow_bitmap(info);
+ }
+
+ fill_composite_hash(&hk, info);
+
+ 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);
+ }
+ }
+
+ text_info->n_bitmaps = nb_bitmaps;
+
memset(event_images, 0, sizeof(*event_images));
event_images->top = device_y - text_info->lines[0].asc;
event_images->height = text_info->height;
@@ -2226,18 +2426,115 @@ 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;
}
+ if (ass_cache_empty(cache->composite_cache, cache->composite_max_size)) {
+ ass_free_images(priv->prev_images_root);
+ priv->prev_images_root = 0;
+ priv->cache_cleared = 1;
+ }
+}
+
+void apply_blur(CombinedBitmapInfo *info, ASS_Renderer *render_priv)
+{
+ int be = info->be;
+ double blur_radius = info->blur * render_priv->blur_scale * 2;
+ ASS_SynthPriv *priv_blur = render_priv->synth_priv;
+ Bitmap *bm_g = info->bm;
+ Bitmap *bm_o = info->bm_o;
+ int border_style = info->border_style;
+
+ if(blur_radius > 0.0 || be){
+ if (bm_o)
+ resize_tmp(priv_blur, bm_o->w, bm_o->h);
+ if (!bm_o || border_style == 3)
+ resize_tmp(priv_blur, bm_g->w, bm_g->h);
+ }
+
+ // Apply box blur (multiple passes, if requested)
+ if (be) {
+ uint16_t* tmp = (uint16_t*)(((uintptr_t)priv_blur->tmp + 0x0F) & ~0x0F);
+ if (bm_o) {
+ unsigned passes = be;
+ unsigned w = bm_o->w;
+ unsigned h = bm_o->h;
+ unsigned stride = bm_o->stride;
+ unsigned char *buf = bm_o->buffer;
+ if(w && h){
+ while(passes--){
+ memset(tmp, 0, stride * 2);
+ if(w < 16){
+ be_blur_c(buf, w, h, stride, tmp);
+ }else{
+ render_priv->be_blur_func(buf, w, h, stride, tmp);
+ }
+ }
+ }
+ }
+ if (!bm_o || border_style == 3) {
+ unsigned passes = be;
+ unsigned w = bm_g->w;
+ unsigned h = bm_g->h;
+ unsigned stride = bm_g->stride;
+ unsigned char *buf = bm_g->buffer;
+ if(w && h){
+ while(passes--){
+ memset(tmp, 0, stride * 2);
+ render_priv->be_blur_func(buf, w, h, stride, tmp);
+ }
+ }
+ }
+ }
+
+ // Apply gaussian blur
+ if (blur_radius > 0.0) {
+ generate_tables(priv_blur, blur_radius);
+ if (bm_o)
+ ass_gauss_blur(bm_o->buffer, priv_blur->tmp,
+ bm_o->w, bm_o->h, bm_o->stride,
+ priv_blur->gt2, priv_blur->g_r,
+ priv_blur->g_w);
+ if (!bm_o || border_style == 3)
+ ass_gauss_blur(bm_g->buffer, priv_blur->tmp,
+ bm_g->w, bm_g->h, bm_g->stride,
+ priv_blur->gt2, priv_blur->g_r,
+ priv_blur->g_w);
+ }
+}
+
+void make_shadow_bitmap(CombinedBitmapInfo *info)
+{
+
+ // 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) {
+ return;
+ }
+
+ int border_style = info->border_style;
+ // Create shadow and fix outline as needed
+ if (info->bm_o && 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)) {
+ info->bm_s = copy_bitmap(info->bm_o);
+ } else if (info->bm_o) {
+ info->bm_s = info->bm_o;
+ info->bm_o = 0;
+ } else
+ info->bm_s = copy_bitmap(info->bm);
+
+ assert(info->bm_s);
+
+ shift_bitmap(info->bm_s, info->shadow_x, info->shadow_y);
}
/**
diff --git a/libass/ass_render.h b/libass/ass_render.h
index a41586b..ab0b704 100644
--- a/libass/ass_render.h
+++ b/libass/ass_render.h
@@ -41,9 +41,11 @@ typedef struct ass_shaper ASS_Shaper;
#include "ass_fontconfig.h"
#include "ass_library.h"
#include "ass_drawing.h"
+#include "ass_bitmap.h"
-#define GLYPH_CACHE_MAX 1000
-#define BITMAP_CACHE_MAX_SIZE 30 * 1048576
+#define GLYPH_CACHE_MAX 10000
+#define BITMAP_CACHE_MAX_SIZE 500 * 1048576
+#define COMPOSITE_CACHE_MAX_SIZE 500 * 1048576
#define PARSED_FADE (1<<0)
#define PARSED_A (1<<1)
@@ -103,6 +105,49 @@ typedef enum {
EF_KARAOKE_KO
} Effect;
+// 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;
+ 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;
+
+ 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;
+} CombinedBitmapInfo;
+
// describes a glyph
// GlyphInfo and TextInfo are used for text centering and word-wrapping operations
typedef struct glyph_info {
@@ -130,6 +175,7 @@ typedef struct glyph_info {
uint32_t c[4]; // colors
FT_Vector advance; // 26.6
FT_Vector cluster_advance;
+ char effect; // the first (leading) glyph of some effect ?
Effect effect_type;
int effect_timing; // time duration of current karaoke word
// after process_karaoke_effects: distance in pixels from the glyph origin.
@@ -170,9 +216,12 @@ typedef struct {
int length;
LineInfo *lines;
int n_lines;
+ CombinedBitmapInfo *combined_bitmaps;
+ unsigned n_bitmaps;
double height;
int max_glyphs;
int max_lines;
+ unsigned max_bitmaps;
} TextInfo;
// Renderer state.
@@ -251,8 +300,23 @@ typedef struct {
Cache *composite_cache;
size_t glyph_max;
size_t bitmap_max_size;
+ size_t composite_max_size;
} CacheStore;
+typedef void (*BitmapBlendFunc)(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src, intptr_t src_stride,
+ intptr_t height, intptr_t width);
+typedef void (*BitmapMulFunc)(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src1, intptr_t src1_stride,
+ uint8_t *src2, intptr_t src2_stride,
+ intptr_t width, intptr_t height);
+typedef void (*BEBlurFunc)(uint8_t *buf, intptr_t w,
+ intptr_t h, intptr_t stride,
+ uint16_t *tmp);
+typedef void (*RestrideBitmapFunc)(uint8_t *dst, intptr_t dst_stride,
+ uint8_t *src, intptr_t src_stride,
+ intptr_t width, intptr_t height);
+
struct ass_renderer {
ASS_Library *library;
FT_Library ftlibrary;
@@ -288,6 +352,12 @@ struct ass_renderer {
TextInfo text_info;
CacheStore cache;
+ BitmapBlendFunc add_bitmaps_func;
+ BitmapBlendFunc sub_bitmaps_func;
+ BitmapMulFunc mul_bitmaps_func;
+ BEBlurFunc be_blur_func;
+ RestrideBitmapFunc restride_bitmap_func;
+
FreeList *free_head;
FreeList *free_tail;
};
@@ -311,6 +381,8 @@ typedef struct {
void reset_render_context(ASS_Renderer *render_priv, ASS_Style *style);
void ass_free_images(ASS_Image *img);
+void make_shadow_bitmap(CombinedBitmapInfo* info);
+void apply_blur(CombinedBitmapInfo* info, ASS_Renderer *render_priv);
// XXX: this is actually in ass.c, includes should be fixed later on
void ass_lazy_track_init(ASS_Library *lib, ASS_Track *track);
diff --git a/libass/ass_shaper.c b/libass/ass_shaper.c
index 944603e..6aca5f5 100644
--- a/libass/ass_shaper.c
+++ b/libass/ass_shaper.c
@@ -726,14 +726,37 @@ void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
// set size and get glyph index
ass_font_get_index(render_priv->fontconfig_priv, info->font,
info->symbol, &info->face_index, &info->glyph_index);
- // shape runs share the same font face and size
+ // shape runs break on: xbord, ybord, xshad, yshad,
+ // all four colors, all four alphas, be, blur, fn, fs,
+ // fscx, fscy, fsp, bold, italic, underline, strikeout,
+ // frx, fry, frz, fax, fay, karaoke start, karaoke type,
+ // and on every line break
if (i > 0 && (last->font != info->font ||
+ last->face_index != info->face_index ||
+ last->script != info->script ||
last->font_size != info->font_size ||
+ last->c[0] != info->c[0] ||
+ last->c[1] != info->c[1] ||
+ last->c[2] != info->c[2] ||
+ last->c[3] != info->c[3] ||
+ last->be != info->be ||
+ last->blur != info->blur ||
+ last->shadow_x != info->shadow_x ||
+ last->shadow_y != info->shadow_y ||
+ last->frx != info->frx ||
+ last->fry != info->fry ||
+ last->frz != info->frz ||
+ last->fax != info->fax ||
+ last->fay != info->fay ||
last->scale_x != info->scale_x ||
last->scale_y != info->scale_y ||
+ last->border_style != info->border_style ||
+ last->border_x != info->border_x ||
+ last->border_y != info->border_y ||
last->hspacing != info->hspacing ||
- last->face_index != info->face_index ||
- last->script != info->script))
+ last->italic != info->italic ||
+ last->bold != info->bold ||
+ last->flags != info->flags))
shape_run++;
info->shape_run_id = shape_run;
}
diff --git a/libass/ass_utils.c b/libass/ass_utils.c
index 3c2c40a..2a95ddc 100644
--- a/libass/ass_utils.c
+++ b/libass/ass_utils.c
@@ -205,6 +205,38 @@ unsigned ass_utf8_get_char(char **str)
}
/**
+ * Original version from http://www.cprogramming.com/tutorial/utf8.c
+ * \brief Converts a single UTF-32 code point to UTF-8
+ * \param dest Buffer to write to. Writes a NULL terminator.
+ * \param ch 32-bit character code to convert
+ * \return number of bytes written
+ * converts a single character and ASSUMES YOU HAVE ENOUGH SPACE
+ */
+unsigned ass_utf8_put_char(char *dest, uint32_t ch)
+{
+ char *orig_dest = dest;
+
+ if (ch < 0x80) {
+ *dest++ = (char)ch;
+ } else if (ch < 0x800) {
+ *dest++ = (ch >> 6) | 0xC0;
+ *dest++ = (ch & 0x3F) | 0x80;
+ } else if (ch < 0x10000) {
+ *dest++ = (ch >> 12) | 0xE0;
+ *dest++ = ((ch >> 6) & 0x3F) | 0x80;
+ *dest++ = (ch & 0x3F) | 0x80;
+ } else if (ch < 0x110000) {
+ *dest++ = (ch >> 18) | 0xF0;
+ *dest++ = ((ch >> 12) & 0x3F) | 0x80;
+ *dest++ = ((ch >> 6) & 0x3F) | 0x80;
+ *dest++ = (ch & 0x3F) | 0x80;
+ }
+
+ *dest = '\0';
+ return dest - orig_dest;
+}
+
+/**
* \brief find style by name
* \param track track
* \param name style name
diff --git a/libass/ass_utils.h b/libass/ass_utils.h
index b797c8c..39a9da0 100644
--- a/libass/ass_utils.h
+++ b/libass/ass_utils.h
@@ -51,6 +51,7 @@ int strtocolor(ASS_Library *library, char **q, uint32_t *res, int hex);
char parse_bool(char *str);
int parse_ycbcr_matrix(char *str);
unsigned ass_utf8_get_char(char **str);
+unsigned ass_utf8_put_char(char *dest, uint32_t ch);
void ass_msg(ASS_Library *priv, int lvl, char *fmt, ...);
int lookup_style(ASS_Track *track, char *name);
ASS_Style *lookup_style_strict(ASS_Track *track, char *name);