summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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