diff options
Diffstat (limited to 'libass/ass_render.c')
-rw-r--r-- | libass/ass_render.c | 2694 |
1 files changed, 0 insertions, 2694 deletions
diff --git a/libass/ass_render.c b/libass/ass_render.c deleted file mode 100644 index 6bc0c61bae..0000000000 --- a/libass/ass_render.c +++ /dev/null @@ -1,2694 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> - * - * This file is part of libass. - * - * libass is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * libass is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with libass; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" - -#include <assert.h> -#include <math.h> -#include <inttypes.h> -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_STROKER_H -#include FT_GLYPH_H -#include FT_SYNTHESIS_H - -#include "ass.h" -#include "ass_font.h" -#include "ass_bitmap.h" -#include "ass_cache.h" -#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); - } - } -} - -ASS_Renderer *ass_renderer_init(ASS_Library *library) -{ - 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; -} - -void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max, - int bitmap_max) -{ - 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; -} - -static void free_list_clear(ASS_Renderer *render_priv) -{ - 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 - * Parameters are the same as ASS_Image fields. - */ -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 *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 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 - * \param color first color, RGBA - * \param color2 second color, RGBA - * \param brk x coordinate relative to glyph origin, color is used to the left of brk, color2 - to the right - * \param tail pointer to the last image's next field, head of the generated list should be stored here - * \return pointer to the new list tail - * Performs clipping. Uses my_draw_bitmap for actual bitmap convertion. - */ -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) -{ - // 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 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_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; -} - -/** - * \brief Calculate overlapping area of two consecutive bitmaps and in case they - * 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_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); -} - -static void free_list_add(ASS_Renderer *render_priv, void *object) -{ - 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; - } -} - -/** - * 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 *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(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 - 1) + aw); - 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(glyph); -blend_vector_exit: - ass_drawing_free(render_priv->state.clip_drawing); - render_priv->state.clip_drawing = 0; -} - -/** - * \brief Convert TextInfo struct to ASS_Image list - * Splits glyphs in halves when needed (for \kf karaoke). - */ -static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x, - int dst_y) -{ - int pen_x, pen_y; - int i; - Bitmap *bm; - ASS_Image *head; - ASS_Image **tail = &head; - ASS_Image **last_tail = 0; - ASS_Image **here_tail = 0; - TextInfo *text_info = &render_priv->text_info; - - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s - || (info->shadow_x == 0 && info->shadow_y == 0) || info->skip) - continue; - - pen_x = - dst_x + (info->pos.x >> 6) + - (int) (info->shadow_x * render_priv->border_scale); - pen_y = - dst_y + (info->pos.y >> 6) + - (int) (info->shadow_y * render_priv->border_scale); - bm = info->bm_s; - - here_tail = tail; - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, - 1000000, tail); - if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) - render_overlap(render_priv, last_tail, here_tail); - - last_tail = here_tail; - } - - last_tail = 0; - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o - || info->skip) - continue; - - pen_x = dst_x + (info->pos.x >> 6); - pen_y = dst_y + (info->pos.y >> 6); - bm = info->bm_o; - - if ((info->effect_type == EF_KARAOKE_KO) - && (info->effect_timing <= (info->bbox.xMax >> 6))) { - // do nothing - } else { - here_tail = tail; - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], - 0, 1000000, tail); - if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) - render_overlap(render_priv, last_tail, here_tail); - - last_tail = here_tail; - } - } - - for (i = 0; i < text_info->length; ++i) { - GlyphInfo *info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm - || info->skip) - continue; - - pen_x = dst_x + (info->pos.x >> 6); - pen_y = dst_y + (info->pos.y >> 6); - bm = info->bm; - - if ((info->effect_type == EF_KARAOKE) - || (info->effect_type == EF_KARAOKE_KO)) { - if (info->effect_timing > (info->bbox.xMax >> 6)) - tail = - render_glyph(render_priv, bm, pen_x, pen_y, - info->c[0], 0, 1000000, tail); - else - tail = - render_glyph(render_priv, bm, pen_x, pen_y, - info->c[1], 0, 1000000, tail); - } else if (info->effect_type == EF_KARAOKE_KF) { - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - info->c[1], info->effect_timing, tail); - } else - tail = - render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - 0, 1000000, tail); - } - - *tail = 0; - blend_vector_clip(render_priv, head); - - return head; -} - -/** - * \brief Mapping between script and screen coordinates - */ -static double x2scr(ASS_Renderer *render_priv, double x) -{ - return x * render_priv->orig_width_nocrop / - render_priv->track->PlayResX + - FFMAX(render_priv->settings.left_margin, 0); -} -static double x2scr_pos(ASS_Renderer *render_priv, double x) -{ - return x * render_priv->orig_width / render_priv->track->PlayResX + - render_priv->settings.left_margin; -} - -/** - * \brief Mapping between script and screen coordinates - */ -static double y2scr(ASS_Renderer *render_priv, double y) -{ - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} -static double y2scr_pos(ASS_Renderer *render_priv, double y) -{ - return y * render_priv->orig_height / render_priv->track->PlayResY + - render_priv->settings.top_margin; -} - -// the same for toptitles -static double y2scr_top(ASS_Renderer *render_priv, double y) -{ - if (render_priv->settings.use_margins) - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY; - else - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} - -// the same for subtitles -static double y2scr_sub(ASS_Renderer *render_priv, double y) -{ - if (render_priv->settings.use_margins) - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, - 0) + FFMAX(render_priv->settings.bottom_margin, 0); - else - return y * render_priv->orig_height_nocrop / - render_priv->track->PlayResY + - FFMAX(render_priv->settings.top_margin, 0); -} - -static void compute_string_bbox(TextInfo *info, DBBox *bbox) -{ - int i; - - if (info->length > 0) { - bbox->xMin = 32000; - bbox->xMax = -32000; - bbox->yMin = -1 * info->lines[0].asc + d6_to_double(info->glyphs[0].pos.y); - bbox->yMax = info->height - info->lines[0].asc + - d6_to_double(info->glyphs[0].pos.y); - - for (i = 0; i < info->length; ++i) { - if (info->glyphs[i].skip) continue; - double s = d6_to_double(info->glyphs[i].pos.x); - double e = s + d6_to_double(info->glyphs[i].advance.x); - bbox->xMin = FFMIN(bbox->xMin, s); - bbox->xMax = FFMAX(bbox->xMax, e); - } - } else - bbox->xMin = bbox->xMax = bbox->yMin = bbox->yMax = 0.; -} - -/** - * \brief partially reset render_context to style values - * Works like {\r}: resets some style overrides - */ -void reset_render_context(ASS_Renderer *render_priv) -{ - render_priv->state.c[0] = render_priv->state.style->PrimaryColour; - render_priv->state.c[1] = render_priv->state.style->SecondaryColour; - render_priv->state.c[2] = render_priv->state.style->OutlineColour; - render_priv->state.c[3] = render_priv->state.style->BackColour; - render_priv->state.flags = - (render_priv->state.style->Underline ? DECO_UNDERLINE : 0) | - (render_priv->state.style->StrikeOut ? DECO_STRIKETHROUGH : 0); - render_priv->state.font_size = render_priv->state.style->FontSize; - - free(render_priv->state.family); - render_priv->state.family = NULL; - render_priv->state.family = strdup(render_priv->state.style->FontName); - render_priv->state.treat_family_as_pattern = - render_priv->state.style->treat_fontname_as_pattern; - render_priv->state.bold = render_priv->state.style->Bold; - render_priv->state.italic = render_priv->state.style->Italic; - update_font(render_priv); - - change_border(render_priv, -1., -1.); - render_priv->state.scale_x = render_priv->state.style->ScaleX; - render_priv->state.scale_y = render_priv->state.style->ScaleY; - render_priv->state.hspacing = render_priv->state.style->Spacing; - render_priv->state.be = 0; - render_priv->state.blur = 0.0; - render_priv->state.shadow_x = render_priv->state.style->Shadow; - render_priv->state.shadow_y = render_priv->state.style->Shadow; - render_priv->state.frx = render_priv->state.fry = 0.; - render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.; - render_priv->state.fax = render_priv->state.fay = 0.; - render_priv->state.wrap_style = render_priv->track->WrapStyle; - - // FIXME: does not reset unsupported attributes. -} - -/** - * \brief Start new event. Reset render_priv->state. - */ -static void -init_render_context(ASS_Renderer *render_priv, ASS_Event *event) -{ - render_priv->state.event = event; - render_priv->state.style = render_priv->track->styles + event->Style; - - reset_render_context(render_priv); - - render_priv->state.evt_type = EVENT_NORMAL; - render_priv->state.alignment = render_priv->state.style->Alignment; - render_priv->state.pos_x = 0; - render_priv->state.pos_y = 0; - render_priv->state.org_x = 0; - render_priv->state.org_y = 0; - render_priv->state.have_origin = 0; - render_priv->state.clip_x0 = 0; - render_priv->state.clip_y0 = 0; - render_priv->state.clip_x1 = render_priv->track->PlayResX; - render_priv->state.clip_y1 = render_priv->track->PlayResY; - render_priv->state.clip_mode = 0; - render_priv->state.detect_collisions = 1; - render_priv->state.fade = 0; - render_priv->state.drawing_mode = 0; - render_priv->state.effect_type = EF_NONE; - render_priv->state.effect_timing = 0; - render_priv->state.effect_skip_timing = 0; - render_priv->state.drawing = - ass_drawing_new(render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->settings.hinting, - render_priv->ftlibrary); - - apply_transition_effects(render_priv, event); -} - -static void free_render_context(ASS_Renderer *render_priv) -{ - free(render_priv->state.family); - ass_drawing_free(render_priv->state.drawing); - - render_priv->state.family = NULL; - render_priv->state.drawing = NULL; -} - -// Calculate the cbox of a series of points -static void -get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end) -{ - box->xMin = box->yMin = INT_MAX; - box->xMax = box->yMax = INT_MIN; - int i; - - for (i = start; i < end; i++) { - box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin; - box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax; - box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin; - box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax; - } -} - -/** - * \brief Fix-up stroker result for huge borders by removing the contours from - * the outline that are harmful. -*/ -static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, - int border_y) -{ - int nc = glyph->outline.n_contours; - int begin, stop; - char modified = 0; - char *valid_cont; - int start = 0; - int end = -1; - FT_BBox *boxes = calloc(nc, sizeof(FT_BBox)); - int i, j; - - // Create a list of cboxes of the contours - for (i = 0; i < nc; i++) { - start = end + 1; - end = glyph->outline.contours[i]; - get_contour_cbox(&boxes[i], glyph->outline.points, start, end); - } - - // if a) contour's cbox is contained in another contours cbox - // b) contour's height or width is smaller than the border*2 - // the contour can be safely removed. - valid_cont = calloc(1, nc); - for (i = 0; i < nc; i++) { - valid_cont[i] = 1; - for (j = 0; j < nc; j++) { - if (i == j) - continue; - if (boxes[i].xMin >= boxes[j].xMin && - boxes[i].xMax <= boxes[j].xMax && - boxes[i].yMin >= boxes[j].yMin && - boxes[i].yMax <= boxes[j].yMax) { - int width = boxes[i].xMax - boxes[i].xMin; - int height = boxes[i].yMax - boxes[i].yMin; - if (width < border_x * 2 || height < border_y * 2) { - valid_cont[i] = 0; - modified = 1; - break; - } - } - } - } - - // Zero-out contours that can be removed; much simpler than copying - if (modified) { - for (i = 0; i < nc; i++) { - if (valid_cont[i]) - continue; - begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1; - stop = glyph->outline.contours[i]; - for (j = begin; j <= stop; j++) { - glyph->outline.points[j].x = 0; - glyph->outline.points[j].y = 0; - glyph->outline.tags[j] = 0; - } - } - } - - free(boxes); - free(valid_cont); -} - -/* - * Replace the outline of a glyph by a contour which makes up a simple - * opaque rectangle. - */ -static void draw_opaque_box(ASS_Renderer *render_priv, uint32_t ch, - FT_Glyph glyph, int sx, int sy) -{ - int asc = 0, desc = 0; - int i; - int adv = d16_to_d6(glyph->advance.x); - double scale_y = render_priv->state.sc |