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.c2694
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.
- */