diff --git a/Makefile b/Makefile index d750e96fd6..7bcf7481ec 100644 --- a/Makefile +++ b/Makefile @@ -125,15 +125,7 @@ SRCS_COMMON-$(LIBA52_INTERNAL) += liba52/crc.c \ liba52/imdct.c \ liba52/parse.c \ -SRCS_COMMON-$(LIBASS) += libass/ass.c \ - libass/ass_bitmap.c \ - libass/ass_cache.c \ - libass/ass_font.c \ - libass/ass_fontconfig.c \ - libass/ass_library.c \ - libass/ass_mp.c \ - libass/ass_render.c \ - libass/ass_utils.c \ +SRCS_COMMON-$(LIBASS) += ass_mp.c \ libmpcodecs/vf_ass.c \ SRCS_COMMON-$(LIBAVCODEC) += av_opts.c \ diff --git a/ass_mp.c b/ass_mp.c new file mode 100644 index 0000000000..207b0454d1 --- /dev/null +++ b/ass_mp.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2006 Evgeniy Stepanov + * + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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 +#include +#include +#include + +#include +#include + +#include "mp_msg.h" +#include "get_path.h" +#include "ass_mp.h" +#include "subreader.h" + +#ifdef CONFIG_FONTCONFIG +#include +#endif + +// libass-related command line options +ass_library_t *ass_library; +int ass_enabled = 0; +float ass_font_scale = 1.; +float ass_line_spacing = 0.; +int ass_top_margin = 0; +int ass_bottom_margin = 0; +#if defined(FC_VERSION) && (FC_VERSION >= 20402) +int extract_embedded_fonts = 1; +#else +int extract_embedded_fonts = 0; +#endif +char **ass_force_style_list = NULL; +int ass_use_margins = 0; +char *ass_color = NULL; +char *ass_border_color = NULL; +char *ass_styles_file = NULL; +int ass_hinting = ASS_HINTING_NATIVE + 4; // native hinting for unscaled osd + +#ifdef CONFIG_FONTCONFIG +extern int font_fontconfig; +#else +static int font_fontconfig = -1; +#endif +extern char *font_name; +extern char *sub_font_name; +extern float text_font_scale_factor; +extern int subtitle_autoscale; + +#ifdef CONFIG_ICONV +extern char *sub_cp; +#else +static char *sub_cp = 0; +#endif + +void process_force_style(ass_track_t *track); + +ass_track_t *ass_default_track(ass_library_t *library) +{ + ass_track_t *track = ass_new_track(library); + + track->track_type = TRACK_TYPE_ASS; + track->Timer = 100.; + track->PlayResY = 288; + track->WrapStyle = 0; + + if (ass_styles_file) + ass_read_styles(track, ass_styles_file, sub_cp); + + if (track->n_styles == 0) { + ass_style_t *style; + int sid; + double fs; + uint32_t c1, c2; + + sid = ass_alloc_style(track); + style = track->styles + sid; + style->Name = strdup("Default"); + style->FontName = (font_fontconfig >= 0 + && sub_font_name) ? strdup(sub_font_name) + : (font_fontconfig >= 0 + && font_name) ? strdup(font_name) : strdup("Sans"); + style->treat_fontname_as_pattern = 1; + + fs = track->PlayResY * text_font_scale_factor / 100.; + // approximate autoscale coefficients + if (subtitle_autoscale == 2) + fs *= 1.3; + else if (subtitle_autoscale == 3) + fs *= 1.4; + style->FontSize = fs; + + if (ass_color) + c1 = strtoll(ass_color, NULL, 16); + else + c1 = 0xFFFF0000; + if (ass_border_color) + c2 = strtoll(ass_border_color, NULL, 16); + else + c2 = 0x00000000; + + style->PrimaryColour = c1; + style->SecondaryColour = c1; + style->OutlineColour = c2; + style->BackColour = 0x00000000; + style->BorderStyle = 1; + style->Alignment = 2; + style->Outline = 2; + style->MarginL = 10; + style->MarginR = 10; + style->MarginV = 5; + style->ScaleX = 1.; + style->ScaleY = 1.; + } + + ass_process_force_style(track); + return track; +} + +static int check_duplicate_plaintext_event(ass_track_t *track) +{ + int i; + ass_event_t *evt = track->events + track->n_events - 1; + + for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with + if (track->events[i].Start == evt->Start && + track->events[i].Duration == evt->Duration && + strcmp(track->events[i].Text, evt->Text) == 0) + return 1; + return 0; +} + +/** + * \brief Convert subtitle to ass_event_t for the given track + * \param ass_track_t track + * \param sub subtitle to convert + * \return event id + * note: assumes that subtitle is _not_ fps-based; caller must manually correct + * Start and Duration in other case. + **/ +int ass_process_subtitle(ass_track_t *track, subtitle *sub) +{ + int eid; + ass_event_t *event; + int len = 0, j; + char *p; + char *end; + + eid = ass_alloc_event(track); + event = track->events + eid; + + event->Start = sub->start * 10; + event->Duration = (sub->end - sub->start) * 10; + event->Style = 0; + + for (j = 0; j < sub->lines; ++j) + len += sub->text[j] ? strlen(sub->text[j]) : 0; + + len += 2 * sub->lines; // '\N', including the one after the last line + len += 6; // {\anX} + len += 1; // '\0' + + event->Text = malloc(len); + end = event->Text + len; + p = event->Text; + + if (sub->alignment) + p += snprintf(p, end - p, "{\\an%d}", sub->alignment); + + for (j = 0; j < sub->lines; ++j) + p += snprintf(p, end - p, "%s\\N", sub->text[j]); + + if (sub->lines > 0) + p -= 2; // remove last "\N" + *p = 0; + + if (check_duplicate_plaintext_event(track)) { + ass_free_event(track, eid); + track->n_events--; + return -1; + } + + mp_msg(MSGT_ASS, MSGL_V, + "plaintext event at %" PRId64 ", +%" PRId64 ": %s \n", + (int64_t) event->Start, (int64_t) event->Duration, event->Text); + + return eid; +} + + +/** + * \brief Convert subdata to ass_track + * \param subdata subtitles struct from subreader + * \param fps video framerate + * \return newly allocated ass_track, filled with subtitles from subdata + */ +ass_track_t *ass_read_subdata(ass_library_t *library, sub_data *subdata, + double fps) +{ + ass_track_t *track; + int i; + + track = ass_default_track(library); + track->name = subdata->filename ? strdup(subdata->filename) : 0; + + for (i = 0; i < subdata->sub_num; ++i) { + int eid = ass_process_subtitle(track, subdata->subtitles + i); + if (eid < 0) + continue; + if (!subdata->sub_uses_time) { + track->events[eid].Start *= 100. / fps; + track->events[eid].Duration *= 100. / fps; + } + } + return track; +} + +void ass_configure(ass_renderer_t *priv, int w, int h, int unscaled) +{ + int hinting; + ass_set_frame_size(priv, w, h); + ass_set_margins(priv, ass_top_margin, ass_bottom_margin, 0, 0); + ass_set_use_margins(priv, ass_use_margins); + ass_set_font_scale(priv, ass_font_scale); + if (!unscaled && (ass_hinting & 4)) + hinting = 0; + else + hinting = ass_hinting & 3; + ass_set_hinting(priv, hinting); + ass_set_line_spacing(priv, ass_line_spacing); +} + +void ass_configure_fonts(ass_renderer_t *priv) +{ + char *dir, *path, *family; + dir = get_path("fonts"); + if (font_fontconfig < 0 && sub_font_name) + path = strdup(sub_font_name); + else if (font_fontconfig < 0 && font_name) + path = strdup(font_name); + else + path = get_path("subfont.ttf"); + if (font_fontconfig >= 0 && sub_font_name) + family = strdup(sub_font_name); + else if (font_fontconfig >= 0 && font_name) + family = strdup(font_name); + else + family = 0; + + ass_set_fonts(priv, path, family, font_fontconfig + 1, NULL, 1); + + free(dir); + free(path); + free(family); +} + +static void message_callback(int level, const char *format, va_list va, void *ctx) +{ + mp_msg(MSGT_ASS, level, "[ass] "); + mp_msg_va(MSGT_ASS, level, format, va); + // libass messages lack trailing \n + mp_msg(MSGT_ASS, level, "\n"); +} + +ass_library_t *ass_init(void) +{ + ass_library_t *priv; + char *path = get_path("fonts"); + priv = ass_library_init(); + ass_set_message_cb(priv, message_callback, NULL); + ass_set_fonts_dir(priv, path); + ass_set_extract_fonts(priv, extract_embedded_fonts); + ass_set_style_overrides(priv, ass_force_style_list); + free(path); + return priv; +} + +int ass_force_reload = 0; // flag set if global ass-related settings were changed + +ass_image_t *ass_mp_render_frame(ass_renderer_t *priv, ass_track_t *track, + long long now, int *detect_change) +{ + if (ass_force_reload) { + ass_set_margins(priv, ass_top_margin, ass_bottom_margin, 0, 0); + ass_set_use_margins(priv, ass_use_margins); + ass_set_font_scale(priv, ass_font_scale); + ass_force_reload = 0; + } + return ass_render_frame(priv, track, now, detect_change); +} diff --git a/ass_mp.h b/ass_mp.h new file mode 100644 index 0000000000..0b335b9921 --- /dev/null +++ b/ass_mp.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006 Evgeniy Stepanov + * + * This file is part of MPlayer. + * + * MPlayer 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. + * + * MPlayer 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. + */ + +#ifndef MPLAYER_ASS_MP_H +#define MPLAYER_ASS_MP_H + +#include + +#include "ass_mp.h" + +#include "subreader.h" + +#ifdef CONFIG_ASS +#include +#include + +extern ass_library_t *ass_library; +extern int ass_enabled; +extern float ass_font_scale; +extern float ass_line_spacing; +extern int ass_top_margin; +extern int ass_bottom_margin; +extern int extract_embedded_fonts; +extern char **ass_force_style_list; +extern int ass_use_margins; +extern char *ass_color; +extern char *ass_border_color; +extern char *ass_styles_file; +extern int ass_hinting; + +ass_track_t *ass_default_track(ass_library_t *library); +int ass_process_subtitle(ass_track_t *track, subtitle *sub); +ass_track_t *ass_read_subdata(ass_library_t *library, sub_data *subdata, + double fps); + +void ass_configure(ass_renderer_t *priv, int w, int h, int hinting); +void ass_configure_fonts(ass_renderer_t *priv); +ass_library_t *ass_init(void); + +extern int ass_force_reload; +ass_image_t *ass_mp_render_frame(ass_renderer_t *priv, ass_track_t *track, + long long now, int *detect_change); + +#else /* CONFIG_ASS */ + +/* Needed for EOSD code using this type to compile */ + +typedef struct ass_image { + int w, h; + int stride; + unsigned char *bitmap; + uint32_t color; + int dst_x, dst_y; + struct ass_image *next; +} ass_image_t; + +#endif + +typedef struct { + ass_image_t *imgs; + int changed; +} mp_eosd_images_t; + + +#endif /* MPLAYER_ASS_MP_H */ diff --git a/command.c b/command.c index 721b145b83..bbe9300e5e 100644 --- a/command.c +++ b/command.c @@ -31,6 +31,7 @@ #include "vobsub.h" #include "spudec.h" #include "get_path.h" +#include "ass_mp.h" #ifdef CONFIG_TV #include "stream/tv.h" #endif @@ -49,10 +50,6 @@ #ifdef CONFIG_DVDNAV #include "stream/stream_dvdnav.h" #endif -#ifdef CONFIG_ASS -#include "libass/ass.h" -#include "libass/ass_mp.h" -#endif #ifdef CONFIG_MENU #include "m_struct.h" #include "libmenu/menu.h" diff --git a/configure b/configure index 8027630a86..e567a8a22a 100755 --- a/configure +++ b/configure @@ -6092,29 +6092,16 @@ echores "$_fontconfig" echocheck "SSA/ASS support" -# libass depends on FreeType -if test "$_freetype" = no ; then - _ass=no - _res_comment="FreeType support needed" -fi - -if test "$_ass" = auto ; then - cat > $TMPC << EOF -#include -#include FT_FREETYPE_H -#if ((FREETYPE_MAJOR < 2) || (FREETYPE_MINOR < 1) || ((FREETYPE_MINOR == 1) && (FREETYPE_PATCH < 8))) -#error "Need FreeType 2.1.8 or newer" -#endif -int main(void) { return 0; } -EOF - _ass=no - cc_check $($_freetypeconfig --cflags) $($_freetypeconfig --libs) && tmp_run && _ass=yes - if test "$_ass" = no ; then - _res_comment="FreeType >= 2.1.8 needed" - fi -fi -if test "$_ass" = yes ; then - def_ass='#define CONFIG_ASS' +if test "$_ass" = auto -o "$_ass" = yes ; then + if $_pkg_config libass; then + _ass=yes + def_ass='#define CONFIG_ASS' + extra_ldflags="$extra_ldflags $($_pkg_config --libs libass)" + extra_cflags="$extra_cflags $($_pkg_config --cflags libass)" + else + _ass=no + def_ass='#undef CONFIG_ASS' + fi else def_ass='#undef CONFIG_ASS' fi diff --git a/libass/ass.c b/libass/ass.c deleted file mode 100644 index 5e9e82c2c4..0000000000 --- a/libass/ass.c +++ /dev/null @@ -1,1134 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_ICONV -#include -#endif - -#include "ass.h" -#include "ass_utils.h" -#include "ass_library.h" -#include "mputils.h" - -typedef enum {PST_UNKNOWN = 0, PST_INFO, PST_STYLES, PST_EVENTS, PST_FONTS} parser_state_t; - -struct parser_priv_s { - parser_state_t state; - char* fontname; - char* fontdata; - int fontdata_size; - int fontdata_used; -}; - -#define ASS_STYLES_ALLOC 20 -#define ASS_EVENTS_ALLOC 200 - -void ass_free_track(ass_track_t* track) { - int i; - - if (track->parser_priv) { - if (track->parser_priv->fontname) - free(track->parser_priv->fontname); - if (track->parser_priv->fontdata) - free(track->parser_priv->fontdata); - free(track->parser_priv); - } - if (track->style_format) - free(track->style_format); - if (track->event_format) - free(track->event_format); - if (track->styles) { - for (i = 0; i < track->n_styles; ++i) - ass_free_style(track, i); - free(track->styles); - } - if (track->events) { - for (i = 0; i < track->n_events; ++i) - ass_free_event(track, i); - free(track->events); - } -} - -/// \brief Allocate a new style struct -/// \param track track -/// \return style id -int ass_alloc_style(ass_track_t* track) { - int sid; - - assert(track->n_styles <= track->max_styles); - - if (track->n_styles == track->max_styles) { - track->max_styles += ASS_STYLES_ALLOC; - track->styles = (ass_style_t*)realloc(track->styles, sizeof(ass_style_t)*track->max_styles); - } - - sid = track->n_styles++; - memset(track->styles + sid, 0, sizeof(ass_style_t)); - return sid; -} - -/// \brief Allocate a new event struct -/// \param track track -/// \return event id -int ass_alloc_event(ass_track_t* track) { - int eid; - - assert(track->n_events <= track->max_events); - - if (track->n_events == track->max_events) { - track->max_events += ASS_EVENTS_ALLOC; - track->events = (ass_event_t*)realloc(track->events, sizeof(ass_event_t)*track->max_events); - } - - eid = track->n_events++; - memset(track->events + eid, 0, sizeof(ass_event_t)); - return eid; -} - -void ass_free_event(ass_track_t* track, int eid) { - ass_event_t* event = track->events + eid; - if (event->Name) - free(event->Name); - if (event->Effect) - free(event->Effect); - if (event->Text) - free(event->Text); - if (event->render_priv) - free(event->render_priv); -} - -void ass_free_style(ass_track_t* track, int sid) { - ass_style_t* style = track->styles + sid; - if (style->Name) - free(style->Name); - if (style->FontName) - free(style->FontName); -} - -// ============================================================================================== - -static void skip_spaces(char** str) { - char* p = *str; - while ((*p==' ') || (*p=='\t')) - ++p; - *str = p; -} - -static void rskip_spaces(char** str, char* limit) { - char* p = *str; - while ((p >= limit) && ((*p==' ') || (*p=='\t'))) - --p; - *str = p; -} - -/** - * \brief find style by name - * \param track track - * \param name style name - * \return index in track->styles - * Returnes 0 if no styles found => expects at least 1 style. - * Parsing code always adds "Default" style in the end. - */ -static int lookup_style(ass_track_t* track, char* name) { - int i; - if (*name == '*') ++name; // FIXME: what does '*' really mean ? - for (i = track->n_styles - 1; i >= 0; --i) { - // FIXME: mb strcasecmp ? - if (strcmp(track->styles[i].Name, name) == 0) - return i; - } - i = track->default_style; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoStyleNamedXFoundUsingY, track, name, track->styles[i].Name); - return i; // use the first style -} - -static uint32_t string2color(char* p) { - uint32_t tmp; - (void)strtocolor(&p, &tmp); - return tmp; -} - -static long long string2timecode(char* p) { - unsigned h, m, s, ms; - long long tm; - int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms); - if (res < 4) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_BadTimestamp); - return 0; - } - tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10; - return tm; -} - -/** - * \brief converts numpad-style align to align. - */ -static int numpad2align(int val) { - int res, v; - v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment - if (v != 0) v = 3 - v; - res = ((val - 1) % 3) + 1; // horizontal alignment - res += v*4; - return res; -} - -#define NEXT(str,token) \ - token = next_token(&str); \ - if (!token) break; - -#define ANYVAL(name,func) \ - } else if (strcasecmp(tname, #name) == 0) { \ - target->name = func(token); \ - mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); - -#define STRVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ - if (target->name != NULL) free(target->name); \ - target->name = strdup(token); \ - mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); - -#define COLORVAL(name) ANYVAL(name,string2color) -#define INTVAL(name) ANYVAL(name,atoi) -#define FPVAL(name) ANYVAL(name,atof) -#define TIMEVAL(name) ANYVAL(name,string2timecode) -#define STYLEVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ - target->name = lookup_style(track, token); \ - mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); - -#define ALIAS(alias,name) \ - if (strcasecmp(tname, #alias) == 0) {tname = #name;} - -static char* next_token(char** str) { - char* p = *str; - char* start; - skip_spaces(&p); - if (*p == '\0') { - *str = p; - return 0; - } - start = p; // start of the token - for (; (*p != '\0') && (*p != ','); ++p) {} - if (*p == '\0') { - *str = p; // eos found, str will point to '\0' at exit - } else { - *p = '\0'; - *str = p + 1; // ',' found, str will point to the next char (beginning of the next token) - } - --p; // end of current token - rskip_spaces(&p, start); - if (p < start) - p = start; // empty token - else - ++p; // the first space character, or '\0' - *p = '\0'; - return start; -} -/** - * \brief Parse the tail of Dialogue line - * \param track track - * \param event parsed data goes here - * \param str string to parse, zero-terminated - * \param n_ignored number of format options to skip at the beginning -*/ -static int process_event_tail(ass_track_t* track, ass_event_t* event, char* str, int n_ignored) -{ - char* token; - char* tname; - char* p = str; - int i; - ass_event_t* target = event; - - char* format = strdup(track->event_format); - char* q = format; // format scanning pointer - - if (track->n_styles == 0) { - // add "Default" style to the end - // will be used if track does not contain a default style (or even does not contain styles at all) - int sid = ass_alloc_style(track); - track->styles[sid].Name = strdup("Default"); - track->styles[sid].FontName = strdup("Arial"); - } - - for (i = 0; i < n_ignored; ++i) { - NEXT(q, tname); - } - - while (1) { - NEXT(q, tname); - if (strcasecmp(tname, "Text") == 0) { - char* last; - event->Text = strdup(p); - if (*event->Text != 0) { - last = event->Text + strlen(event->Text) - 1; - if (last >= event->Text && *last == '\r') - *last = 0; - } - mp_msg(MSGT_ASS, MSGL_DBG2, "Text = %s\n", event->Text); - event->Duration -= event->Start; - free(format); - return 0; // "Text" is always the last - } - NEXT(p, token); - - ALIAS(End,Duration) // temporarily store end timecode in event->Duration - if (0) { // cool ;) - INTVAL(Layer) - STYLEVAL(Style) - STRVAL(Name) - STRVAL(Effect) - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - TIMEVAL(Start) - TIMEVAL(Duration) - } - } - free(format); - return 1; -} - -/** - * \brief Parse command line style overrides (--ass-force-style option) - * \param track track to apply overrides to - * The format for overrides is [StyleName.]Field=Value - */ -void process_force_style(ass_track_t* track) { - char **fs, *eq, *dt, *style, *tname, *token; - ass_style_t* target; - int sid; - char** list = track->library->style_overrides; - - if (!list) return; - - for (fs = list; *fs; ++fs) { - eq = strrchr(*fs, '='); - if (!eq) - continue; - *eq = '\0'; - token = eq + 1; - - if(!strcasecmp(*fs, "PlayResX")) - track->PlayResX = atoi(token); - else if(!strcasecmp(*fs, "PlayResY")) - track->PlayResY = atoi(token); - else if(!strcasecmp(*fs, "Timer")) - track->Timer = atof(token); - else if(!strcasecmp(*fs, "WrapStyle")) - track->WrapStyle = atoi(token); - else if(!strcasecmp(*fs, "ScaledBorderAndShadow")) - track->ScaledBorderAndShadow = parse_bool(token); - - dt = strrchr(*fs, '.'); - if (dt) { - *dt = '\0'; - style = *fs; - tname = dt + 1; - } else { - style = NULL; - tname = *fs; - } - for (sid = 0; sid < track->n_styles; ++sid) { - if (style == NULL || strcasecmp(track->styles[sid].Name, style) == 0) { - target = track->styles + sid; - if (0) { - STRVAL(FontName) - COLORVAL(PrimaryColour) - COLORVAL(SecondaryColour) - COLORVAL(OutlineColour) - COLORVAL(BackColour) - FPVAL(FontSize) - INTVAL(Bold) - INTVAL(Italic) - INTVAL(Underline) - INTVAL(StrikeOut) - FPVAL(Spacing) - INTVAL(Angle) - INTVAL(BorderStyle) - INTVAL(Alignment) - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - INTVAL(Encoding) - FPVAL(ScaleX) - FPVAL(ScaleY) - FPVAL(Outline) - FPVAL(Shadow) - } - } - } - *eq = '='; - if (dt) *dt = '.'; - } -} - -/** - * \brief Parse the Style line - * \param track track - * \param str string to parse, zero-terminated - * Allocates a new style struct. -*/ -static int process_style(ass_track_t* track, char *str) -{ - - char* token; - char* tname; - char* p = str; - char* format; - char* q; // format scanning pointer - int sid; - ass_style_t* style; - ass_style_t* target; - - if (!track->style_format) { - // no style format header - // probably an ancient script version - if (track->track_type == TRACK_TYPE_SSA) - track->style_format = strdup("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," - "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline," - "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"); - else - track->style_format = strdup("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," - "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut," - "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow," - "Alignment, MarginL, MarginR, MarginV, Encoding"); - } - - q = format = strdup(track->style_format); - - mp_msg(MSGT_ASS, MSGL_V, "[%p] Style: %s\n", track, str); - - sid = ass_alloc_style(track); - - style = track->styles + sid; - target = style; -// fill style with some default values - style->ScaleX = 100.; - style->ScaleY = 100.; - - while (1) { - NEXT(q, tname); - NEXT(p, token); - -// ALIAS(TertiaryColour,OutlineColour) // ignore TertiaryColour; it appears only in SSA, and is overridden by BackColour - - if (0) { // cool ;) - STRVAL(Name) - if ((strcmp(target->Name, "Default")==0) || (strcmp(target->Name, "*Default")==0)) - track->default_style = sid; - STRVAL(FontName) - COLORVAL(PrimaryColour) - COLORVAL(SecondaryColour) - COLORVAL(OutlineColour) // TertiaryColor - COLORVAL(BackColour) - // SSA uses BackColour for both outline and shadow - // this will destroy SSA's TertiaryColour, but i'm not going to use it anyway - if (track->track_type == TRACK_TYPE_SSA) - target->OutlineColour = target->BackColour; - FPVAL(FontSize) - INTVAL(Bold) - INTVAL(Italic) - INTVAL(Underline) - INTVAL(StrikeOut) - FPVAL(Spacing) - INTVAL(Angle) - INTVAL(BorderStyle) - INTVAL(Alignment) - if (track->track_type == TRACK_TYPE_ASS) - target->Alignment = numpad2align(target->Alignment); - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - INTVAL(Encoding) - FPVAL(ScaleX) - FPVAL(ScaleY) - FPVAL(Outline) - FPVAL(Shadow) - } - } - style->ScaleX /= 100.; - style->ScaleY /= 100.; - style->Bold = !!style->Bold; - style->Italic = !!style->Italic; - style->Underline = !!style->Underline; - if (!style->Name) - style->Name = strdup("Default"); - if (!style->FontName) - style->FontName = strdup("Arial"); - // skip '@' at the start of the font name - if (*style->FontName == '@') { - p = style->FontName; - style->FontName = strdup(p + 1); - free(p); - } - free(format); - return 0; - -} - -static int process_styles_line(ass_track_t* track, char *str) -{ - if (!strncmp(str,"Format:", 7)) { - char* p = str + 7; - skip_spaces(&p); - track->style_format = strdup(p); - mp_msg(MSGT_ASS, MSGL_DBG2, "Style format: %s\n", track->style_format); - } else if (!strncmp(str,"Style:", 6)) { - char* p = str + 6; - skip_spaces(&p); - process_style(track, p); - } - return 0; -} - -static int process_info_line(ass_track_t* track, char *str) -{ - if (!strncmp(str, "PlayResX:", 9)) { - track->PlayResX = atoi(str + 9); - } else if (!strncmp(str,"PlayResY:", 9)) { - track->PlayResY = atoi(str + 9); - } else if (!strncmp(str,"Timer:", 6)) { - track->Timer = atof(str + 6); - } else if (!strncmp(str,"WrapStyle:", 10)) { - track->WrapStyle = atoi(str + 10); - } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) { - track->ScaledBorderAndShadow = parse_bool(str + 22); - } - return 0; -} - -static int process_events_line(ass_track_t* track, char *str) -{ - if (!strncmp(str, "Format:", 7)) { - char* p = str + 7; - skip_spaces(&p); - track->event_format = strdup(p); - mp_msg(MSGT_ASS, MSGL_DBG2, "Event format: %s\n", track->event_format); - } else if (!strncmp(str, "Dialogue:", 9)) { - // This should never be reached for embedded subtitles. - // They have slightly different format and are parsed in ass_process_chunk, - // called directly from demuxer - int eid; - ass_event_t* event; - - str += 9; - skip_spaces(&str); - - eid = ass_alloc_event(track); - event = track->events + eid; - - process_event_tail(track, event, str, 0); - } else { - mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str); - } - return 0; -} - -// Copied from mkvtoolnix -static unsigned char* decode_chars(unsigned char c1, unsigned char c2, - unsigned char c3, unsigned char c4, unsigned char* dst, int cnt) -{ - uint32_t value; - unsigned char bytes[3]; - int i; - - value = ((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 - 33); - bytes[2] = value & 0xff; - bytes[1] = (value & 0xff00) >> 8; - bytes[0] = (value & 0xff0000) >> 16; - - for (i = 0; i < cnt; ++i) - *dst++ = bytes[i]; - return dst; -} - -static int decode_font(ass_track_t* track) -{ - unsigned char* p; - unsigned char* q; - int i; - int size; // original size - int dsize; // decoded size - unsigned char* buf = 0; - - mp_msg(MSGT_ASS, MSGL_V, "font: %d bytes encoded data \n", track->parser_priv->fontdata_used); - size = track->parser_priv->fontdata_used; - if (size % 4 == 1) { - mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_BadEncodedDataSize); - goto error_decode_font; - } - buf = malloc(size / 4 * 3 + 2); - q = buf; - for (i = 0, p = (unsigned char*)track->parser_priv->fontdata; i < size / 4; i++, p+=4) { - q = decode_chars(p[0], p[1], p[2], p[3], q, 3); - } - if (size % 4 == 2) { - q = decode_chars(p[0], p[1], 0, 0, q, 1); - } else if (size % 4 == 3) { - q = decode_chars(p[0], p[1], p[2], 0, q, 2); - } - dsize = q - buf; - assert(dsize <= size / 4 * 3 + 2); - - if (track->library->extract_fonts) { - ass_add_font(track->library, track->parser_priv->fontname, (char*)buf, dsize); - buf = 0; - } - -error_decode_font: - if (buf) free(buf); - free(track->parser_priv->fontname); - free(track->parser_priv->fontdata); - track->parser_priv->fontname = 0; - track->parser_priv->fontdata = 0; - track->parser_priv->fontdata_size = 0; - track->parser_priv->fontdata_used = 0; - return 0; -} - -static int process_fonts_line(ass_track_t* track, char *str) -{ - int len; - - if (!strncmp(str, "fontname:", 9)) { - char* p = str + 9; - skip_spaces(&p); - if (track->parser_priv->fontname) { - decode_font(track); - } - track->parser_priv->fontname = strdup(p); - mp_msg(MSGT_ASS, MSGL_V, "fontname: %s\n", track->parser_priv->fontname); - return 0; - } - - if (!track->parser_priv->fontname) { - mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str); - return 0; - } - - len = strlen(str); - if (len > 80) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontLineTooLong, len, str); - return 0; - } - if (track->parser_priv->fontdata_used + len > track->parser_priv->fontdata_size) { - track->parser_priv->fontdata_size += 100 * 1024; - track->parser_priv->fontdata = realloc(track->parser_priv->fontdata, track->parser_priv->fontdata_size); - } - memcpy(track->parser_priv->fontdata + track->parser_priv->fontdata_used, str, len); - track->parser_priv->fontdata_used += len; - - return 0; -} - -/** - * \brief Parse a header line - * \param track track - * \param str string to parse, zero-terminated -*/ -static int process_line(ass_track_t* track, char *str) -{ - if (!strncasecmp(str, "[Script Info]", 13)) { - track->parser_priv->state = PST_INFO; - } else if (!strncasecmp(str, "[V4 Styles]", 11)) { - track->parser_priv->state = PST_STYLES; - track->track_type = TRACK_TYPE_SSA; - } else if (!strncasecmp(str, "[V4+ Styles]", 12)) { - track->parser_priv->state = PST_STYLES; - track->track_type = TRACK_TYPE_ASS; - } else if (!strncasecmp(str, "[Events]", 8)) { - track->parser_priv->state = PST_EVENTS; - } else if (!strncasecmp(str, "[Fonts]", 7)) { - track->parser_priv->state = PST_FONTS; - } else { - switch (track->parser_priv->state) { - case PST_INFO: - process_info_line(track, str); - break; - case PST_STYLES: - process_styles_line(track, str); - break; - case PST_EVENTS: - process_events_line(track, str); - break; - case PST_FONTS: - process_fonts_line(track, str); - break; - default: - break; - } - } - - // there is no explicit end-of-font marker in ssa/ass - if ((track->parser_priv->state != PST_FONTS) && (track->parser_priv->fontname)) - decode_font(track); - - return 0; -} - -static int process_text(ass_track_t* track, char* str) -{ - char* p = str; - while(1) { - char* q; - while (1) { - if ((*p=='\r')||(*p=='\n')) ++p; - else if (p[0]=='\xef' && p[1]=='\xbb' && p[2]=='\xbf') p+=3; // U+FFFE (BOM) - else break; - } - for (q=p; ((*q!='\0')&&(*q!='\r')&&(*q!='\n')); ++q) {}; - if (q==p) - break; - if (*q != '\0') - *(q++) = '\0'; - process_line(track, p); - if (*q == '\0') - break; - p = q; - } - return 0; -} - -/** - * \brief Process a chunk of subtitle stream data. - * \param track track - * \param data string to parse - * \param size length of data -*/ -void ass_process_data(ass_track_t* track, char* data, int size) -{ - char* str = malloc(size + 1); - - memcpy(str, data, size); - str[size] = '\0'; - - mp_msg(MSGT_ASS, MSGL_V, "event: %s\n", str); - process_text(track, str); - free(str); -} - -/** - * \brief Process CodecPrivate section of subtitle stream - * \param track track - * \param data string to parse - * \param size length of data - CodecPrivate section contains [Stream Info] and [V4+ Styles] ([V4 Styles] for SSA) sections -*/ -void ass_process_codec_private(ass_track_t* track, char *data, int size) -{ - ass_process_data(track, data, size); - - if (!track->event_format) { - // probably an mkv produced by ancient mkvtoolnix - // such files don't have [Events] and Format: headers - track->parser_priv->state = PST_EVENTS; - if (track->track_type == TRACK_TYPE_SSA) - track->event_format = strdup("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"); - else - track->event_format = strdup("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text"); - } - - process_force_style(track); -} - -static int check_duplicate_event(ass_track_t* track, int ReadOrder) -{ - int i; - for (i = 0; in_events - 1; ++i) // ignoring last event, it is the one we are comparing with - if (track->events[i].ReadOrder == ReadOrder) - return 1; - return 0; -} - -/** - * \brief Process a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary). - * \param track track - * \param data string to parse - * \param size length of data - * \param timecode starting time of the event (milliseconds) - * \param duration duration of the event (milliseconds) -*/ -void ass_process_chunk(ass_track_t* track, char *data, int size, long long timecode, long long duration) -{ - char* str; - int eid; - char* p; - char* token; - ass_event_t* event; - - if (!track->event_format) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EventFormatHeaderMissing); - return; - } - - str = malloc(size + 1); - memcpy(str, data, size); - str[size] = '\0'; - mp_msg(MSGT_ASS, MSGL_V, "event at %" PRId64 ", +%" PRId64 ": %s \n", (int64_t)timecode, (int64_t)duration, str); - - eid = ass_alloc_event(track); - event = track->events + eid; - - p = str; - - do { - NEXT(p, token); - event->ReadOrder = atoi(token); - if (check_duplicate_event(track, event->ReadOrder)) - break; - - NEXT(p, token); - event->Layer = atoi(token); - - process_event_tail(track, event, p, 3); - - event->Start = timecode; - event->Duration = duration; - - free(str); - return; -// dump_events(tid); - } while (0); - // some error - ass_free_event(track, eid); - track->n_events--; - free(str); -} - -#ifdef CONFIG_ICONV -/** \brief recode buffer to utf-8 - * constraint: codepage != 0 - * \param data pointer to text buffer - * \param size buffer size - * \return a pointer to recoded buffer, caller is responsible for freeing it -**/ -static char* sub_recode(char* data, size_t size, char* codepage) -{ - static iconv_t icdsc = (iconv_t)(-1); - char* tocp = "UTF-8"; - char* outbuf; - assert(codepage); - - { - const char* cp_tmp = codepage; -#ifdef CONFIG_ENCA - char enca_lang[3], enca_fallback[100]; - if (sscanf(codepage, "enca:%2s:%99s", enca_lang, enca_fallback) == 2 - || sscanf(codepage, "ENCA:%2s:%99s", enca_lang, enca_fallback) == 2) { - cp_tmp = guess_buffer_cp((unsigned char*)data, size, enca_lang, enca_fallback); - } -#endif - if ((icdsc = iconv_open (tocp, cp_tmp)) != (iconv_t)(-1)){ - mp_msg(MSGT_ASS,MSGL_V,"LIBSUB: opened iconv descriptor.\n"); - } else - mp_msg(MSGT_ASS,MSGL_ERR,MSGTR_LIBASS_ErrorOpeningIconvDescriptor); - } - - { - size_t osize = size; - size_t ileft = size; - size_t oleft = size - 1; - char* ip; - char* op; - size_t rc; - int clear = 0; - - outbuf = malloc(osize); - ip = data; - op = outbuf; - - while (1) { - if (ileft) - rc = iconv(icdsc, &ip, &ileft, &op, &oleft); - else {// clear the conversion state and leave - clear = 1; - rc = iconv(icdsc, NULL, NULL, &op, &oleft); - } - if (rc == (size_t)(-1)) { - if (errno == E2BIG) { - size_t offset = op - outbuf; - outbuf = (char*)realloc(outbuf, osize + size); - op = outbuf + offset; - osize += size; - oleft += size; - } else { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorRecodingFile); - return NULL; - } - } else - if (clear) - break; - } - outbuf[osize - oleft - 1] = 0; - } - - if (icdsc != (iconv_t)(-1)) { - (void)iconv_close(icdsc); - icdsc = (iconv_t)(-1); - mp_msg(MSGT_ASS,MSGL_V,"LIBSUB: closed iconv descriptor.\n"); - } - - return outbuf; -} -#endif // ICONV - -/** - * \brief read file contents into newly allocated buffer - * \param fname file name - * \param bufsize out: file size - * \return pointer to file contents. Caller is responsible for its deallocation. - */ -static char* read_file(char* fname, size_t *bufsize) -{ - int res; - long sz; - long bytes_read; - char* buf; - - FILE* fp = fopen(fname, "rb"); - if (!fp) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FopenFailed, fname); - return 0; - } - res = fseek(fp, 0, SEEK_END); - if (res == -1) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FseekFailed, fname); - fclose(fp); - return 0; - } - - sz = ftell(fp); - rewind(fp); - - if (sz > 10*1024*1024) { - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_RefusingToLoadSubtitlesLargerThan10M, fname); - fclose(fp); - return 0; - } - - mp_msg(MSGT_ASS, MSGL_V, "file size: %ld\n", sz); - - buf = malloc(sz + 1); - assert(buf); - bytes_read = 0; - do { - res = fread(buf + bytes_read, 1, sz - bytes_read, fp); - if (res <= 0) { - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_ReadFailed, errno, strerror(errno)); - fclose(fp); - free(buf); - return 0; - } - bytes_read += res; - } while (sz - bytes_read > 0); - buf[sz] = '\0'; - fclose(fp); - - if (bufsize) - *bufsize = sz; - return buf; -} - -/* - * \param buf pointer to subtitle text in utf-8 - */ -static ass_track_t* parse_memory(ass_library_t* library, char* buf) -{ - ass_track_t* track; - int i; - - track = ass_new_track(library); - - // process header - process_text(track, buf); - - // external SSA/ASS subs does not have ReadOrder field - for (i = 0; i < track->n_events; ++i) - track->events[i].ReadOrder = i; - - // there is no explicit end-of-font marker in ssa/ass - if (track->parser_priv->fontname) - decode_font(track); - - if (track->track_type == TRACK_TYPE_UNKNOWN) { - ass_free_track(track); - return 0; - } - - process_force_style(track); - - return track; -} - -/** - * \brief Read subtitles from memory. - * \param library libass library object - * \param buf pointer to subtitles text - * \param bufsize size of buffer - * \param codepage recode buffer contents from given codepage - * \return newly allocated track -*/ -ass_track_t* ass_read_memory(ass_library_t* library, char* buf, size_t bufsize, char* codepage) -{ - ass_track_t* track; - int need_free = 0; - - if (!buf) - return 0; - -#ifdef CONFIG_ICONV - if (codepage) - buf = sub_recode(buf, bufsize, codepage); - if (!buf) - return 0; - else - need_free = 1; -#endif - track = parse_memory(library, buf); - if (need_free) - free(buf); - if (!track) - return 0; - - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileMemory, track->n_styles, track->n_events); - return track; -} - -char* read_file_recode(char* fname, char* codepage, size_t* size) -{ - char* buf; - size_t bufsize; - - buf = read_file(fname, &bufsize); - if (!buf) - return 0; -#ifdef CONFIG_ICONV - if (codepage) { - char* tmpbuf = sub_recode(buf, bufsize, codepage); - free(buf); - buf = tmpbuf; - } - if (!buf) - return 0; -#endif - *size = bufsize; - return buf; -} - -/** - * \brief Read subtitles from file. - * \param library libass library object - * \param fname file name - * \param codepage recode buffer contents from given codepage - * \return newly allocated track -*/ -ass_track_t* ass_read_file(ass_library_t* library, char* fname, char* codepage) -{ - char* buf; - ass_track_t* track; - size_t bufsize; - - buf = read_file_recode(fname, codepage, &bufsize); - if (!buf) - return 0; - track = parse_memory(library, buf); - free(buf); - if (!track) - return 0; - - track->name = strdup(fname); - - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname, track->n_styles, track->n_events); - -// dump_events(forced_tid); - return track; -} - -/** - * \brief read styles from file into already initialized track - */ -int ass_read_styles(ass_track_t* track, char* fname, char* codepage) -{ - char* buf; - parser_state_t old_state; - size_t sz; - - buf = read_file(fname, &sz); - if (!buf) - return 1; -#ifdef CONFIG_ICONV - if (codepage) { - char* tmpbuf; - tmpbuf = sub_recode(buf, sz, codepage); - free(buf); - buf = tmpbuf; - } - if (!buf) - return 0; -#endif - - old_state = track->parser_priv->state; - track->parser_priv->state = PST_STYLES; - process_text(track, buf); - track->parser_priv->state = old_state; - - return 0; -} - -long long ass_step_sub(ass_track_t* track, long long now, int movement) { - int i; - - if (movement == 0) return 0; - if (track->n_events == 0) return 0; - - if (movement < 0) - for (i = 0; (i < track->n_events) && ((long long)(track->events[i].Start + track->events[i].Duration) <= now); ++i) {} - else - for (i = track->n_events - 1; (i >= 0) && ((long long)(track->events[i].Start) > now); --i) {} - - // -1 and n_events are ok - assert(i >= -1); assert(i <= track->n_events); - i += movement; - if (i < 0) i = 0; - if (i >= track->n_events) i = track->n_events - 1; - return ((long long)track->events[i].Start) - now; -} - -ass_track_t* ass_new_track(ass_library_t* library) { - ass_track_t* track = calloc(1, sizeof(ass_track_t)); - track->library = library; - track->ScaledBorderAndShadow = 1; - track->parser_priv = calloc(1, sizeof(parser_priv_t)); - return track; -} diff --git a/libass/ass.h b/libass/ass.h deleted file mode 100644 index e98b4264b7..0000000000 --- a/libass/ass.h +++ /dev/null @@ -1,229 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_ASS_H -#define LIBASS_ASS_H - -#include -#include "ass_types.h" - -/// Libass renderer object. Contents are private. -typedef struct ass_renderer_s ass_renderer_t; - -/// a linked list of images produced by ass renderer -typedef struct ass_image_s { - int w, h; // bitmap width/height - int stride; // bitmap stride - unsigned char* bitmap; // 1bpp stride*h alpha buffer - uint32_t color; // RGBA - int dst_x, dst_y; // bitmap placement inside the video frame - - struct ass_image_s* next; // linked list -} ass_image_t; - -/// Hinting type -typedef enum {ASS_HINTING_NONE = 0, - ASS_HINTING_LIGHT, - ASS_HINTING_NORMAL, - ASS_HINTING_NATIVE -} ass_hinting_t; - -/** - * \brief initialize the library - * \return library handle or NULL if failed - */ -ass_library_t* ass_library_init(void); - -/** - * \brief finalize the library - * \param priv library handle - */ -void ass_library_done(ass_library_t*); - -/** - * \brief set private font directory - * It is used for saving embedded fonts and also in font lookup. - */ -void ass_set_fonts_dir(ass_library_t* priv, const char* fonts_dir); - -void ass_set_extract_fonts(ass_library_t* priv, int extract); - -void ass_set_style_overrides(ass_library_t* priv, char** list); - -/** - * \brief initialize the renderer - * \param priv library handle - * \return renderer handle or NULL if failed - */ -ass_renderer_t* ass_renderer_init(ass_library_t*); - -/** - * \brief finalize the renderer - * \param priv renderer handle - */ -void ass_renderer_done(ass_renderer_t* priv); - -void ass_set_frame_size(ass_renderer_t* priv, int w, int h); -void ass_set_margins(ass_renderer_t* priv, int t, int b, int l, int r); -void ass_set_use_margins(ass_renderer_t* priv, int use); -void ass_set_aspect_ratio(ass_renderer_t* priv, double ar); -void ass_set_font_scale(ass_renderer_t* priv, double font_scale); -void ass_set_hinting(ass_renderer_t* priv, ass_hinting_t ht); -void ass_set_line_spacing(ass_renderer_t* priv, double line_spacing); - -/** - * \brief set font lookup defaults - */ -int ass_set_fonts(ass_renderer_t* priv, const char* default_font, const char* default_family); - -/** - * \brief set font lookup defaults, don't use fontconfig even if it is available - */ -int ass_set_fonts_nofc(ass_renderer_t* priv, const char* default_font, const char* default_family); - -/** - * \brief render a frame, producing a list of ass_image_t - * \param priv library - * \param track subtitle track - * \param now video timestamp in milliseconds - */ -ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change); - - -// The following functions operate on track objects and do not need an ass_renderer // - -/** - * \brief allocate a new empty track object - * \return pointer to empty track - */ -ass_track_t* ass_new_track(ass_library_t*); - -/** - * \brief deallocate track and all its child objects (styles and events) - * \param track track to deallocate - */ -void ass_free_track(ass_track_t* track); - -/** - * \brief allocate new style - * \param track track - * \return newly allocated style id - */ -int ass_alloc_style(ass_track_t* track); - -/** - * \brief allocate new event - * \param track track - * \return newly allocated event id - */ -int ass_alloc_event(ass_track_t* track); - -/** - * \brief delete a style - * \param track track - * \param sid style id - * Deallocates style data. Does not modify track->n_styles. - */ -void ass_free_style(ass_track_t* track, int sid); - -/** - * \brief delete an event - * \param track track - * \param eid event id - * Deallocates event data. Does not modify track->n_events. - */ -void ass_free_event(ass_track_t* track, int eid); - -/** - * \brief Parse a chunk of subtitle stream data. - * \param track track - * \param data string to parse - * \param size length of data - */ -void ass_process_data(ass_track_t* track, char* data, int size); - -/** - * \brief Parse Codec Private section of subtitle stream - * \param track target track - * \param data string to parse - * \param size length of data - */ -void ass_process_codec_private(ass_track_t* track, char *data, int size); - -/** - * \brief Parse a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary). - * \param track track - * \param data string to parse - * \param size length of data - * \param timecode starting time of the event (milliseconds) - * \param duration duration of the event (milliseconds) -*/ -void ass_process_chunk(ass_track_t* track, char *data, int size, long long timecode, long long duration); - -char* read_file_recode(char* fname, char* codepage, size_t* size); - -/** - * \brief Read subtitles from file. - * \param fname file name - * \return newly allocated track -*/ -ass_track_t* ass_read_file(ass_library_t* library, char* fname, char* codepage); - -/** - * \brief Read subtitles from memory. - * \param library libass library object - * \param buf pointer to subtitles text - * \param bufsize size of buffer - * \param codepage recode buffer contents from given codepage - * \return newly allocated track -*/ -ass_track_t* ass_read_memory(ass_library_t* library, char* buf, size_t bufsize, char* codepage); -/** - * \brief read styles from file into already initialized track - * \return 0 on success - */ -int ass_read_styles(ass_track_t* track, char* fname, char* codepage); - -/** - * \brief Add a memory font. - * \param name attachment name - * \param data binary font data - * \param data_size data size -*/ -void ass_add_font(ass_library_t* library, char* name, char* data, int data_size); - -/** - * \brief Remove all fonts stored in ass_library object - */ -void ass_clear_fonts(ass_library_t* library); - -/** - * \brief Calculates timeshift from now to the start of some other subtitle event, depending on movement parameter - * \param track subtitle track - * \param now current time, ms - * \param movement how many events to skip from the one currently displayed - * +2 means "the one after the next", -1 means "previous" - * \return timeshift, ms - */ -long long ass_step_sub(ass_track_t* track, long long now, int movement); - -#endif /* LIBASS_ASS_H */ diff --git a/libass/ass_bitmap.c b/libass/ass_bitmap.c deleted file mode 100644 index 302827af10..0000000000 --- a/libass/ass_bitmap.c +++ /dev/null @@ -1,336 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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 -#include -#include -#include -#include -#include FT_GLYPH_H - -#include "mputils.h" -#include "ass_bitmap.h" - -struct ass_synth_priv_s { - int tmp_w, tmp_h; - unsigned short* tmp; - - int g_r; - int g_w; - - unsigned *g; - unsigned *gt2; - - double radius; -}; - -static const unsigned int maxcolor = 255; -static const unsigned base = 256; - -static int generate_tables(ass_synth_priv_t* priv, double radius) -{ - double A = log(1.0/base)/(radius*radius*2); - int mx, i; - double volume_diff, volume_factor = 0; - unsigned volume; - - if (priv->radius == radius) - return 0; - else - priv->radius = radius; - - priv->g_r = ceil(radius); - priv->g_w = 2*priv->g_r+1; - - if (priv->g_r) { - priv->g = realloc(priv->g, priv->g_w * sizeof(unsigned)); - priv->gt2 = realloc(priv->gt2, 256 * priv->g_w * sizeof(unsigned)); - if (priv->g==NULL || priv->gt2==NULL) { - return -1; - } - } - - if (priv->g_r) { - // gaussian curve with volume = 256 - for (volume_diff=10000000; volume_diff>0.0000001; volume_diff*=0.5){ - volume_factor+= volume_diff; - volume=0; - for (i = 0; ig_w; ++i) { - priv->g[i] = (unsigned)(exp(A * (i-priv->g_r)*(i-priv->g_r)) * volume_factor + .5); - volume+= priv->g[i]; - } - if(volume>256) volume_factor-= volume_diff; - } - volume=0; - for (i = 0; ig_w; ++i) { - priv->g[i] = (unsigned)(exp(A * (i-priv->g_r)*(i-priv->g_r)) * volume_factor + .5); - volume+= priv->g[i]; - } - - // gauss table: - for(mx=0;mxg_w;mx++){ - for(i=0;i<256;i++){ - priv->gt2[mx+i*priv->g_w] = i*priv->g[mx]; - } - } - } - - return 0; -} - -static void resize_tmp(ass_synth_priv_t* priv, int w, int h) -{ - if (priv->tmp_w >= w && priv->tmp_h >= h) - return; - if (priv->tmp_w == 0) - priv->tmp_w = 64; - if (priv->tmp_h == 0) - priv->tmp_h = 64; - while (priv->tmp_w < w) priv->tmp_w *= 2; - while (priv->tmp_h < h) priv->tmp_h *= 2; - if (priv->tmp) - free(priv->tmp); - priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short)); -} - -ass_synth_priv_t* ass_synth_init(double radius) -{ - ass_synth_priv_t* priv = calloc(1, sizeof(ass_synth_priv_t)); - generate_tables(priv, radius); - return priv; -} - -void ass_synth_done(ass_synth_priv_t* priv) -{ - if (priv->tmp) - free(priv->tmp); - if (priv->g) - free(priv->g); - if (priv->gt2) - free(priv->gt2); - free(priv); -} - -static bitmap_t* alloc_bitmap(int w, int h) -{ - bitmap_t* bm; - bm = calloc(1, sizeof(bitmap_t)); - bm->buffer = malloc(w*h); - bm->w = w; - bm->h = h; - bm->left = bm->top = 0; - return bm; -} - -void ass_free_bitmap(bitmap_t* bm) -{ - if (bm) { - if (bm->buffer) free(bm->buffer); - free(bm); - } -} - -static bitmap_t* copy_bitmap(const bitmap_t* src) -{ - bitmap_t* dst = alloc_bitmap(src->w, src->h); - dst->left = src->left; - dst->top = src->top; - memcpy(dst->buffer, src->buffer, src->w * src->h); - return dst; -} - -static int check_glyph_area(FT_Glyph glyph) -{ - FT_BBox bbox; - long long dx, dy; - FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox); - dx = bbox.xMax - bbox.xMin; - dy = bbox.yMax - bbox.yMin; - if (dx * dy > 8000000) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_GlyphBBoxTooLarge, (int)dx, (int)dy); - return 1; - } else - return 0; -} - -static bitmap_t* glyph_to_bitmap_internal(FT_Glyph glyph, int bord) -{ - FT_BitmapGlyph bg; - FT_Bitmap* bit; - bitmap_t* bm; - int w, h; - unsigned char* src; - unsigned char* dst; - int i; - int error; - - if (check_glyph_area(glyph)) - return 0; - error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FT_Glyph_To_BitmapError, error); - return 0; - } - - bg = (FT_BitmapGlyph)glyph; - bit = &(bg->bitmap); - if (bit->pixel_mode != FT_PIXEL_MODE_GRAY) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UnsupportedPixelMode, (int)(bit->pixel_mode)); - FT_Done_Glyph(glyph); - return 0; - } - - w = bit->width; - h = bit->rows; - bm = alloc_bitmap(w + 2*bord, h + 2*bord); - memset(bm->buffer, 0, bm->w * bm->h); - bm->left = bg->left - bord; - bm->top = - bg->top - bord; - - src = bit->buffer; - dst = bm->buffer + bord + bm->w * bord; - for (i = 0; i < h; ++i) { - memcpy(dst, src, w); - src += bit->pitch; - dst += bm->w; - } - - FT_Done_Glyph(glyph); - return bm; -} - -/** - * \brief fix outline bitmap and generate shadow bitmap - * Two things are done here: - * 1. Glyph bitmap is subtracted from outline bitmap. This way looks much better in some cases. - * 2. Shadow bitmap is created as a sum of glyph and outline bitmaps. - */ -static bitmap_t* fix_outline_and_shadow(bitmap_t* bm_g, bitmap_t* bm_o) -{ - int x, y; - const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left; - const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top; - const int r = bm_o->left + bm_o->w < bm_g->left + bm_g->w ? bm_o->left + bm_o->w : bm_g->left + bm_g->w; - const int b = bm_o->top + bm_o->h < bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h; - - bitmap_t* bm_s = copy_bitmap(bm_o); - - unsigned char* g = bm_g->buffer + (t - bm_g->top) * bm_g->w + (l - bm_g->left); - unsigned char* o = bm_o->buffer + (t - bm_o->top) * bm_o->w + (l - bm_o->left); - unsigned char* s = bm_s->buffer + (t - bm_s->top) * bm_s->w + (l - bm_s->left); - - for (y = 0; y < b - t; ++y) { - for (x = 0; x < r - l; ++x) { - unsigned char c_g, c_o; - c_g = g[x]; - c_o = o[x]; - o[x] = (c_o > c_g) ? c_o - (c_g/2) : 0; - s[x] = (c_o < 0xFF - c_g) ? c_o + c_g : 0xFF; - } - g += bm_g->w; - o += bm_o->w; - s += bm_s->w; - } - - assert(bm_s); - return bm_s; -} - -/** - * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel - * This blur is the same as the one employed by vsfilter. - */ -static void be_blur(unsigned char *buf, int w, int h) { - unsigned int x, y; - unsigned int old_sum, new_sum; - - for (y=0; y> 2; - old_sum = new_sum; - } - } - - for (x=0; x> 2; - old_sum = new_sum; - } - } -} - -int glyph_to_bitmap(ass_synth_priv_t* priv_blur, - FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g, - bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius) -{ - int bord = be ? (be/4+1) : 0; - blur_radius *= 2; - bord = (blur_radius > 0.0) ? blur_radius : bord; - - assert(bm_g && bm_o && bm_s); - - *bm_g = *bm_o = *bm_s = 0; - - if (glyph) - *bm_g = glyph_to_bitmap_internal(glyph, bord); - if (!*bm_g) - return 1; - - if (outline_glyph) { - *bm_o = glyph_to_bitmap_internal(outline_glyph, bord); - if (!*bm_o) { - ass_free_bitmap(*bm_g); - return 1; - } - } - if (*bm_o) - resize_tmp(priv_blur, (*bm_o)->w, (*bm_o)->h); - resize_tmp(priv_blur, (*bm_g)->w, (*bm_g)->h); - - if (be) { - while (be--) { - if (*bm_o) - be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h); - else - be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h); - } - } else { - if (blur_radius > 0.0) { - generate_tables(priv_blur, blur_radius); - if (*bm_o) - blur((*bm_o)->buffer, priv_blur->tmp, (*bm_o)->w, (*bm_o)->h, (*bm_o)->w, (int*)priv_blur->gt2, priv_blur->g_r, priv_blur->g_w); - else - blur((*bm_g)->buffer, priv_blur->tmp, (*bm_g)->w, (*bm_g)->h, (*bm_g)->w, (int*)priv_blur->gt2, priv_blur->g_r, priv_blur->g_w); - } - } - if (*bm_o) - *bm_s = fix_outline_and_shadow(*bm_g, *bm_o); - else - *bm_s = copy_bitmap(*bm_g); - - assert(bm_s); - return 0; -} diff --git a/libass/ass_bitmap.h b/libass/ass_bitmap.h deleted file mode 100644 index c0b4ad201f..0000000000 --- a/libass/ass_bitmap.h +++ /dev/null @@ -1,53 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_BITMAP_H -#define LIBASS_BITMAP_H - -#include -#include FT_GLYPH_H - -typedef struct ass_synth_priv_s ass_synth_priv_t; - -ass_synth_priv_t* ass_synth_init(double); -void ass_synth_done(ass_synth_priv_t* priv); - -typedef struct bitmap_s { - int left, top; - int w, h; // width, height - unsigned char* buffer; // w x h buffer -} bitmap_t; - -/** - * \brief perform glyph rendering - * \param glyph original glyph - * \param outline_glyph "border" glyph, produced from original by FreeType's glyph stroker - * \param bm_g out: pointer to the bitmap of original glyph is returned here - * \param bm_o out: pointer to the bitmap of outline (border) glyph is returned here - * \param bm_g out: pointer to the bitmap of glyph shadow is returned here - * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps - */ -int glyph_to_bitmap(ass_synth_priv_t* priv_blur, FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g, bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius); - -void ass_free_bitmap(bitmap_t* bm); - -#endif /* LIBASS_BITMAP_H */ diff --git a/libass/ass_cache.c b/libass/ass_cache.c deleted file mode 100644 index 2150d27ec2..0000000000 --- a/libass/ass_cache.c +++ /dev/null @@ -1,385 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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 -#include -#include FT_FREETYPE_H -#include FT_GLYPH_H - -#include - -#include "mputils.h" -#include "ass.h" -#include "ass_fontconfig.h" -#include "ass_font.h" -#include "ass_bitmap.h" -#include "ass_cache.h" - - -typedef struct hashmap_item_s { - void* key; - void* value; - struct hashmap_item_s* next; -} hashmap_item_t; -typedef hashmap_item_t* hashmap_item_p; - -struct hashmap_s { - int nbuckets; - size_t key_size, value_size; - hashmap_item_p* root; - hashmap_item_dtor_t item_dtor; // a destructor for hashmap key/value pairs - hashmap_key_compare_t key_compare; - hashmap_hash_t hash; - // stats - int hit_count; - int miss_count; - int count; -}; - -#define FNV1_32A_INIT (unsigned)0x811c9dc5 - -static inline unsigned fnv_32a_buf(void* buf, size_t len, unsigned hval) -{ - unsigned char *bp = buf; - unsigned char *be = bp + len; - while (bp < be) { - hval ^= (unsigned)*bp++; - hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); - } - return hval; -} -static inline unsigned fnv_32a_str(char* str, unsigned hval) -{ - unsigned char* s = (unsigned char*)str; - while (*s) { - hval ^= (unsigned)*s++; - hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); - } - return hval; -} - -static unsigned hashmap_hash(void* buf, size_t len) -{ - return fnv_32a_buf(buf, len, FNV1_32A_INIT); -} - -static int hashmap_key_compare(void* a, void* b, size_t size) -{ - return memcmp(a, b, size) == 0; -} - -static void hashmap_item_dtor(void* key, size_t key_size, void* value, size_t value_size) -{ - free(key); - free(value); -} - -hashmap_t* hashmap_init(size_t key_size, size_t value_size, int nbuckets, - hashmap_item_dtor_t item_dtor, hashmap_key_compare_t key_compare, - hashmap_hash_t hash) -{ - hashmap_t* map = calloc(1, sizeof(hashmap_t)); - map->nbuckets = nbuckets; - map->key_size = key_size; - map->value_size = value_size; - map->root = calloc(nbuckets, sizeof(hashmap_item_p)); - map->item_dtor = item_dtor ? item_dtor : hashmap_item_dtor; - map->key_compare = key_compare ? key_compare : hashmap_key_compare; - map->hash = hash ? hash : hashmap_hash; - return map; -} - -void hashmap_done(hashmap_t* map) -{ - int i; - // print stats - if (map->count > 0 || map->hit_count + map->miss_count > 0) - mp_msg(MSGT_ASS, MSGL_V, "cache statistics: \n total accesses: %d\n hits: %d\n misses: %d\n object count: %d\n", - map->hit_count + map->miss_count, map->hit_count, map->miss_count, map->count); - - for (i = 0; i < map->nbuckets; ++i) { - hashmap_item_t* item = map->root[i]; - while (item) { - hashmap_item_t* next = item->next; - map->item_dtor(item->key, map->key_size, item->value, map->value_size); - free(item); - item = next; - } - } - free(map->root); - free(map); -} - -// does nothing if key already exists -void* hashmap_insert(hashmap_t* map, void* key, void* value) -{ - unsigned hash = map->hash(key, map->key_size); - hashmap_item_t** next = map->root + (hash % map->nbuckets); - while (*next) { - if (map->key_compare(key, (*next)->key, map->key_size)) - return (*next)->value; - next = &((*next)->next); - assert(next); - } - (*next) = malloc(sizeof(hashmap_item_t)); - (*next)->key = malloc(map->key_size); - (*next)->value = malloc(map->value_size); - memcpy((*next)->key, key, map->key_size); - memcpy((*next)->value, value, map->value_size); - (*next)->next = 0; - - map->count ++; - return (*next)->value; -} - -void* hashmap_find(hashmap_t* map, void* key) -{ - unsigned hash = map->hash(key, map->key_size); - hashmap_item_t* item = map->root[hash % map->nbuckets]; - while (item) { - if (map->key_compare(key, item->key, map->key_size)) { - map->hit_count++; - return item->value; - } - item = item->next; - } - map->miss_count++; - return 0; -} - -//--------------------------------- -// font cache - -hashmap_t* font_cache; - -static unsigned font_desc_hash(void* buf, size_t len) -{ - ass_font_desc_t* desc = buf; - unsigned hval; - hval = fnv_32a_str(desc->family, FNV1_32A_INIT); - hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval); - hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval); - return hval; -} - -static int font_compare(void* key1, void* key2, size_t key_size) { - ass_font_desc_t* a = key1; - ass_font_desc_t* b = key2; - if (strcmp(a->family, b->family) != 0) - return 0; - if (a->bold != b->bold) - return 0; - if (a->italic != b->italic) - return 0; - if (a->treat_family_as_pattern != b->treat_family_as_pattern) - return 0; - return 1; -} - -static void font_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) -{ - ass_font_free(value); - free(key); -} - -ass_font_t* ass_font_cache_find(ass_font_desc_t* desc) -{ - return hashmap_find(font_cache, desc); -} - -/** - * \brief Add a face struct to cache. - * \param font font struct -*/ -void* ass_font_cache_add(ass_font_t* font) -{ - return hashmap_insert(font_cache, &(font->desc), font); -} - -void ass_font_cache_init(void) -{ - font_cache = hashmap_init(sizeof(ass_font_desc_t), - sizeof(ass_font_t), - 1000, - font_hash_dtor, font_compare, font_desc_hash); -} - -void ass_font_cache_done(void) -{ - hashmap_done(font_cache); -} - - -// Create hash/compare functions for bitmap and glyph -#define CREATE_HASH_FUNCTIONS -#include "ass_cache_template.c" -#define CREATE_COMPARISON_FUNCTIONS -#include "ass_cache_template.c" - -//--------------------------------- -// bitmap cache - -hashmap_t* bitmap_cache; - -static void bitmap_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) -{ - bitmap_hash_val_t* v = value; - if (v->bm) ass_free_bitmap(v->bm); - if (v->bm_o) ass_free_bitmap(v->bm_o); - if (v->bm_s) ass_free_bitmap(v->bm_s); - free(key); - free(value); -} - -void* cache_add_bitmap(bitmap_hash_key_t* key, bitmap_hash_val_t* val) -{ - return hashmap_insert(bitmap_cache, key, val); -} - -/** - * \brief Get a bitmap from bitmap cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -bitmap_hash_val_t* cache_find_bitmap(bitmap_hash_key_t* key) -{ - return hashmap_find(bitmap_cache, key); -} - -void ass_bitmap_cache_init(void) -{ - bitmap_cache = hashmap_init(sizeof(bitmap_hash_key_t), - sizeof(bitmap_hash_val_t), - 0xFFFF + 13, - bitmap_hash_dtor, bitmap_compare, - bitmap_hash); -} - -void ass_bitmap_cache_done(void) -{ - hashmap_done(bitmap_cache); -} - -void ass_bitmap_cache_reset(void) -{ - ass_bitmap_cache_done(); - ass_bitmap_cache_init(); -} - -//--------------------------------- -// glyph cache - -hashmap_t* glyph_cache; - -static void glyph_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) -{ - glyph_hash_val_t* v = value; - if (v->glyph) FT_Done_Glyph(v->glyph); - if (v->outline_glyph) FT_Done_Glyph(v->outline_glyph); - free(key); - free(value); -} - -void* cache_add_glyph(glyph_hash_key_t* key, glyph_hash_val_t* val) -{ - return hashmap_insert(glyph_cache, key, val); -} - -/** - * \brief Get a glyph from glyph cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -glyph_hash_val_t* cache_find_glyph(glyph_hash_key_t* key) -{ - return hashmap_find(glyph_cache, key); -} - -void ass_glyph_cache_init(void) -{ - glyph_cache = hashmap_init(sizeof(glyph_hash_key_t), - sizeof(glyph_hash_val_t), - 0xFFFF + 13, - glyph_hash_dtor, glyph_compare, glyph_hash); -} - -void ass_glyph_cache_done(void) -{ - hashmap_done(glyph_cache); -} - -void ass_glyph_cache_reset(void) -{ - ass_glyph_cache_done(); - ass_glyph_cache_init(); -} - - -//--------------------------------- -// composite cache - -hashmap_t* composite_cache; - -static void composite_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) -{ - composite_hash_val_t* v = value; - free(v->a); - free(v->b); - free(key); - free(value); -} - -void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val) -{ - return hashmap_insert(composite_cache, key, val); -} - -/** - * \brief Get a composite bitmap from composite cache. - * \param key hash key - * \return requested hash val or 0 if not found -*/ -composite_hash_val_t* cache_find_composite(composite_hash_key_t* key) -{ - return hashmap_find(composite_cache, key); -} - -void ass_composite_cache_init(void) -{ - composite_cache = hashmap_init(sizeof(composite_hash_key_t), - sizeof(composite_hash_val_t), - 0xFFFF + 13, - composite_hash_dtor, NULL, NULL); -} - -void ass_composite_cache_done(void) -{ - hashmap_done(composite_cache); -} - -void ass_composite_cache_reset(void) -{ - ass_composite_cache_done(); - ass_composite_cache_init(); -} diff --git a/libass/ass_cache.h b/libass/ass_cache.h deleted file mode 100644 index 863ba6fdf6..0000000000 --- a/libass/ass_cache.h +++ /dev/null @@ -1,98 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_CACHE_H -#define LIBASS_CACHE_H - -#include "ass.h" -#include "ass_font.h" -#include "ass_bitmap.h" - -void ass_font_cache_init(void); -ass_font_t* ass_font_cache_find(ass_font_desc_t* desc); -void* ass_font_cache_add(ass_font_t* font); -void ass_font_cache_done(void); - - -// Create definitions for bitmap_hash_key and glyph_hash_key -#define CREATE_STRUCT_DEFINITIONS -#include "ass_cache_template.c" - -typedef struct bitmap_hash_val_s { - bitmap_t* bm; // the actual bitmaps - bitmap_t* bm_o; - bitmap_t* bm_s; -} bitmap_hash_val_t; - -void ass_bitmap_cache_init(void); -void* cache_add_bitmap(bitmap_hash_key_t* key, bitmap_hash_val_t* val); -bitmap_hash_val_t* cache_find_bitmap(bitmap_hash_key_t* key); -void ass_bitmap_cache_reset(void); -void ass_bitmap_cache_done(void); - - -// Cache for composited bitmaps -typedef struct composite_hash_key_s { - int aw, ah, bw, bh; - int ax, ay, bx, by; - bitmap_hash_key_t a; - bitmap_hash_key_t b; -} composite_hash_key_t; - -typedef struct composite_hash_val_s { - unsigned char* a; - unsigned char* b; -} composite_hash_val_t; - -void ass_composite_cache_init(void); -void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val); -composite_hash_val_t* cache_find_composite(composite_hash_key_t* key); -void ass_composite_cache_reset(void); -void ass_composite_cache_done(void); - - -typedef struct glyph_hash_val_s { - FT_Glyph glyph; - FT_Glyph outline_glyph; - FT_BBox bbox_scaled; // bbox after scaling, but before rotation - FT_Vector advance; // 26.6, advance distance to the next bitmap in line -} glyph_hash_val_t; - -void ass_glyph_cache_init(void); -void* cache_add_glyph(glyph_hash_key_t* key, glyph_hash_val_t* val); -glyph_hash_val_t* cache_find_glyph(glyph_hash_key_t* key); -void ass_glyph_cache_reset(void); -void ass_glyph_cache_done(void); - -typedef struct hashmap_s hashmap_t; -typedef void (*hashmap_item_dtor_t)(void* key, size_t key_size, void* value, size_t value_size); -typedef int (*hashmap_key_compare_t)(void* key1, void* key2, size_t key_size); -typedef unsigned (*hashmap_hash_t)(void* key, size_t key_size); - -hashmap_t* hashmap_init(size_t key_size, size_t value_size, int nbuckets, - hashmap_item_dtor_t item_dtor, hashmap_key_compare_t key_compare, - hashmap_hash_t hash); -void hashmap_done(hashmap_t* map); -void* hashmap_insert(hashmap_t* map, void* key, void* value); -void* hashmap_find(hashmap_t* map, void* key); - -#endif /* LIBASS_CACHE_H */ diff --git a/libass/ass_cache_template.c b/libass/ass_cache_template.c deleted file mode 100644 index 7f9ec95f57..0000000000 --- a/libass/ass_cache_template.c +++ /dev/null @@ -1,88 +0,0 @@ -#ifdef CREATE_STRUCT_DEFINITIONS -#undef CREATE_STRUCT_DEFINITIONS -#define START(funcname, structname) \ - typedef struct structname { -#define GENERIC(type, member) \ - type member; -#define FTVECTOR(member) \ - FT_Vector member; -#define END(typedefnamename) \ - } typedefnamename; - -#elif defined(CREATE_COMPARISON_FUNCTIONS) -#undef CREATE_COMPARISON_FUNCTIONS -#define START(funcname, structname) \ - static int funcname##_compare(void *key1, void *key2, size_t key_size) \ - { \ - struct structname *a = key1; \ - struct structname *b = key2; \ - return // conditions follow -#define GENERIC(type, member) \ - a->member == b->member && -#define FTVECTOR(member) \ - a->member.x == b->member.x && a->member.y == b->member.y && -#define END(typedefname) \ - 1; \ - } - -#elif defined(CREATE_HASH_FUNCTIONS) -#undef CREATE_HASH_FUNCTIONS -#define START(funcname, structname) \ - static unsigned funcname##_hash(void *buf, size_t len) \ - { \ - struct structname *p = buf; \ - unsigned hval = FNV1_32A_INIT; -#define GENERIC(type, member) \ - hval = fnv_32a_buf(&p->member, sizeof(p->member), hval); -#define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y); -#define END(typedefname) \ - return hval; \ - } - -#else -#error missing defines -#endif - - - -// describes a bitmap; bitmaps with equivalents structs are considered identical -START(bitmap, bipmap_hash_key_s) - GENERIC(char, bitmap) // bool : true = bitmap, false = outline - GENERIC(ass_font_t *, font) - GENERIC(double, size) // font size - GENERIC(uint32_t, ch) // character code - GENERIC(unsigned, outline) // border width, 16.16 fixed point value - GENERIC(int, bold) - GENERIC(int, italic) - GENERIC(char, be) // blur edges - GENERIC(double, blur) // gaussian blur - GENERIC(unsigned, scale_x) // 16.16 - GENERIC(unsigned, scale_y) // 16.16 - GENERIC(int, frx) // signed 16.16 - GENERIC(int, fry) // signed 16.16 - GENERIC(int, frz) // signed 16.16 - // shift vector that was added to glyph before applying rotation - // = 0, if frx = fry = frx = 0 - // = (glyph base point) - (rotation origin), otherwise - GENERIC(int, shift_x) - GENERIC(int, shift_y) - FTVECTOR(advance) // subpixel shift vector -END(bitmap_hash_key_t) - -// describes an outline glyph -START(glyph, glyph_hash_key_s) - GENERIC(ass_font_t *, font) - GENERIC(double, size) // font size - GENERIC(uint32_t, ch) // character code - GENERIC(int, bold) - GENERIC(int, italic) - GENERIC(unsigned, scale_x) // 16.16 - GENERIC(unsigned, scale_y) // 16.16 - FTVECTOR(advance) // subpixel shift vector - GENERIC(unsigned, outline) // border width, 16.16 -END(glyph_hash_key_t) - -#undef START -#undef GENERIC -#undef FTVECTOR -#undef END diff --git a/libass/ass_font.c b/libass/ass_font.c deleted file mode 100644 index 73ee1c4a5b..0000000000 --- a/libass/ass_font.c +++ /dev/null @@ -1,371 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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 -#include -#include FT_FREETYPE_H -#include FT_SYNTHESIS_H -#include FT_GLYPH_H -#include FT_TRUETYPE_TABLES_H - -#include "ass.h" -#include "ass_library.h" -#include "ass_font.h" -#include "ass_bitmap.h" -#include "ass_cache.h" -#include "ass_fontconfig.h" -#include "ass_utils.h" -#include "mputils.h" - -/** - * Select Microfost Unicode CharMap, if the font has one. - * Otherwise, let FreeType decide. - */ -static void charmap_magic(FT_Face face) -{ - int i; - for (i = 0; i < face->num_charmaps; ++i) { - FT_CharMap cmap = face->charmaps[i]; - unsigned pid = cmap->platform_id; - unsigned eid = cmap->encoding_id; - if (pid == 3 /*microsoft*/ && (eid == 1 /*unicode bmp*/ || eid == 10 /*full unicode*/)) { - FT_Set_Charmap(face, cmap); - return; - } - } - - if (!face->charmap) { - if (face->num_charmaps == 0) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoCharmaps); - return; - } - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoCharmapAutodetected); - FT_Set_Charmap(face, face->charmaps[0]); - return; - } -} - -static void update_transform(ass_font_t* font) -{ - int i; - FT_Matrix m; - m.xx = double_to_d16(font->scale_x); - m.yy = double_to_d16(font->scale_y); - m.xy = m.yx = 0; - for (i = 0; i < font->n_faces; ++i) - FT_Set_Transform(font->faces[i], &m, &font->v); -} - -/** - * \brief find a memory font by name - */ -static int find_font(ass_library_t* library, char* name) -{ - int i; - for (i = 0; i < library->num_fontdata; ++i) - if (strcasecmp(name, library->fontdata[i].name) == 0) - return i; - return -1; -} - -static void face_set_size(FT_Face face, double size); - -static void buggy_font_workaround(FT_Face face) -{ - // Some fonts have zero Ascender/Descender fields in 'hhea' table. - // In this case, get the information from 'os2' table or, as - // a last resort, from face.bbox. - if (face->ascender + face->descender == 0 || face->height == 0) { - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - if (os2) { - face->ascender = os2->sTypoAscender; - face->descender = os2->sTypoDescender; - face->height = face->ascender - face->descender; - } else { - face->ascender = face->bbox.yMax; - face->descender = face->bbox.yMin; - face->height = face->ascender - face->descender; - } - } -} - -/** - * \brief Select a face with the given charcode and add it to ass_font_t - * \return index of the new face in font->faces, -1 if failed - */ -static int add_face(void* fc_priv, ass_font_t* font, uint32_t ch) -{ - char* path; - int index; - FT_Face face; - int error; - int mem_idx; - - if (font->n_faces == ASS_FONT_MAX_FACES) - return -1; - - path = fontconfig_select(fc_priv, font->desc.family, font->desc.treat_family_as_pattern, font->desc.bold, - font->desc.italic, &index, ch); - if (!path) - return -1; - - mem_idx = find_font(font->library, path); - if (mem_idx >= 0) { - error = FT_New_Memory_Face(font->ftlibrary, (unsigned char*)font->library->fontdata[mem_idx].data, - font->library->fontdata[mem_idx].size, 0, &face); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, path); - return -1; - } - } else { - error = FT_New_Face(font->ftlibrary, path, index, &face); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningFont, path, index); - return -1; - } - } - charmap_magic(face); - buggy_font_workaround(face); - - font->faces[font->n_faces++] = face; - update_transform(font); - face_set_size(face, font->size); - return font->n_faces - 1; -} - -/** - * \brief Create a new ass_font_t according to "desc" argument - */ -ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_priv, ass_font_desc_t* desc) -{ - int error; - ass_font_t* fontp; - ass_font_t font; - - fontp = ass_font_cache_find(desc); - if (fontp) - return fontp; - - font.library = library; - font.ftlibrary = ftlibrary; - font.n_faces = 0; - font.desc.family = strdup(desc->family); - font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; - font.desc.bold = desc->bold; - font.desc.italic = desc->italic; - - font.scale_x = font.scale_y = 1.; - font.v.x = font.v.y = 0; - font.size = 0.; - - error = add_face(fc_priv, &font, 0); - if (error == -1) { - free(font.desc.family); - return 0; - } else - return ass_font_cache_add(&font); -} - -/** - * \brief Set font transformation matrix and shift vector - **/ -void ass_font_set_transform(ass_font_t* font, double scale_x, double scale_y, FT_Vector* v) -{ - font->scale_x = scale_x; - font->scale_y = scale_y; - font->v.x = v->x; - font->v.y = v->y; - update_transform(font); -} - -static void face_set_size(FT_Face face, double size) -{ -#if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1)) - TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - double mscale = 1.; - FT_Size_RequestRec rq; - FT_Size_Metrics *m = &face->size->metrics; - // VSFilter uses metrics from TrueType OS/2 table - // The idea was borrowed from asa (http://asa.diac24.net) - if (hori && os2) { - int hori_height = hori->Ascender - hori->Descender; - int os2_height = os2->usWinAscent + os2->usWinDescent; - if (hori_height && os2_height) - mscale = (double)hori_height / os2_height; - } - memset(&rq, 0, sizeof(rq)); - rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; - rq.width = 0; - rq.height = double_to_d6(size * mscale); - rq.horiResolution = rq.vertResolution = 0; - FT_Request_Size(face, &rq); - m->ascender /= mscale; - m->descender /= mscale; - m->height /= mscale; -#else - FT_Set_Char_Size(face, 0, double_to_d6(size), 0, 0); -#endif -} - -/** - * \brief Set font size - **/ -void ass_font_set_size(ass_font_t* font, double size) -{ - int i; - if (font->size != size) { - font->size = size; - for (i = 0; i < font->n_faces; ++i) - face_set_size(font->faces[i], size); - } -} - -/** - * \brief Get maximal font ascender and descender. - * \param ch character code - * The values are extracted from the font face that provides glyphs for the given character - **/ -void ass_font_get_asc_desc(ass_font_t* font, uint32_t ch, int* asc, int* desc) -{ - int i; - for (i = 0; i < font->n_faces; ++i) { - FT_Face face = font->faces[i]; - if (FT_Get_Char_Index(face, ch)) { - *asc = face->size->metrics.ascender; - *desc = - face->size->metrics.descender; - return; - } - } - - *asc = *desc = 0; -} - -/** - * \brief Get a glyph - * \param ch character code - **/ -FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch, ass_hinting_t hinting) -{ - int error; - int index = 0; - int i; - FT_Glyph glyph; - FT_Face face = 0; - int flags = 0; - - if (ch < 0x20) - return 0; - if (font->n_faces == 0) - return 0; - - for (i = 0; i < font->n_faces; ++i) { - face = font->faces[i]; - index = FT_Get_Char_Index(face, ch); - if (index) - break; - } - -#ifdef CONFIG_FONTCONFIG - if (index == 0) { - int face_idx; - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_GlyphNotFoundReselectingFont, - ch, font->desc.family, font->desc.bold, font->desc.italic); - face_idx = add_face(fontconfig_priv, font, ch); - if (face_idx >= 0) { - face = font->faces[face_idx]; - index = FT_Get_Char_Index(face, ch); - if (index == 0) { - mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_GlyphNotFound, - ch, font->desc.family, font->desc.bold, font->desc.italic); - } - } - } -#endif - - switch (hinting) { - case ASS_HINTING_NONE: flags = FT_LOAD_NO_HINTING; break; - case ASS_HINTING_LIGHT: flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; break; - case ASS_HINTING_NORMAL: flags = FT_LOAD_FORCE_AUTOHINT; break; - case ASS_HINTING_NATIVE: flags = 0; break; - } - - error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph); - return 0; - } - -#if (FREETYPE_MAJOR > 2) || \ - ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR >= 2)) || \ - ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR == 1) && (FREETYPE_PATCH >= 10)) -// FreeType >= 2.1.10 required - if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && - (font->desc.italic > 55)) { - FT_GlyphSlot_Oblique(face->glyph); - } -#endif - error = FT_Get_Glyph(face->glyph, &glyph); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph); - return 0; - } - - return glyph; -} - -/** - * \brief Get kerning for the pair of glyphs. - **/ -FT_Vector ass_font_get_kerning(ass_font_t* font, uint32_t c1, uint32_t c2) -{ - FT_Vector v = {0, 0}; - int i; - - for (i = 0; i < font->n_faces; ++i) { - FT_Face face = font->faces[i]; - int i1 = FT_Get_Char_Index(face, c1); - int i2 = FT_Get_Char_Index(face, c2); - if (i1 && i2) { - if (FT_HAS_KERNING(face)) - FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v); - return v; - } - if (i1 || i2) // these glyphs are from different font faces, no kerning information - return v; - } - return v; -} - -/** - * \brief Deallocate ass_font_t - **/ -void ass_font_free(ass_font_t* font) -{ - int i; - for (i = 0; i < font->n_faces; ++i) - if (font->faces[i]) FT_Done_Face(font->faces[i]); - if (font->desc.family) free(font->desc.family); - free(font); -} diff --git a/libass/ass_font.h b/libass/ass_font.h deleted file mode 100644 index 520431821b..0000000000 --- a/libass/ass_font.h +++ /dev/null @@ -1,60 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_FONT_H -#define LIBASS_FONT_H - -#include -#include -#include FT_GLYPH_H -#include "ass.h" -#include "ass_types.h" - -typedef struct ass_font_desc_s { - char* family; - unsigned bold; - unsigned italic; - int treat_family_as_pattern; -} ass_font_desc_t; - -#define ASS_FONT_MAX_FACES 10 - -typedef struct ass_font_s { - ass_font_desc_t desc; - ass_library_t* library; - FT_Library ftlibrary; - FT_Face faces[ASS_FONT_MAX_FACES]; - int n_faces; - double scale_x, scale_y; // current transform - FT_Vector v; // current shift - double size; -} ass_font_t; - -ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_priv, ass_font_desc_t* desc); -void ass_font_set_transform(ass_font_t* font, double scale_x, double scale_y, FT_Vector* v); -void ass_font_set_size(ass_font_t* font, double size); -void ass_font_get_asc_desc(ass_font_t* font, uint32_t ch, int* asc, int* desc); -FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch, ass_hinting_t hinting); -FT_Vector ass_font_get_kerning(ass_font_t* font, uint32_t c1, uint32_t c2); -void ass_font_free(ass_font_t* font); - -#endif /* LIBASS_FONT_H */ diff --git a/libass/ass_fontconfig.c b/libass/ass_fontconfig.c deleted file mode 100644 index 0b030bdf85..0000000000 --- a/libass/ass_fontconfig.c +++ /dev/null @@ -1,516 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include FT_FREETYPE_H - -#include "mputils.h" -#include "ass.h" -#include "ass_library.h" -#include "ass_fontconfig.h" - -#ifdef CONFIG_FONTCONFIG -#include -#include -#endif - -struct fc_instance_s { -#ifdef CONFIG_FONTCONFIG - FcConfig* config; -#endif - char* family_default; - char* path_default; - int index_default; -}; - -#ifdef CONFIG_FONTCONFIG - -// 4yo fontconfig does not have these. -// They are only needed for debug output, anyway. -#ifndef FC_FULLNAME -#define FC_FULLNAME "fullname" -#endif -#ifndef FC_EMBOLDEN -#define FC_EMBOLDEN "embolden" -#endif - -/** - * \brief Low-level font selection. - * \param priv private data - * \param family font family - * \param treat_family_as_pattern treat family as fontconfig pattern - * \param bold font weight value - * \param italic font slant value - * \param index out: font index inside a file - * \param code: the character that should be present in the font, can be 0 - * \return font file path -*/ -static char* _select_font(fc_instance_t* priv, const char* family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int* index, uint32_t code) -{ - FcBool rc; - FcResult result; - FcPattern *pat = NULL, *rpat = NULL; - int r_index, r_slant, r_weight; - FcChar8 *r_family, *r_style, *r_file, *r_fullname; - FcBool r_outline, r_embolden; - FcCharSet* r_charset; - FcFontSet* fset = NULL; - int curf; - char* retval = NULL; - int family_cnt; - - *index = 0; - - if (treat_family_as_pattern) - pat = FcNameParse((const FcChar8*)family); - else - pat = FcPatternCreate(); - - if (!pat) - goto error; - - if (!treat_family_as_pattern) { - FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)family); - - // In SSA/ASS fonts are sometimes referenced by their "full name", - // which is usually a concatenation of family name and font - // style (ex. Ottawa Bold). Full name is available from - // FontConfig pattern element FC_FULLNAME, but it is never - // used for font matching. - // Therefore, I'm removing words from the end of the name one - // by one, and adding shortened names to the pattern. It seems - // that the first value (full name in this case) has - // precedence in matching. - // An alternative approach could be to reimplement FcFontSort - // using FC_FULLNAME instead of FC_FAMILY. - family_cnt = 1; - { - char* s = strdup(family); - char* p = s + strlen(s); - while (--p > s) - if (*p == ' ' || *p == '-') { - *p = '\0'; - FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)s); - ++ family_cnt; - } - free(s); - } - } - FcPatternAddBool(pat, FC_OUTLINE, FcTrue); - FcPatternAddInteger(pat, FC_SLANT, italic); - FcPatternAddInteger(pat, FC_WEIGHT, bold); - - FcDefaultSubstitute(pat); - - rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern); - if (!rc) - goto error; - - fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result); - if (!fset) - goto error; - - for (curf = 0; curf < fset->nfont; ++curf) { - FcPattern* curp = fset->fonts[curf]; - - result = FcPatternGetBool(curp, FC_OUTLINE, 0, &r_outline); - if (result != FcResultMatch) - continue; - if (r_outline != FcTrue) - continue; - if (!code) - break; - result = FcPatternGetCharSet(curp, FC_CHARSET, 0, &r_charset); - if (result != FcResultMatch) - continue; - if (FcCharSetHasChar(r_charset, code)) - break; - } - - if (curf >= fset->nfont) - goto error; - -#if (FC_VERSION >= 20297) - if (!treat_family_as_pattern) { - // Remove all extra family names from original pattern. - // After this, FcFontRenderPrepare will select the most relevant family - // name in case there are more than one of them. - for (; family_cnt > 1; --family_cnt) - FcPatternRemove(pat, FC_FAMILY, family_cnt - 1); - } -#endif - - rpat = FcFontRenderPrepare(priv->config, pat, fset->fonts[curf]); - if (!rpat) - goto error; - - result = FcPatternGetInteger(rpat, FC_INDEX, 0, &r_index); - if (result != FcResultMatch) - goto error; - *index = r_index; - - result = FcPatternGetString(rpat, FC_FILE, 0, &r_file); - if (result != FcResultMatch) - goto error; - retval = strdup((const char*)r_file); - - result = FcPatternGetString(rpat, FC_FAMILY, 0, &r_family); - if (result != FcResultMatch) - r_family = NULL; - - result = FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname); - if (result != FcResultMatch) - r_fullname = NULL; - - if (!treat_family_as_pattern && - !(r_family && strcasecmp((const char*)r_family, family) == 0) && - !(r_fullname && strcasecmp((const char*)r_fullname, family) == 0)) - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne, - (const char*)(r_fullname ? r_fullname : r_family), family); - - result = FcPatternGetString(rpat, FC_STYLE, 0, &r_style); - if (result != FcResultMatch) - r_style = NULL; - - result = FcPatternGetInteger(rpat, FC_SLANT, 0, &r_slant); - if (result != FcResultMatch) - r_slant = 0; - - result = FcPatternGetInteger(rpat, FC_WEIGHT, 0, &r_weight); - if (result != FcResultMatch) - r_weight = 0; - - result = FcPatternGetBool(rpat, FC_EMBOLDEN, 0, &r_embolden); - if (result != FcResultMatch) - r_embolden = 0; - - mp_msg(MSGT_ASS, MSGL_V, "[ass] Font info: family '%s', style '%s', fullname '%s'," - " slant %d, weight %d%s\n", - (const char*)r_family, (const char*)r_style, (const char*)r_fullname, - r_slant, r_weight, r_embolden ? ", embolden" : ""); - - error: - if (pat) FcPatternDestroy(pat); - if (rpat) FcPatternDestroy(rpat); - if (fset) FcFontSetDestroy(fset); - return retval; -} - -/** - * \brief Find a font. Use default family or path if necessary. - * \param priv_ private data - * \param family font family - * \param treat_family_as_pattern treat family as fontconfig pattern - * \param bold font weight value - * \param italic font slant value - * \param index out: font index inside a file - * \param code: the character that should be present in the font, can be 0 - * \return font file path -*/ -char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int* index, uint32_t code) -{ - char* res = 0; - if (!priv->config) { - *index = priv->index_default; - return priv->path_default; - } - if (family && *family) - res = _select_font(priv, family, treat_family_as_pattern, bold, italic, index, code); - if (!res && priv->family_default) { - res = _select_font(priv, priv->family_default, 0, bold, italic, index, code); - if (res) - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily, - family, bold, italic, res, *index); - } - if (!res && priv->path_default) { - res = priv->path_default; - *index = priv->index_default; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont, - family, bold, italic, res, *index); - } - if (!res) { - res = _select_font(priv, "Arial", 0, bold, italic, index, code); - if (res) - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily, - family, bold, italic, res, *index); - } - if (res) - mp_msg(MSGT_ASS, MSGL_V, "fontconfig_select: (%s, %d, %d) -> %s, %d\n", - family, bold, italic, res, *index); - return res; -} - -#if (FC_VERSION < 20402) -static char* validate_fname(char* name) -{ - char* fname; - char* p; - char* q; - unsigned code; - int sz = strlen(name); - - q = fname = malloc(sz + 1); - p = name; - while (*p) { - code = utf8_get_char(&p); - if (code == 0) - break; - if ( (code > 0x7F) || - (code == '\\') || - (code == '/') || - (code == ':') || - (code == '*') || - (code == '?') || - (code == '<') || - (code == '>') || - (code == '|') || - (code == 0)) - { - *q++ = '_'; - } else { - *q++ = code; - } - if (p - name > sz) - break; - } - *q = 0; - return fname; -} -#endif - -/** - * \brief Process memory font. - * \param priv private data - * \param library library object - * \param ftlibrary freetype library object - * \param idx index of the processed font in library->fontdata - * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace. - * With older FontConfig versions, save the font to ~/.mplayer/fonts. -*/ -static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Library ftlibrary, int idx) -{ - int rc; - const char* name = library->fontdata[idx].name; - const char* data = library->fontdata[idx].data; - int data_size = library->fontdata[idx].size; - -#if (FC_VERSION < 20402) - struct stat st; - char* fname; - const char* fonts_dir = library->fonts_dir; - char buf[1000]; - FILE* fp = NULL; - - if (!fonts_dir) - return; - rc = stat(fonts_dir, &st); - if (rc) { - int res; -#ifndef __MINGW32__ - res = mkdir(fonts_dir, 0700); -#else - res = mkdir(fonts_dir); -#endif - if (res) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FailedToCreateDirectory, fonts_dir); - } - } else if (!S_ISDIR(st.st_mode)) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir); - } - - fname = validate_fname((char*)name); - - snprintf(buf, 1000, "%s/%s", fonts_dir, fname); - free(fname); - - fp = fopen(buf, "wb"); - if (!fp) return; - - fwrite(data, data_size, 1, fp); - fclose(fp); - -#else // (FC_VERSION >= 20402) - FT_Face face; - FcPattern* pattern; - FcFontSet* fset; - FcBool res; - int face_index, num_faces = 1; - - for (face_index = 0; face_index < num_faces; ++face_index) { - rc = FT_New_Memory_Face(ftlibrary, (unsigned char*)data, data_size, face_index, &face); - if (rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, name); - return; - } - num_faces = face->num_faces; - - pattern = FcFreeTypeQueryFace(face, (unsigned char*)name, 0, FcConfigGetBlanks(priv->config)); - if (!pattern) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFreeTypeQueryFace"); - FT_Done_Face(face); - return; - } - - fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication - if (!fset) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcConfigGetFonts"); - FT_Done_Face(face); - return; - } - - res = FcFontSetAdd(fset, pattern); - if (!res) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFontSetAdd"); - FT_Done_Face(face); - return; - } - - FT_Done_Face(face); - } -#endif -} - -/** - * \brief Init fontconfig. - * \param library libass library object - * \param ftlibrary freetype library object - * \param family default font family - * \param path default font path - * \return pointer to fontconfig private data -*/ -fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path, int fc) -{ - int rc; - fc_instance_t* priv = calloc(1, sizeof(fc_instance_t)); - const char* dir = library->fonts_dir; - int i; - - if (!fc) { - mp_msg(MSGT_ASS, MSGL_WARN, - MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed); - goto exit; - } - - rc = FcInit(); - assert(rc); - - priv->config = FcConfigGetCurrent(); - if (!priv->config) { - mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed); - goto exit; - } - - for (i = 0; i < library->num_fontdata; ++i) - process_fontdata(priv, library, ftlibrary, i); - - if (dir) { - if (FcDirCacheValid((const FcChar8 *)dir) == FcFalse) - { - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache); - if (FcGetVersion() >= 20390 && FcGetVersion() < 20400) - mp_msg(MSGT_ASS, MSGL_WARN, - MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported); - // FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir() - if (FcGetVersion() < 20390) { - FcFontSet* fcs; - FcStrSet* fss; - fcs = FcFontSetCreate(); - fss = FcStrSetCreate(); - rc = FcStrSetAdd(fss, (const FcChar8*)dir); - if (!rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcStrSetAddFailed); - goto ErrorFontCache; - } - - rc = FcDirScan(fcs, fss, NULL, FcConfigGetBlanks(priv->config), - (const FcChar8 *)dir, FcFalse); - if (!rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirScanFailed); - goto ErrorFontCache; - } - - rc = FcDirSave(fcs, fss, (const FcChar8 *)dir); - if (!rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirSave); - goto ErrorFontCache; - } - ErrorFontCache: - ; - } - } - - rc = FcConfigAppFontAddDir(priv->config, (const FcChar8*)dir); - if (!rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcConfigAppFontAddDirFailed); - } - } - - priv->family_default = family ? strdup(family) : NULL; -exit: - priv->path_default = path ? strdup(path) : NULL; - priv->index_default = 0; - - return priv; -} - -#else /* CONFIG_FONTCONFIG */ - -char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int* index, uint32_t code) -{ - *index = priv->index_default; - return priv->path_default; -} - -fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path, int fc) -{ - fc_instance_t* priv; - - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed); - - priv = calloc(1, sizeof(fc_instance_t)); - - priv->path_default = strdup(path); - priv->index_default = 0; - return priv; -} - -#endif - -void fontconfig_done(fc_instance_t* priv) -{ - // don't call FcFini() here, library can still be used by some code - if (priv && priv->path_default) free(priv->path_default); - if (priv && priv->family_default) free(priv->family_default); - if (priv) free(priv); -} diff --git a/libass/ass_fontconfig.h b/libass/ass_fontconfig.h deleted file mode 100644 index 77806909cf..0000000000 --- a/libass/ass_fontconfig.h +++ /dev/null @@ -1,41 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_FONTCONFIG_H -#define LIBASS_FONTCONFIG_H - -#include -#include "ass_types.h" -#include -#include FT_FREETYPE_H - -#ifdef CONFIG_FONTCONFIG -#include -#endif - -typedef struct fc_instance_s fc_instance_t; - -fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path, int fc); -char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, unsigned bold, unsigned italic, int* index, uint32_t code); -void fontconfig_done(fc_instance_t* priv); - -#endif /* LIBASS_FONTCONFIG_H */ diff --git a/libass/ass_library.c b/libass/ass_library.c deleted file mode 100644 index 304f2326de..0000000000 --- a/libass/ass_library.c +++ /dev/null @@ -1,115 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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 -#include -#include -#include - -#include "ass.h" -#include "ass_library.h" - - -ass_library_t* ass_library_init(void) -{ - return calloc(1, sizeof(ass_library_t)); -} - -void ass_library_done(ass_library_t* priv) -{ - if (priv) { - ass_set_fonts_dir(priv, NULL); - ass_set_style_overrides(priv, NULL); - ass_clear_fonts(priv); - free(priv); - } -} - -void ass_set_fonts_dir(ass_library_t* priv, const char* fonts_dir) -{ - if (priv->fonts_dir) - free(priv->fonts_dir); - - priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0; -} - -void ass_set_extract_fonts(ass_library_t* priv, int extract) -{ - priv->extract_fonts = !!extract; -} - -void ass_set_style_overrides(ass_library_t* priv, char** list) -{ - char** p; - char** q; - int cnt; - - if (priv->style_overrides) { - for (p = priv->style_overrides; *p; ++p) - free(*p); - free(priv->style_overrides); - } - - if (!list) return; - - for (p = list, cnt = 0; *p; ++p, ++cnt) {} - - priv->style_overrides = malloc((cnt + 1) * sizeof(char*)); - for (p = list, q = priv->style_overrides; *p; ++p, ++q) - *q = strdup(*p); - priv->style_overrides[cnt] = NULL; -} - -static void grow_array(void **array, int nelem, size_t elsize) -{ - if (!(nelem & 31)) - *array = realloc(*array, (nelem + 32) * elsize); -} - -void ass_add_font(ass_library_t* priv, char* name, char* data, int size) -{ - int idx = priv->num_fontdata; - if (!name || !data || !size) - return; - grow_array((void**)&priv->fontdata, priv->num_fontdata, sizeof(*priv->fontdata)); - - priv->fontdata[idx].name = strdup(name); - - priv->fontdata[idx].data = malloc(size); - memcpy(priv->fontdata[idx].data, data, size); - - priv->fontdata[idx].size = size; - - priv->num_fontdata ++; -} - -void ass_clear_fonts(ass_library_t* priv) -{ - int i; - for (i = 0; i < priv->num_fontdata; ++i) { - free(priv->fontdata[i].name); - free(priv->fontdata[i].data); - } - free(priv->fontdata); - priv->fontdata = NULL; - priv->num_fontdata = 0; -} diff --git a/libass/ass_library.h b/libass/ass_library.h deleted file mode 100644 index ecf46f342e..0000000000 --- a/libass/ass_library.h +++ /dev/null @@ -1,41 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_LIBRARY_H -#define LIBASS_LIBRARY_H - -typedef struct ass_fontdata_s { - char* name; - char* data; - int size; -} ass_fontdata_t; - -struct ass_library_s { - char* fonts_dir; - int extract_fonts; - char** style_overrides; - - ass_fontdata_t* fontdata; - int num_fontdata; -}; - -#endif /* LIBASS_LIBRARY_H */ diff --git a/libass/ass_mp.c b/libass/ass_mp.c deleted file mode 100644 index b3c78b60ac..0000000000 --- a/libass/ass_mp.c +++ /dev/null @@ -1,279 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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 -#include -#include - -#include "mp_msg.h" -#include "get_path.h" - -#include "ass.h" -#include "ass_utils.h" -#include "ass_mp.h" -#include "ass_library.h" - -#ifdef CONFIG_FONTCONFIG -#include -#endif - -// libass-related command line options -ass_library_t* ass_library; -int ass_enabled = 0; -float ass_font_scale = 1.; -float ass_line_spacing = 0.; -int ass_top_margin = 0; -int ass_bottom_margin = 0; -#if defined(FC_VERSION) && (FC_VERSION >= 20402) -int extract_embedded_fonts = 1; -#else -int extract_embedded_fonts = 0; -#endif -char **ass_force_style_list = NULL; -int ass_use_margins = 0; -char* ass_color = NULL; -char* ass_border_color = NULL; -char* ass_styles_file = NULL; -int ass_hinting = ASS_HINTING_NATIVE + 4; // native hinting for unscaled osd - -#ifdef CONFIG_FONTCONFIG -extern int font_fontconfig; -#else -static int font_fontconfig = -1; -#endif -extern char* font_name; -extern char* sub_font_name; -extern float text_font_scale_factor; -extern int subtitle_autoscale; - -#ifdef CONFIG_ICONV -extern char* sub_cp; -#else -static char* sub_cp = 0; -#endif - -void process_force_style(ass_track_t* track); - -ass_track_t* ass_default_track(ass_library_t* library) { - ass_track_t* track = ass_new_track(library); - - track->track_type = TRACK_TYPE_ASS; - track->Timer = 100.; - track->PlayResY = 288; - track->WrapStyle = 0; - - if (ass_styles_file) - ass_read_styles(track, ass_styles_file, sub_cp); - - if (track->n_styles == 0) { - ass_style_t* style; - int sid; - double fs; - uint32_t c1, c2; - - sid = ass_alloc_style(track); - style = track->styles + sid; - style->Name = strdup("Default"); - style->FontName = (font_fontconfig >= 0 && sub_font_name) ? strdup(sub_font_name) : (font_fontconfig >= 0 && font_name) ? strdup(font_name) : strdup("Sans"); - style->treat_fontname_as_pattern = 1; - - fs = track->PlayResY * text_font_scale_factor / 100.; - // approximate autoscale coefficients - if (subtitle_autoscale == 2) - fs *= 1.3; - else if (subtitle_autoscale == 3) - fs *= 1.4; - style->FontSize = fs; - - if (ass_color) c1 = strtoll(ass_color, NULL, 16); - else c1 = 0xFFFF0000; - if (ass_border_color) c2 = strtoll(ass_border_color, NULL, 16); - else c2 = 0x00000000; - - style->PrimaryColour = c1; - style->SecondaryColour = c1; - style->OutlineColour = c2; - style->BackColour = 0x00000000; - style->BorderStyle = 1; - style->Alignment = 2; - style->Outline = 2; - style->MarginL = 10; - style->MarginR = 10; - style->MarginV = 5; - style->ScaleX = 1.; - style->ScaleY = 1.; - } - - process_force_style(track); - return track; -} - -static int check_duplicate_plaintext_event(ass_track_t* track) -{ - int i; - ass_event_t* evt = track->events + track->n_events - 1; - - for (i = 0; in_events - 1; ++i) // ignoring last event, it is the one we are comparing with - if (track->events[i].Start == evt->Start && - track->events[i].Duration == evt->Duration && - strcmp(track->events[i].Text, evt->Text) == 0) - return 1; - return 0; -} - -/** - * \brief Convert subtitle to ass_event_t for the given track - * \param ass_track_t track - * \param sub subtitle to convert - * \return event id - * note: assumes that subtitle is _not_ fps-based; caller must manually correct - * Start and Duration in other case. - **/ -int ass_process_subtitle(ass_track_t* track, subtitle* sub) -{ - int eid; - ass_event_t* event; - int len = 0, j; - char* p; - char* end; - - eid = ass_alloc_event(track); - event = track->events + eid; - - event->Start = sub->start * 10; - event->Duration = (sub->end - sub->start) * 10; - event->Style = 0; - - for (j = 0; j < sub->lines; ++j) - len += sub->text[j] ? strlen(sub->text[j]) : 0; - - len += 2 * sub->lines; // '\N', including the one after the last line - len += 6; // {\anX} - len += 1; // '\0' - - event->Text = malloc(len); - end = event->Text + len; - p = event->Text; - - if (sub->alignment) - p += snprintf(p, end - p, "{\\an%d}", sub->alignment); - - for (j = 0; j < sub->lines; ++j) - p += snprintf(p, end - p, "%s\\N", sub->text[j]); - - if (sub->lines > 0) p-=2; // remove last "\N" - *p = 0; - - if (check_duplicate_plaintext_event(track)) { - ass_free_event(track, eid); - track->n_events--; - return -1; - } - - mp_msg(MSGT_ASS, MSGL_V, "plaintext event at %" PRId64 ", +%" PRId64 ": %s \n", - (int64_t)event->Start, (int64_t)event->Duration, event->Text); - - return eid; -} - - -/** - * \brief Convert subdata to ass_track - * \param subdata subtitles struct from subreader - * \param fps video framerate - * \return newly allocated ass_track, filled with subtitles from subdata - */ -ass_track_t* ass_read_subdata(ass_library_t* library, sub_data* subdata, double fps) { - ass_track_t* track; - int i; - - track = ass_default_track(library); - track->name = subdata->filename ? strdup(subdata->filename) : 0; - - for (i = 0; i < subdata->sub_num; ++i) { - int eid = ass_process_subtitle(track, subdata->subtitles + i); - if (eid < 0) - continue; - if (!subdata->sub_uses_time) { - track->events[eid].Start *= 100. / fps; - track->events[eid].Duration *= 100. / fps; - } - } - return track; -} - -void ass_configure(ass_renderer_t* priv, int w, int h, int unscaled) { - int hinting; - ass_set_frame_size(priv, w, h); - ass_set_margins(priv, ass_top_margin, ass_bottom_margin, 0, 0); - ass_set_use_margins(priv, ass_use_margins); - ass_set_font_scale(priv, ass_font_scale); - if (!unscaled && (ass_hinting & 4)) - hinting = 0; - else - hinting = ass_hinting & 3; - ass_set_hinting(priv, hinting); - ass_set_line_spacing(priv, ass_line_spacing); -} - -void ass_configure_fonts(ass_renderer_t* priv) { - char *dir, *path, *family; - dir = get_path("fonts"); - if (font_fontconfig < 0 && sub_font_name) path = strdup(sub_font_name); - else if (font_fontconfig < 0 && font_name) path = strdup(font_name); - else path = get_path("subfont.ttf"); - if (font_fontconfig >= 0 && sub_font_name) family = strdup(sub_font_name); - else if (font_fontconfig >= 0 && font_name) family = strdup(font_name); - else family = 0; - - if (font_fontconfig >= 0) - ass_set_fonts(priv, path, family); - else - ass_set_fonts_nofc(priv, path, family); - - free(dir); - free(path); - free(family); -} - -ass_library_t* ass_init(void) { - ass_library_t* priv; - char* path = get_path("fonts"); - priv = ass_library_init(); - ass_set_fonts_dir(priv, path); - ass_set_extract_fonts(priv, extract_embedded_fonts); - ass_set_style_overrides(priv, ass_force_style_list); - free(path); - return priv; -} - -int ass_force_reload = 0; // flag set if global ass-related settings were changed - -ass_image_t* ass_mp_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change) { - if (ass_force_reload) { - ass_set_margins(priv, ass_top_margin, ass_bottom_margin, 0, 0); - ass_set_use_margins(priv, ass_use_margins); - ass_set_font_scale(priv, ass_font_scale); - ass_force_reload = 0; - } - return ass_render_frame(priv, track, now, detect_change); -} diff --git a/libass/ass_mp.h b/libass/ass_mp.h deleted file mode 100644 index 64b411a550..0000000000 --- a/libass/ass_mp.h +++ /dev/null @@ -1,60 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_MP_H -#define LIBASS_MP_H - -#include "subreader.h" -#include "ass_types.h" -#include "ass.h" - -extern ass_library_t* ass_library; -extern int ass_enabled; -extern float ass_font_scale; -extern float ass_line_spacing; -extern int ass_top_margin; -extern int ass_bottom_margin; -extern int extract_embedded_fonts; -extern char **ass_force_style_list; -extern int ass_use_margins; -extern char* ass_color; -extern char* ass_border_color; -extern char* ass_styles_file; -extern int ass_hinting; - -ass_track_t* ass_default_track(ass_library_t* library); -int ass_process_subtitle(ass_track_t* track, subtitle* sub); -ass_track_t* ass_read_subdata(ass_library_t* library, sub_data* subdata, double fps); - -void ass_configure(ass_renderer_t* priv, int w, int h, int hinting); -void ass_configure_fonts(ass_renderer_t* priv); -ass_library_t* ass_init(void); - -typedef struct { - ass_image_t* imgs; - int changed; -} mp_eosd_images_t; - -extern int ass_force_reload; -ass_image_t* ass_mp_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change); - -#endif /* LIBASS_MP_H */ 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 - * - * 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 -#include -#include -#include -#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 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(','); - mystrtoll(&p, &t3); - skip(','); - mystrtoll(&p, &t4); - } - skip(')'); - render_context.fade = interpolate_alpha(frame_context.time - render_context.event->Start, t1, t2, t3, t4, a1, a2, a3); - } else if (mystrcmp(&p, "org")) { - int v1, v2; - skip('('); - mystrtoi(&p, &v1); - skip(','); - mystrtoi(&p, &v2); - skip(')'); - mp_msg(MSGT_ASS, MSGL_DBG2, "org(%d, %d)\n", v1, v2); - // render_context.evt_type = EVENT_POSITIONED; - if (!render_context.have_origin) { - render_context.org_x = v1; - render_context.org_y = v2; - render_context.have_origin = 1; - render_context.detect_collisions = 0; - } - } else if (mystrcmp(&p, "t")) { - double v[3]; - int v1, v2; - double v3; - int cnt; - long long t1, t2, t, delta_t; - double k; - skip('('); - for (cnt = 0; cnt < 3; ++cnt) { - if (*p == '\\') - break; - v[cnt] = strtod(p, &p); - skip(','); - } - if (cnt == 3) { - v1 = v[0]; v2 = v[1]; v3 = v[2]; - } else if (cnt == 2) { - v1 = v[0]; v2 = v[1]; v3 = 1.; - } else if (cnt == 1) { - v1 = 0; v2 = render_context.event->Duration; v3 = v[0]; - } else { // cnt == 0 - v1 = 0; v2 = render_context.event->Duration; v3 = 1.; - } - render_context.detect_collisions = 0; - t1 = v1; - t2 = v2; - delta_t = v2 - v1; - if (v3 < 0.) - v3 = 0.; - t = frame_context.time - render_context.event->Start; // FIXME: move to render_context - if (t <= t1) - k = 0.; - else if (t >= t2) - k = 1.; - else { - assert(delta_t != 0.); - k = pow(((double)(t - t1)) / delta_t, v3); - } - while (*p == '\\') - p = parse_tag(p, k); // maybe k*pwr ? no, specs forbid nested \t's - skip_to(')'); // in case there is some unknown tag or a comment - skip(')'); - } else if (mystrcmp(&p, "clip")) { - 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(')'); - if (res) { - render_context.clip_x0 = render_context.clip_x0 * (1-pwr) + x0 * pwr; - render_context.clip_x1 = render_context.clip_x1 * (1-pwr) + x1 * pwr; - render_context.clip_y0 = render_context.clip_y0 * (1-pwr) + y0 * pwr; - render_context.clip_y1 = render_context.clip_y1 * (1-pwr) + y1 * pwr; - } else { - render_context.clip_x0 = 0; - render_context.clip_y0 = 0; - render_context.clip_x1 = frame_context.track->PlayResX; - render_context.clip_y1 = frame_context.track->PlayResY; - } - } else if (mystrcmp(&p, "c")) { - uint32_t val; - if (!strtocolor(&p, &val)) - val = render_context.style->PrimaryColour; - mp_msg(MSGT_ASS, MSGL_DBG2, "color: %X\n", val); - change_color(&render_context.c[0], val, pwr); - } else if ((*p >= '1') && (*p <= '4') && (++p) && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) { - char n = *(p-2); - int cidx = n - '1'; - char cmd = *(p-1); - uint32_t val; - assert((n >= '1') && (n <= '4')); - if (!strtocolor(&p, &val)) - switch(n) { - case '1': val = render_context.style->PrimaryColour; break; - case '2': val = render_context.style->SecondaryColour; break; - case '3': val = render_context.style->OutlineColour; break; - case '4': val = render_context.style->BackColour; break; - default : val = 0; break; // impossible due to assert; avoid compilation warning - } - switch (cmd) { - case 'c': change_color(render_context.c + cidx, val, pwr); break; - case 'a': change_alpha(render_context.c + cidx, val >> 24, pwr); break; - default: mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_BadCommand, n, cmd); break; - } - mp_msg(MSGT_ASS, MSGL_DBG2, "single c/a at %f: %c%c = %X \n", pwr, n, cmd, render_context.c[cidx]); - } else if (mystrcmp(&p, "r")) { - reset_render_context(); - } else if (mystrcmp(&p, "be")) { - int val; - if (mystrtoi(&p, &val)) { - // Clamp to a safe upper limit, since high values need excessive CPU - val = (val < 0) ? 0 : val; - val = (val > MAX_BE) ? MAX_BE : val; - render_context.be = val; - } else - render_context.be = 0; - } else if (mystrcmp(&p, "b")) { - int b; - if (mystrtoi(&p, &b)) { - if (pwr >= .5) - render_context.bold = b; - } else - render_context.bold = render_context.style->Bold; - update_font(); - } else if (mystrcmp(&p, "i")) { - int i; - if (mystrtoi(&p, &i)) { - if (pwr >= .5) - render_context.italic = i; - } else - render_context.italic = render_context.style->Italic; - update_font(); - } else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) { - int val = 0; - mystrtoi(&p, &val); - render_context.effect_type = EF_KARAOKE_KF; - if (render_context.effect_timing) - render_context.effect_skip_timing += render_context.effect_timing; - render_context.effect_timing = val * 10; - } else if (mystrcmp(&p, "ko")) { - int val = 0; - mystrtoi(&p, &val); - render_context.effect_type = EF_KARAOKE_KO; - if (render_context.effect_timing) - render_context.effect_skip_timing += render_context.effect_timing; - render_context.effect_timing = val * 10; - } else if (mystrcmp(&p, "k")) { - int val = 0; - mystrtoi(&p, &val); - render_context.effect_type = EF_KARAOKE; - if (render_context.effect_timing) - render_context.effect_skip_timing += render_context.effect_timing; - render_context.effect_timing = val * 10; - } else if (mystrcmp(&p, "shad")) { - int val; - if (mystrtoi(&p, &val)) - render_context.shadow = val; - else - render_context.shadow = render_context.style->Shadow; - } else if (mystrcmp(&p, "pbo")) { - int val = 0; - mystrtoi(&p, &val); // ignored - } else if (mystrcmp(&p, "p")) { - int val; - if (!mystrtoi(&p, &val)) - val = 0; - render_context.drawing_mode = !!val; - } - - return p; - -#undef skip -#undef skip_to -} - -/** - * \brief Get next ucs4 char from string, parsing and executing style overrides - * \param str string pointer - * \return ucs4 code of the next char - * On return str points to the unparsed part of the string - */ -static unsigned get_next_char(char** str) -{ - char* p = *str; - unsigned chr; - if (*p == '{') { // '\0' goes here - p++; - while (1) { - p = parse_tag(p, 1.); - if (*p == '}') { // end of tag - p++; - if (*p == '{') { - p++; - continue; - } else - break; - } else if (*p != '\\') - mp_msg(MSGT_ASS, MSGL_V, "Unable to parse: \"%s\" \n", p); - if (*p == 0) - break; - } - } - if (*p == '\t') { - ++p; - *str = p; - return ' '; - } - if (*p == '\\') { - if ((*(p+1) == 'N') || ((*(p+1) == 'n') && (frame_context.track->WrapStyle == 2))) { - p += 2; - *str = p; - return '\n'; - } else if ((*(p+1) == 'n') || (*(p+1) == 'h')) { - p += 2; - *str = p; - return ' '; - } - } - chr = utf8_get_char((const char **)&p); - *str = p; - return chr; -} - -static void apply_transition_effects(ass_event_t* event) -{ - int v[4]; - int cnt; - char* p = event->Effect; - - if (!p || !*p) return; - - cnt = 0; - while (cnt < 4 && (p = strchr(p, ';'))) { - v[cnt++] = atoi(++p); - } - - if (strncmp(event->Effect, "Banner;", 7) == 0) { - int delay; - if (cnt < 1) { - mp_msg(MSGT_ASS, MSGL_V, "Error parsing effect: %s \n", event->Effect); - return; - } - if (cnt >= 2 && v[1] == 0) // right-to-left - render_context.scroll_direction = SCROLL_RL; - else // left-to-right - render_context.scroll_direction = SCROLL_LR; - - delay = v[0]; - if (delay == 0) delay = 1; // ? - render_context.scroll_shift = (frame_context.time - render_context.event->Start) / delay; - render_context.evt_type = EVENT_HSCROLL; - return; - } - - if (strncmp(event->Effect, "Scroll up;", 10) == 0) { - render_context.scroll_direction = SCROLL_BT; - } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) { - render_context.scroll_direction = SCROLL_TB; - } else { - mp_msg(MSGT_ASS, MSGL_V, "Unknown transition effect: %s \n", event->Effect); - return; - } - // parse scroll up/down parameters - { - int delay; - int y0, y1; - if (cnt < 3) { - mp_msg(MSGT_ASS, MSGL_V, "Error parsing effect: %s \n", event->Effect); - return; - } - delay = v[2]; - if (delay == 0) delay = 1; // ? - render_context.scroll_shift = (frame_context.time - render_context.event->Start) / delay; - if (v[0] < v[1]) { - y0 = v[0]; y1 = v[1]; - } else { - y0 = v[1]; y1 = v[0]; - } - if (y1 == 0) - y1 = frame_context.track->PlayResY; // y0=y1=0 means fullscreen scrolling - render_context.clip_y0 = y0; - render_context.clip_y1 = y1; - render_context.evt_type = EVENT_VSCROLL; - render_context.detect_collisions = 0; - } - -} - -/** - * \brief partially reset render_context to style values - * Works like {\r}: resets some style overrides - */ -static void reset_render_context(void) -{ - render_context.c[0] = render_context.style->PrimaryColour; - render_context.c[1] = render_context.style->SecondaryColour; - render_context.c[2] = render_context.style->OutlineColour; - render_context.c[3] = render_context.style->BackColour; - render_context.font_size = render_context.style->FontSize; - - if (render_context.family) - free(render_context.family); - render_context.family = strdup(render_context.style->FontName); - render_context.treat_family_as_pattern = render_context.style->treat_fontname_as_pattern; - render_context.bold = render_context.style->Bold; - render_context.italic = render_context.style->Italic; - update_font(); - - change_border(-1.); - render_context.scale_x = render_context.style->ScaleX; - render_context.scale_y = render_context.style->ScaleY; - render_context.hspacing = render_context.style->Spacing; - render_context.be = 0; - render_context.blur = 0.0; - render_context.shadow = render_context.style->Shadow; - render_context.frx = render_context.fry = 0.; - render_context.frz = M_PI * render_context.style->Angle / 180.; - - // FIXME: does not reset unsupported attributes. -} - -/** - * \brief Start new event. Reset render_context. - */ -static void init_render_context(ass_event_t* event) -{ - render_context.event = event; - render_context.style = frame_context.track->styles + event->Style; - - reset_render_context(); - - render_context.evt_type = EVENT_NORMAL; - render_context.alignment = render_context.style->Alignment; - render_context.pos_x = 0; - render_context.pos_y = 0; - render_context.org_x = 0; - render_context.org_y = 0; - render_context.have_origin = 0; - render_context.clip_x0 = 0; - render_context.clip_y0 = 0; - render_context.clip_x1 = frame_context.track->PlayResX; - render_context.clip_y1 = frame_context.track->PlayResY; - render_context.detect_collisions = 1; - render_context.fade = 0; - render_context.drawing_mode = 0; - render_context.effect_type = EF_NONE; - render_context.effect_timing = 0; - render_context.effect_skip_timing = 0; - - apply_transition_effects(event); -} - -static void free_render_context(void) -{ -} - -/** - * \brief Get normal and outline (border) glyphs - * \param symbol ucs4 char - * \param info out: struct filled with extracted data - * \param advance subpixel shift vector used for cache lookup - * Tries to get both glyphs from cache. - * If they can't be found, gets a glyph from font face, generates outline with FT_Stroker, - * and add them to cache. - * The glyphs are returned in info->glyph and info->outline_glyph - */ -static void get_outline_glyph(int symbol, glyph_info_t* info, FT_Vector* advance) -{ - int error; - glyph_hash_val_t* val; - glyph_hash_key_t key; - memset(&key, 0, sizeof(key)); - key.font = render_context.font; - key.size = render_context.font_size; - key.ch = symbol; - key.scale_x = (render_context.scale_x * 0xFFFF); - key.scale_y = (render_context.scale_y * 0xFFFF); - key.advance = *advance; - key.bold = render_context.bold; - key.italic = render_context.italic; - key.outline = render_context.border * 0xFFFF; - - memset(info, 0, sizeof(glyph_info_t)); - - val = cache_find_glyph(&key); - if (val) { - FT_Glyph_Copy(val->glyph, &info->glyph); - if (val->outline_glyph) - FT_Glyph_Copy(val->outline_glyph, &info->outline_glyph); - info->bbox = val->bbox_scaled; - info->advance.x = val->advance.x; - info->advance.y = val->advance.y; - } else { - glyph_hash_val_t v; - info->glyph = ass_font_get_glyph(frame_context.ass_priv->fontconfig_priv, render_context.font, symbol, global_settings->hinting); - if (!info->glyph) - return; - info->advance.x = d16_to_d6(info->glyph->advance.x); - info->advance.y = d16_to_d6(info->glyph->advance.y); - FT_Glyph_Get_CBox( info->glyph, FT_GLYPH_BBOX_PIXELS, &info->bbox); - - if (render_context.stroker) { - info->outline_glyph = info->glyph; - error = FT_Glyph_StrokeBorder( &(info->outline_glyph), render_context.stroker, 0 , 0 ); // don't destroy original - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FT_Glyph_Stroke_Error, error); - } - } - - memset(&v, 0, sizeof(v)); - FT_Glyph_Copy(info->glyph, &v.glyph); - if (info->outline_glyph) - FT_Glyph_Copy(info->outline_glyph, &v.outline_glyph); - v.advance = info->advance; - v.bbox_scaled = info->bbox; - cache_add_glyph(&key, &v); - } -} - -static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, double frx, double fry, double frz); - -/** - * \brief Get bitmaps for a glyph - * \param info glyph info - * Tries to get glyph bitmaps from bitmap cache. - * If they can't be found, they are generated by rotating and rendering the glyph. - * After that, bitmaps are added to the cache. - * They are returned in info->bm (glyph), info->bm_o (outline) and info->bm_s (shadow). - */ -static void get_bitmap_glyph(glyph_info_t* info) -{ - bitmap_hash_val_t* val; - bitmap_hash_key_t* key = &info->hash_key; - - val = cache_find_bitmap(key); -/* val = 0; */ - - if (val) { - info->bm = val->bm; - info->bm_o = val->bm_o; - info->bm_s = val->bm_s; - } else { - FT_Vector shift; - bitmap_hash_val_t hash_val; - int error; - info->bm = info->bm_o = info->bm_s = 0; - if (info->glyph && info->symbol != '\n' && info->symbol != 0) { - // calculating rotation shift vector (from rotation origin to the glyph basepoint) - shift.x = int_to_d6(info->hash_key.shift_x); - shift.y = int_to_d6(info->hash_key.shift_y); - // apply rotation - transform_3d(shift, &info->glyph, &info->outline_glyph, info->frx, info->fry, info->frz); - - // render glyph - error = glyph_to_bitmap(ass_renderer->synth_priv, - info->glyph, info->outline_glyph, - &info->bm, &info->bm_o, - &info->bm_s, info->be, info->blur * frame_context.border_scale); - if (error) - info->symbol = 0; - - // add bitmaps to cache - hash_val.bm_o = info->bm_o; - hash_val.bm = info->bm; - hash_val.bm_s = info->bm_s; - cache_add_bitmap(&(info->hash_key), &hash_val); - } - } - // deallocate glyphs - if (info->glyph) - FT_Done_Glyph(info->glyph); - if (info->outline_glyph) - FT_Done_Glyph(info->outline_glyph); -} - -/** - * This function goes through text_info and calculates text parameters. - * The following text_info fields are filled: - * height - * lines[].height - * lines[].asc - * lines[].desc - */ -static void measure_text(void) -{ - int cur_line = 0, max_asc = 0, max_desc = 0; - int i; - text_info.height = 0; - for (i = 0; i < text_info.length + 1; ++i) { - if ((i == text_info.length) || text_info.glyphs[i].linebreak) { - text_info.lines[cur_line].asc = max_asc; - text_info.lines[cur_line].desc = max_desc; - text_info.height += max_asc + max_desc; - cur_line ++; - max_asc = max_desc = 0; - } - if (i < text_info.length) { - glyph_info_t* cur = text_info.glyphs + i; - if (cur->asc > max_asc) - max_asc = cur->asc; - if (cur->desc > max_desc) - max_desc = cur->desc; - } - } - text_info.height += (text_info.n_lines - 1) * double_to_d6(global_settings->line_spacing); -} - -/** - * \brief rearrange text between lines - * \param max_text_width maximal text line width in pixels - * The algo is similar to the one in libvo/sub.c: - * 1. Place text, wrapping it when current line is full - * 2. Try moving words from the end of a line to the beginning of the next one while it reduces - * the difference in lengths between this two lines. - * The result may not be optimal, but usually is good enough. - */ -static void wrap_lines_smart(int max_text_width) -{ - int i, j; - glyph_info_t *cur, *s1, *e1, *s2, *s3, *w; - int last_space; - int break_type; - int exit; - int pen_shift_x; - int pen_shift_y; - int cur_line; - - last_space = -1; - text_info.n_lines = 1; - break_type = 0; - s1 = text_info.glyphs; // current line start - for (i = 0; i < text_info.length; ++i) { - int break_at, s_offset, len; - cur = text_info.glyphs + i; - break_at = -1; - s_offset = s1->bbox.xMin + s1->pos.x; - len = (cur->bbox.xMax + cur->pos.x) - s_offset; - - if (cur->symbol == '\n') { - break_type = 2; - break_at = i; - mp_msg(MSGT_ASS, MSGL_DBG2, "forced line break at %d\n", break_at); - } - - if ((len >= max_text_width) && (frame_context.track->WrapStyle != 2)) { - break_type = 1; - break_at = last_space; - if (break_at == -1) - break_at = i - 1; - if (break_at == -1) - break_at = 0; - mp_msg(MSGT_ASS, MSGL_DBG2, "overfill at %d\n", i); - mp_msg(MSGT_ASS, MSGL_DBG2, "line break at %d\n", break_at); - } - - if (break_at != -1) { - // need to use one more line - // marking break_at+1 as start of a new line - int lead = break_at + 1; // the first symbol of the new line - if (text_info.n_lines >= MAX_LINES) { - // to many lines ! - // no more linebreaks - for (j = lead; j < text_info.length; ++j) - text_info.glyphs[j].linebreak = 0; - break; - } - if (lead < text_info.length) - text_info.glyphs[lead].linebreak = break_type; - last_space = -1; - s1 = text_info.glyphs + lead; - s_offset = s1->bbox.xMin + s1->pos.x; - text_info.n_lines ++; - } - - if (cur->symbol == ' ') - last_space = i; - - // make sure the hard linebreak is not forgotten when - // there was a new soft linebreak just inserted - if (cur->symbol == '\n' && break_type == 1) - i--; - } -#define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y)) - exit = 0; - while (!exit) { - exit = 1; - w = s3 = text_info.glyphs; - s1 = s2 = 0; - for (i = 0; i <= text_info.length; ++i) { - cur = text_info.glyphs + i; - if ((i == text_info.length) || cur->linebreak) { - s1 = s2; - s2 = s3; - s3 = cur; - if (s1 && (s2->linebreak == 1)) { // have at least 2 lines, and linebreak is 'soft' - int l1, l2, l1_new, l2_new; - - w = s2; - do { --w; } while ((w > s1) && (w->symbol == ' ')); - while ((w > s1) && (w->symbol != ' ')) { --w; } - e1 = w; - while ((e1 > s1) && (e1->symbol == ' ')) { --e1; } - if (w->symbol == ' ') ++w; - - l1 = ((s2-1)->bbox.xMax + (s2-1)->pos.x) - (s1->bbox.xMin + s1->pos.x); - l2 = ((s3-1)->bbox.xMax + (s3-1)->pos.x) - (s2->bbox.xMin + s2->pos.x); - l1_new = (e1->bbox.xMax + e1->pos.x) - (s1->bbox.xMin + s1->pos.x); - l2_new = ((s3-1)->bbox.xMax + (s3-1)->pos.x) - (w->bbox.xMin + w->pos.x); - - if (DIFF(l1_new, l2_new) < DIFF(l1, l2)) { - w->linebreak = 1; - s2->linebreak = 0; - exit = 0; - } - } - } - if (i == text_info.length) - break; - } - - } - assert(text_info.n_lines >= 1); -#undef DIFF - - measure_text(); - - pen_shift_x = 0; - pen_shift_y = 0; - cur_line = 1; - for (i = 0; i < text_info.length; ++i) { - cur = text_info.glyphs + i; - if (cur->linebreak) { - int height = text_info.lines[cur_line - 1].desc + text_info.lines[cur_line].asc; - cur_line ++; - pen_shift_x = - cur->pos.x; - pen_shift_y += d6_to_int(height + double_to_d6(global_settings->line_spacing)); - mp_msg(MSGT_ASS, MSGL_DBG2, "shifting from %d to %d by (%d, %d)\n", i, text_info.length - 1, pen_shift_x, pen_shift_y); - } - cur->pos.x += pen_shift_x; - cur->pos.y += pen_shift_y; - } -} - -/** - * \brief determine karaoke effects - * Karaoke effects cannot be calculated during parse stage (get_next_char()), - * so they are done in a separate step. - * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's - * (the first glyph of the karaoke word)'s effect_type and effect_timing. - * This function: - * 1. sets effect_type for all glyphs in the word (_karaoke_ word) - * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts - * (left part is filled with PrimaryColour, right one - with SecondaryColour). - */ -static void process_karaoke_effects(void) -{ - glyph_info_t *cur, *cur2; - glyph_info_t *s1, *e1; // start and end of the current word - glyph_info_t *s2; // start of the next word - int i; - int timing; // current timing - int tm_start, tm_end; // timings at start and end of the current word - int tm_current; - double dt; - int x; - int x_start, x_end; - - tm_current = frame_context.time - render_context.event->Start; - timing = 0; - s1 = s2 = 0; - for (i = 0; i <= text_info.length; ++i) { - cur = text_info.glyphs + i; - if ((i == text_info.length) || (cur->effect_type != EF_NONE)) { - s1 = s2; - s2 = cur; - if (s1) { - e1 = s2 - 1; - tm_start = timing + s1->effect_skip_timing; - tm_end = tm_start + s1->effect_timing; - timing = tm_end; - x_start = 1000000; - x_end = -1000000; - for (cur2 = s1; cur2 <= e1; ++cur2) { - x_start = FFMIN(x_start, cur2->bbox.xMin + cur2->pos.x); - x_end = FFMAX(x_end, cur2->bbox.xMax + cur2->pos.x); - } - - dt = (tm_current - tm_start); - if ((s1->effect_type == EF_KARAOKE) || (s1->effect_type == EF_KARAOKE_KO)) { - if (dt > 0) - x = x_end + 1; - else - x = x_start; - } else if (s1->effect_type == EF_KARAOKE_KF) { - dt /= (tm_end - tm_start); - x = x_start + (x_end - x_start) * dt; - } else { - mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_UnknownEffectType_InternalError); - continue; - } - - for (cur2 = s1; cur2 <= e1; ++cur2) { - cur2->effect_type = s1->effect_type; - cur2->effect_timing = x - cur2->pos.x; - } - } - } - } -} - -/** - * \brief Calculate base point for positioning and rotation - * \param bbox text bbox - * \param alignment alignment - * \param bx, by out: base point coordinates - */ -static void get_base_point(FT_BBox bbox, int alignment, int* bx, int* by) -{ - const int halign = alignment & 3; - const int valign = alignment & 12; - if (bx) - switch(halign) { - case HALIGN_LEFT: - *bx = bbox.xMin; - break; - case HALIGN_CENTER: - *bx = (bbox.xMax + bbox.xMin) / 2; - break; - case HALIGN_RIGHT: - *bx = bbox.xMax; - break; - } - if (by) - switch(valign) { - case VALIGN_TOP: - *by = bbox.yMin; - break; - case VALIGN_CENTER: - *by = (bbox.yMax + bbox.yMin) / 2; - break; - case VALIGN_SUB: - *by = bbox.yMax; - break; - } -} - -/** - * \brief Apply transformation to outline points of a glyph - * Applies rotations given by frx, fry and frz and projects the points back - * onto the screen plane. - */ -static void transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry, double frz) { - double sx = sin(frx); - double sy = sin(fry); - double sz = sin(frz); - double cx = cos(frx); - double cy = cos(fry); - double cz = cos(frz); - FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline; - FT_Vector* p = outline->points; - double x, y, z, xx, yy, zz; - int i; - - for (i=0; in_points; i++) { - x = p[i].x + shift.x; - y = p[i].y + shift.y; - z = 0.; - - xx = x*cz + y*sz; - yy = -(x*sz - y*cz); - zz = z; - - x = xx; - y = yy*cx + zz*sx; - z = yy*sx - zz*cx; - - xx = x*cy + z*sy; - yy = y; - zz = x*sy - z*cy; - - zz = FFMAX(zz, -19000); - - x = (xx * 20000) / (zz + 20000); - y = (yy * 20000) / (zz + 20000); - p[i].x = x - shift.x + 0.5; - p[i].y = y - shift.y + 0.5; - } -} - -/** - * \brief Apply 3d transformation to several objects - * \param shift FreeType vector - * \param glyph FreeType glyph - * \param glyph2 FreeType glyph - * \param frx x-axis rotation angle - * \param fry y-axis rotation angle - * \param frz z-axis rotation angle - * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it. - */ -static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, double frx, double fry, double frz) -{ - frx = - frx; - frz = - frz; - if (frx != 0. || fry != 0. || frz != 0.) { - if (glyph && *glyph) - transform_3d_points(shift, *glyph, frx, fry, frz); - - if (glyph2 && *glyph2) - transform_3d_points(shift, *glyph2, frx, fry, frz); - } -} - - -/** - * \brief Main ass rendering function, glues everything together - * \param event event to render - * \param event_images struct containing resulting images, will also be initialized - * Process event, appending resulting ass_image_t's to images_root. - */ -static int ass_render_event(ass_event_t* event, event_images_t* event_images) -{ - char* p; - FT_UInt previous; - FT_UInt num_glyphs; - FT_Vector pen; - unsigned code; - FT_BBox bbox; - int i, j; - FT_Vector shift; - int MarginL, MarginR, MarginV; - int last_break; - int alignment, halign, valign; - int device_x = 0, device_y = 0; - - if (event->Style >= frame_context.track->n_styles) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoStyleFound); - return 1; - } - if (!event->Text) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EmptyEvent); - return 1; - } - - init_render_context(event); - - text_info.length = 0; - pen.x = 0; - pen.y = 0; - previous = 0; - num_glyphs = 0; - p = event->Text; - // Event parsing. - while (1) { - // get next char, executing style override - // this affects render_context - do { - code = get_next_char(&p); - } while (code && render_context.drawing_mode); // skip everything in drawing mode - - // face could have been changed in get_next_char - if (!render_context.font) { - free_render_context(); - return 1; - } - - if (code == 0) - break; - - if (text_info.length >= MAX_GLYPHS) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_MAX_GLYPHS_Reached, - (int)(event - frame_context.track->events), event->Start, event->Duration, event->Text); - break; - } - - if ( previous && code ) { - FT_Vector delta; - delta = ass_font_get_kerning(render_context.font, previous, code); - pen.x += delta.x * render_context.scale_x; - pen.y += delta.y * render_context.scale_y; - } - - shift.x = pen.x & SUBPIXEL_MASK; - shift.y = pen.y & SUBPIXEL_MASK; - - if (render_context.evt_type == EVENT_POSITIONED) { - shift.x += double_to_d6(x2scr_pos(render_context.pos_x)) & SUBPIXEL_MASK; - shift.y -= double_to_d6(y2scr_pos(render_context.pos_y)) & SUBPIXEL_MASK; - } - - ass_font_set_transform(render_context.font, - render_context.scale_x * frame_context.font_scale_x, - render_context.scale_y, - &shift ); - - get_outline_glyph(code, text_info.glyphs + text_info.length, &shift); - - text_info.glyphs[text_info.length].pos.x = pen.x >> 6; - text_info.glyphs[text_info.length].pos.y = pen.y >> 6; - - pen.x += text_info.glyphs[text_info.length].advance.x; - pen.x += double_to_d6(render_context.hspacing); - pen.y += text_info.glyphs[text_info.length].advance.y; - - previous = code; - - text_info.glyphs[text_info.length].symbol = code; - text_info.glyphs[text_info.length].linebreak = 0; - for (i = 0; i < 4; ++i) { - uint32_t clr = render_context.c[i]; - change_alpha(&clr, mult_alpha(_a(clr), render_context.fade), 1.); - text_info.glyphs[text_info.length].c[i] = clr; - } - text_info.glyphs[text_info.length].effect_type = render_context.effect_type; - text_info.glyphs[text_info.length].effect_timing = render_context.effect_timing; - text_info.glyphs[text_info.length].effect_skip_timing = render_context.effect_skip_timing; - text_info.glyphs[text_info.length].be = render_context.be; - text_info.glyphs[text_info.length].blur = render_context.blur; - text_info.glyphs[text_info.length].shadow = render_context.shadow; - text_info.glyphs[text_info.length].frx = render_context.frx; - text_info.glyphs[text_info.length].fry = render_context.fry; - text_info.glyphs[text_info.length].frz = render_context.frz; - ass_font_get_asc_desc(render_context.font, code, - &text_info.glyphs[text_info.length].asc, - &text_info.glyphs[text_info.length].desc); - text_info.glyphs[text_info.length].asc *= render_context.scale_y; - text_info.glyphs[text_info.length].desc *= render_context.scale_y; - - // fill bitmap_hash_key - text_info.glyphs[text_info.length].hash_key.font = render_context.font; - text_info.glyphs[text_info.length].hash_key.size = render_context.font_size; - text_info.glyphs[text_info.length].hash_key.outline = render_context.border * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.scale_x = render_context.scale_x * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.scale_y = render_context.scale_y * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.frx = render_context.frx * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.fry = render_context.fry * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.frz = render_context.frz * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.bold = render_context.bold; - text_info.glyphs[text_info.length].hash_key.italic = render_context.italic; - text_info.glyphs[text_info.length].hash_key.ch = code; - text_info.glyphs[text_info.length].hash_key.advance = shift; - text_info.glyphs[text_info.length].hash_key.be = render_context.be; - text_info.glyphs[text_info.length].hash_key.blur = render_context.blur; - - text_info.length++; - - render_context.effect_type = EF_NONE; - render_context.effect_timing = 0; - render_context.effect_skip_timing = 0; - } - - if (text_info.length == 0) { - // no valid symbols in the event; this can be smth like {comment} - free_render_context(); - return 1; - } - - // depends on glyph x coordinates being monotonous, so it should be done before line wrap - process_karaoke_effects(); - - // alignments - alignment = render_context.alignment; - halign = alignment & 3; - valign = alignment & 12; - - MarginL = (event->MarginL) ? event->MarginL : render_context.style->MarginL; - MarginR = (event->MarginR) ? event->MarginR : render_context.style->MarginR; - MarginV = (event->MarginV) ? event->MarginV : render_context.style->MarginV; - - if (render_context.evt_type != EVENT_HSCROLL) { - int max_text_width; - - // calculate max length of a line - max_text_width = x2scr(frame_context.track->PlayResX - MarginR) - x2scr(MarginL); - - // rearrange text in several lines - wrap_lines_smart(max_text_width); - - // align text - last_break = -1; - for (i = 1; i < text_info.length + 1; ++i) { // (text_info.length + 1) is the end of the last line - if ((i == text_info.length) || text_info.glyphs[i].linebreak) { - int width, shift = 0; - glyph_info_t* first_glyph = text_info.glyphs + last_break + 1; - glyph_info_t* last_glyph = text_info.glyphs + i - 1; - - while ((last_glyph > first_glyph) && ((last_glyph->symbol == '\n') || (last_glyph->symbol == 0))) - last_glyph --; - - width = last_glyph->pos.x + d6_to_int(last_glyph->advance.x) - first_glyph->pos.x; - if (halign == HALIGN_LEFT) { // left aligned, no action - shift = 0; - } else if (halign == HALIGN_RIGHT) { // right aligned - shift = max_text_width - width; - } else if (halign == HALIGN_CENTER) { // centered - shift = (max_text_width - width) / 2; - } - for (j = last_break + 1; j < i; ++j) { - text_info.glyphs[j].pos.x += shift; - } - last_break = i - 1; - } - } - } else { // render_context.evt_type == EVENT_HSCROLL - measure_text(); - } - - // determing text bounding box - compute_string_bbox(&text_info, &bbox); - - // determine device coordinates for text - - // x coordinate for everything except positioned events - if (render_context.evt_type == EVENT_NORMAL || - render_context.evt_type == EVENT_VSCROLL) { - device_x = x2scr(MarginL); - } else if (render_context.evt_type == EVENT_HSCROLL) { - if (render_context.scroll_direction == SCROLL_RL) - device_x = x2scr(frame_context.track->PlayResX - render_context.scroll_shift); - else if (render_context.scroll_direction == SCROLL_LR) - device_x = x2scr(render_context.scroll_shift) - (bbox.xMax - bbox.xMin); - } - - // y coordinate for everything except positioned events - if (render_context.evt_type == EVENT_NORMAL || - render_context.evt_type == EVENT_HSCROLL) { - if (valign == VALIGN_TOP) { // toptitle - device_y = y2scr_top(MarginV) + d6_to_int(text_info.lines[0].asc); - } else if (valign == VALIGN_CENTER) { // midtitle - int scr_y = y2scr(frame_context.track->PlayResY / 2); - device_y = scr_y - (bbox.yMax - bbox.yMin) / 2; - } else { // subtitle - int scr_y; - if (valign != VALIGN_SUB) - mp_msg(MSGT_ASS, MSGL_V, "Invalid valign, supposing 0 (subtitle)\n"); - scr_y = y2scr_sub(frame_context.track->PlayResY - MarginV); - device_y = scr_y; - device_y -= d6_to_int(text_info.height); - device_y += d6_to_int(text_info.lines[0].asc); - } - } else if (render_context.evt_type == EVENT_VSCROLL) { - if (render_context.scroll_direction == SCROLL_TB) - device_y = y2scr(render_context.clip_y0 + render_context.scroll_shift) - (bbox.yMax - bbox.yMin); - else if (render_context.scroll_direction == SCROLL_BT) - device_y = y2scr(render_context.clip_y1 - render_context.scroll_shift); - } - - // positioned events are totally different - if (render_context.evt_type == EVENT_POSITIONED) { - int base_x = 0; - int base_y = 0; - mp_msg(MSGT_ASS, MSGL_DBG2, "positioned event at %f, %f\n", render_context.pos_x, render_context.pos_y); - get_base_point(bbox, alignment, &base_x, &base_y); - device_x = x2scr_pos(render_context.pos_x) - base_x; - device_y = y2scr_pos(render_context.pos_y) - base_y; - } - - // fix clip coordinates (they depend on alignment) - if (render_context.evt_type == EVENT_NORMAL || - render_context.evt_type == EVENT_HSCROLL || - render_context.evt_type == EVENT_VSCROLL) { - render_context.clip_x0 = x2scr(render_context.clip_x0); - render_context.clip_x1 = x2scr(render_context.clip_x1); - if (valign == VALIGN_TOP) { - render_context.clip_y0 = y2scr_top(render_context.clip_y0); - render_context.clip_y1 = y2scr_top(render_context.clip_y1); - } else if (valign == VALIGN_CENTER) { - render_context.clip_y0 = y2scr(render_context.clip_y0); - render_context.clip_y1 = y2scr(render_context.clip_y1); - } else if (valign == VALIGN_SUB) { - render_context.clip_y0 = y2scr_sub(render_context.clip_y0); - render_context.clip_y1 = y2scr_sub(render_context.clip_y1); - } - } else if (render_context.evt_type == EVENT_POSITIONED) { - render_context.clip_x0 = x2scr_pos(render_context.clip_x0); - render_context.clip_x1 = x2scr_pos(render_context.clip_x1); - render_context.clip_y0 = y2scr_pos(render_context.clip_y0); - render_context.clip_y1 = y2scr_pos(render_context.clip_y1); - } - - // calculate rotation parameters - { - FT_Vector center; - - if (render_context.have_origin) { - center.x = x2scr(render_context.org_x); - center.y = y2scr(render_context.org_y); - } else { - int bx = 0, by = 0; - get_base_point(bbox, alignment, &bx, &by); - center.x = device_x + bx; - center.y = device_y + by; - } - - for (i = 0; i < text_info.length; ++i) { - glyph_info_t* info = text_info.glyphs + i; - - if (info->hash_key.frx || info->hash_key.fry || info->hash_key.frz) { - info->hash_key.shift_x = info->pos.x + device_x - center.x; - info->hash_key.shift_y = - (info->pos.y + device_y - center.y); - } else { - info->hash_key.shift_x = 0; - info->hash_key.shift_y = 0; - } - } - } - - // convert glyphs to bitmaps - for (i = 0; i < text_info.length; ++i) - get_bitmap_glyph(text_info.glyphs + i); - - memset(event_images, 0, sizeof(*event_images)); - event_images->top = device_y - d6_to_int(text_info.lines[0].asc); - event_images->height = d6_to_int(text_info.height); - event_images->detect_collisions = render_context.detect_collisions; - event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1; - event_images->event = event; - event_images->imgs = render_text(&text_info, device_x, device_y); - - free_render_context(); - - return 0; -} - -/** - * \brief deallocate image list - * \param img list pointer - */ -static void ass_free_images(ass_image_t* img) -{ - while (img) { - ass_image_t* next = img->next; - free(img); - img = next; - } -} - -static void ass_reconfigure(ass_renderer_t* priv) -{ - priv->render_id = ++last_render_id; - ass_glyph_cache_reset(); - ass_bitmap_cache_reset(); - ass_composite_cache_reset(); - ass_free_images(priv->prev_images_root); - priv->prev_images_root = 0; -} - -void ass_set_frame_size(ass_renderer_t* priv, int w, int h) -{ - if (priv->settings.frame_width != w || priv->settings.frame_height != h) { - priv->settings.frame_width = w; - priv->settings.frame_height = h; - if (priv->settings.aspect == 0.) - priv->settings.aspect = ((double)w) / h; - ass_reconfigure(priv); - } -} - -void ass_set_margins(ass_renderer_t* priv, int t, int b, int l, int r) -{ - if (priv->settings.left_margin != l || - priv->settings.right_margin != r || - priv->settings.top_margin != t || - priv->settings.bottom_margin != b) { - priv->settings.left_margin = l; - priv->settings.right_margin = r; - priv->settings.top_margin = t; - priv->settings.bottom_margin = b; - ass_reconfigure(priv); - } -} - -void ass_set_use_margins(ass_renderer_t* priv, int use) -{ - priv->settings.use_margins = use; -} - -void ass_set_aspect_ratio(ass_renderer_t* priv, double ar) -{ - if (priv->settings.aspect != ar) { - priv->settings.aspect = ar; - ass_reconfigure(priv); - } -} - -void ass_set_font_scale(ass_renderer_t* priv, double font_scale) -{ - if (priv->settings.font_size_coeff != font_scale) { - priv->settings.font_size_coeff = font_scale; - ass_reconfigure(priv); - } -} - -void ass_set_hinting(ass_renderer_t* priv, ass_hinting_t ht) -{ - if (priv->settings.hinting != ht) { - priv->settings.hinting = ht; - ass_reconfigure(priv); - } -} - -void ass_set_line_spacing(ass_renderer_t* priv, double line_spacing) -{ - priv->settings.line_spacing = line_spacing; -} - -static int ass_set_fonts_(ass_renderer_t* priv, const char* default_font, const char* default_family, int fc) -{ - if (priv->settings.default_font) - free(priv->settings.default_font); - if (priv->settings.default_family) - free(priv->settings.default_family); - - priv->settings.default_font = default_font ? strdup(default_font) : 0; - priv->settings.default_family = default_family ? strdup(default_family) : 0; - - if (priv->fontconfig_priv) - fontconfig_done(priv->fontconfig_priv); - priv->fontconfig_priv = fontconfig_init(priv->library, priv->ftlibrary, default_family, default_font, fc); - - return !!priv->fontconfig_priv; -} - -int ass_set_fonts(ass_renderer_t* priv, const char* default_font, const char* default_family) -{ - return ass_set_fonts_(priv, default_font, default_family, 1); -} - -int ass_set_fonts_nofc(ass_renderer_t* priv, const char* default_font, const char* default_family) -{ - return ass_set_fonts_(priv, default_font, default_family, 0); -} - -/** - * \brief Start a new frame - */ -static int ass_start_frame(ass_renderer_t *priv, ass_track_t* track, long long now) -{ - ass_renderer = priv; - global_settings = &priv->settings; - - if (!priv->settings.frame_width && !priv->settings.frame_height) - return 1; // library not initialized - - if (track->n_events == 0) - return 1; // nothing to do - - frame_context.ass_priv = priv; - frame_context.width = global_settings->frame_width; - frame_context.height = global_settings->frame_height; - frame_context.orig_width = global_settings->frame_width - global_settings->left_margin - global_settings->right_margin; - frame_context.orig_height = global_settings->frame_height - global_settings->top_margin - global_settings->bottom_margin; - frame_context.orig_width_nocrop = global_settings->frame_width - - FFMAX(global_settings->left_margin, 0) - - FFMAX(global_settings->right_margin, 0); - frame_context.orig_height_nocrop = global_settings->frame_height - - FFMAX(global_settings->top_margin, 0) - - FFMAX(global_settings->bottom_margin, 0); - frame_context.track = track; - frame_context.time = now; - - ass_lazy_track_init(); - - frame_context.font_scale = global_settings->font_size_coeff * - frame_context.orig_height / frame_context.track->PlayResY; - if (frame_context.track->ScaledBorderAndShadow) - frame_context.border_scale = ((double)frame_context.orig_height) / frame_context.track->PlayResY; - else - frame_context.border_scale = 1.; - - frame_context.font_scale_x = 1.; - - priv->prev_images_root = priv->images_root; - priv->images_root = 0; - - return 0; -} - -static int cmp_event_layer(const void* p1, const void* p2) -{ - ass_event_t* e1 = ((event_images_t*)p1)->event; - ass_event_t* e2 = ((event_images_t*)p2)->event; - if (e1->Layer < e2->Layer) - return -1; - if (e1->Layer > e2->Layer) - return 1; - if (e1->ReadOrder < e2->ReadOrder) - return -1; - if (e1->ReadOrder > e2->ReadOrder) - return 1; - return 0; -} - -#define MAX_EVENTS 100 - -static render_priv_t* get_render_priv(ass_event_t* event) -{ - if (!event->render_priv) - event->render_priv = calloc(1, sizeof(render_priv_t)); - // FIXME: check render_id - if (ass_renderer->render_id != event->render_priv->render_id) { - memset(event->render_priv, 0, sizeof(render_priv_t)); - event->render_priv->render_id = ass_renderer->render_id; - } - return event->render_priv; -} - -typedef struct segment_s { - int a, b; // top and height -} segment_t; - -static int overlap(segment_t* s1, segment_t* s2) -{ - if (s1->a >= s2->b || s2->a >= s1->b) - return 0; - return 1; -} - -static int cmp_segment(const void* p1, const void* p2) -{ - return ((segment_t*)p1)->a - ((segment_t*)p2)->a; -} - -static void shift_event(event_images_t* ei, int shift) -{ - ass_image_t* cur = ei->imgs; - while (cur) { - cur->dst_y += shift; - // clip top and bottom - if (cur->dst_y < 0) { - int clip = - cur->dst_y; - cur->h -= clip; - cur->bitmap += clip * cur->stride; - cur->dst_y = 0; - } - if (cur->dst_y + cur->h >= frame_context.height) { - int clip = cur->dst_y + cur->h - frame_context.height; - cur->h -= clip; - } - if (cur->h <= 0) { - cur->h = 0; - cur->dst_y = 0; - } - cur = cur->next; - } - ei->top += shift; -} - -// dir: 1 - move down -// -1 - move up -static int fit_segment(segment_t* s, segment_t* fixed, int* cnt, int dir) -{ - int i; - int shift = 0; - - if (dir == 1) // move down - for (i = 0; i < *cnt; ++i) { - if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b) - continue; - shift = fixed[i].b - s->a; - } - else // dir == -1, move up - for (i = *cnt-1; i >= 0; --i) { - if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b) - continue; - shift = fixed[i].a - s->b; - } - - fixed[*cnt].a = s->a + shift; - fixed[*cnt].b = s->b + shift; - (*cnt)++; - qsort(fixed, *cnt, sizeof(segment_t), cmp_segment); - - return shift; -} - -static void fix_collisions(event_images_t* imgs, int cnt) -{ - segment_t used[MAX_EVENTS]; - int cnt_used = 0; - int i, j; - - // fill used[] with fixed events - for (i = 0; i < cnt; ++i) { - render_priv_t* priv; - if (!imgs[i].detect_collisions) continue; - priv = get_render_priv(imgs[i].event); - if (priv->height > 0) { // it's a fixed event - segment_t s; - s.a = priv->top; - s.b = priv->top + priv->height; - if (priv->height != imgs[i].height) { // no, it's not - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EventHeightHasChanged); - priv->top = 0; - priv->height = 0; - } - for (j = 0; j < cnt_used; ++j) - if (overlap(&s, used + j)) { // no, it's not - priv->top = 0; - priv->height = 0; - } - if (priv->height > 0) { // still a fixed event - used[cnt_used].a = priv->top; - used[cnt_used].b = priv->top + priv->height; - cnt_used ++; - shift_event(imgs + i, priv->top - imgs[i].top); - } - } - } - qsort(used, cnt_used, sizeof(segment_t), cmp_segment); - - // try to fit other events in free spaces - for (i = 0; i < cnt; ++i) { - render_priv_t* priv; - if (!imgs[i].detect_collisions) continue; - priv = get_render_priv(imgs[i].event); - if (priv->height == 0) { // not a fixed event - int shift; - segment_t s; - s.a = imgs[i].top; - s.b = imgs[i].top + imgs[i].height; - shift = fit_segment(&s, used, &cnt_used, imgs[i].shift_direction); - if (shift) shift_event(imgs + i, shift); - // make it fixed - priv->top = imgs[i].top; - priv->height = imgs[i].height; - } - - } -} - -/** - * \brief compare two images - * \param i1 first image - * \param i2 second image - * \return 0 if identical, 1 if different positions, 2 if different content - */ -static int ass_image_compare(ass_image_t *i1, ass_image_t *i2) -{ - if (i1->w != i2->w) return 2; - if (i1->h != i2->h) return 2; - if (i1->stride != i2->stride) return 2; - if (i1->color != i2->color) return 2; - if (i1->bitmap != i2->bitmap) - return 2; - if (i1->dst_x != i2->dst_x) return 1; - if (i1->dst_y != i2->dst_y) return 1; - return 0; -} - -/** - * \brief compare current and previous image list - * \param priv library handle - * \return 0 if identical, 1 if different positions, 2 if different content - */ -static int ass_detect_change(ass_renderer_t *priv) -{ - ass_image_t* img, *img2; - int diff; - - img = priv->prev_images_root; - img2 = priv->images_root; - diff = 0; - while (img && diff < 2) { - ass_image_t* next, *next2; - next = img->next; - if (img2) { - int d = ass_image_compare(img, img2); - if (d > diff) diff = d; - next2 = img2->next; - } else { - // previous list is shorter - diff = 2; - break; - } - img = next; - img2 = next2; - } - - // is the previous list longer? - if (img2) - diff = 2; - - return diff; -} - -/** - * \brief render a frame - * \param priv library handle - * \param track track - * \param now current video timestamp (ms) - * \param detect_change a value describing how the new images differ from the previous ones will be written here: - * 0 if identical, 1 if different positions, 2 if different content. - * Can be NULL, in that case no detection is performed. - */ -ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change) -{ - int i, cnt, rc; - event_images_t* last; - ass_image_t** tail; - - // init frame - rc = ass_start_frame(priv, track, now); - if (rc != 0) - return 0; - - // render events separately - cnt = 0; - for (i = 0; i < track->n_events; ++i) { - ass_event_t* event = track->events + i; - if ( (event->Start <= now) && (now < (event->Start + event->Duration)) ) { - if (cnt >= priv->eimg_size) { - priv->eimg_size += 100; - priv->eimg = realloc(priv->eimg, priv->eimg_size * sizeof(event_images_t)); - } - rc = ass_render_event(event, priv->eimg + cnt); - if (!rc) ++cnt; - } - } - - // sort by layer - qsort(priv->eimg, cnt, sizeof(event_images_t), cmp_event_layer); - - // call fix_collisions for each group of events with the same layer - last = priv->eimg; - for (i = 1; i < cnt; ++i) - if (last->event->Layer != priv->eimg[i].event->Layer) { - fix_collisions(last, priv->eimg + i - last); - last = priv->eimg + i; - } - if (cnt > 0) - fix_collisions(last, priv->eimg + cnt - last); - - // concat lists - tail = &ass_renderer->images_root; - for (i = 0; i < cnt; ++i) { - ass_image_t* cur = priv->eimg[i].imgs; - while (cur) { - *tail = cur; - tail = &cur->next; - cur = cur->next; - } - } - - if (detect_change) - *detect_change = ass_detect_change(priv); - - // free the previous image list - ass_free_images(priv->prev_images_root); - priv->prev_images_root = 0; - - return ass_renderer->images_root; -} diff --git a/libass/ass_types.h b/libass/ass_types.h deleted file mode 100644 index d43fef3137..0000000000 --- a/libass/ass_types.h +++ /dev/null @@ -1,119 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_TYPES_H -#define LIBASS_TYPES_H - -#include - -#define VALIGN_SUB 0 -#define VALIGN_CENTER 8 -#define VALIGN_TOP 4 -#define HALIGN_LEFT 1 -#define HALIGN_CENTER 2 -#define HALIGN_RIGHT 3 - -/// ass Style: line -typedef struct ass_style_s { - char* Name; - char* FontName; - double FontSize; - uint32_t PrimaryColour; - uint32_t SecondaryColour; - uint32_t OutlineColour; - uint32_t BackColour; - int Bold; - int Italic; - int Underline; - int StrikeOut; - double ScaleX; - double ScaleY; - double Spacing; - int Angle; - int BorderStyle; - double Outline; - double Shadow; - int Alignment; - int MarginL; - int MarginR; - int MarginV; -// int AlphaLevel; - int Encoding; - int treat_fontname_as_pattern; -} ass_style_t; - -typedef struct render_priv_s render_priv_t; - -/// ass_event_t corresponds to a single Dialogue line -/// Text is stored as-is, style overrides will be parsed later -typedef struct ass_event_s { - long long Start; // ms - long long Duration; // ms - - int ReadOrder; - int Layer; - int Style; - char* Name; - int MarginL; - int MarginR; - int MarginV; - char* Effect; - char* Text; - - render_priv_t* render_priv; -} ass_event_t; - -typedef struct parser_priv_s parser_priv_t; - -typedef struct ass_library_s ass_library_t; - -/// ass track represent either an external script or a matroska subtitle stream (no real difference between them) -/// it can be used in rendering after the headers are parsed (i.e. events format line read) -typedef struct ass_track_s { - int n_styles; // amount used - int max_styles; // amount allocated - int n_events; - int max_events; - ass_style_t* styles; // array of styles, max_styles length, n_styles used - ass_event_t* events; // the same as styles - - char* style_format; // style format line (everything after "Format: ") - char* event_format; // event format line - - enum {TRACK_TYPE_UNKNOWN = 0, TRACK_TYPE_ASS, TRACK_TYPE_SSA} track_type; - - // script header fields - int PlayResX; - int PlayResY; - double Timer; - int WrapStyle; - char ScaledBorderAndShadow; - - - int default_style; // index of default style - char* name; // file name in case of external subs, 0 for streams - - ass_library_t* library; - parser_priv_t* parser_priv; -} ass_track_t; - -#endif /* LIBASS_TYPES_H */ diff --git a/libass/ass_utils.c b/libass/ass_utils.c deleted file mode 100644 index 9a89e8afae..0000000000 --- a/libass/ass_utils.c +++ /dev/null @@ -1,135 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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 -#include -#include -#include FT_GLYPH_H - -#include "mputils.h" -#include "ass_utils.h" - -int mystrtoi(char** p, int* res) -{ - // NOTE: base argument is ignored, but not used in libass anyway - double temp_res; - char* start = *p; - temp_res = strtod(*p, p); - *res = (int) (temp_res + 0.5); - if (*p != start) return 1; - else return 0; -} - -int mystrtoll(char** p, long long* res) -{ - double temp_res; - char* start = *p; - temp_res = strtod(*p, p); - *res = (long long) (temp_res + 0.5); - if (*p != start) return 1; - else return 0; -} - -int mystrtou32(char** p, int base, uint32_t* res) -{ - char* start = *p; - *res = strtoll(*p, p, base); - if (*p != start) return 1; - else return 0; -} - -int mystrtod(char** p, double* res) -{ - char* start = *p; - *res = strtod(*p, p); - if (*p != start) return 1; - else return 0; -} - -int strtocolor(char** q, uint32_t* res) -{ - uint32_t color = 0; - int result; - char* p = *q; - - if (*p == '&') ++p; - else mp_msg(MSGT_ASS, MSGL_DBG2, "suspicious color format: \"%s\"\n", p); - - if (*p == 'H' || *p == 'h') { - ++p; - result = mystrtou32(&p, 16, &color); - } else { - result = mystrtou32(&p, 0, &color); - } - - { - unsigned char* tmp = (unsigned char*)(&color); - unsigned char b; - b = tmp[0]; tmp[0] = tmp[3]; tmp[3] = b; - b = tmp[1]; tmp[1] = tmp[2]; tmp[2] = b; - } - if (*p == '&') ++p; - *q = p; - - *res = color; - return result; -} - -// Return a boolean value for a string -char parse_bool(char* str) { - while (*str == ' ' || *str == '\t') - str++; - if (!strncasecmp(str, "yes", 3)) - return 1; - else if (strtol(str, NULL, 10) > 0) - return 1; - return 0; -} - -#if 0 -static void sprint_tag(uint32_t tag, char* dst) -{ - dst[0] = (tag >> 24) & 0xFF; - dst[1] = (tag >> 16) & 0xFF; - dst[2] = (tag >> 8) & 0xFF; - dst[3] = tag & 0xFF; - dst[4] = 0; -} - -void dump_glyph(FT_Glyph g) -{ - char tag[5]; - int i; - FT_OutlineGlyph og = (FT_OutlineGlyph)g; - FT_Outline* o = &(og->outline); - sprint_tag(g->format, tag); - printf("glyph: %p \n", g); - printf("format: %s \n", tag); - printf("outline: %p \n", o); - printf("contours: %d, points: %d, points ptr: %p \n", o->n_contours, o->n_points, o->points); - for (i = 0; i < o->n_points; ++i) { - printf(" point %f, %f \n", d6_to_double(o->points[i].x), d6_to_double(o->points[i].y)); - } -} -#endif diff --git a/libass/ass_utils.h b/libass/ass_utils.h deleted file mode 100644 index 8c5f3e8f49..0000000000 --- a/libass/ass_utils.h +++ /dev/null @@ -1,66 +0,0 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_UTILS_H -#define LIBASS_UTILS_H - -#include - -int mystrtoi(char** p, int* res); -int mystrtoll(char** p, long long* res); -int mystrtou32(char** p, int base, uint32_t* res); -int mystrtod(char** p, double* res); -int strtocolor(char** q, uint32_t* res); -char parse_bool(char* str); - -static inline int d6_to_int(int x) { - return (x + 32) >> 6; -} -static inline int d16_to_int(int x) { - return (x + 32768) >> 16; -} -static inline int int_to_d6(int x) { - return x << 6; -} -static inline int int_to_d16(int x) { - return x << 16; -} -static inline int d16_to_d6(int x) { - return (x + 512) >> 10; -} -static inline int d6_to_d16(int x) { - return x << 10; -} -static inline double d6_to_double(int x) { - return x / 64.; -} -static inline int double_to_d6(double x) { - return (int)(x * 64); -} -static inline double d16_to_double(int x) { - return ((double)x) / 0x10000; -} -static inline int double_to_d16(double x) { - return (int)(x * 0x10000); -} - -#endif /* LIBASS_UTILS_H */ diff --git a/libass/mputils.h b/libass/mputils.h deleted file mode 100644 index cff269357f..0000000000 --- a/libass/mputils.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2006 Evgeniy Stepanov - * - * 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. - */ - -#ifndef LIBASS_MPUTILS_H -#define LIBASS_MPUTILS_H - -#include "mp_msg.h" -#include "help_mp.h" -#include "libvo/font_load.h" // for blur() -#include "subreader.h" // for guess_buffer_cp -#include "libvo/sub.h" // for utf8_get_char -#include "libavutil/common.h" - -#endif /* LIBASS_MPUTILS_H */ diff --git a/libmpcodecs/vf_ass.c b/libmpcodecs/vf_ass.c index b13b148600..4c2f9bef7c 100644 --- a/libmpcodecs/vf_ass.c +++ b/libmpcodecs/vf_ass.c @@ -42,8 +42,7 @@ #include "m_option.h" #include "m_struct.h" -#include "libass/ass.h" -#include "libass/ass_mp.h" +#include "ass_mp.h" #define _r(c) ((c)>>24) #define _g(c) (((c)>>16)&0xFF) @@ -97,7 +96,7 @@ static int config(struct vf_instance* vf, if (vf->priv->ass_priv) { ass_configure(vf->priv->ass_priv, vf->priv->outw, vf->priv->outh, 0); - ass_set_aspect_ratio(vf->priv->ass_priv, ((double)d_width) / d_height); + ass_set_aspect_ratio(vf->priv->ass_priv, 1, 1); } return vf_next_config(vf, vf->priv->outw, vf->priv->outh, d_width, d_height, flags, outfmt); diff --git a/libmpcodecs/vf_vo.c b/libmpcodecs/vf_vo.c index 2e4257fb21..6ec5904053 100644 --- a/libmpcodecs/vf_vo.c +++ b/libmpcodecs/vf_vo.c @@ -12,8 +12,7 @@ #include "libvo/video_out.h" #ifdef CONFIG_ASS -#include "libass/ass.h" -#include "libass/ass_mp.h" +#include "ass_mp.h" extern ass_track_t* ass_track; #endif @@ -28,6 +27,7 @@ struct vf_priv_s { #ifdef CONFIG_ASS ass_renderer_t* ass_priv; int prev_visibility; + double scale_ratio; #endif }; #define video_out (vf->priv->vo) @@ -67,6 +67,8 @@ static int config(struct vf_instance* vf, return 0; #ifdef CONFIG_ASS + vf->priv->scale_ratio = (double) d_width / d_height * height / width; + if (vf->priv->ass_priv) ass_configure(vf->priv->ass_priv, width, height, !!(vf->default_caps & VFCAP_EOSD_UNSCALED)); #endif @@ -133,7 +135,7 @@ static int control(struct vf_instance* vf, int request, void* data) if (vo_control(video_out, VOCTRL_GET_EOSD_RES, &res) == VO_TRUE) { ass_set_frame_size(vf->priv->ass_priv, res.w, res.h); ass_set_margins(vf->priv->ass_priv, res.mt, res.mb, res.ml, res.mr); - ass_set_aspect_ratio(vf->priv->ass_priv, (double)res.w / res.h); + ass_set_aspect_ratio(vf->priv->ass_priv, vf->priv->scale_ratio, 1); } images.imgs = ass_mp_render_frame(vf->priv->ass_priv, ass_track, (pts+sub_delay) * 1000 + .5, &images.changed); diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c index 5c05dbf585..9c984a11a9 100644 --- a/libmpdemux/demux_mkv.c +++ b/libmpdemux/demux_mkv.c @@ -44,8 +44,7 @@ #include "subreader.h" #include "libvo/sub.h" -#include "libass/ass.h" -#include "libass/ass_mp.h" +#include "ass_mp.h" #include "libavutil/common.h" diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c index c9142f1441..349131f12b 100644 --- a/libmpdemux/demuxer.c +++ b/libmpdemux/demuxer.c @@ -42,10 +42,7 @@ #include "libaf/af_format.h" -#ifdef CONFIG_ASS -#include "libass/ass.h" -#include "libass/ass_mp.h" -#endif +#include "ass_mp.h" #ifdef CONFIG_LIBAVCODEC #include "libavcodec/avcodec.h" diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c index 602829164d..ceb70ec32e 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -32,8 +32,7 @@ #include "gl_common.h" #include "aspect.h" #include "fastmemcpy.h" -#include "libass/ass.h" -#include "libass/ass_mp.h" +#include "ass_mp.h" static const vo_info_t info = { diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c index 597edeb8b3..281bfe60e2 100644 --- a/libvo/vo_vdpau.c +++ b/libvo/vo_vdpau.c @@ -50,8 +50,7 @@ #include "libavutil/common.h" #include "libavutil/mathematics.h" -#include "libass/ass.h" -#include "libass/ass_mp.h" +#include "ass_mp.h" static vo_info_t info = { "VDPAU with X11", diff --git a/mencoder.c b/mencoder.c index 6627332c17..47b0efb8a1 100644 --- a/mencoder.c +++ b/mencoder.c @@ -221,10 +221,7 @@ void mplayer_put_key(struct mp_fifo *fifo, int code) { } -#ifdef CONFIG_ASS -#include "libass/ass.h" -#include "libass/ass_mp.h" -#endif +#include "ass_mp.h" char *current_module; #include "mpcommon.h" diff --git a/mp_msg.c b/mp_msg.c index 5b573a389f..7314dd25af 100644 --- a/mp_msg.c +++ b/mp_msg.c @@ -165,16 +165,14 @@ static void print_msg_module(FILE* stream, int mod) fprintf(stream, ": "); } -void mp_msg(int mod, int lev, const char *format, ... ){ - va_list va; +void mp_msg_va(int mod, int lev, const char *format, va_list va) +{ char tmp[MSGSIZE_MAX]; FILE *stream = lev <= MSGL_WARN ? stderr : stdout; static int header = 1; if (!mp_msg_test(mod, lev)) return; // do not display - va_start(va, format); vsnprintf(tmp, MSGSIZE_MAX, format, va); - va_end(va); tmp[MSGSIZE_MAX-2] = '\n'; tmp[MSGSIZE_MAX-1] = 0; @@ -218,6 +216,15 @@ void mp_msg(int mod, int lev, const char *format, ... ){ fflush(stream); } +void mp_msg(int mod, int lev, const char *format, ...) +{ + va_list va; + va_start(va, format); + mp_msg_va(mod, lev, format, va); + va_end(va); +} + + char *mp_gtext(const char *string) { return string; diff --git a/mp_msg.h b/mp_msg.h index a19da44d0a..91b6b86449 100644 --- a/mp_msg.h +++ b/mp_msg.h @@ -1,6 +1,8 @@ #ifndef MPLAYER_MP_MSG_H #define MPLAYER_MP_MSG_H +#include + // defined in mplayer.c and mencoder.c extern int verbose; @@ -116,6 +118,8 @@ int mp_msg_test(int mod, int lev); char *mp_gtext(const char *string); #define mp_tmsg mp_msg +void mp_msg_va(int mod, int lev, const char *format, va_list va); + #ifdef __GNUC__ void mp_msg(int mod, int lev, const char *format, ... ) __attribute__ ((format (printf, 3, 4))); # ifdef MP_DEBUG diff --git a/mpcommon.c b/mpcommon.c index 053a46d5a4..83cf2b6881 100644 --- a/mpcommon.c +++ b/mpcommon.c @@ -22,8 +22,7 @@ double sub_last_pts = -303; #ifdef CONFIG_ASS -#include "libass/ass.h" -#include "libass/ass_mp.h" +#include "ass_mp.h" ass_track_t* ass_track = 0; // current track to render #endif diff --git a/mplayer.c b/mplayer.c index ac02447eb5..8697e47a85 100644 --- a/mplayer.c +++ b/mplayer.c @@ -309,10 +309,7 @@ char *vobsub_name=NULL; int subcc_enabled=0; int suboverlap_enabled = 1; -#ifdef CONFIG_ASS -#include "libass/ass.h" -#include "libass/ass_mp.h" -#endif +#include "ass_mp.h" char* current_module=NULL; // for debugging