diff options
author | greg <greg@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2010-01-08 18:35:44 +0000 |
---|---|---|
committer | greg <greg@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2010-01-08 18:35:44 +0000 |
commit | 9aea06cf7dde9592672e4dba09fb03b2b89d6863 (patch) | |
tree | 0062d1e95bc58bd51028e14a6d88240a1d94ee7d /libass/ass_render.c | |
parent | f51c039c5fcdc24c3f33e1a48bed73bdb17e9e11 (diff) | |
download | mpv-9aea06cf7dde9592672e4dba09fb03b2b89d6863.tar.bz2 mpv-9aea06cf7dde9592672e4dba09fb03b2b89d6863.tar.xz |
Update internal libass copy to commit 8db4a5
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@30242 b3059339-0415-0410-9bf9-f77b7e298cf2
Diffstat (limited to 'libass/ass_render.c')
-rw-r--r-- | libass/ass_render.c | 4661 |
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)); |