summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrigori Goronzy <greg@blackbox>2009-07-16 02:17:06 +0200
committerGrigori Goronzy <greg@blackbox>2009-07-16 02:21:51 +0200
commited8ecf731cc77292e613b7cfe13fa64068040c97 (patch)
treeed51b66d0d4de9159414357b0b23dd1862519c40
parent94d41042a9006375eecf5332e9a59657a0c8a492 (diff)
downloadlibass-ed8ecf731cc77292e613b7cfe13fa64068040c97.tar.bz2
libass-ed8ecf731cc77292e613b7cfe13fa64068040c97.tar.xz
Implement vector clips
Make it possible to use drawings for clipping with \clip and \iclip. parse_tag was extended to parse drawings in \clip or \iclip tags in case parsing them as rectangular clips fails. These clip drawings are later rasterized and used for blending bitmaps, just after bitmaps are assembled into a list in render_text. Currently, the cache is not utilized for storing the parsed drawings or blended bitmaps.
-rw-r--r--libass/ass_drawing.c19
-rw-r--r--libass/ass_drawing.h2
-rw-r--r--libass/ass_render.c226
3 files changed, 224 insertions, 23 deletions
diff --git a/libass/ass_drawing.c b/libass/ass_drawing.c
index f75bbd9..091ec87 100644
--- a/libass/ass_drawing.c
+++ b/libass/ass_drawing.c
@@ -106,7 +106,7 @@ static void drawing_prepare(ass_drawing_t *drawing)
* \brief Finish a drawing. This only sets the horizontal advance according
* to the glyph's bbox at the moment.
*/
-static void drawing_finish(ass_drawing_t *drawing)
+static void drawing_finish(ass_drawing_t *drawing, int raw_mode)
{
int i, offset;
FT_BBox bbox;
@@ -127,6 +127,13 @@ static void drawing_finish(ass_drawing_t *drawing)
printf("contour %d\n", ol->contours[i]);
#endif
+ ass_msg(drawing->library, MSGL_V,
+ "Parsed drawing with %d points and %d contours", ol->n_points,
+ ol->n_contours);
+
+ if (raw_mode)
+ return;
+
FT_Outline_Get_CBox(&drawing->glyph->outline, &bbox);
drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin);
@@ -138,10 +145,6 @@ static void drawing_finish(ass_drawing_t *drawing)
drawing->scale_y);
for (i = 0; i < ol->n_points; i++)
ol->points[i].y += offset;
-
- ass_msg(drawing->library, MSGL_V,
- "Parsed drawing with %d points and %d contours", ol->n_points,
- ol->n_contours);
}
/*
@@ -361,7 +364,7 @@ ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
ass_drawing_t* drawing;
drawing = calloc(1, sizeof(*drawing));
- drawing->text = malloc(DRAWING_INITIAL_SIZE);
+ drawing->text = calloc(1, DRAWING_INITIAL_SIZE);
drawing->size = DRAWING_INITIAL_SIZE;
drawing->ftlibrary = lib;
@@ -411,7 +414,7 @@ void ass_drawing_hash(ass_drawing_t* drawing)
/*
* \brief Convert token list to outline. Calls the line and curve evaluators.
*/
-FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing)
+FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing, int raw_mode)
{
int started = 0;
ass_drawing_token_t *token;
@@ -474,7 +477,7 @@ FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing)
}
}
- drawing_finish(drawing);
+ drawing_finish(drawing, raw_mode);
drawing_free_tokens(drawing->tokens);
return &drawing->glyph;
}
diff --git a/libass/ass_drawing.h b/libass/ass_drawing.h
index dfd68f0..25cc4a7 100644
--- a/libass/ass_drawing.h
+++ b/libass/ass_drawing.h
@@ -72,6 +72,6 @@ ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
void ass_drawing_free(ass_drawing_t* drawing);
void ass_drawing_add_char(ass_drawing_t* drawing, char symbol);
void ass_drawing_hash(ass_drawing_t* drawing);
-FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing);
+FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing, int raw_mode);
#endif /* LIBASS_DRAWING_H */
diff --git a/libass/ass_render.c b/libass/ass_render.c
index ae65119..9e96345 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -59,6 +59,11 @@ typedef struct double_vector_s {
double y;
} double_vector_t;
+typedef struct free_list_s {
+ void *object;
+ struct free_list_s *next;
+} free_list_t;
+
typedef struct ass_settings_s {
int frame_width;
int frame_height;
@@ -174,6 +179,8 @@ typedef struct render_context_s {
double shadow_y;
int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags
ass_drawing_t *drawing; // current drawing
+ ass_drawing_t *clip_drawing;// clip vector
+ int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip
effect_t effect_type;
int effect_timing;
@@ -230,6 +237,9 @@ struct ass_renderer_s {
render_context_t state;
text_info_t text_info;
cache_store_t cache;
+
+ free_list_t *free_head;
+ free_list_t *free_tail;
};
struct render_priv_s {
@@ -663,6 +673,126 @@ render_overlap(ass_renderer_t *render_priv, ass_image_t **last_tail,
cache_add_composite(render_priv->cache.composite_cache, &hk, &chv);
}
+static void free_list_add(ass_renderer_t *render_priv, void *object)
+{
+ if (!render_priv->free_head) {
+ render_priv->free_head = calloc(1, sizeof(free_list_t));
+ render_priv->free_head->object = object;
+ render_priv->free_tail = render_priv->free_head;
+ } else {
+ free_list_t *l = calloc(1, sizeof(free_list_t));
+ l->object = object;
+ render_priv->free_tail->next = l;
+ render_priv->free_tail = render_priv->free_tail->next;
+ }
+}
+
+/**
+ * Iterate through a list of bitmaps and blend with clip vector, if
+ * applicable. The blended bitmaps are added to a free list which is freed
+ * at the start of a new frame.
+ */
+static void blend_vector_clip(ass_renderer_t *render_priv,
+ ass_image_t *head)
+{
+ FT_Glyph glyph;
+ FT_BitmapGlyph clip_bm;
+ ass_image_t *cur;
+ ass_drawing_t *drawing = render_priv->state.clip_drawing;
+
+ if (!drawing)
+ return;
+
+ // Rasterize it
+ FT_Glyph_Copy((FT_Glyph) drawing->glyph, &glyph);
+ FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
+ clip_bm = (FT_BitmapGlyph) glyph;
+ clip_bm->top = -clip_bm->top;
+
+ assert(clip_bm->bitmap.pitch >= 0);
+
+ // 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 ax, ay, aw, ah, as;
+ int bx, by, bw, bh, bs;
+ int aleft, atop, bleft, btop;
+ unsigned char *abuffer, *bbuffer, *nbuffer;
+
+ abuffer = cur->bitmap;
+ bbuffer = clip_bm->bitmap.buffer;
+ ax = cur->dst_x;
+ ay = cur->dst_y;
+ aw = cur->w;
+ ah = cur->h;
+ as = cur->stride;
+ bx = clip_bm->left;
+ by = clip_bm->top;
+ bw = clip_bm->bitmap.width;
+ bh = clip_bm->bitmap.rows;
+ bs = clip_bm->bitmap.pitch;
+
+ // 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);
+ aleft = left - ax;
+ atop = top - ay;
+ w = right - left;
+ h = bottom - top;
+ bleft = left - bx;
+ btop = top - by;
+
+ if (render_priv->state.clip_drawing_mode) {
+ // Inverse clip
+ if (ax + aw < bx || ay + ah < by || ax > bx + bw ||
+ ay > by + bh) {
+ continue;
+ }
+
+ // Allocate new buffer and add to free list
+ nbuffer = malloc(as * ah);
+ free_list_add(render_priv, nbuffer);
+
+ // Blend together
+ memcpy(nbuffer, abuffer, as * ah);
+ 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]);
+ }
+ } else {
+ // Regular clip
+ if (ax + aw < bx || ay + ah < by || ax > bx + bw ||
+ ay > by + bh) {
+ cur->w = cur->h = 0;
+ continue;
+ }
+
+ // Allocate new buffer and add to free list
+ nbuffer = calloc(as, ah);
+ free_list_add(render_priv, nbuffer);
+
+ // 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;
+ }
+ }
+ cur->bitmap = nbuffer;
+ }
+
+ // Free clip vector and its bitmap, we don't need it anymore
+ FT_Done_Glyph((FT_Glyph) drawing->glyph);
+ FT_Done_Glyph(glyph);
+ ass_drawing_free(render_priv->state.clip_drawing);
+ render_priv->state.clip_drawing = 0;
+}
+
/**
* \brief Convert text_info_t struct to ass_image_t list
* Splits glyphs in halves when needed (for \kf karaoke).
@@ -760,6 +890,8 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x,
}
*tail = 0;
+ blend_vector_clip(render_priv, head);
+
return head;
}
@@ -1018,6 +1150,53 @@ interpolate_alpha(long long now,
return a;
}
+#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;}
+#define skip(x) if (*p == (x)) ++p; else { return p; }
+#define skipopt(x) if (*p == (x)) { ++p; }
+
+/**
+ * Parse a vector clip into an outline, using the proper scaling
+ * parameters. Translate it to correct for screen borders, if needed.
+ */
+static char *parse_vector_clip(ass_renderer_t *render_priv, char *p)
+{
+ int scale = 1;
+ int res = 0;
+ ass_drawing_t *drawing;
+ render_priv->state.clip_drawing = ass_drawing_new(
+ render_priv->fontconfig_priv,
+ render_priv->state.font,
+ render_priv->settings.hinting,
+ render_priv->ftlibrary);
+ drawing = render_priv->state.clip_drawing;
+ skipopt('(');
+ res = mystrtoi(&p, &scale);
+ skipopt(',')
+ if (!res)
+ scale = 1;
+ drawing->scale = scale;
+ drawing->scale_x = render_priv->font_scale_x * render_priv->font_scale;
+ drawing->scale_y = render_priv->font_scale;
+ while (*p != ')' && *p != '}' && p != 0)
+ ass_drawing_add_char(drawing, *p++);
+ skipopt(')');
+ ass_drawing_parse(drawing, 1);
+ // We need to translate the clip according to screen borders
+ if (render_priv->settings.left_margin != 0 ||
+ render_priv->settings.top_margin != 0) {
+ FT_Vector trans = {
+ .x = int_to_d6(render_priv->settings.left_margin),
+ .y = -int_to_d6(render_priv->settings.top_margin),
+ };
+ FT_Outline_Translate(&drawing->glyph->outline, trans.x, trans.y);
+ }
+ ass_msg(render_priv->library, MSGL_DBG2,
+ "Parsed vector clip: scale %d, scales (%f, %f) string [%s]\n",
+ scale, drawing->scale_x, drawing->scale_y, drawing->text);
+
+ return p;
+}
+
static void reset_render_context(ass_renderer_t *);
/**
@@ -1027,9 +1206,6 @@ static void reset_render_context(ass_renderer_t *);
*/
static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
{
-#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;}
-#define skip(x) if (*p == (x)) ++p; else { return p; }
-
skip_to('\\');
skip('\\');
if ((*p == '}') || (*p == 0))
@@ -1081,15 +1257,16 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
} else if (mystrcmp(&p, "iclip")) {
int x0, y0, x1, y1;
int res = 1;
- skip('(');
+ char *start = p;
+ skipopt('(');
res &= mystrtoi(&p, &x0);
- skip(',');
+ skipopt(',');
res &= mystrtoi(&p, &y0);
- skip(',');
+ skipopt(',');
res &= mystrtoi(&p, &x1);
- skip(',');
+ skipopt(',');
res &= mystrtoi(&p, &y1);
- skip(')');
+ skipopt(')');
if (res) {
render_priv->state.clip_x0 =
render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
@@ -1100,6 +1277,9 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
render_priv->state.clip_y1 =
render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
render_priv->state.clip_mode = 1;
+ } else if (!render_priv->state.clip_drawing) {
+ p = parse_vector_clip(render_priv, start);
+ render_priv->state.clip_drawing_mode = 1;
} else
render_priv->state.clip_mode = 0;
} else if (mystrcmp(&p, "blur")) {
@@ -1398,17 +1578,18 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
skip_to(')'); // in case there is some unknown tag or a comment
skip(')');
} else if (mystrcmp(&p, "clip")) {
+ char *start = p;
int x0, y0, x1, y1;
int res = 1;
- skip('(');
+ skipopt('(');
res &= mystrtoi(&p, &x0);
- skip(',');
+ skipopt(',');
res &= mystrtoi(&p, &y0);
- skip(',');
+ skipopt(',');
res &= mystrtoi(&p, &x1);
- skip(',');
+ skipopt(',');
res &= mystrtoi(&p, &y1);
- skip(')');
+ skipopt(')');
if (res) {
render_priv->state.clip_x0 =
render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
@@ -1418,6 +1599,10 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
render_priv->state.clip_y1 =
render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
+ // Might be a vector clip
+ } else if (!render_priv->state.clip_drawing) {
+ p = parse_vector_clip(render_priv, start);
+ render_priv->state.clip_drawing_mode = 0;
} else {
render_priv->state.clip_x0 = 0;
render_priv->state.clip_y0 = 0;
@@ -1556,6 +1741,7 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
return p;
#undef skip
+#undef skipopt
#undef skip_to
}
@@ -1948,7 +2134,7 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol,
} else {
glyph_hash_val_t v;
if (drawing->hash) {
- ass_drawing_parse(drawing);
+ ass_drawing_parse(drawing, 0);
FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph);
} else {
info->glyph =
@@ -3017,6 +3203,18 @@ ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track,
if (render_priv->library != track->library)
return 1;
+ // Clear the list of object to be freed.
+ if (render_priv->free_head) {
+ free_list_t *item = render_priv->free_head;
+ while(item) {
+ free_list_t *oi = item;
+ free(item->object);
+ item = item->next;
+ free(oi);
+ }
+ render_priv->free_head = NULL;
+ }
+
ass_settings_t *settings_priv = &render_priv->settings;
if (!render_priv->settings.frame_width