]> granicus.if.org Git - libass/commitdiff
Subpixel accurate shadow rendering
authorGrigori Goronzy <greg@hein>
Mon, 29 Jun 2009 00:10:02 +0000 (02:10 +0200)
committerGrigori Goronzy <greg@hein>
Mon, 29 Jun 2009 00:10:02 +0000 (02:10 +0200)
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.

libass/ass_bitmap.c
libass/ass_bitmap.h
libass/ass_cache_template.c
libass/ass_render.c

index 46ead6eae68e348bac85c30a1c148c5311386f95..d7573ef7bd2c9409ae3b977ecb91c1011ab64cd2 100644 (file)
@@ -272,6 +272,56 @@ static bitmap_t *fix_outline_and_shadow(bitmap_t *bm_g, bitmap_t *bm_o)
     return bm_s;
 }
 
+/**
+ * \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;
 }
index 73a36698b742a6e5f4f1e38f96a320b9920795e4..f6b138058e991aee7c6edf632c9602c75cf28eb9 100644 (file)
@@ -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);
 
index 6c536fbb6f058255850c5a718a577693b01cb02b..dd9adbd4eaac66430c0ba395896a6baa880800ec 100644 (file)
@@ -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
index 2abb9d7fccc1b64a86be8da868050aa58c88401e..c779f3b17e690debd2a5d8aa9bdf611079a8b167 100644 (file)
@@ -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++;