summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrigori Goronzy <greg@hein>2009-06-29 02:10:02 +0200
committerGrigori Goronzy <greg@hein>2009-06-29 02:10:02 +0200
commite8433c6ff67409c55ff005fa3d7a28e52d8ec21b (patch)
treeae5f9b0a4ea53bb2c2c87b935eccdee0f1b298ab
parenta75f6a8d3d3ed6410cc2a4efa866a99152d78af6 (diff)
downloadlibass-e8433c6ff67409c55ff005fa3d7a28e52d8ec21b.tar.bz2
libass-e8433c6ff67409c55ff005fa3d7a28e52d8ec21b.tar.xz
Subpixel accurate shadow rendering
Shadows are handled different from glyph and outline. A shadow is the sum of glyph and outline bitmap; therefore shifting to the correct subpixel amount before rasterization is out of the question. Instead, the bitmap is shifted by its subpixel amount after being built from shadow and glyph. The bitmap cache was extended for this.
-rw-r--r--libass/ass_bitmap.c57
-rw-r--r--libass/ass_bitmap.h2
-rw-r--r--libass/ass_cache_template.c1
-rw-r--r--libass/ass_render.c18
4 files changed, 72 insertions, 6 deletions
diff --git a/libass/ass_bitmap.c b/libass/ass_bitmap.c
index 46ead6e..d7573ef 100644
--- a/libass/ass_bitmap.c
+++ b/libass/ass_bitmap.c
@@ -273,6 +273,56 @@ static bitmap_t *fix_outline_and_shadow(bitmap_t *bm_g, bitmap_t *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(unsigned char *buf, int w, int h, int shift_x,
+ int shift_y)
+{
+ int x, y, b;
+
+ // Shift in x direction
+ if (shift_x > 0) {
+ for (y = 0; y < h; y++) {
+ for (x = w - 1; x > 0; x--) {
+ b = (buf[x + y * w - 1] * shift_x) / 64;
+ buf[x + y * w - 1] -= b;
+ buf[x + y * w] += b;
+ }
+ }
+ } else if (shift_x < 0) {
+ shift_x = -shift_x;
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w - 1; x++) {
+ b = (buf[x + y * w + 1] * shift_x) / 64;
+ buf[x + y * w + 1] -= b;
+ buf[x + y * w] += b;
+ }
+ }
+ }
+
+ // Shift in y direction
+ if (shift_y > 0) {
+ for (x = 0; x < w; x++) {
+ for (y = h - 1; y > 0; y--) {
+ b = (buf[x + (y - 1) * w] * shift_y) / 64;
+ buf[x + (y - 1) * w] -= b;
+ buf[x + y * w] += b;
+ }
+ }
+ } else if (shift_y < 0) {
+ shift_y = -shift_y;
+ for (x = 0; x < w; x++) {
+ for (y = 0; y < h - 1; y++) {
+ b = (buf[x + (y + 1) * w] * shift_y) / 64;
+ buf[x + (y + 1) * w] -= b;
+ buf[x + y * w] += b;
+ }
+ }
+ }
+}
+
+/**
* \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
* This blur is the same as the one employed by vsfilter.
*/
@@ -303,11 +353,13 @@ static void be_blur(unsigned char *buf, int w, int h)
int glyph_to_bitmap(ass_synth_priv_t *priv_blur,
FT_Glyph glyph, FT_Glyph outline_glyph,
bitmap_t **bm_g, bitmap_t **bm_o, bitmap_t **bm_s,
- int be, double blur_radius)
+ int be, double blur_radius, FT_Vector shadow_offset)
{
int bord = be ? (be / 4 + 1) : 0;
blur_radius *= 2;
bord = (blur_radius > 0.0) ? blur_radius + 1 : bord;
+ if (bord == 0 && (shadow_offset.x || shadow_offset.y))
+ bord = 1;
assert(bm_g && bm_o && bm_s);
@@ -356,6 +408,9 @@ int glyph_to_bitmap(ass_synth_priv_t *priv_blur,
else
*bm_s = copy_bitmap(*bm_g);
+ shift_bitmap((*bm_s)->buffer, (*bm_s)->w,(*bm_s)->h,
+ shadow_offset.x, shadow_offset.y);
+
assert(bm_s);
return 0;
}
diff --git a/libass/ass_bitmap.h b/libass/ass_bitmap.h
index 73a3669..f6b1380 100644
--- a/libass/ass_bitmap.h
+++ b/libass/ass_bitmap.h
@@ -47,7 +47,7 @@ typedef struct bitmap_s {
int glyph_to_bitmap(ass_synth_priv_t *priv_blur, FT_Glyph glyph,
FT_Glyph outline_glyph, bitmap_t **bm_g,
bitmap_t **bm_o, bitmap_t **bm_s, int be,
- double blur_radius);
+ double blur_radius, FT_Vector shadow_offset);
void ass_free_bitmap(bitmap_t *bm);
diff --git a/libass/ass_cache_template.c b/libass/ass_cache_template.c
index 6c536fb..dd9adbd 100644
--- a/libass/ass_cache_template.c
+++ b/libass/ass_cache_template.c
@@ -77,6 +77,7 @@ START(bitmap, bipmap_hash_key_s)
GENERIC(int, shift_x)
GENERIC(int, shift_y)
FTVECTOR(advance) // subpixel shift vector
+ FTVECTOR(shadow_offset) // shadow subpixel shift
END(bitmap_hash_key_t)
// describes an outline glyph
diff --git a/libass/ass_render.c b/libass/ass_render.c
index 2abb9d7..c779f3b 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -41,7 +41,6 @@
#define MAX_LINES_INITIAL 64
#define BLUR_MAX_RADIUS 100.0
#define MAX_BE 100
-#define ROUND(x) ((int) ((x) + .5))
#define SUBPIXEL_MASK 63 // d6 bitmask for subpixel accuracy adjustment
static int last_render_id = 0;
@@ -574,10 +573,10 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x,
pen_x =
dst_x + (info->pos.x >> 6) +
- ROUND(info->shadow_x * render_priv->border_scale);
+ (int) (info->shadow_x * render_priv->border_scale);
pen_y =
dst_y + (info->pos.y >> 6) +
- ROUND(info->shadow_y * render_priv->border_scale);
+ (int) (info->shadow_y * render_priv->border_scale);
bm = info->bm_s;
here_tail = tail;
@@ -1874,7 +1873,8 @@ get_bitmap_glyph(ass_renderer_t *render_priv, glyph_info_t *info)
info->glyph, info->outline_glyph,
&info->bm, &info->bm_o,
&info->bm_s, info->be,
- info->blur * render_priv->border_scale);
+ info->blur * render_priv->border_scale,
+ info->hash_key.shadow_offset);
if (error)
info->symbol = 0;
@@ -2439,6 +2439,16 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
render_priv->state.be;
text_info->glyphs[text_info->length].hash_key.blur =
render_priv->state.blur;
+ text_info->glyphs[text_info->length].hash_key.shadow_offset.x =
+ double_to_d6(
+ render_priv->state.shadow_x * render_priv->border_scale -
+ (int) (render_priv->state.shadow_x *
+ render_priv->border_scale));
+ text_info->glyphs[text_info->length].hash_key.shadow_offset.y =
+ double_to_d6(
+ render_priv->state.shadow_y * render_priv->border_scale -
+ (int) (render_priv->state.shadow_y *
+ render_priv->border_scale));
text_info->length++;