summaryrefslogtreecommitdiffstats
path: root/libass/ass_render.c
diff options
context:
space:
mode:
authorUoti Urpala <uau@glyph.nonexistent.invalid>2009-07-25 07:24:39 +0300
committerUoti Urpala <uau@glyph.nonexistent.invalid>2009-07-26 20:22:43 +0300
commit546c3fb53ce64fde5cba3e06012d244e73ae497a (patch)
tree8708989ac0b06edc091a71d73508ffc465fd669e /libass/ass_render.c
parent6fbcf16cfb0c6482bef87a0e8ac2162bca4cdbfd (diff)
downloadmpv-546c3fb53ce64fde5cba3e06012d244e73ae497a.tar.bz2
mpv-546c3fb53ce64fde5cba3e06012d244e73ae497a.tar.xz
Remove internal libass tree
Remove the libass/ directory and use the newest standalone version of the library instead.
Diffstat (limited to 'libass/ass_render.c')
-rw-r--r--libass/ass_render.c2616
1 files changed, 0 insertions, 2616 deletions
diff --git a/libass/ass_render.c b/libass/ass_render.c
deleted file mode 100644
index e3896e89a4..0000000000
--- a/libass/ass_render.c
+++ /dev/null
@@ -1,2616 +0,0 @@
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
-/*
- * 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 "mputils.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"
-
-#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_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);
- }
- }
-}
-
-ass_renderer_t* ass_renderer_init(ass_library_t* library)
-{
- 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;
-}
-
-void ass_renderer_done(ass_renderer_t* 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);
-}
-
-/**
- * \brief Create a new ass_image_t
- * Parameters are the same as ass_image_t 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)
-{
- ass_image_t* img = calloc(1, sizeof(ass_image_t));
-
- 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;
-}
-
-/**
- * \brief convert bitmap glyph into ass_image_t 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_t** render_glyph(bitmap_t* bm, int dst_x, int dst_y, uint32_t color, uint32_t color2, int brk, ass_image_t** 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_t* img;
-
- dst_x += bm->left;
- dst_y += bm->top;
- brk -= bm->left;
-
- // clipping
- clip_x0 = render_context.clip_x0;
- clip_y0 = render_context.clip_y0;
- clip_x1 = render_context.clip_x1;
- clip_y1 = render_context.clip_y1;
- 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;
-}
-
-/**
- * \brief Calculate overlapping area of two consecutive bitmaps and in case they
- * overlap, composite 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 = (*last_tail)->bitmap;
- b = (*tail)->bitmap;
- (*last_tail)->bitmap = malloc(as*ah);
- (*tail)->bitmap = malloc(bs*bh);
- memcpy((*last_tail)->bitmap, a, as*ah);
- memcpy((*tail)->bitmap, b, bs*bh);
-
- // 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);
-}
-
-/**
- * \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)
-{
- 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;
-}
-
-/**
- * \brief Mapping between script and screen coordinates
- */
-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;
-}
-/**
- * \brief Mapping between script and screen coordinates
- */
-static double y2scr(double y) {
- return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY +
- FFMAX(global_settings->top_margin, 0);
-}
-static double y2scr_pos(double y) {
- return y * frame_context.orig_height / frame_context.track->PlayResY +
- global_settings->top_margin;
-}
-
-// the same for toptitles
-static int y2scr_top(double y) {
- if (global_settings->use_margins)
- return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY;
- else
- return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY +
- FFMAX(global_settings->top_margin, 0);
-}
-// the same for subtitles
-static int y2scr_sub(double y) {
- if (global_settings->use_margins)
- return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY +
- FFMAX(global_settings->top_margin, 0) +
- FFMAX(global_settings->bottom_margin, 0);
- else
- return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY +
- FFMAX(global_settings->top_margin, 0);
-}
-
-static void compute_string_bbox( text_info_t* info, FT_BBox *abbox ) {
- FT_BBox bbox;
- int i;
-
- if (text_info.length > 0) {
- bbox.xMin = 32000;
- bbox.xMax = -32000;
- bbox.yMin = - d6_to_int(text_info.lines[0].asc) + text_info.glyphs[0].pos.y;
- bbox.yMax = d6_to_int(text_info.height - text_info.lines[0].asc) + text_info.glyphs[0].pos.y;
-
- for (i = 0; i < text_info.length; ++i) {
- int s = text_info.glyphs[i].pos.x;
- int e = s + d6_to_int(text_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;
-
- /* return string bbox */
- *abbox = bbox;
-}
-
-
-/**
- * \brief Check if starting part of (*p) matches sample. If true, shift p to the first symbol after the matching part.
- */
-static inline int mystrcmp(char** p, const char* sample) {
- int len = strlen(sample);
- if (strncmp(*p, sample, len) == 0) {
- (*p) += len;
- return 1;
- } else
- return 0;
-}
-
-static void change_font_size(double sz)
-{
- double size = sz * frame_context.font_scale;
-
- if (size < 1)
- size = 1;
- else if (size > frame_context.height * 2)
- size = frame_context.height * 2;
-
- ass_font_set_size(render_context.font, size);
-
- render_context.font_size = sz;
-}
-
-/**
- * \brief Change current font, using setting from render_context.
- */
-static void update_font(void)
-{
- unsigned val;
- ass_renderer_t* priv = frame_context.ass_priv;
- ass_font_desc_t desc;
- desc.family = strdup(render_context.family);
- desc.treat_family_as_pattern = render_context.treat_family_as_pattern;
-
- val = render_context.bold;
- // 0 = normal, 1 = bold, >1 = exact weight
- if (val == 0) val = 80; // normal
- else if (val == 1) val = 200; // bold
- desc.bold = val;
-
- val = render_context.italic;
- if (val == 0) val = 0; // normal
- else if (val == 1) val = 110; //italic
- desc.italic = val;
-
- render_context.font = ass_font_new(priv->library, priv->ftlibrary, priv->fontconfig_priv, &desc);
- free(desc.family);
-
- if (render_context.font)
- change_font_size(render_context.font_size);
-}
-
-/**
- * \brief Change border width
- * negative value resets border to style value
- */
-static void change_border(double border)
-{
- int b;
- if (!render_context.font) return;
-
- if (border < 0) {
- if (render_context.style->BorderStyle == 1)
- border = render_context.style->Outline;
- else
- border = 1.;
- }
- render_context.border = border;
-
- b = 64 * border * frame_context.border_scale;
- if (b > 0) {
- if (!render_context.stroker) {
- int error;
-#if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1))
- error = FT_Stroker_New( ass_renderer->ftlibrary, &render_context.stroker );
-#else // < 2.2
- error = FT_Stroker_New( render_context.font->faces[0]->memory, &render_context.stroker );
-#endif
- if (error) {
- mp_msg(MSGT_ASS, MSGL_V, "failed to get stroker\n");
- render_context.stroker = 0;
- }
- }
- if (render_context.stroker)
- FT_Stroker_Set( render_context.stroker, b,
- FT_STROKER_LINECAP_ROUND,
- FT_STROKER_LINEJOIN_ROUND,
- 0 );
- } else {
- FT_Stroker_Done(render_context.stroker);
- render_context.stroker = 0;
- }
-}
-
-#define _r(c) ((c)>>24)
-#define _g(c) (((c)>>16)&0xFF)
-#define _b(c) (((c)>>8)&0xFF)
-#define _a(c) ((c)&0xFF)
-
-/**
- * \brief Calculate a weighted average of two colors
- * calculates c1*(1-a) + c2*a, but separately for each component except alpha
- */
-static void change_color(uint32_t* var, uint32_t new, double pwr)
-{
- (*var)= ((uint32_t)(_r(*var) * (1 - pwr) + _r(new) * pwr) << 24) +
- ((uint32_t)(_g(*var) * (1 - pwr) + _g(new) * pwr) << 16) +
- ((uint32_t)(_b(*var) * (1 - pwr) + _b(new) * pwr) << 8) +
- _a(*var);
-}
-
-// like change_color, but for alpha component only
-static void change_alpha(uint32_t* var, uint32_t new, double pwr)
-{
- *var = (_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) + (_a(*var) * (1 - pwr) + _a(new) * pwr);
-}
-
-/**
- * \brief Multiply two alpha values
- * \param a first value
- * \param b second value
- * \return result of multiplication
- * Parameters and result are limited by 0xFF.
- */
-static uint32_t mult_alpha(uint32_t a, uint32_t b)
-{
- return 0xFF - (0xFF - a) * (0xFF - b) / 0xFF;
-}
-
-/**
- * \brief Calculate alpha value by piecewise linear function
- * Used for \fad, \fade implementation.
- */
-static unsigned interpolate_alpha(long long now,
- long long t1, long long t2, long long t3, long long t4,
- unsigned a1, unsigned a2, unsigned a3)
-{
- unsigned a;
- double cf;
- if (now <= t1) {
- a = a1;
- } else if (now >= t4) {
- a = a3;
- } else if (now < t2) { // and > t1
- cf = ((double)(now - t1)) / (t2 - t1);
- a = a1 * (1 - cf) + a2 * cf;
- } else if (now > t3) {
- cf = ((double)(now - t3)) / (t4 - t3);
- a = a2 * (1 - cf) + a3 * cf;
- } else { // t2 <= now <= t3
- a = a2;
- }
-
- return a;
-}
-
-static void reset_render_context(void);
-
-/**
- * \brief Parse style override tag.
- * \param p string to parse
- * \param pwr multiplier for some tag effects (comes from \t tags)
- */
-static char* parse_tag(char* p, double pwr) {
-#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;}
-#define skip(x) if (*p == (x)) ++p; else { return p; }
-
- skip_to('\\');
- skip('\\');
- if ((*p == '}') || (*p == 0))
- return p;
-
- // New tags introduced in vsfilter 2.39
- if (mystrcmp(&p, "xbord")) {
- double val;
- if (mystrtod(&p, &val))
- mp_msg(MSGT_ASS, MSGL_V, "stub: \\xbord%.2f\n", val);
- } else if (mystrcmp(&p, "ybord")) {
- double val;
- if (mystrtod(&p, &val))
- mp_msg(MSGT_ASS, MSGL_V, "stub: \\ybord%.2f\n", val);
- } else if (mystrcmp(&p, "xshad")) {
- int val;
- if (mystrtoi(&p, &val))
- mp_msg(MSGT_ASS, MSGL_V, "stub: \\xshad%d\n", val);
- } else if (mystrcmp(&p, "yshad")) {
- int val;
- if (mystrtoi(&p, &val))
- mp_msg(MSGT_ASS, MSGL_V, "stub: \\yshad%d\n", val);
- } else if (mystrcmp(&p, "fax")) {
- int val;
- if (mystrtoi(&p, &val))
- mp_msg(MSGT_ASS, MSGL_V, "stub: \\fax%d\n", val);
- } else if (mystrcmp(&p, "fay")) {
- int val;
- if (mystrtoi(&p, &val))
- mp_msg(MSGT_ASS, MSGL_V, "stub: \\fay%d\n", val);
- } else if (mystrcmp(&p, "iclip")) {
- int x0, y0, x1, y1;
- int res = 1;
- skip('(');
- res &= mystrtoi(&p, &x0);
- skip(',');
- res &= mystrtoi(&p, &y0);
- skip(',');
- res &= mystrtoi(&p, &x1);
- skip(',');
- res &= mystrtoi(&p, &y1);
- skip(')');
- mp_msg(MSGT_ASS, MSGL_V, "stub: \\iclip(%d,%d,%d,%d)\n", x0, y0, x1, y1);
- } else if (mystrcmp(&p, "blur")) {
- double val;
- if (mystrtod(&p, &val)) {
- val = (val < 0) ? 0 : val;
- val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
- render_context.blur = val;
- } else
- render_context.blur = 0.0;
- // ASS standard tags
- } else if (mystrcmp(&p, "fsc")) {
- char tp = *p++;
- double val;
- if (tp == 'x') {
- if (mystrtod(&p, &val)) {
- val /= 100;
- render_context.scale_x = render_context.scale_x * ( 1 - pwr) + val * pwr;
- } else
- render_context.scale_x = render_context.style->ScaleX;
- } else if (tp == 'y') {
- if (mystrtod(&p, &val)) {
- val /= 100;
- render_context.scale_y = render_context.scale_y * ( 1 - pwr) + val * pwr;
- } else
- render_context.scale_y = render_context.style->ScaleY;
- }
- } else if (mystrcmp(&p, "fsp")) {
- double val;
- if (mystrtod(&p, &val))
- render_context.hspacing = render_context.hspacing * ( 1 - pwr ) + val * pwr;
- else
- render_context.hspacing = render_context.style->Spacing;
- } else if (mystrcmp(&p, "fs")) {
- double val;
- if (mystrtod(&p, &val))
- val = render_context.font_size * ( 1 - pwr ) + val * pwr;
- else
- val = render_context.style->FontSize;
- if (render_context.font)
- change_font_size(val);
- } else if (mystrcmp(&p, "bord")) {
- double val;
- if (mystrtod(&p, &val))
- val = render_context.border * ( 1 - pwr ) + val * pwr;
- else
- val = -1.; // reset to default
- change_border(val);
- } else if (mystrcmp(&p, "move")) {
- double x1, x2, y1, y2;
- long long t1, t2, delta_t, t;
- double x, y;
- double k;
- skip('(');
- mystrtod(&p, &x1);
- skip(',');
- mystrtod(&p, &y1);
- skip(',');
- mystrtod(&p, &x2);
- skip(',');
- mystrtod(&p, &y2);
- if (*p == ',') {
- skip(',');
- mystrtoll(&p, &t1);
- skip(',');
- mystrtoll(&p, &t2);
- mp_msg(MSGT_ASS, MSGL_DBG2, "movement6: (%f, %f) -> (%f, %f), (%" PRId64 " .. %" PRId64 ")\n",
- x1, y1, x2, y2, (int64_t)t1, (int64_t)t2);
- } else {
- t1 = 0;
- t2 = render_context.event->Duration;
- mp_msg(MSGT_ASS, MSGL_DBG2, "movement: (%f, %f) -> (%f, %f)\n", x1, y1, x2, y2);
- }
- skip(')');
- delta_t = t2 - t1;
- t = frame_context.time - render_context.event->Start;
- if (t < t1)
- k = 0.;
- else if (t > t2)
- k = 1.;
- else k = ((double)(t - t1)) / delta_t;
- x = k * (x2 - x1) + x1;
- y = k * (y2 - y1) + y1;
- if (render_context.evt_type != EVENT_POSITIONED) {
- render_context.pos_x = x;
- render_context.pos_y = y;
- render_context.detect_collisions = 0;
- render_context.evt_type = EVENT_POSITIONED;
- }
- } else if (mystrcmp(&p, "frx")) {
- double val;
- if (mystrtod(&p, &val)) {
- val *= M_PI / 180;
- render_context.frx = val * pwr + render_context.frx * (1-pwr);
- } else
- render_context.frx = 0.;
- } else if (mystrcmp(&p, "fry")) {
- double val;
- if (mystrtod(&p, &val)) {
- val *= M_PI / 180;
- render_context.fry = val * pwr + render_context.fry * (1-pwr);
- } else
- render_context.fry = 0.;
- } else if (mystrcmp(&p, "frz") || mystrcmp(&p, "fr")) {
- double val;
- if (mystrtod(&p, &val)) {
- val *= M_PI / 180;
- render_context.frz = val * pwr + render_context.frz * (1-pwr);
- } else
- render_context.frz = M_PI * render_context.style->Angle / 180.;
- } else if (mystrcmp(&p, "fn")) {
- char* start = p;
- char* family;
- skip_to('\\');
- if (p > start) {
- family = malloc(p - start + 1);
- strncpy(family, start, p - start);
- family[p - start] = '\0';
- } else
- family = strdup(render_context.style->FontName);
- if (render_context.family)
- free(render_context.family);
- render_context.family = family;
- update_font();
- } else if (mystrcmp(&p, "alpha")) {
- uint32_t val;
- int i;
- if (strtocolor(&p, &val)) {
- unsigned char a = val >> 24;
- for (i = 0; i < 4; ++i)
- change_alpha(&render_context.c[i], a, pwr);
- } else {
- change_alpha(&render_context.c[0], render_context.style->PrimaryColour, pwr);
- change_alpha(&render_context.c[1], render_context.style->SecondaryColour, pwr);
- change_alpha(&render_context.c[2], render_context.style->OutlineColour, pwr);
- change_alpha(&render_context.c[3], render_context.style->BackColour, pwr);
- }
- // FIXME: simplify
- } else if (mystrcmp(&p, "an")) {
- int val;
- if (mystrtoi(&p, &val) && val) {
- int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment
- mp_msg(MSGT_ASS, MSGL_DBG2, "an %d\n", val);
- if (v != 0) v = 3 - v;
- val = ((val - 1) % 3) + 1; // horizontal alignment
- val += v*4;
- mp_msg(MSGT_ASS, MSGL_DBG2, "align %d\n", val);
- render_context.alignment = val;
- } else
- render_context.alignment = render_context.style->Alignment;
- } else if (mystrcmp(&p, "a")) {
- int val;
- if (mystrtoi(&p, &val) && val)
- render_context.alignment = val;
- else
- render_context.alignment = render_context.style->Alignment;
- } else if (mystrcmp(&p, "pos")) {
- double v1, v2;
- skip('(');
- mystrtod(&p, &v1);
- skip(',');
- mystrtod(&p, &v2);
- skip(')');
- mp_msg(MSGT_ASS, MSGL_DBG2, "pos(%f, %f)\n", v1, v2);
- if (render_context.evt_type == EVENT_POSITIONED) {
- mp_msg(MSGT_ASS, MSGL_V, "Subtitle has a new \\pos "
- "after \\move or \\pos, ignoring\n");
- } else {
- render_context.evt_type = EVENT_POSITIONED;
- render_context.detect_collisions = 0;
- render_context.pos_x = v1;
- render_context.pos_y = v2;
- }
- } else if (mystrcmp(&p, "fad")) {
- int a1, a2, a3;
- long long t1, t2, t3, t4;
- if (*p == 'e') ++p; // either \fad or \fade
- skip('(');
- mystrtoi(&p, &a1);
- skip(',');
- mystrtoi(&p, &a2);
- if (*p == ')') {
- // 2-argument version (\fad, according to specs)
- // a1 and a2 are fade-in and fade-out durations
- t1 = 0;
- t4 = render_context.event->Duration;
- t2 = a1;
- t3 = t4 - a2;
- a1 = 0xFF;
- a2 = 0;
- a3 = 0xFF;
- } else {
- // 6-argument version (\fade)
- // a1 and a2 (and a3) are opacity values
- skip(',');
- mystrtoi(&p, &a3);
- skip(',');
- mystrtoll(&p, &t1);
- skip(',');
- mystrtoll(&p, &t2);
- skip(','