summaryrefslogtreecommitdiffstats
path: root/libass/ass_render.c
diff options
context:
space:
mode:
Diffstat (limited to 'libass/ass_render.c')
-rw-r--r--libass/ass_render.c4661
1 files changed, 2360 insertions, 2301 deletions
diff --git a/libass/ass_render.c b/libass/ass_render.c
index 3048a2a233..6bc0c61bae 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -1,5 +1,3 @@
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
@@ -31,8 +29,6 @@
#include FT_GLYPH_H
#include FT_SYNTHESIS_H
-#include "mputils.h"
-
#include "ass.h"
#include "ass_font.h"
#include "ass_bitmap.h"
@@ -40,301 +36,283 @@
#include "ass_utils.h"
#include "ass_fontconfig.h"
#include "ass_library.h"
+#include "ass_drawing.h"
+#include "ass_render.h"
+#include "ass_parse.h"
+
+#define MAX_GLYPHS_INITIAL 1024
+#define MAX_LINES_INITIAL 64
+#define SUBPIXEL_MASK 63
+#define SUBPIXEL_ACCURACY 7 // d6 mask for subpixel accuracy adjustment
+#define GLYPH_CACHE_MAX 1000
+#define BITMAP_CACHE_MAX_SIZE 50 * 1048576
+
+static void ass_lazy_track_init(ASS_Renderer *render_priv)
+{
+ ASS_Track *track = render_priv->track;
+
+ if (track->PlayResX && track->PlayResY)
+ return;
+ if (!track->PlayResX && !track->PlayResY) {
+ ass_msg(render_priv->library, MSGL_WARN,
+ "Neither PlayResX nor PlayResY defined. Assuming 384x288");
+ track->PlayResX = 384;
+ track->PlayResY = 288;
+ } else {
+ if (!track->PlayResY && track->PlayResX == 1280) {
+ track->PlayResY = 1024;
+ ass_msg(render_priv->library, MSGL_WARN,
+ "PlayResY undefined, setting to %d", track->PlayResY);
+ } else if (!track->PlayResY) {
+ track->PlayResY = track->PlayResX * 3 / 4;
+ ass_msg(render_priv->library, MSGL_WARN,
+ "PlayResY undefined, setting to %d", track->PlayResY);
+ } else if (!track->PlayResX && track->PlayResY == 1024) {
+ track->PlayResX = 1280;
+ ass_msg(render_priv->library, MSGL_WARN,
+ "PlayResX undefined, setting to %d", track->PlayResX);
+ } else if (!track->PlayResX) {
+ track->PlayResX = track->PlayResY * 4 / 3;
+ ass_msg(render_priv->library, MSGL_WARN,
+ "PlayResX undefined, setting to %d", track->PlayResX);
+ }
+ }
+}
-#define MAX_GLYPHS 3000
-#define MAX_LINES 300
-#define BLUR_MAX_RADIUS 50.0
-#define MAX_BE 100
-#define ROUND(x) ((int) ((x) + .5))
-#define SUBPIXEL_MASK 56 // d6 bitmask for subpixel accuracy adjustment
-
-static int last_render_id = 0;
-
-typedef struct ass_settings_s {
- int frame_width;
- int frame_height;
- double font_size_coeff; // font size multiplier
- double line_spacing; // additional line spacing (in frame pixels)
- int top_margin; // height of top margin. Everything except toptitles is shifted down by top_margin.
- int bottom_margin; // height of bottom margin. (frame_height - top_margin - bottom_margin) is original video height.
- int left_margin;
- int right_margin;
- int use_margins; // 0 - place all subtitles inside original frame
- // 1 - use margins for placing toptitles and subtitles
- double aspect; // frame aspect ratio, d_width / d_height.
- ass_hinting_t hinting;
-
- char* default_font;
- char* default_family;
-} ass_settings_t;
-
-// a rendered event
-typedef struct event_images_s {
- ass_image_t* imgs;
- int top, height;
- int detect_collisions;
- int shift_direction;
- ass_event_t* event;
-} event_images_t;
-
-struct ass_renderer_s {
- ass_library_t* library;
- FT_Library ftlibrary;
- fc_instance_t* fontconfig_priv;
- ass_settings_t settings;
- int render_id;
- ass_synth_priv_t* synth_priv;
-
- ass_image_t* images_root; // rendering result is stored here
- ass_image_t* prev_images_root;
-
- event_images_t* eimg; // temporary buffer for sorting rendered events
- int eimg_size; // allocated buffer size
-};
-
-typedef enum {EF_NONE = 0, EF_KARAOKE, EF_KARAOKE_KF, EF_KARAOKE_KO} effect_t;
-
-// describes a glyph
-// glyph_info_t and text_info_t are used for text centering and word-wrapping operations
-typedef struct glyph_info_s {
- unsigned symbol;
- FT_Glyph glyph;
- FT_Glyph outline_glyph;
- bitmap_t* bm; // glyph bitmap
- bitmap_t* bm_o; // outline bitmap
- bitmap_t* bm_s; // shadow bitmap
- FT_BBox bbox;
- FT_Vector pos;
- char linebreak; // the first (leading) glyph of some line ?
- uint32_t c[4]; // colors
- FT_Vector advance; // 26.6
- effect_t 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 effect_skip_timing; // delay after the end of last karaoke word
- int asc, desc; // font max ascender and descender
-// int height;
- int be; // blur edges
- double blur; // gaussian blur
- double shadow;
- double frx, fry, frz; // rotation
-
- bitmap_hash_key_t hash_key;
-} glyph_info_t;
-
-typedef struct line_info_s {
- int asc, desc;
-} line_info_t;
-
-typedef struct text_info_s {
- glyph_info_t* glyphs;
- int length;
- line_info_t lines[MAX_LINES];
- int n_lines;
- int height;
-} text_info_t;
-
-
-// Renderer state.
-// Values like current font face, color, screen position, clipping and so on are stored here.
-typedef struct render_context_s {
- ass_event_t* event;
- ass_style_t* style;
-
- ass_font_t* font;
- char* font_path;
- double font_size;
-
- FT_Stroker stroker;
- int alignment; // alignment overrides go here; if zero, style value will be used
- double frx, fry, frz;
- enum { EVENT_NORMAL, // "normal" top-, sub- or mid- title
- EVENT_POSITIONED, // happens after pos(,), margins are ignored
- EVENT_HSCROLL, // "Banner" transition effect, text_width is unlimited
- EVENT_VSCROLL // "Scroll up", "Scroll down" transition effects
- } evt_type;
- double pos_x, pos_y; // position
- double org_x, org_y; // origin
- char have_origin; // origin is explicitly defined; if 0, get_base_point() is used
- double scale_x, scale_y;
- double hspacing; // distance between letters, in pixels
- double border; // outline width
- uint32_t c[4]; // colors(Primary, Secondary, so on) in RGBA
- int clip_x0, clip_y0, clip_x1, clip_y1;
- char detect_collisions;
- uint32_t fade; // alpha from \fad
- char be; // blur edges
- double blur; // gaussian blur
- double shadow;
- int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags
-
- effect_t effect_type;
- int effect_timing;
- int effect_skip_timing;
-
- enum { SCROLL_LR, // left-to-right
- SCROLL_RL,
- SCROLL_TB, // top-to-bottom
- SCROLL_BT
- } scroll_direction; // for EVENT_HSCROLL, EVENT_VSCROLL
- int scroll_shift;
-
- // face properties
- char* family;
- unsigned bold;
- unsigned italic;
- int treat_family_as_pattern;
-
-} render_context_t;
-
-// frame-global data
-typedef struct frame_context_s {
- ass_renderer_t* ass_priv;
- int width, height; // screen dimensions
- int orig_height; // frame height ( = screen height - margins )
- int orig_width; // frame width ( = screen width - margins )
- int orig_height_nocrop; // frame height ( = screen height - margins + cropheight)
- int orig_width_nocrop; // frame width ( = screen width - margins + cropwidth)
- ass_track_t* track;
- long long time; // frame's timestamp, ms
- double font_scale;
- double font_scale_x; // x scale applied to all glyphs to preserve text aspect ratio
- double border_scale;
-} frame_context_t;
-
-static ass_renderer_t* ass_renderer;
-static ass_settings_t* global_settings;
-static text_info_t text_info;
-static render_context_t render_context;
-static frame_context_t frame_context;
-
-struct render_priv_s {
- int top, height;
- int render_id;
-};
-
-static void ass_lazy_track_init(void)
+ASS_Renderer *ass_renderer_init(ASS_Library *library)
{
- ass_track_t* track = frame_context.track;
- if (track->PlayResX && track->PlayResY)
- return;
- if (!track->PlayResX && !track->PlayResY) {
- mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NeitherPlayResXNorPlayResYDefined);
- track->PlayResX = 384;
- track->PlayResY = 288;
- } else {
- double orig_aspect = (global_settings->aspect * frame_context.height * frame_context.orig_width) /
- frame_context.orig_height / frame_context.width;
- if (!track->PlayResY && track->PlayResX == 1280) {
- track->PlayResY = 1024;
- mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResYUndefinedSettingY, track->PlayResY);
- } else if (!track->PlayResY) {
- track->PlayResY = track->PlayResX / orig_aspect + .5;
- mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResYUndefinedSettingY, track->PlayResY);
- } else if (!track->PlayResX && track->PlayResY == 1024) {
- track->PlayResX = 1280;
- mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResXUndefinedSettingX, track->PlayResX);
- } else if (!track->PlayResX) {
- track->PlayResX = track->PlayResY * orig_aspect + .5;
- mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResXUndefinedSettingX, track->PlayResX);
- }
- }
+ int error;
+ FT_Library ft;
+ ASS_Renderer *priv = 0;
+ int vmajor, vminor, vpatch;
+
+ error = FT_Init_FreeType(&ft);
+ if (error) {
+ ass_msg(library, MSGL_FATAL, "%s failed", "FT_Init_FreeType");
+ goto ass_init_exit;
+ }
+
+ FT_Library_Version(ft, &vmajor, &vminor, &vpatch);
+ ass_msg(library, MSGL_V, "FreeType library version: %d.%d.%d",
+ vmajor, vminor, vpatch);
+ ass_msg(library, MSGL_V, "FreeType headers version: %d.%d.%d",
+ FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
+
+ priv = calloc(1, sizeof(ASS_Renderer));
+ if (!priv) {
+ FT_Done_FreeType(ft);
+ goto ass_init_exit;
+ }
+
+ priv->synth_priv = ass_synth_init(BLUR_MAX_RADIUS);
+
+ priv->library = library;
+ priv->ftlibrary = ft;
+ // images_root and related stuff is zero-filled in calloc
+
+ priv->cache.font_cache = ass_font_cache_init(library);
+ priv->cache.bitmap_cache = ass_bitmap_cache_init(library);
+ priv->cache.composite_cache = ass_composite_cache_init(library);
+ priv->cache.glyph_cache = ass_glyph_cache_init(library);
+ priv->cache.glyph_max = GLYPH_CACHE_MAX;
+ priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE;
+
+ priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL;
+ priv->text_info.max_lines = MAX_LINES_INITIAL;
+ priv->text_info.glyphs =
+ calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo));
+ priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo));
+
+ ass_init_exit:
+ if (priv)
+ ass_msg(library, MSGL_INFO, "Init");
+ else
+ ass_msg(library, MSGL_ERR, "Init failed");
+
+ return priv;
}
-ass_renderer_t* ass_renderer_init(ass_library_t* library)
+void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max,
+ int bitmap_max)
{
- int error;
- FT_Library ft;
- ass_renderer_t* priv = 0;
- int vmajor, vminor, vpatch;
-
- memset(&render_context, 0, sizeof(render_context));
- memset(&frame_context, 0, sizeof(frame_context));
- memset(&text_info, 0, sizeof(text_info));
-
- error = FT_Init_FreeType( &ft );
- if ( error ) {
- mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FT_Init_FreeTypeFailed);
- goto ass_init_exit;
- }
-
- FT_Library_Version(ft, &vmajor, &vminor, &vpatch);
- mp_msg(MSGT_ASS, MSGL_V, "FreeType library version: %d.%d.%d\n",
- vmajor, vminor, vpatch);
- mp_msg(MSGT_ASS, MSGL_V, "FreeType headers version: %d.%d.%d\n",
- FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
-
- priv = calloc(1, sizeof(ass_renderer_t));
- if (!priv) {
- FT_Done_FreeType(ft);
- goto ass_init_exit;
- }
-
- priv->synth_priv = ass_synth_init(BLUR_MAX_RADIUS);
-
- priv->library = library;
- priv->ftlibrary = ft;
- // images_root and related stuff is zero-filled in calloc
-
- ass_font_cache_init();
- ass_bitmap_cache_init();
- ass_composite_cache_init();
- ass_glyph_cache_init();
-
- text_info.glyphs = calloc(MAX_GLYPHS, sizeof(glyph_info_t));
-
-ass_init_exit:
- if (priv) mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_Init);
- else mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_InitFailed);
-
- return priv;
+ render_priv->cache.glyph_max = glyph_max ? glyph_max : GLYPH_CACHE_MAX;
+ render_priv->cache.bitmap_max_size = bitmap_max ? 1048576 * bitmap_max :
+ BITMAP_CACHE_MAX_SIZE;
}
-void ass_renderer_done(ass_renderer_t* priv)
+static void free_list_clear(ASS_Renderer *render_priv)
{
- ass_font_cache_done();
- ass_bitmap_cache_done();
- ass_composite_cache_done();
- ass_glyph_cache_done();
- if (render_context.stroker) {
- FT_Stroker_Done(render_context.stroker);
- render_context.stroker = 0;
- }
- if (priv && priv->ftlibrary) FT_Done_FreeType(priv->ftlibrary);
- if (priv && priv->fontconfig_priv) fontconfig_done(priv->fontconfig_priv);
- if (priv && priv->synth_priv) ass_synth_done(priv->synth_priv);
- if (priv && priv->eimg) free(priv->eimg);
- if (priv) free(priv);
- if (text_info.glyphs) free(text_info.glyphs);
+ if (render_priv->free_head) {
+ FreeList *item = render_priv->free_head;
+ while(item) {
+ FreeList *oi = item;
+ free(item->object);
+ item = item->next;
+ free(oi);
+ }
+ render_priv->free_head = NULL;
+ }
+}
+
+static void ass_free_images(ASS_Image *img);
+
+void ass_renderer_done(ASS_Renderer *render_priv)
+{
+ ass_font_cache_done(render_priv->cache.font_cache);
+ ass_bitmap_cache_done(render_priv->cache.bitmap_cache);
+ ass_composite_cache_done(render_priv->cache.composite_cache);
+ ass_glyph_cache_done(render_priv->cache.glyph_cache);
+
+ ass_free_images(render_priv->images_root);
+ ass_free_images(render_priv->prev_images_root);
+
+ if (render_priv->state.stroker) {
+ FT_Stroker_Done(render_priv->state.stroker);
+ render_priv->state.stroker = 0;
+ }
+ if (render_priv && render_priv->ftlibrary)
+ FT_Done_FreeType(render_priv->ftlibrary);
+ if (render_priv && render_priv->fontconfig_priv)
+ fontconfig_done(render_priv->fontconfig_priv);
+ if (render_priv && render_priv->synth_priv)
+ ass_synth_done(render_priv->synth_priv);
+ if (render_priv && render_priv->eimg)
+ free(render_priv->eimg);
+ free(render_priv->text_info.glyphs);
+ free(render_priv->text_info.lines);
+
+ free(render_priv->settings.default_font);
+ free(render_priv->settings.default_family);
+
+ free_list_clear(render_priv);
+ free(render_priv);
}
/**
- * \brief Create a new ass_image_t
- * Parameters are the same as ass_image_t fields.
+ * \brief Create a new ASS_Image
+ * Parameters are the same as ASS_Image fields.
*/
-static ass_image_t* my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitmap_h, int stride, int dst_x, int dst_y, uint32_t color)
+static ASS_Image *my_draw_bitmap(unsigned char *bitmap, int bitmap_w,
+ int bitmap_h, int stride, int dst_x,
+ int dst_y, uint32_t color)
{
- ass_image_t* img = calloc(1, sizeof(ass_image_t));
-
- assert(dst_x >= 0);
- assert(dst_y >= 0);
- assert(dst_x + bitmap_w <= frame_context.width);
- assert(dst_y + bitmap_h <= frame_context.height);
-
- img->w = bitmap_w;
- img->h = bitmap_h;
- img->stride = stride;
- img->bitmap = bitmap;
- img->color = color;
- img->dst_x = dst_x;
- img->dst_y = dst_y;
-
- return img;
+ ASS_Image *img = calloc(1, sizeof(ASS_Image));
+
+ img->w = bitmap_w;
+ img->h = bitmap_h;
+ img->stride = stride;
+ img->bitmap = bitmap;
+ img->color = color;
+ img->dst_x = dst_x;
+ img->dst_y = dst_y;
+
+ return img;
+}
+
+static double x2scr_pos(ASS_Renderer *render_priv, double x);
+static double y2scr_pos(ASS_Renderer *render_priv, double y);
+
+/*
+ * \brief Convert bitmap glyphs into ASS_Image list with inverse clipping
+ *
+ * Inverse clipping with the following strategy:
+ * - find rectangle from (x0, y0) to (cx0, y1)
+ * - find rectangle from (cx0, y0) to (cx1, cy0)
+ * - find rectangle from (cx0, cy1) to (cx1, y1)
+ * - find rectangle from (cx1, y0) to (x1, y1)
+ * These rectangles can be invalid and in this case are discarded.
+ * Afterwards, they are clipped against the screen coordinates.
+ * In an additional pass, the rectangles need to be split up left/right for
+ * karaoke effects. This can result in a lot of bitmaps (6 to be exact).
+ */
+static ASS_Image **render_glyph_i(ASS_Renderer *render_priv,
+ Bitmap *bm, int dst_x, int dst_y,
+ uint32_t color, uint32_t color2, int brk,
+ ASS_Image **tail)
+{
+ int i, j, x0, y0, x1, y1, cx0, cy0, cx1, cy1, sx, sy, zx, zy;
+ Rect r[4];
+ ASS_Image *img;
+
+ dst_x += bm->left;
+ dst_y += bm->top;
+
+ // we still need to clip against screen boundaries
+ zx = x2scr_pos(render_priv, 0);
+ zy = y2scr_pos(render_priv, 0);
+ sx = x2scr_pos(render_priv, render_priv->track->PlayResX);
+ sy = y2scr_pos(render_priv, render_priv->track->PlayResY);
+
+ x0 = 0;
+ y0 = 0;
+ x1 = bm->w;
+ y1 = bm->h;
+ cx0 = render_priv->state.clip_x0 - dst_x;
+ cy0 = render_priv->state.clip_y0 - dst_y;
+ cx1 = render_priv->state.clip_x1 - dst_x;
+ cy1 = render_priv->state.clip_y1 - dst_y;
+
+ // calculate rectangles and discard invalid ones while we're at it.
+ i = 0;
+ r[i].x0 = x0;
+ r[i].y0 = y0;
+ r[i].x1 = (cx0 > x1) ? x1 : cx0;
+ r[i].y1 = y1;
+ if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
+ r[i].x0 = (cx0 < 0) ? x0 : cx0;
+ r[i].y0 = y0;
+ r[i].x1 = (cx1 > x1) ? x1 : cx1;
+ r[i].y1 = (cy0 > y1) ? y1 : cy0;
+ if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
+ r[i].x0 = (cx0 < 0) ? x0 : cx0;
+ r[i].y0 = (cy1 < 0) ? y0 : cy1;
+ r[i].x1 = (cx1 > x1) ? x1 : cx1;
+ r[i].y1 = y1;
+ if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
+ r[i].x0 = (cx1 < 0) ? x0 : cx1;
+ r[i].y0 = y0;
+ r[i].x1 = x1;
+ r[i].y1 = y1;
+ if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++;
+
+ // clip each rectangle to screen coordinates
+ for (j = 0; j < i; j++) {
+ r[j].x0 = (r[j].x0 + dst_x < zx) ? zx - dst_x : r[j].x0;
+ r[j].y0 = (r[j].y0 + dst_y < zy) ? zy - dst_y : r[j].y0;
+ r[j].x1 = (r[j].x1 + dst_x > sx) ? sx - dst_x : r[j].x1;
+ r[j].y1 = (r[j].y1 + dst_y > sy) ? sy - dst_y : r[j].y1;
+ }
+
+ // draw the rectangles
+ for (j = 0; j < i; j++) {
+ int lbrk = brk;
+ // kick out rectangles that are invalid now
+ if (r[j].x1 <= r[j].x0 || r[j].y1 <= r[j].y0)
+ continue;
+ // split up into left and right for karaoke, if needed
+ if (lbrk > r[j].x0) {
+ if (lbrk > r[j].x1) lbrk = r[j].x1;
+ img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + r[j].x0,
+ lbrk - r[j].x0, r[j].y1 - r[j].y0,
+ bm->w, dst_x + r[j].x0, dst_y + r[j].y0, color);
+ *tail = img;
+ tail = &img->next;
+ }
+ if (lbrk < r[j].x1) {
+ if (lbrk < r[j].x0) lbrk = r[j].x0;
+ img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + lbrk,
+ r[j].x1 - lbrk, r[j].y1 - r[j].y0,
+ bm->w, dst_x + lbrk, dst_y + r[j].y0, color2);
+ *tail = img;
+ tail = &img->next;
+ }
+ }
+
+ return tail;
}
/**
- * \brief convert bitmap glyph into ass_image_t struct(s)
+ * \brief convert bitmap glyph into ASS_Image struct(s)
* \param bit freetype bitmap glyph, FT_PIXEL_MODE_GRAY
* \param dst_x bitmap x coordinate in video frame
* \param dst_y bitmap y coordinate in video frame
@@ -345,1156 +323,871 @@ static ass_image_t* my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitm
* \return pointer to the new list tail
* Performs clipping. Uses my_draw_bitmap for actual bitmap convertion.
*/
-static ass_image_t** render_glyph(bitmap_t* bm, int dst_x, int dst_y, uint32_t color, uint32_t color2, int brk, ass_image_t** tail)
+static ASS_Image **
+render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y,
+ uint32_t color, uint32_t color2, int brk, ASS_Image **tail)
{
- // brk is relative to dst_x
- // color = color left of brk
- // color2 = color right of brk
- int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap
- int tmp;
- ass_image_t* img;
-
- const int clip_x0 = render_context.clip_x0;
- const int clip_y0 = render_context.clip_y0;
- const int clip_x1 = render_context.clip_x1;
- const int clip_y1 = render_context.clip_y1;
-
- dst_x += bm->left;
- dst_y += bm->top;
- brk -= bm->left;
-
- b_x0 = 0;
- b_y0 = 0;
- b_x1 = bm->w;
- b_y1 = bm->h;
-
- tmp = dst_x - clip_x0;
- if (tmp < 0) {
- mp_msg(MSGT_ASS, MSGL_DBG2, "clip left\n");
- b_x0 = - tmp;
- }
- tmp = dst_y - clip_y0;
- if (tmp < 0) {
- mp_msg(MSGT_ASS, MSGL_DBG2, "clip top\n");
- b_y0 = - tmp;
- }
- tmp = clip_x1 - dst_x - bm->w;
- if (tmp < 0) {
- mp_msg(MSGT_ASS, MSGL_DBG2, "clip right\n");
- b_x1 = bm->w + tmp;
- }
- tmp = clip_y1 - dst_y - bm->h;
- if (tmp < 0) {
- mp_msg(MSGT_ASS, MSGL_DBG2, "clip bottom\n");
- b_y1 = bm->h + tmp;
- }
-
- if ((b_y0 >= b_y1) || (b_x0 >= b_x1))
- return tail;
-
- if (brk > b_x0) { // draw left part
- if (brk > b_x1) brk = b_x1;
- img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0,
- brk - b_x0, b_y1 - b_y0, bm->w,
- dst_x + b_x0, dst_y + b_y0, color);
- *tail = img;
- tail = &img->next;
- }
- if (brk < b_x1) { // draw right part
- if (brk < b_x0) brk = b_x0;
- img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk,
- b_x1 - brk, b_y1 - b_y0, bm->w,
- dst_x + brk, dst_y + b_y0, color2);
- *tail = img;
- tail = &img->next;
- }
- return tail;
+ // Inverse clipping in use?
+ if (render_priv->state.clip_mode)
+ return render_glyph_i(render_priv, bm, dst_x, dst_y, color, color2,
+ brk, tail);
+
+ // brk is relative to dst_x
+ // color = color left of brk
+ // color2 = color right of brk
+ int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap
+ int clip_x0, clip_y0, clip_x1, clip_y1;
+ int tmp;
+ ASS_Image *img;
+
+ dst_x += bm->left;
+ dst_y += bm->top;
+ brk -= bm->left;
+
+ // clipping
+ clip_x0 = FFMINMAX(render_priv->state.clip_x0, 0, render_priv->width);
+ clip_y0 = FFMINMAX(render_priv->state.clip_y0, 0, render_priv->height);
+ clip_x1 = FFMINMAX(render_priv->state.clip_x1, 0, render_priv->width);
+ clip_y1 = FFMINMAX(render_priv->state.clip_y1, 0, render_priv->height);
+ b_x0 = 0;
+ b_y0 = 0;
+ b_x1 = bm->w;
+ b_y1 = bm->h;
+
+ tmp = dst_x - clip_x0;
+ if (tmp < 0) {
+ ass_msg(render_priv->library, MSGL_DBG2, "clip left");
+ b_x0 = -tmp;
+ }
+ tmp = dst_y - clip_y0;
+ if (tmp < 0) {
+ ass_msg(render_priv->library, MSGL_DBG2, "clip top");
+ b_y0 = -tmp;
+ }
+ tmp = clip_x1 - dst_x - bm->w;
+ if (tmp < 0) {
+ ass_msg(render_priv->library, MSGL_DBG2, "clip right");
+ b_x1 = bm->w + tmp;
+ }
+ tmp = clip_y1 - dst_y - bm->h;
+ if (tmp < 0) {
+ ass_msg(render_priv->library, MSGL_DBG2, "clip bottom");
+ b_y1 = bm->h + tmp;
+ }
+
+ if ((b_y0 >= b_y1) || (b_x0 >= b_x1))
+ return tail;
+
+ if (brk > b_x0) { // draw left part
+ if (brk > b_x1)
+ brk = b_x1;
+ img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0,
+ brk - b_x0, b_y1 - b_y0, bm->w,
+ dst_x + b_x0, dst_y + b_y0, color);
+ *tail = img;
+ tail = &img->next;
+ }
+ if (brk < b_x1) { // draw right part
+ if (brk < b_x0)
+ brk = b_x0;
+ img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk,
+ b_x1 - brk, b_y1 - b_y0, bm->w,
+ dst_x + brk, dst_y + b_y0, color2);
+ *tail = img;
+ tail = &img->next;
+ }
+ return tail;
}
/**
- * \brief Replaces the bitmap buffer in ass_image_t with its copy.
- *
- * @param img Image to operate on.
- * @return Address of the old buffer.
+ * \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_data(ass_image_t* img)
+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;
+ 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, composite them together
+ * 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_image_t** last_tail, ass_image_t** tail, bitmap_hash_key_t *last_hash, bitmap_hash_key_t* hash) {
- int left, top, bottom, right;
- int old_left, old_top, w, h, cur_left, cur_top;
- int x, y, opos, cpos;
- char m;
- composite_hash_key_t hk;
- composite_hash_val_t *hv;
- composite_hash_key_t *nhk;
- 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
- memset(&hk, 0, sizeof(hk));
- memcpy(&hk.a, last_hash, sizeof(*last_hash));
- memcpy(&hk.b, hash, sizeof(*hash));
- hk.aw = aw;
- hk.ah = ah;
- hk.bw = bw;
- hk.bh = bh;
- hk.ax = ax;
- hk.ay = ay;
- hk.bx = bx;
- hk.by = by;
- hv = cache_find_composite(&hk);
- if (hv) {
- (*last_tail)->bitmap = hv->a;
- (*tail)->bitmap = hv->b;
- return;
- }
-
- // Allocate new bitmaps and copy over data
- a = clone_bitmap_data(*last_tail);
- b = clone_bitmap_data(*tail);
-
- // Composite 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 = (a[opos] > b[cpos]) ? a[opos] : b[cpos];
- (*last_tail)->bitmap[opos] = 0;
- (*tail)->bitmap[cpos] = m;
- }
-
- // Insert bitmaps into the cache
- nhk = calloc(1, sizeof(*nhk));
- memcpy(nhk, &hk, sizeof(*nhk));
- hv = calloc(1, sizeof(*hv));
- hv->a = (*last_tail)->bitmap;
- hv->b = (*tail)->bitmap;
- cache_add_composite(nhk, hv);
+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
+ memset(&hk, 0, sizeof(hk));
+ 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 = cache_find_composite(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;
+ cache_add_composite(render_priv->cache.composite_cache, &hk, &chv);
}
-/**
- * \brief Convert text_info_t struct to ass_image_t list
- * Splits glyphs in halves when needed (for \kf karaoke).
- */
-static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y)
+static void free_list_add(ASS_Renderer *render_priv, void *object)
{
- int pen_x, pen_y;
- int i;
- bitmap_t* bm;
- ass_image_t* head;
- ass_image_t** tail = &head;
- ass_image_t** last_tail = 0;
- ass_image_t** here_tail = 0;
- bitmap_hash_key_t* last_hash = 0;
-
- for (i = 0; i < text_info->length; ++i) {
- glyph_info_t* info = text_info->glyphs + i;
- if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s || (info->shadow == 0))
- continue;
-
- pen_x = dst_x + info->pos.x + ROUND(info->shadow * frame_context.border_scale);
- pen_y = dst_y + info->pos.y + ROUND(info->shadow * frame_context.border_scale);
- bm = info->bm_s;
-
- here_tail = tail;
- tail = render_glyph(bm, pen_x, pen_y, info->c[3], 0, 1000000, tail);
- if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0))
- render_overlap(last_tail, here_tail, last_hash, &info->hash_key);
- last_tail = here_tail;
- last_hash = &info->hash_key;
- }
-
- last_tail = 0;
- for (i = 0; i < text_info->length; ++i) {
- glyph_info_t* info = text_info->glyphs + i;
- if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o)
- continue;
-
- pen_x = dst_x + info->pos.x;
- pen_y = dst_y + info->pos.y;
- bm = info->bm_o;
-
- if ((info->effect_type == EF_KARAOKE_KO) && (info->effect_timing <= info->bbox.xMax)) {
- // do nothing
- } else {
- here_tail = tail;
- tail = render_glyph(bm, pen_x, pen_y, info->c[2], 0, 1000000, tail);
- if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0))
- render_overlap(last_tail, here_tail, last_hash, &info->hash_key);
- last_tail = here_tail;
- last_hash = &info->hash_key;
- }
- }
- for (i = 0; i < text_info->length; ++i) {
- glyph_info_t* info = text_info->glyphs + i;
- if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm)
- continue;
-
- pen_x = dst_x + info->pos.x;
- pen_y = dst_y + info->pos.y;
- bm = info->bm;
-
- if ((info->effect_type == EF_KARAOKE) || (info->effect_type == EF_KARAOKE_KO)) {
- if (info->effect_timing > info->bbox.xMax)
- tail = render_glyph(bm, pen_x, pen_y, info->c[0], 0, 1000000, tail);
- else
- tail = render_glyph(bm, pen_x, pen_y, info->c[1], 0, 1000000, tail);
- } else if (info->effect_type == EF_KARAOKE_KF) {
- tail = render_glyph(bm, pen_x, pen_y, info->c[0], info->c[1], info->effect_timing, tail);
- } else
- tail = render_glyph(bm, pen_x, pen_y, info->c[0], 0, 1000000, tail);
- }
-
- *tail = 0;
- return head;
+ if (!render_priv->free_head) {
+ render_priv->free_head = calloc(1, sizeof(FreeList));
+ render_priv->free_head->object = object;
+ render_priv->free_tail = render_priv->free_head;
+ } else {
+ FreeList *l = calloc(1, sizeof(FreeList));
+ l->object = object;
+ render_priv->free_tail->next = l;
+ render_priv->free_tail = render_priv->free_tail->next;
+ }
}
/**
- * \brief Mapping between script and screen coordinates
+ * 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 int x2scr(double x) {
- return x*frame_context.orig_width_nocrop / frame_context.track->PlayResX +
- FFMAX(global_settings->left_margin, 0);
-}
-static double x2scr_pos(double x) {
- return x*frame_context.orig_width / frame_context.track->PlayResX +
- global_settings->left_margin;
+static void blend_vector_clip(ASS_Renderer *render_priv,
+ ASS_Image *head)
+{
+ FT_Glyph glyph;
+ FT_BitmapGlyph clip_bm;
+ ASS_Image *cur;
+ ASS_Drawing *drawing = render_priv->state.clip_drawing;
+ int error;
+
+ if (!drawing)
+ return;
+
+ // Rasterize it
+ FT_Glyph_Copy((FT_Glyph) drawing->glyph, &glyph);
+ error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
+ if (error) {
+ ass_msg(render_priv->library, MSGL_V,
+ "Clip vector rasterization failed: %d. Skipping.", error);
+ goto blend_vector_exit;
+ }
+ clip_bm = (FT_BitmapGlyph) glyph;
+ clip_bm->top = -clip_bm->top;
+
+ assert(c