-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#include "ass_library.h"
#include "mputils.h"
-typedef enum {PST_UNKNOWN = 0, PST_INFO, PST_STYLES, PST_EVENTS, PST_FONTS} parser_state_t;
+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;
+ 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);
- }
+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;
+int ass_alloc_style(ass_track_t *track)
+{
+ int sid;
- assert(track->n_styles <= track->max_styles);
+ 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);
- }
+ 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;
+ 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;
+int ass_alloc_event(ass_track_t *track)
+{
+ int eid;
- assert(track->n_events <= track->max_events);
+ 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);
- }
+ 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;
+ 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_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);
+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 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;
+static void rskip_spaces(char **str, char *limit)
+{
+ char *p = *str;
+ while ((p >= limit) && ((*p == ' ') || (*p == '\t')))
+ --p;
+ *str = p;
}
/**
* 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 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 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;
+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;
+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) \
#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;
+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 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)
+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;
+ 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;
}
/**
* \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 = '.';
- }
+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 = '.';
+ }
}
/**
* \param str string to parse, zero-terminated
* Allocates a new style struct.
*/
-static int process_style(ass_track_t* track, char *str)
+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;
+ 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;
+ 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)
+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;
+ 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)
+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;
+ 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)
+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;
+ 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)
+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;
+ 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)
+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;
+ 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)
+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;
+ 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;
}
/**
* \param track track
* \param str string to parse, zero-terminated
*/
-static int process_line(ass_track_t* track, char *str)
+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;
+ 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)
+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;
+ 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;
}
/**
* \param data string to parse
* \param size length of data
*/
-void ass_process_data(ass_track_t* track, char* data, int size)
+void ass_process_data(ass_track_t *track, char *data, int size)
{
- char* str = malloc(size + 1);
+ char *str = malloc(size + 1);
- memcpy(str, data, size);
- str[size] = '\0';
+ memcpy(str, data, size);
+ str[size] = '\0';
- mp_msg(MSGT_ASS, MSGL_V, "event: %s\n", str);
- process_text(track, str);
- free(str);
+ mp_msg(MSGT_ASS, MSGL_V, "event: %s\n", str);
+ process_text(track, str);
+ free(str);
}
/**
* \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)
+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);
+ 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)
+static int check_duplicate_event(ass_track_t *track, int ReadOrder)
{
- int i;
- for (i = 0; i<track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with
- if (track->events[i].ReadOrder == ReadOrder)
- return 1;
- return 0;
+ int i;
+ for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with
+ if (track->events[i].ReadOrder == ReadOrder)
+ return 1;
+ return 0;
}
/**
* \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)
+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);
+ 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
* \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 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);
+ static iconv_t icdsc = (iconv_t) (-1);
+ char *tocp = "UTF-8";
+ char *outbuf;
+ assert(codepage);
- {
- const char* cp_tmp = 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);
- }
+ 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;
+ 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
+#endif // ICONV
/**
* \brief read file contents into newly allocated buffer
* \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)
+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;
+ 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)
+static ass_track_t *parse_memory(ass_library_t *library, char *buf)
{
- ass_track_t* track;
- int i;
+ ass_track_t *track;
+ int i;
- track = ass_new_track(library);
+ track = ass_new_track(library);
- // process header
- process_text(track, buf);
+ // 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;
+ // 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);
+ // 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;
- }
+ if (track->track_type == TRACK_TYPE_UNKNOWN) {
+ ass_free_track(track);
+ return 0;
+ }
- process_force_style(track);
+ process_force_style(track);
- return track;
+ return track;
}
/**
* \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 *ass_read_memory(ass_library_t *library, char *buf,
+ size_t bufsize, char *codepage)
{
- ass_track_t* track;
- int need_free = 0;
+ ass_track_t *track;
+ int need_free = 0;
- if (!buf)
- return 0;
+ if (!buf)
+ return 0;
#ifdef CONFIG_ICONV
- if (codepage)
- buf = sub_recode(buf, bufsize, codepage);
- if (!buf)
- return 0;
- else
- need_free = 1;
+ 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;
+ 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 *read_file_recode(char *fname, char *codepage, size_t *size)
{
- char* buf;
- size_t bufsize;
+ char *buf;
+ size_t bufsize;
- buf = read_file(fname, &bufsize);
- if (!buf)
- return 0;
+ 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;
+ if (codepage) {
+ char *tmpbuf = sub_recode(buf, bufsize, codepage);
+ free(buf);
+ buf = tmpbuf;
+ }
+ if (!buf)
+ return 0;
#endif
- *size = bufsize;
- return buf;
+ *size = bufsize;
+ return buf;
}
/**
* \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)
+ass_track_t *ass_read_file(ass_library_t *library, char *fname,
+ char *codepage)
{
- char* buf;
- ass_track_t* track;
- size_t bufsize;
+ 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;
+ 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);
+ track->name = strdup(fname);
- mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname, track->n_styles, track->n_events);
+ mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname,
+ track->n_styles, track->n_events);
-// dump_events(forced_tid);
- return track;
+// 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)
+int ass_read_styles(ass_track_t *track, char *fname, char *codepage)
{
- char* buf;
- parser_state_t old_state;
- size_t sz;
+ char *buf;
+ parser_state_t old_state;
+ size_t sz;
- buf = read_file(fname, &sz);
- if (!buf)
- return 1;
+ 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;
+ 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;
+ old_state = track->parser_priv->state;
+ track->parser_priv->state = PST_STYLES;
+ process_text(track, buf);
+ track->parser_priv->state = old_state;
- return 0;
+ 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;
+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;
+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;
}
-
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
/// 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
+ 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
+ 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
+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);
+ass_library_t *ass_library_init(void);
/**
* \brief finalize the library
* \param priv library handle
*/
-void ass_library_done(ass_library_t*);
+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_fonts_dir(ass_library_t *priv, const char *fonts_dir);
-void ass_set_extract_fonts(ass_library_t* priv, int extract);
+void ass_set_extract_fonts(ass_library_t *priv, int extract);
-void ass_set_style_overrides(ass_library_t* priv, char** list);
+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*);
+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_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);
+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);
+int ass_set_fonts(ass_renderer_t *priv, const char *default_font,
+ const char *default_family, int fc);
/**
* \brief render a frame, producing a list of ass_image_t
* \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);
+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*);
+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);
+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);
+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);
+int ass_alloc_event(ass_track_t *track);
/**
* \brief delete a style
* \param sid style id
* Deallocates style data. Does not modify track->n_styles.
*/
-void ass_free_style(ass_track_t* track, int sid);
+void ass_free_style(ass_track_t *track, int sid);
/**
* \brief delete an event
* \param eid event id
* Deallocates event data. Does not modify track->n_events.
*/
-void ass_free_event(ass_track_t* track, int eid);
+void ass_free_event(ass_track_t *track, int eid);
/**
* \brief Parse a chunk of subtitle stream data.
* \param data string to parse
* \param size length of data
*/
-void ass_process_data(ass_track_t* track, char* data, int size);
+void ass_process_data(ass_track_t *track, char *data, int size);
/**
* \brief Parse Codec Private section of subtitle stream
* \param data string to parse
* \param size length of data
*/
-void ass_process_codec_private(ass_track_t* track, char *data, int size);
+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 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);
+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);
+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);
+ass_track_t *ass_read_file(ass_library_t *library, char *fname,
+ char *codepage);
/**
* \brief Read subtitles from memory.
* \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 *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);
+int ass_read_styles(ass_track_t *track, char *fname, char *codepage);
/**
* \brief Add a memory font.
* \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);
+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);
+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
* +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);
+long long ass_step_sub(ass_track_t *track, long long now, int movement);
-#endif /* LIBASS_ASS_H */
+#endif /* LIBASS_ASS_H */
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#include "ass_bitmap.h"
struct ass_synth_priv_s {
- int tmp_w, tmp_h;
- unsigned short* tmp;
+ int tmp_w, tmp_h;
+ unsigned short *tmp;
- int g_r;
- int g_w;
+ int g_r;
+ int g_w;
- unsigned *g;
- unsigned *gt2;
+ unsigned *g;
+ unsigned *gt2;
- double radius;
+ double radius;
};
static const unsigned int maxcolor = 255;
static const unsigned base = 256;
-static int generate_tables(ass_synth_priv_t* priv, double radius)
+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; i<priv->g_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; i<priv->g_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;mx<priv->g_w;mx++){
- for(i=0;i<256;i++){
- priv->gt2[mx+i*priv->g_w] = i*priv->g[mx];
- }
- }
- }
-
- return 0;
+ 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; i < priv->g_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; i < priv->g_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; mx < priv->g_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)
+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));
+ 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 *ass_synth_init(double radius)
{
- ass_synth_priv_t* priv = calloc(1, sizeof(ass_synth_priv_t));
- generate_tables(priv, radius);
- return priv;
+ 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)
+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);
+ 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)
+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;
+ 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)
+void ass_free_bitmap(bitmap_t *bm)
{
- if (bm) {
- if (bm->buffer) free(bm->buffer);
- free(bm);
- }
+ if (bm) {
+ if (bm->buffer)
+ free(bm->buffer);
+ free(bm);
+ }
}
-static bitmap_t* copy_bitmap(const bitmap_t* src)
+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;
+ 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;
+ 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)
+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;
+ 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;
}
/**
* 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)
+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;
+ 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<h; y++) {
- old_sum = 2 * buf[y*w];
- for (x=0; x<w-1; x++) {
- new_sum = buf[y*w+x] + buf[y*w+x+1];
- buf[y*w+x] = (old_sum + new_sum) >> 2;
- old_sum = new_sum;
- }
- }
-
- for (x=0; x<w; x++) {
- old_sum = 2 * buf[x];
- for (y=0; y<h-1; y++) {
- new_sum = buf[y*w+x] + buf[(y+1)*w+x];
- buf[y*w+x] = (old_sum + new_sum) >> 2;
- old_sum = new_sum;
- }
- }
+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 < h; y++) {
+ old_sum = 2 * buf[y * w];
+ for (x = 0; x < w - 1; x++) {
+ new_sum = buf[y * w + x] + buf[y * w + x + 1];
+ buf[y * w + x] = (old_sum + new_sum) >> 2;
+ old_sum = new_sum;
+ }
+ }
+
+ for (x = 0; x < w; x++) {
+ old_sum = 2 * buf[x];
+ for (y = 0; y < h - 1; y++) {
+ new_sum = buf[y * w + x] + buf[(y + 1) * w + x];
+ buf[y * w + x] = (old_sum + new_sum) >> 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 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+1 : 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;
+ int bord = be ? (be / 4 + 1) : 0;
+ blur_radius *= 2;
+ bord = (blur_radius > 0.0) ? blur_radius + 1 : 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;
}
-
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
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);
+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
+ int left, top;
+ int w, h; // width, height
+ unsigned char *buffer; // w x h buffer
} bitmap_t;
/**
* \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);
+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);
+void ass_free_bitmap(bitmap_t *bm);
-#endif /* LIBASS_BITMAP_H */
+#endif /* LIBASS_BITMAP_H */
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#define FNV1_32A_INIT (unsigned)0x811c9dc5
-static inline unsigned fnv_32a_buf(void* buf, size_t len, unsigned hval)
+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;
+ 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)
+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;
+ 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)
+static unsigned hashmap_hash(void *buf, size_t len)
{
- return fnv_32a_buf(buf, len, FNV1_32A_INIT);
+ return fnv_32a_buf(buf, len, FNV1_32A_INIT);
}
-static int hashmap_key_compare(void* a, void* b, size_t size)
+static int hashmap_key_compare(void *a, void *b, size_t size)
{
- return memcmp(a, b, size) == 0;
+ return memcmp(a, b, size) == 0;
}
-static void hashmap_item_dtor(void* key, size_t key_size, void* value, size_t value_size)
+static void hashmap_item_dtor(void *key, size_t key_size, void *value,
+ size_t value_size)
{
- free(key);
- free(value);
+ 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 *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;
+ 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)
+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);
+ 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);
+ 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;
+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
-static unsigned font_desc_hash(void* buf, size_t len)
+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;
+ 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 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)
+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_free(value);
+ free(key);
}
-ass_font_t* ass_font_cache_find(hashmap_t* font_cache, ass_font_desc_t* desc)
+ass_font_t *ass_font_cache_find(hashmap_t *font_cache,
+ ass_font_desc_t *desc)
{
- return hashmap_find(font_cache, desc);
+ return hashmap_find(font_cache, desc);
}
/**
* \brief Add a face struct to cache.
* \param font font struct
*/
-void* ass_font_cache_add(hashmap_t* font_cache, ass_font_t* font)
+void *ass_font_cache_add(hashmap_t *font_cache, ass_font_t *font)
{
- return hashmap_insert(font_cache, &(font->desc), font);
+ return hashmap_insert(font_cache, &(font->desc), font);
}
-hashmap_t* ass_font_cache_init(void)
+hashmap_t *ass_font_cache_init(void)
{
- hashmap_t* font_cache;
- font_cache = hashmap_init(sizeof(ass_font_desc_t),
- sizeof(ass_font_t),
- 1000,
- font_hash_dtor, font_compare, font_desc_hash);
- return font_cache;
+ hashmap_t *font_cache;
+ font_cache = hashmap_init(sizeof(ass_font_desc_t),
+ sizeof(ass_font_t),
+ 1000,
+ font_hash_dtor, font_compare, font_desc_hash);
+ return font_cache;
}
-void ass_font_cache_done(hashmap_t* font_cache)
+void ass_font_cache_done(hashmap_t *font_cache)
{
- hashmap_done(font_cache);
+ hashmap_done(font_cache);
}
//---------------------------------
// bitmap cache
-static void bitmap_hash_dtor(void* key, size_t key_size, void* value, size_t value_size)
+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);
+ 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(hashmap_t* bitmap_cache, bitmap_hash_key_t* key, bitmap_hash_val_t* val)
+void *cache_add_bitmap(hashmap_t *bitmap_cache, bitmap_hash_key_t *key,
+ bitmap_hash_val_t *val)
{
- return hashmap_insert(bitmap_cache, key, val);
+ return hashmap_insert(bitmap_cache, key, val);
}
/**
* \param key hash key
* \return requested hash val or 0 if not found
*/
-bitmap_hash_val_t* cache_find_bitmap(hashmap_t* bitmap_cache, bitmap_hash_key_t* key)
+bitmap_hash_val_t *cache_find_bitmap(hashmap_t *bitmap_cache,
+ bitmap_hash_key_t *key)
{
- return hashmap_find(bitmap_cache, key);
+ return hashmap_find(bitmap_cache, key);
}
-hashmap_t* ass_bitmap_cache_init(void)
+hashmap_t *ass_bitmap_cache_init(void)
{
- hashmap_t* bitmap_cache;
- bitmap_cache = hashmap_init(sizeof(bitmap_hash_key_t),
- sizeof(bitmap_hash_val_t),
- 0xFFFF + 13,
- bitmap_hash_dtor, bitmap_compare,
- bitmap_hash);
- return bitmap_cache;
+ hashmap_t *bitmap_cache;
+ bitmap_cache = hashmap_init(sizeof(bitmap_hash_key_t),
+ sizeof(bitmap_hash_val_t),
+ 0xFFFF + 13,
+ bitmap_hash_dtor, bitmap_compare,
+ bitmap_hash);
+ return bitmap_cache;
}
-void ass_bitmap_cache_done(hashmap_t* bitmap_cache)
+void ass_bitmap_cache_done(hashmap_t *bitmap_cache)
{
- hashmap_done(bitmap_cache);
+ hashmap_done(bitmap_cache);
}
-hashmap_t* ass_bitmap_cache_reset(hashmap_t* bitmap_cache)
+hashmap_t *ass_bitmap_cache_reset(hashmap_t *bitmap_cache)
{
- ass_bitmap_cache_done(bitmap_cache);
- return ass_bitmap_cache_init();
+ ass_bitmap_cache_done(bitmap_cache);
+ return ass_bitmap_cache_init();
}
//---------------------------------
// glyph cache
-static void glyph_hash_dtor(void* key, size_t key_size, void* value, size_t value_size)
+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);
+ 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(hashmap_t* glyph_cache, glyph_hash_key_t* key, glyph_hash_val_t* val)
+void *cache_add_glyph(hashmap_t *glyph_cache, glyph_hash_key_t *key,
+ glyph_hash_val_t *val)
{
- return hashmap_insert(glyph_cache, key, val);
+ return hashmap_insert(glyph_cache, key, val);
}
/**
* \param key hash key
* \return requested hash val or 0 if not found
*/
-glyph_hash_val_t* cache_find_glyph(hashmap_t* glyph_cache, glyph_hash_key_t* key)
+glyph_hash_val_t *cache_find_glyph(hashmap_t *glyph_cache,
+ glyph_hash_key_t *key)
{
- return hashmap_find(glyph_cache, key);
+ return hashmap_find(glyph_cache, key);
}
-hashmap_t* ass_glyph_cache_init(void)
+hashmap_t *ass_glyph_cache_init(void)
{
- hashmap_t* glyph_cache;
- glyph_cache = hashmap_init(sizeof(glyph_hash_key_t),
- sizeof(glyph_hash_val_t),
- 0xFFFF + 13,
- glyph_hash_dtor, glyph_compare, glyph_hash);
- return glyph_cache;
+ hashmap_t *glyph_cache;
+ glyph_cache = hashmap_init(sizeof(glyph_hash_key_t),
+ sizeof(glyph_hash_val_t),
+ 0xFFFF + 13,
+ glyph_hash_dtor, glyph_compare, glyph_hash);
+ return glyph_cache;
}
-void ass_glyph_cache_done(hashmap_t* glyph_cache)
+void ass_glyph_cache_done(hashmap_t *glyph_cache)
{
- hashmap_done(glyph_cache);
+ hashmap_done(glyph_cache);
}
-hashmap_t* ass_glyph_cache_reset(hashmap_t* glyph_cache)
+hashmap_t *ass_glyph_cache_reset(hashmap_t *glyph_cache)
{
- ass_glyph_cache_done(glyph_cache);
- return ass_glyph_cache_init();
+ ass_glyph_cache_done(glyph_cache);
+ return ass_glyph_cache_init();
}
//---------------------------------
// composite cache
-static void composite_hash_dtor(void* key, size_t key_size, void* value, size_t value_size)
+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);
+ composite_hash_val_t *v = value;
+ free(v->a);
+ free(v->b);
+ free(key);
+ free(value);
}
-void* cache_add_composite(hashmap_t* composite_cache, composite_hash_key_t* key, composite_hash_val_t* val)
+void *cache_add_composite(hashmap_t *composite_cache,
+ composite_hash_key_t *key,
+ composite_hash_val_t *val)
{
- return hashmap_insert(composite_cache, key, val);
+ return hashmap_insert(composite_cache, key, val);
}
/**
* \param key hash key
* \return requested hash val or 0 if not found
*/
-composite_hash_val_t* cache_find_composite(hashmap_t* composite_cache, composite_hash_key_t* key)
+composite_hash_val_t *cache_find_composite(hashmap_t *composite_cache,
+ composite_hash_key_t *key)
{
- return hashmap_find(composite_cache, key);
+ return hashmap_find(composite_cache, key);
}
-hashmap_t* ass_composite_cache_init(void)
+hashmap_t *ass_composite_cache_init(void)
{
- hashmap_t* composite_cache;
- composite_cache = hashmap_init(sizeof(composite_hash_key_t),
- sizeof(composite_hash_val_t),
- 0xFFFF + 13,
- composite_hash_dtor, NULL, NULL);
- return composite_cache;
+ hashmap_t *composite_cache;
+ composite_cache = hashmap_init(sizeof(composite_hash_key_t),
+ sizeof(composite_hash_val_t),
+ 0xFFFF + 13,
+ composite_hash_dtor, NULL, NULL);
+ return composite_cache;
}
-void ass_composite_cache_done(hashmap_t* composite_cache)
+void ass_composite_cache_done(hashmap_t *composite_cache)
{
- hashmap_done(composite_cache);
+ hashmap_done(composite_cache);
}
-hashmap_t* ass_composite_cache_reset(hashmap_t* composite_cache)
+hashmap_t *ass_composite_cache_reset(hashmap_t *composite_cache)
{
- ass_composite_cache_done(composite_cache);
- return ass_composite_cache_init();
+ ass_composite_cache_done(composite_cache);
+ return ass_composite_cache_init();
}
-
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#include "ass_font.h"
#include "ass_bitmap.h"
-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);
+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);
typedef struct hashmap_item_s {
- void* key;
- void* value;
- struct hashmap_item_s* next;
+ void *key;
+ void *value;
+ struct hashmap_item_s *next;
} hashmap_item_t;
-typedef hashmap_item_t* hashmap_item_p;
+typedef hashmap_item_t *hashmap_item_p;
typedef 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;
+ 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;
} hashmap_t;
-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);
+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);
-hashmap_t* ass_font_cache_init(void);
-ass_font_t* ass_font_cache_find(hashmap_t*, ass_font_desc_t* desc);
-void* ass_font_cache_add(hashmap_t*, ass_font_t* font);
-void ass_font_cache_done(hashmap_t*);
+hashmap_t *ass_font_cache_init(void);
+ass_font_t *ass_font_cache_find(hashmap_t *, ass_font_desc_t *desc);
+void *ass_font_cache_add(hashmap_t *, ass_font_t *font);
+void ass_font_cache_done(hashmap_t *);
// 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_t *bm; // the actual bitmaps
+ bitmap_t *bm_o;
+ bitmap_t *bm_s;
} bitmap_hash_val_t;
-hashmap_t* ass_bitmap_cache_init(void);
-void* cache_add_bitmap(hashmap_t*, bitmap_hash_key_t* key, bitmap_hash_val_t* val);
-bitmap_hash_val_t* cache_find_bitmap(hashmap_t* bitmap_cache, bitmap_hash_key_t* key);
-hashmap_t* ass_bitmap_cache_reset(hashmap_t* bitmap_cache);
-void ass_bitmap_cache_done(hashmap_t* bitmap_cache);
+hashmap_t *ass_bitmap_cache_init(void);
+void *cache_add_bitmap(hashmap_t *, bitmap_hash_key_t *key,
+ bitmap_hash_val_t *val);
+bitmap_hash_val_t *cache_find_bitmap(hashmap_t *bitmap_cache,
+ bitmap_hash_key_t *key);
+hashmap_t *ass_bitmap_cache_reset(hashmap_t *bitmap_cache);
+void ass_bitmap_cache_done(hashmap_t *bitmap_cache);
// 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;
+ 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;
+ unsigned char *a;
+ unsigned char *b;
} composite_hash_val_t;
-hashmap_t* ass_composite_cache_init(void);
-void* cache_add_composite(hashmap_t*, composite_hash_key_t* key, composite_hash_val_t* val);
-composite_hash_val_t* cache_find_composite(hashmap_t* composite_cache, composite_hash_key_t* key);
-hashmap_t* ass_composite_cache_reset(hashmap_t* composite_cache);
-void ass_composite_cache_done(hashmap_t* composite_cache);
+hashmap_t *ass_composite_cache_init(void);
+void *cache_add_composite(hashmap_t *, composite_hash_key_t *key,
+ composite_hash_val_t *val);
+composite_hash_val_t *cache_find_composite(hashmap_t *composite_cache,
+ composite_hash_key_t *key);
+hashmap_t *ass_composite_cache_reset(hashmap_t *composite_cache);
+void ass_composite_cache_done(hashmap_t *composite_cache);
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
+ 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;
-hashmap_t* ass_glyph_cache_init(void);
-void* cache_add_glyph(hashmap_t*, glyph_hash_key_t* key, glyph_hash_val_t* val);
-glyph_hash_val_t* cache_find_glyph(hashmap_t* glyph_cache, glyph_hash_key_t* key);
-hashmap_t* ass_glyph_cache_reset(hashmap_t* glyph_cache);
-void ass_glyph_cache_done(hashmap_t* glyph_cache);
+hashmap_t *ass_glyph_cache_init(void);
+void *cache_add_glyph(hashmap_t *, glyph_hash_key_t *key,
+ glyph_hash_val_t *val);
+glyph_hash_val_t *cache_find_glyph(hashmap_t *glyph_cache,
+ glyph_hash_key_t *key);
+hashmap_t *ass_glyph_cache_reset(hashmap_t *glyph_cache);
+void ass_glyph_cache_done(hashmap_t *glyph_cache);
-#endif /* LIBASS_CACHE_H */
+#endif /* LIBASS_CACHE_H */
{ \
struct structname *a = key1; \
struct structname *b = key2; \
- return // conditions follow
+ return // conditions follow
#define GENERIC(type, member) \
a->member == b->member &&
#define FTVECTOR(member) \
// 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(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
+ 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
+ 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
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
*/
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;
- }
+ 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)
+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);
+ 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)
+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;
+ 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;
- }
- }
+ // 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)
+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;
+ 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(void* font_cache, ass_library_t* library, FT_Library ftlibrary, void* fc_priv, ass_font_desc_t* desc)
+ass_font_t *ass_font_new(void *font_cache, 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((hashmap_t*)font_cache, 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((hashmap_t*)font_cache, &font);
+ int error;
+ ass_font_t *fontp;
+ ass_font_t font;
+
+ fontp = ass_font_cache_find((hashmap_t *) font_cache, 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((hashmap_t *) font_cache, &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)
+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);
+ 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;
+ 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);
+ 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)
+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);
- }
+ int i;
+ if (font->size != size) {
+ font->size = size;
+ for (i = 0; i < font->n_faces; ++i)
+ face_set_size(font->faces[i], size);
+ }
}
/**
* \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)
+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;
+ 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)
+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;
- }
+ 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);
- }
- }
- }
+ 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;
- }
-
+ 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);
- }
+ 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;
- }
+ error = FT_Get_Glyph(face->glyph, &glyph);
+ if (error) {
+ mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph);
+ return 0;
+ }
- return glyph;
+ 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 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;
+ 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)
+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);
+ 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);
}
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#include "ass_types.h"
typedef struct ass_font_desc_s {
- char* family;
- unsigned bold;
- unsigned italic;
- int treat_family_as_pattern;
+ 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_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;
// FIXME: passing the hashmap via a void pointer is very ugly.
-ass_font_t* ass_font_new(void* font_cache, 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);
+ass_font_t *ass_font_new(void *font_cache, 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 */
+#endif /* LIBASS_FONT_H */
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
struct fc_instance_s {
#ifdef CONFIG_FONTCONFIG
- FcConfig* config;
+ FcConfig *config;
#endif
- char* family_default;
- char* path_default;
- int index_default;
+ char *family_default;
+ char *path_default;
+ int index_default;
};
#ifdef CONFIG_FONTCONFIG
* \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)
+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;
+ 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);
- }
+ 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;
+ 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;
}
/**
* \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 *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;
+ 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)
+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;
+ 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
* 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)
+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;
+ 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;
+ 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);
+ res = mkdir(fonts_dir, 0700);
#else
- res = mkdir(fonts_dir);
+ 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);
- }
+ 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
}
* \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)
+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;
+ 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 */
+#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)
+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;
+ *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 *fontconfig_init(ass_library_t *library,
+ FT_Library ftlibrary, const char *family,
+ const char *path, int fc)
{
- fc_instance_t* priv;
+ fc_instance_t *priv;
- mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed);
+ mp_msg(MSGT_ASS, MSGL_WARN,
+ MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed);
- priv = calloc(1, sizeof(fc_instance_t));
+ priv = calloc(1, sizeof(fc_instance_t));
- priv->path_default = strdup(path);
- priv->index_default = 0;
- return priv;
+ priv->path_default = strdup(path);
+ priv->index_default = 0;
+ return priv;
}
#endif
-void fontconfig_done(fc_instance_t* priv)
+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);
+ // 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);
}
-
-
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
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);
+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 */
+#endif /* LIBASS_FONTCONFIG_H */
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#include "ass_library.h"
-ass_library_t* ass_library_init(void)
+ass_library_t *ass_library_init(void)
{
- return calloc(1, sizeof(ass_library_t));
+ return calloc(1, sizeof(ass_library_t));
}
-void ass_library_done(ass_library_t* priv)
+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);
- }
+ 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)
+void ass_set_fonts_dir(ass_library_t *priv, const char *fonts_dir)
{
- if (priv->fonts_dir)
- free(priv->fonts_dir);
+ if (priv->fonts_dir)
+ free(priv->fonts_dir);
- priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0;
+ priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0;
}
-void ass_set_extract_fonts(ass_library_t* priv, int extract)
+void ass_set_extract_fonts(ass_library_t *priv, int extract)
{
- priv->extract_fonts = !!extract;
+ priv->extract_fonts = !!extract;
}
-void ass_set_style_overrides(ass_library_t* priv, char** list)
+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;
+ 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);
+ if (!(nelem & 31))
+ *array = realloc(*array, (nelem + 32) * elsize);
}
-void ass_add_font(ass_library_t* priv, char* name, char* data, int size)
+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));
+ 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].name = strdup(name);
- priv->fontdata[idx].data = malloc(size);
- memcpy(priv->fontdata[idx].data, data, size);
+ priv->fontdata[idx].data = malloc(size);
+ memcpy(priv->fontdata[idx].data, data, size);
- priv->fontdata[idx].size = size;
+ priv->fontdata[idx].size = size;
- priv->num_fontdata ++;
+ priv->num_fontdata++;
}
-void ass_clear_fonts(ass_library_t* priv)
+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;
+ 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;
}
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#define LIBASS_LIBRARY_H
typedef struct ass_fontdata_s {
- char* name;
- char* data;
- int size;
+ char *name;
+ char *data;
+ int size;
} ass_fontdata_t;
struct ass_library_s {
- char* fonts_dir;
- int extract_fonts;
- char** style_overrides;
+ char *fonts_dir;
+ int extract_fonts;
+ char **style_overrides;
- ass_fontdata_t* fontdata;
- int num_fontdata;
+ ass_fontdata_t *fontdata;
+ int num_fontdata;
};
-#endif /* LIBASS_LIBRARY_H */
+#endif /* LIBASS_LIBRARY_H */
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#define BLUR_MAX_RADIUS 100.0
#define MAX_BE 100
#define ROUND(x) ((int) ((x) + .5))
-#define SUBPIXEL_MASK 56 // d6 bitmask for subpixel accuracy adjustment
+#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;
+ 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;
+ ass_image_t *imgs;
+ int top, height;
+ int detect_collisions;
+ int shift_direction;
+ ass_event_t *event;
} event_images_t;
-typedef enum {EF_NONE = 0, EF_KARAOKE, EF_KARAOKE_KF, EF_KARAOKE_KO} effect_t;
+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;
+ 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;
+ int asc, desc;
} line_info_t;
typedef struct text_info_s {
- glyph_info_t* glyphs;
- int length;
- line_info_t* lines;
- int n_lines;
- int height;
- int max_glyphs;
- int max_lines;
+ glyph_info_t *glyphs;
+ int length;
+ line_info_t *lines;
+ int n_lines;
+ int height;
+ int max_glyphs;
+ int max_lines;
} 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;
+ 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;
typedef struct cache_store_s {
- hashmap_t* font_cache;
- hashmap_t* glyph_cache;
- hashmap_t* bitmap_cache;
- hashmap_t* composite_cache;
+ hashmap_t *font_cache;
+ hashmap_t *glyph_cache;
+ hashmap_t *bitmap_cache;
+ hashmap_t *composite_cache;
} cache_store_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
-
- // frame-global data
- 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;
-
- render_context_t state;
- text_info_t text_info;
- cache_store_t cache;
+ 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
+
+ // frame-global data
+ 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;
+
+ render_context_t state;
+ text_info_t text_info;
+ cache_store_t cache;
};
struct render_priv_s {
- int top, height;
- int render_id;
+ int top, height;
+ int render_id;
};
-static void ass_lazy_track_init(ass_renderer_t* render_priv)
+static void ass_lazy_track_init(ass_renderer_t *render_priv)
{
- ass_track_t* track = render_priv->track;
- ass_settings_t* settings_priv = &render_priv->settings;
-
- 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 = (settings_priv->aspect * render_priv->height * render_priv->orig_width) /
- render_priv->orig_height / render_priv->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_track_t *track = render_priv->track;
+ ass_settings_t *settings_priv = &render_priv->settings;
+
+ 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 =
+ (settings_priv->aspect * render_priv->height *
+ render_priv->orig_width) / render_priv->orig_height /
+ render_priv->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)
+ass_renderer_t *ass_renderer_init(ass_library_t *library)
{
- int error;
- FT_Library ft;
- ass_renderer_t* priv = 0;
- int vmajor, vminor, vpatch;
-
- 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
-
- priv->cache.font_cache = ass_font_cache_init();
- priv->cache.bitmap_cache = ass_bitmap_cache_init();
- priv->cache.composite_cache = ass_composite_cache_init();
- priv->cache.glyph_cache = ass_glyph_cache_init();
-
- priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL;
- priv->text_info.max_lines = MAX_LINES_INITIAL;
- priv->text_info.glyphs = calloc(MAX_GLYPHS_INITIAL, sizeof(glyph_info_t));
- priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(line_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;
+ int error;
+ FT_Library ft;
+ ass_renderer_t *priv = 0;
+ int vmajor, vminor, vpatch;
+
+ 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
+
+ priv->cache.font_cache = ass_font_cache_init();
+ priv->cache.bitmap_cache = ass_bitmap_cache_init();
+ priv->cache.composite_cache = ass_composite_cache_init();
+ priv->cache.glyph_cache = ass_glyph_cache_init();
+
+ priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL;
+ priv->text_info.max_lines = MAX_LINES_INITIAL;
+ priv->text_info.glyphs =
+ calloc(MAX_GLYPHS_INITIAL, sizeof(glyph_info_t));
+ priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(line_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* render_priv)
+void ass_renderer_done(ass_renderer_t *render_priv)
{
- ass_font_cache_done(render_priv->cache.font_cache);
- ass_bitmap_cache_done(render_priv->cache.bitmap_cache);
- ass_composite_cache_done(render_priv->cache.composite_cache);
- ass_glyph_cache_done(render_priv->cache.glyph_cache);
- if (render_priv->state.stroker) {
- FT_Stroker_Done(render_priv->state.stroker);
- render_priv->state.stroker = 0;
- }
- if (render_priv && render_priv->ftlibrary) FT_Done_FreeType(render_priv->ftlibrary);
- if (render_priv && render_priv->fontconfig_priv) fontconfig_done(render_priv->fontconfig_priv);
- if (render_priv && render_priv->synth_priv) ass_synth_done(render_priv->synth_priv);
- if (render_priv && render_priv->eimg) free(render_priv->eimg);
- free(render_priv);
- free(render_priv->text_info.glyphs);
- free(render_priv->text_info.lines);
+ ass_font_cache_done(render_priv->cache.font_cache);
+ ass_bitmap_cache_done(render_priv->cache.bitmap_cache);
+ ass_composite_cache_done(render_priv->cache.composite_cache);
+ ass_glyph_cache_done(render_priv->cache.glyph_cache);
+ if (render_priv->state.stroker) {
+ FT_Stroker_Done(render_priv->state.stroker);
+ render_priv->state.stroker = 0;
+ }
+ if (render_priv && render_priv->ftlibrary)
+ FT_Done_FreeType(render_priv->ftlibrary);
+ if (render_priv && render_priv->fontconfig_priv)
+ fontconfig_done(render_priv->fontconfig_priv);
+ if (render_priv && render_priv->synth_priv)
+ ass_synth_done(render_priv->synth_priv);
+ if (render_priv && render_priv->eimg)
+ free(render_priv->eimg);
+ free(render_priv);
+ free(render_priv->text_info.glyphs);
+ free(render_priv->text_info.lines);
}
/**
* \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)
+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));
+ 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;
+ 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;
+ return img;
}
/**
* \return pointer to the new list tail
* Performs clipping. Uses my_draw_bitmap for actual bitmap convertion.
*/
-static ass_image_t** render_glyph(ass_renderer_t* render_priv, bitmap_t* bm, int dst_x, int dst_y, uint32_t color, uint32_t color2, int brk, ass_image_t** tail)
+static ass_image_t **render_glyph(ass_renderer_t *render_priv,
+ 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_priv->state.clip_x0;
- clip_y0 = render_priv->state.clip_y0;
- clip_x1 = render_priv->state.clip_x1;
- clip_y1 = render_priv->state.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;
+ // 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_priv->state.clip_x0;
+ clip_y0 = render_priv->state.clip_y0;
+ clip_x1 = render_priv->state.clip_x1;
+ clip_y1 = render_priv->state.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;
}
/**
* Mainly useful for translucent glyphs and especially borders, to avoid the
* luminance adding up where they overlap (which looks ugly)
*/
-static void render_overlap(ass_renderer_t* render_priv, 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(render_priv->cache.composite_cache, &hk);
- if (hv) {
- (*last_tail)->bitmap = hv->a;
- (*tail)->bitmap = hv->b;
- return;
- }
-
- // Allocate new bitmaps and copy over data
- a = (*last_tail)->bitmap;
- b = (*tail)->bitmap;
- (*last_tail)->bitmap = malloc(as*ah);
- (*tail)->bitmap = malloc(bs*bh);
- memcpy((*last_tail)->bitmap, a, as*ah);
- memcpy((*tail)->bitmap, b, bs*bh);
-
- // Composite overlapping area
- for (y=0; y<h; y++)
- for (x=0; x<w; x++) {
- opos = (old_top+y)*(as) + (old_left+x);
- cpos = (cur_top+y)*(bs) + (cur_left+x);
- m = (a[opos] > b[cpos]) ? a[opos] : b[cpos];
- (*last_tail)->bitmap[opos] = 0;
- (*tail)->bitmap[cpos] = m;
- }
-
- // Insert bitmaps into the cache
- nhk = calloc(1, sizeof(*nhk));
- memcpy(nhk, &hk, sizeof(*nhk));
- hv = calloc(1, sizeof(*hv));
- hv->a = (*last_tail)->bitmap;
- hv->b = (*tail)->bitmap;
- cache_add_composite(render_priv->cache.composite_cache, nhk, hv);
+static void
+render_overlap(ass_renderer_t *render_priv, 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(render_priv->cache.composite_cache, &hk);
+ if (hv) {
+ (*last_tail)->bitmap = hv->a;
+ (*tail)->bitmap = hv->b;
+ return;
+ }
+ // Allocate new bitmaps and copy over data
+ a = (*last_tail)->bitmap;
+ b = (*tail)->bitmap;
+ (*last_tail)->bitmap = malloc(as * ah);
+ (*tail)->bitmap = malloc(bs * bh);
+ memcpy((*last_tail)->bitmap, a, as * ah);
+ memcpy((*tail)->bitmap, b, bs * bh);
+
+ // Composite overlapping area
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++) {
+ opos = (old_top + y) * (as) + (old_left + x);
+ cpos = (cur_top + y) * (bs) + (cur_left + x);
+ m = (a[opos] > b[cpos]) ? a[opos] : b[cpos];
+ (*last_tail)->bitmap[opos] = 0;
+ (*tail)->bitmap[cpos] = m;
+ }
+
+ // Insert bitmaps into the cache
+ nhk = calloc(1, sizeof(*nhk));
+ memcpy(nhk, &hk, sizeof(*nhk));
+ hv = calloc(1, sizeof(*hv));
+ hv->a = (*last_tail)->bitmap;
+ hv->b = (*tail)->bitmap;
+ cache_add_composite(render_priv->cache.composite_cache, 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(ass_renderer_t* render_priv, int dst_x, int dst_y)
+static ass_image_t *render_text(ass_renderer_t *render_priv, 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;
- text_info_t* text_info = &render_priv->text_info;
-
- 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 * render_priv->border_scale);
- pen_y = dst_y + info->pos.y + ROUND(info->shadow * render_priv->border_scale);
- bm = info->bm_s;
-
- here_tail = tail;
- tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, 1000000, tail);
- if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0))
- render_overlap(render_priv, last_tail, here_tail, last_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(render_priv, bm, pen_x, pen_y, info->c[2], 0, 1000000, tail);
- if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0))
- render_overlap(render_priv, last_tail, here_tail, last_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(render_priv, bm, pen_x, pen_y, info->c[0], 0, 1000000, tail);
- else
- tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[1], 0, 1000000, tail);
- } else if (info->effect_type == EF_KARAOKE_KF) {
- tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], info->c[1], info->effect_timing, tail);
- } else
- tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], 0, 1000000, tail);
- }
-
- *tail = 0;
- return head;
+ 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;
+ text_info_t *text_info = &render_priv->text_info;
+
+ 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 * render_priv->border_scale);
+ pen_y =
+ dst_y + info->pos.y +
+ ROUND(info->shadow * render_priv->border_scale);
+ bm = info->bm_s;
+
+ here_tail = tail;
+ tail =
+ render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0,
+ 1000000, tail);
+ if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0))
+ render_overlap(render_priv, last_tail, here_tail, last_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(render_priv, bm, pen_x, pen_y, info->c[2],
+ 0, 1000000, tail);
+ if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0))
+ render_overlap(render_priv, last_tail, here_tail,
+ last_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(render_priv, bm, pen_x, pen_y,
+ info->c[0], 0, 1000000, tail);
+ else
+ tail =
+ render_glyph(render_priv, bm, pen_x, pen_y,
+ info->c[1], 0, 1000000, tail);
+ } else if (info->effect_type == EF_KARAOKE_KF) {
+ tail =
+ render_glyph(render_priv, bm, pen_x, pen_y, info->c[0],
+ info->c[1], info->effect_timing, tail);
+ } else
+ tail =
+ render_glyph(render_priv, bm, pen_x, pen_y, info->c[0],
+ 0, 1000000, tail);
+ }
+
+ *tail = 0;
+ return head;
}
/**
* \brief Mapping between script and screen coordinates
*/
-static int x2scr(ass_renderer_t* render_priv, double x) {
- return x*render_priv->orig_width_nocrop / render_priv->track->PlayResX +
- FFMAX(render_priv->settings.left_margin, 0);
+static int x2scr(ass_renderer_t *render_priv, double x)
+{
+ return x * render_priv->orig_width_nocrop /
+ render_priv->track->PlayResX +
+ FFMAX(render_priv->settings.left_margin, 0);
}
-static double x2scr_pos(ass_renderer_t* render_priv, double x) {
- return x*render_priv->orig_width / render_priv->track->PlayResX +
- render_priv->settings.left_margin;
+static double x2scr_pos(ass_renderer_t *render_priv, double x)
+{
+ return x * render_priv->orig_width / render_priv->track->PlayResX +
+ render_priv->settings.left_margin;
}
+
/**
* \brief Mapping between script and screen coordinates
*/
-static double y2scr(ass_renderer_t* render_priv, double y) {
- return y * render_priv->orig_height_nocrop / render_priv->track->PlayResY +
- FFMAX(render_priv->settings.top_margin, 0);
+static double y2scr(ass_renderer_t *render_priv, double y)
+{
+ return y * render_priv->orig_height_nocrop /
+ render_priv->track->PlayResY +
+ FFMAX(render_priv->settings.top_margin, 0);
}
-static double y2scr_pos(ass_renderer_t* render_priv, double y) {
- return y * render_priv->orig_height / render_priv->track->PlayResY +
- render_priv->settings.top_margin;
+static double y2scr_pos(ass_renderer_t *render_priv, double y)
+{
+ return y * render_priv->orig_height / render_priv->track->PlayResY +
+ render_priv->settings.top_margin;
}
// the same for toptitles
-static int y2scr_top(ass_renderer_t* render_priv, double y) {
- if (render_priv->settings.use_margins)
- return y * render_priv->orig_height_nocrop / render_priv->track->PlayResY;
- else
- return y * render_priv->orig_height_nocrop / render_priv->track->PlayResY +
- FFMAX(render_priv->settings.top_margin, 0);
+static int y2scr_top(ass_renderer_t *render_priv, double y)
+{
+ if (render_priv->settings.use_margins)
+ return y * render_priv->orig_height_nocrop /
+ render_priv->track->PlayResY;
+ else
+ return y * render_priv->orig_height_nocrop /
+ render_priv->track->PlayResY +
+ FFMAX(render_priv->settings.top_margin, 0);
}
+
// the same for subtitles
-static int y2scr_sub(ass_renderer_t* render_priv, double y) {
- if (render_priv->settings.use_margins)
- return y * render_priv->orig_height_nocrop / render_priv->track->PlayResY +
- FFMAX(render_priv->settings.top_margin, 0) +
- FFMAX(render_priv->settings.bottom_margin, 0);
- else
- return y * render_priv->orig_height_nocrop / render_priv->track->PlayResY +
- FFMAX(render_priv->settings.top_margin, 0);
+static int y2scr_sub(ass_renderer_t *render_priv, double y)
+{
+ if (render_priv->settings.use_margins)
+ return y * render_priv->orig_height_nocrop /
+ render_priv->track->PlayResY +
+ FFMAX(render_priv->settings.top_margin,
+ 0) + FFMAX(render_priv->settings.bottom_margin, 0);
+ else
+ return y * render_priv->orig_height_nocrop /
+ render_priv->track->PlayResY +
+ FFMAX(render_priv->settings.top_margin, 0);
}
-static void compute_string_bbox( text_info_t* info, FT_BBox *abbox ) {
- FT_BBox bbox;
- int i;
-
- if (info->length > 0) {
- bbox.xMin = 32000;
- bbox.xMax = -32000;
- bbox.yMin = - d6_to_int(info->lines[0].asc) + info->glyphs[0].pos.y;
- bbox.yMax = d6_to_int(info->height - info->lines[0].asc) + info->glyphs[0].pos.y;
-
- for (i = 0; i < info->length; ++i) {
- int s = info->glyphs[i].pos.x;
- int e = s + d6_to_int(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;
+static void compute_string_bbox(text_info_t *info, FT_BBox * abbox)
+{
+ FT_BBox bbox;
+ int i;
+
+ if (info->length > 0) {
+ bbox.xMin = 32000;
+ bbox.xMax = -32000;
+ bbox.yMin = -d6_to_int(info->lines[0].asc) + info->glyphs[0].pos.y;
+ bbox.yMax =
+ d6_to_int(info->height - info->lines[0].asc) +
+ info->glyphs[0].pos.y;
+
+ for (i = 0; i < info->length; ++i) {
+ int s = info->glyphs[i].pos.x;
+ int e = s + d6_to_int(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 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(ass_renderer_t* render_priv, double sz)
+static void change_font_size(ass_renderer_t *render_priv, double sz)
{
- double size = sz * render_priv->font_scale;
+ double size = sz * render_priv->font_scale;
- if (size < 1)
- size = 1;
- else if (size > render_priv->height * 2)
- size = render_priv->height * 2;
+ if (size < 1)
+ size = 1;
+ else if (size > render_priv->height * 2)
+ size = render_priv->height * 2;
- ass_font_set_size(render_priv->state.font, size);
+ ass_font_set_size(render_priv->state.font, size);
- render_priv->state.font_size = sz;
+ render_priv->state.font_size = sz;
}
/**
* \brief Change current font, using setting from render_priv->state.
*/
-static void update_font(ass_renderer_t* render_priv)
+static void update_font(ass_renderer_t *render_priv)
{
- unsigned val;
- ass_font_desc_t desc;
- desc.family = strdup(render_priv->state.family);
- desc.treat_family_as_pattern = render_priv->state.treat_family_as_pattern;
-
- val = render_priv->state.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_priv->state.italic;
- if (val == 0) val = 0; // normal
- else if (val == 1) val = 110; //italic
- desc.italic = val;
-
- render_priv->state.font = ass_font_new(render_priv->cache.font_cache, render_priv->library, render_priv->ftlibrary, render_priv->fontconfig_priv, &desc);
- free(desc.family);
-
- if (render_priv->state.font)
- change_font_size(render_priv, render_priv->state.font_size);
+ unsigned val;
+ ass_font_desc_t desc;
+ desc.family = strdup(render_priv->state.family);
+ desc.treat_family_as_pattern =
+ render_priv->state.treat_family_as_pattern;
+
+ val = render_priv->state.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_priv->state.italic;
+ if (val == 0)
+ val = 0; // normal
+ else if (val == 1)
+ val = 110; //italic
+ desc.italic = val;
+
+ render_priv->state.font =
+ ass_font_new(render_priv->cache.font_cache, render_priv->library,
+ render_priv->ftlibrary, render_priv->fontconfig_priv,
+ &desc);
+ free(desc.family);
+
+ if (render_priv->state.font)
+ change_font_size(render_priv, render_priv->state.font_size);
}
/**
* \brief Change border width
* negative value resets border to style value
*/
-static void change_border(ass_renderer_t* render_priv, double border)
+static void change_border(ass_renderer_t *render_priv, double border)
{
- int b;
- if (!render_priv->state.font) return;
-
- if (border < 0) {
- if (render_priv->state.style->BorderStyle == 1)
- border = render_priv->state.style->Outline;
- else
- border = 1.;
- }
- render_priv->state.border = border;
-
- b = 64 * border * render_priv->border_scale;
- if (b > 0) {
- if (!render_priv->state.stroker) {
- int error;
+ int b;
+ if (!render_priv->state.font)
+ return;
+
+ if (border < 0) {
+ if (render_priv->state.style->BorderStyle == 1)
+ border = render_priv->state.style->Outline;
+ else
+ border = 1.;
+ }
+ render_priv->state.border = border;
+
+ b = 64 * border * render_priv->border_scale;
+ if (b > 0) {
+ if (!render_priv->state.stroker) {
+ int error;
#if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1))
- error = FT_Stroker_New( render_priv->ftlibrary, &render_priv->state.stroker );
-#else // < 2.2
- error = FT_Stroker_New( render_priv->state.font->faces[0]->memory, &render_priv->state.stroker );
+ error =
+ FT_Stroker_New(render_priv->ftlibrary,
+ &render_priv->state.stroker);
+#else // < 2.2
+ error =
+ FT_Stroker_New(render_priv->state.font->faces[0]->
+ memory, &render_priv->state.stroker);
#endif
- if (error) {
- mp_msg(MSGT_ASS, MSGL_V, "failed to get stroker\n");
- render_priv->state.stroker = 0;
- }
- }
- if (render_priv->state.stroker)
- FT_Stroker_Set( render_priv->state.stroker, b,
- FT_STROKER_LINECAP_ROUND,
- FT_STROKER_LINEJOIN_ROUND,
- 0 );
- } else {
- FT_Stroker_Done(render_priv->state.stroker);
- render_priv->state.stroker = 0;
- }
+ if (error) {
+ mp_msg(MSGT_ASS, MSGL_V, "failed to get stroker\n");
+ render_priv->state.stroker = 0;
+ }
+ }
+ if (render_priv->state.stroker)
+ FT_Stroker_Set(render_priv->state.stroker, b,
+ FT_STROKER_LINECAP_ROUND,
+ FT_STROKER_LINEJOIN_ROUND, 0);
+ } else {
+ FT_Stroker_Done(render_priv->state.stroker);
+ render_priv->state.stroker = 0;
+ }
}
#define _r(c) ((c)>>24)
* \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)
+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);
+ (*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)
+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);
+ *var =
+ (_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) +
+ (_a(*var) * (1 - pwr) + _a(new) * pwr);
}
/**
*/
static uint32_t mult_alpha(uint32_t a, uint32_t b)
{
- return 0xFF - (0xFF - a) * (0xFF - b) / 0xFF;
+ 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)
+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;
+ 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(ass_renderer_t*);
+static void reset_render_context(ass_renderer_t *);
/**
* \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(ass_renderer_t* render_priv, char* p, double pwr) {
+static char *parse_tag(ass_renderer_t *render_priv, 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 = render_priv->state.blur * (1-pwr) + val*pwr;
- val = (val < 0) ? 0 : val;
- val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
- render_priv->state.blur = val;
- } else
- render_priv->state.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_priv->state.scale_x = render_priv->state.scale_x * ( 1 - pwr) + val * pwr;
- } else
- render_priv->state.scale_x = render_priv->state.style->ScaleX;
- } else if (tp == 'y') {
- if (mystrtod(&p, &val)) {
- val /= 100;
- render_priv->state.scale_y = render_priv->state.scale_y * ( 1 - pwr) + val * pwr;
- } else
- render_priv->state.scale_y = render_priv->state.style->ScaleY;
- }
- } else if (mystrcmp(&p, "fsp")) {
- double val;
- if (mystrtod(&p, &val))
- render_priv->state.hspacing = render_priv->state.hspacing * ( 1 - pwr ) + val * pwr;
- else
- render_priv->state.hspacing = render_priv->state.style->Spacing;
- } else if (mystrcmp(&p, "fs")) {
- double val;
- if (mystrtod(&p, &val))
- val = render_priv->state.font_size * ( 1 - pwr ) + val * pwr;
- else
- val = render_priv->state.style->FontSize;
- if (render_priv->state.font)
- change_font_size(render_priv, val);
- } else if (mystrcmp(&p, "bord")) {
- double val;
- if (mystrtod(&p, &val))
- val = render_priv->state.border * ( 1 - pwr ) + val * pwr;
- else
- val = -1.; // reset to default
- change_border(render_priv, 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_priv->state.event->Duration;
- mp_msg(MSGT_ASS, MSGL_DBG2, "movement: (%f, %f) -> (%f, %f)\n", x1, y1, x2, y2);
- }
- skip(')');
- delta_t = t2 - t1;
- t = render_priv->time - render_priv->state.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_priv->state.evt_type != EVENT_POSITIONED) {
- render_priv->state.pos_x = x;
- render_priv->state.pos_y = y;
- render_priv->state.detect_collisions = 0;
- render_priv->state.evt_type = EVENT_POSITIONED;
- }
- } else if (mystrcmp(&p, "frx")) {
- double val;
- if (mystrtod(&p, &val)) {
- val *= M_PI / 180;
- render_priv->state.frx = val * pwr + render_priv->state.frx * (1-pwr);
- } else
- render_priv->state.frx = 0.;
- } else if (mystrcmp(&p, "fry")) {
- double val;
- if (mystrtod(&p, &val)) {
- val *= M_PI / 180;
- render_priv->state.fry = val * pwr + render_priv->state.fry * (1-pwr);
- } else
- render_priv->state.fry = 0.;
- } else if (mystrcmp(&p, "frz") || mystrcmp(&p, "fr")) {
- double val;
- if (mystrtod(&p, &val)) {
- val *= M_PI / 180;
- render_priv->state.frz = val * pwr + render_priv->state.frz * (1-pwr);
- } else
- render_priv->state.frz = M_PI * render_priv->state.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_priv->state.style->FontName);
- if (render_priv->state.family)
- free(render_priv->state.family);
- render_priv->state.family = family;
- update_font(render_priv);
- } 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_priv->state.c[i], a, pwr);
- } else {
- change_alpha(&render_priv->state.c[0], render_priv->state.style->PrimaryColour, pwr);
- change_alpha(&render_priv->state.c[1], render_priv->state.style->SecondaryColour, pwr);
- change_alpha(&render_priv->state.c[2], render_priv->state.style->OutlineColour, pwr);
- change_alpha(&render_priv->state.c[3], render_priv->state.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_priv->state.alignment = val;
- } else
- render_priv->state.alignment = render_priv->state.style->Alignment;
- } else if (mystrcmp(&p, "a")) {
- int val;
- if (mystrtoi(&p, &val) && val)
- render_priv->state.alignment = val;
- else
- render_priv->state.alignment = render_priv->state.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_priv->state.evt_type == EVENT_POSITIONED) {
- mp_msg(MSGT_ASS, MSGL_V, "Subtitle has a new \\pos "
- "after \\move or \\pos, ignoring\n");
- } else {
- render_priv->state.evt_type = EVENT_POSITIONED;
- render_priv->state.detect_collisions = 0;
- render_priv->state.pos_x = v1;
- render_priv->state.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_priv->state.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_priv->state.fade = interpolate_alpha(render_priv->time - render_priv->state.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_priv->state.evt_type = EVENT_POSITIONED;
- if (!render_priv->state.have_origin) {
- render_priv->state.org_x = v1;
- render_priv->state.org_y = v2;
- render_priv->state.have_origin = 1;
- render_priv->state.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_priv->state.event->Duration; v3 = v[0];
- } else { // cnt == 0
- v1 = 0; v2 = render_priv->state.event->Duration; v3 = 1.;
- }
- render_priv->state.detect_collisions = 0;
- t1 = v1;
- t2 = v2;
- delta_t = v2 - v1;
- if (v3 < 0.)
- v3 = 0.;
- t = render_priv->time - render_priv->state.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(render_priv, 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_priv->state.clip_x0 = render_priv->state.clip_x0 * (1-pwr) + x0 * pwr;
- render_priv->state.clip_x1 = render_priv->state.clip_x1 * (1-pwr) + x1 * pwr;
- render_priv->state.clip_y0 = render_priv->state.clip_y0 * (1-pwr) + y0 * pwr;
- render_priv->state.clip_y1 = render_priv->state.clip_y1 * (1-pwr) + y1 * pwr;
- } else {
- render_priv->state.clip_x0 = 0;
- render_priv->state.clip_y0 = 0;
- render_priv->state.clip_x1 = render_priv->track->PlayResX;
- render_priv->state.clip_y1 = render_priv->track->PlayResY;
- }
- } else if (mystrcmp(&p, "c")) {
- uint32_t val;
- if (!strtocolor(&p, &val))
- val = render_priv->state.style->PrimaryColour;
- mp_msg(MSGT_ASS, MSGL_DBG2, "color: %X\n", val);
- change_color(&render_priv->state.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_priv->state.style->PrimaryColour; break;
- case '2': val = render_priv->state.style->SecondaryColour; break;
- case '3': val = render_priv->state.style->OutlineColour; break;
- case '4': val = render_priv->state.style->BackColour; break;
- default : val = 0; break; // impossible due to assert; avoid compilation warning
- }
- switch (cmd) {
- case 'c': change_color(render_priv->state.c + cidx, val, pwr); break;
- case 'a': change_alpha(render_priv->state.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_priv->state.c[cidx]);
- } else if (mystrcmp(&p, "r")) {
- reset_render_context(render_priv);
- } 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_priv->state.be = val;
- } else
- render_priv->state.be = 0;
- } else if (mystrcmp(&p, "b")) {
- int b;
- if (mystrtoi(&p, &b)) {
- if (pwr >= .5)
- render_priv->state.bold = b;
- } else
- render_priv->state.bold = render_priv->state.style->Bold;
- update_font(render_priv);
- } else if (mystrcmp(&p, "i")) {
- int i;
- if (mystrtoi(&p, &i)) {
- if (pwr >= .5)
- render_priv->state.italic = i;
- } else
- render_priv->state.italic = render_priv->state.style->Italic;
- update_font(render_priv);
- } else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) {
- int val = 0;
- mystrtoi(&p, &val);
- render_priv->state.effect_type = EF_KARAOKE_KF;
- if (render_priv->state.effect_timing)
- render_priv->state.effect_skip_timing += render_priv->state.effect_timing;
- render_priv->state.effect_timing = val * 10;
- } else if (mystrcmp(&p, "ko")) {
- int val = 0;
- mystrtoi(&p, &val);
- render_priv->state.effect_type = EF_KARAOKE_KO;
- if (render_priv->state.effect_timing)
- render_priv->state.effect_skip_timing += render_priv->state.effect_timing;
- render_priv->state.effect_timing = val * 10;
- } else if (mystrcmp(&p, "k")) {
- int val = 0;
- mystrtoi(&p, &val);
- render_priv->state.effect_type = EF_KARAOKE;
- if (render_priv->state.effect_timing)
- render_priv->state.effect_skip_timing += render_priv->state.effect_timing;
- render_priv->state.effect_timing = val * 10;
- } else if (mystrcmp(&p, "shad")) {
- int val;
- if (mystrtoi(&p, &val))
- render_priv->state.shadow = val;
- else
- render_priv->state.shadow = render_priv->state.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_priv->state.drawing_mode = !!val;
- }
-
- 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 = render_priv->state.blur * (1 - pwr) + val * pwr;
+ val = (val < 0) ? 0 : val;
+ val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
+ render_priv->state.blur = val;
+ } else
+ render_priv->state.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_priv->state.scale_x =
+ render_priv->state.scale_x * (1 - pwr) + val * pwr;
+ } else
+ render_priv->state.scale_x =
+ render_priv->state.style->ScaleX;
+ } else if (tp == 'y') {
+ if (mystrtod(&p, &val)) {
+ val /= 100;
+ render_priv->state.scale_y =
+ render_priv->state.scale_y * (1 - pwr) + val * pwr;
+ } else
+ render_priv->state.scale_y =
+ render_priv->state.style->ScaleY;
+ }
+ } else if (mystrcmp(&p, "fsp")) {
+ double val;
+ if (mystrtod(&p, &val))
+ render_priv->state.hspacing =
+ render_priv->state.hspacing * (1 - pwr) + val * pwr;
+ else
+ render_priv->state.hspacing = render_priv->state.style->Spacing;
+ } else if (mystrcmp(&p, "fs")) {
+ double val;
+ if (mystrtod(&p, &val))
+ val = render_priv->state.font_size * (1 - pwr) + val * pwr;
+ else
+ val = render_priv->state.style->FontSize;
+ if (render_priv->state.font)
+ change_font_size(render_priv, val);
+ } else if (mystrcmp(&p, "bord")) {
+ double val;
+ if (mystrtod(&p, &val))
+ val = render_priv->state.border * (1 - pwr) + val * pwr;
+ else
+ val = -1.; // reset to default
+ change_border(render_priv, 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_priv->state.event->Duration;
+ mp_msg(MSGT_ASS, MSGL_DBG2,
+ "movement: (%f, %f) -> (%f, %f)\n", x1, y1, x2, y2);
+ }
+ skip(')');
+ delta_t = t2 - t1;
+ t = render_priv->time - render_priv->state.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_priv->state.evt_type != EVENT_POSITIONED) {
+ render_priv->state.pos_x = x;
+ render_priv->state.pos_y = y;
+ render_priv->state.detect_collisions = 0;
+ render_priv->state.evt_type = EVENT_POSITIONED;
+ }
+ } else if (mystrcmp(&p, "frx")) {
+ double val;
+ if (mystrtod(&p, &val)) {
+ val *= M_PI / 180;
+ render_priv->state.frx =
+ val * pwr + render_priv->state.frx * (1 - pwr);
+ } else
+ render_priv->state.frx = 0.;
+ } else if (mystrcmp(&p, "fry")) {
+ double val;
+ if (mystrtod(&p, &val)) {
+ val *= M_PI / 180;
+ render_priv->state.fry =
+ val * pwr + render_priv->state.fry * (1 - pwr);
+ } else
+ render_priv->state.fry = 0.;
+ } else if (mystrcmp(&p, "frz") || mystrcmp(&p, "fr")) {
+ double val;
+ if (mystrtod(&p, &val)) {
+ val *= M_PI / 180;
+ render_priv->state.frz =
+ val * pwr + render_priv->state.frz * (1 - pwr);
+ } else
+ render_priv->state.frz =
+ M_PI * render_priv->state.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_priv->state.style->FontName);
+ if (render_priv->state.family)
+ free(render_priv->state.family);
+ render_priv->state.family = family;
+ update_font(render_priv);
+ } 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_priv->state.c[i], a, pwr);
+ } else {
+ change_alpha(&render_priv->state.c[0],
+ render_priv->state.style->PrimaryColour, pwr);
+ change_alpha(&render_priv->state.c[1],
+ render_priv->state.style->SecondaryColour, pwr);
+ change_alpha(&render_priv->state.c[2],
+ render_priv->state.style->OutlineColour, pwr);
+ change_alpha(&render_priv->state.c[3],
+ render_priv->state.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_priv->state.alignment = val;
+ } else
+ render_priv->state.alignment =
+ render_priv->state.style->Alignment;
+ } else if (mystrcmp(&p, "a")) {
+ int val;
+ if (mystrtoi(&p, &val) && val)
+ render_priv->state.alignment = val;
+ else
+ render_priv->state.alignment =
+ render_priv->state.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_priv->state.evt_type == EVENT_POSITIONED) {
+ mp_msg(MSGT_ASS, MSGL_V, "Subtitle has a new \\pos "
+ "after \\move or \\pos, ignoring\n");
+ } else {
+ render_priv->state.evt_type = EVENT_POSITIONED;
+ render_priv->state.detect_collisions = 0;
+ render_priv->state.pos_x = v1;
+ render_priv->state.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_priv->state.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_priv->state.fade =
+ interpolate_alpha(render_priv->time -
+ render_priv->state.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_priv->state.evt_type = EVENT_POSITIONED;
+ if (!render_priv->state.have_origin) {
+ render_priv->state.org_x = v1;
+ render_priv->state.org_y = v2;
+ render_priv->state.have_origin = 1;
+ render_priv->state.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_priv->state.event->Duration;
+ v3 = v[0];
+ } else { // cnt == 0
+ v1 = 0;
+ v2 = render_priv->state.event->Duration;
+ v3 = 1.;
+ }
+ render_priv->state.detect_collisions = 0;
+ t1 = v1;
+ t2 = v2;
+ delta_t = v2 - v1;
+ if (v3 < 0.)
+ v3 = 0.;
+ t = render_priv->time - render_priv->state.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(render_priv, 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_priv->state.clip_x0 =
+ render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
+ render_priv->state.clip_x1 =
+ render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
+ render_priv->state.clip_y0 =
+ render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
+ render_priv->state.clip_y1 =
+ render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
+ } else {
+ render_priv->state.clip_x0 = 0;
+ render_priv->state.clip_y0 = 0;
+ render_priv->state.clip_x1 = render_priv->track->PlayResX;
+ render_priv->state.clip_y1 = render_priv->track->PlayResY;
+ }
+ } else if (mystrcmp(&p, "c")) {
+ uint32_t val;
+ if (!strtocolor(&p, &val))
+ val = render_priv->state.style->PrimaryColour;
+ mp_msg(MSGT_ASS, MSGL_DBG2, "color: %X\n", val);
+ change_color(&render_priv->state.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_priv->state.style->PrimaryColour;
+ break;
+ case '2':
+ val = render_priv->state.style->SecondaryColour;
+ break;
+ case '3':
+ val = render_priv->state.style->OutlineColour;
+ break;
+ case '4':
+ val = render_priv->state.style->BackColour;
+ break;
+ default:
+ val = 0;
+ break; // impossible due to assert; avoid compilation warning
+ }
+ switch (cmd) {
+ case 'c':
+ change_color(render_priv->state.c + cidx, val, pwr);
+ break;
+ case 'a':
+ change_alpha(render_priv->state.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_priv->state.c[cidx]);
+ } else if (mystrcmp(&p, "r")) {
+ reset_render_context(render_priv);
+ } 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_priv->state.be = val;
+ } else
+ render_priv->state.be = 0;
+ } else if (mystrcmp(&p, "b")) {
+ int b;
+ if (mystrtoi(&p, &b)) {
+ if (pwr >= .5)
+ render_priv->state.bold = b;
+ } else
+ render_priv->state.bold = render_priv->state.style->Bold;
+ update_font(render_priv);
+ } else if (mystrcmp(&p, "i")) {
+ int i;
+ if (mystrtoi(&p, &i)) {
+ if (pwr >= .5)
+ render_priv->state.italic = i;
+ } else
+ render_priv->state.italic = render_priv->state.style->Italic;
+ update_font(render_priv);
+ } else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) {
+ int val = 0;
+ mystrtoi(&p, &val);
+ render_priv->state.effect_type = EF_KARAOKE_KF;
+ if (render_priv->state.effect_timing)
+ render_priv->state.effect_skip_timing +=
+ render_priv->state.effect_timing;
+ render_priv->state.effect_timing = val * 10;
+ } else if (mystrcmp(&p, "ko")) {
+ int val = 0;
+ mystrtoi(&p, &val);
+ render_priv->state.effect_type = EF_KARAOKE_KO;
+ if (render_priv->state.effect_timing)
+ render_priv->state.effect_skip_timing +=
+ render_priv->state.effect_timing;
+ render_priv->state.effect_timing = val * 10;
+ } else if (mystrcmp(&p, "k")) {
+ int val = 0;
+ mystrtoi(&p, &val);
+ render_priv->state.effect_type = EF_KARAOKE;
+ if (render_priv->state.effect_timing)
+ render_priv->state.effect_skip_timing +=
+ render_priv->state.effect_timing;
+ render_priv->state.effect_timing = val * 10;
+ } else if (mystrcmp(&p, "shad")) {
+ int val;
+ if (mystrtoi(&p, &val))
+ render_priv->state.shadow = val;
+ else
+ render_priv->state.shadow = render_priv->state.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_priv->state.drawing_mode = !!val;
+ }
+
+ return p;
#undef skip
#undef skip_to
* \return ucs4 code of the next char
* On return str points to the unparsed part of the string
*/
-static unsigned get_next_char(ass_renderer_t* render_priv, char** str)
+static unsigned get_next_char(ass_renderer_t *render_priv, char **str)
{
- char* p = *str;
- unsigned chr;
- if (*p == '{') { // '\0' goes here
- p++;
- while (1) {
- p = parse_tag(render_priv, 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') && (render_priv->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((char **)&p);
- *str = p;
- return chr;
+ char *p = *str;
+ unsigned chr;
+ if (*p == '{') { // '\0' goes here
+ p++;
+ while (1) {
+ p = parse_tag(render_priv, 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')
+ && (render_priv->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((char **) &p);
+ *str = p;
+ return chr;
}
-static void apply_transition_effects(ass_renderer_t* render_priv, ass_event_t* event)
+static void
+apply_transition_effects(ass_renderer_t *render_priv, 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_priv->state.scroll_direction = SCROLL_RL;
- else // left-to-right
- render_priv->state.scroll_direction = SCROLL_LR;
-
- delay = v[0];
- if (delay == 0) delay = 1; // ?
- render_priv->state.scroll_shift = (render_priv->time - render_priv->state.event->Start) / delay;
- render_priv->state.evt_type = EVENT_HSCROLL;
- return;
- }
-
- if (strncmp(event->Effect, "Scroll up;", 10) == 0) {
- render_priv->state.scroll_direction = SCROLL_BT;
- } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) {
- render_priv->state.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_priv->state.scroll_shift = (render_priv->time - render_priv->state.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 = render_priv->track->PlayResY; // y0=y1=0 means fullscreen scrolling
- render_priv->state.clip_y0 = y0;
- render_priv->state.clip_y1 = y1;
- render_priv->state.evt_type = EVENT_VSCROLL;
- render_priv->state.detect_collisions = 0;
- }
+ 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_priv->state.scroll_direction = SCROLL_RL;
+ else // left-to-right
+ render_priv->state.scroll_direction = SCROLL_LR;
+
+ delay = v[0];
+ if (delay == 0)
+ delay = 1; // ?
+ render_priv->state.scroll_shift =
+ (render_priv->time - render_priv->state.event->Start) / delay;
+ render_priv->state.evt_type = EVENT_HSCROLL;
+ return;
+ }
+
+ if (strncmp(event->Effect, "Scroll up;", 10) == 0) {
+ render_priv->state.scroll_direction = SCROLL_BT;
+ } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) {
+ render_priv->state.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_priv->state.scroll_shift =
+ (render_priv->time - render_priv->state.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 = render_priv->track->PlayResY; // y0=y1=0 means fullscreen scrolling
+ render_priv->state.clip_y0 = y0;
+ render_priv->state.clip_y1 = y1;
+ render_priv->state.evt_type = EVENT_VSCROLL;
+ render_priv->state.detect_collisions = 0;
+ }
}
* \brief partially reset render_context to style values
* Works like {\r}: resets some style overrides
*/
-static void reset_render_context(ass_renderer_t* render_priv)
+static void reset_render_context(ass_renderer_t *render_priv)
{
- render_priv->state.c[0] = render_priv->state.style->PrimaryColour;
- render_priv->state.c[1] = render_priv->state.style->SecondaryColour;
- render_priv->state.c[2] = render_priv->state.style->OutlineColour;
- render_priv->state.c[3] = render_priv->state.style->BackColour;
- render_priv->state.font_size = render_priv->state.style->FontSize;
-
- if (render_priv->state.family)
- free(render_priv->state.family);
- render_priv->state.family = strdup(render_priv->state.style->FontName);
- render_priv->state.treat_family_as_pattern = render_priv->state.style->treat_fontname_as_pattern;
- render_priv->state.bold = render_priv->state.style->Bold;
- render_priv->state.italic = render_priv->state.style->Italic;
- update_font(render_priv);
-
- change_border(render_priv, -1.);
- render_priv->state.scale_x = render_priv->state.style->ScaleX;
- render_priv->state.scale_y = render_priv->state.style->ScaleY;
- render_priv->state.hspacing = render_priv->state.style->Spacing;
- render_priv->state.be = 0;
- render_priv->state.blur = 0.0;
- render_priv->state.shadow = render_priv->state.style->Shadow;
- render_priv->state.frx = render_priv->state.fry = 0.;
- render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.;
-
- // FIXME: does not reset unsupported attributes.
+ render_priv->state.c[0] = render_priv->state.style->PrimaryColour;
+ render_priv->state.c[1] = render_priv->state.style->SecondaryColour;
+ render_priv->state.c[2] = render_priv->state.style->OutlineColour;
+ render_priv->state.c[3] = render_priv->state.style->BackColour;
+ render_priv->state.font_size = render_priv->state.style->FontSize;
+
+ if (render_priv->state.family)
+ free(render_priv->state.family);
+ render_priv->state.family = strdup(render_priv->state.style->FontName);
+ render_priv->state.treat_family_as_pattern =
+ render_priv->state.style->treat_fontname_as_pattern;
+ render_priv->state.bold = render_priv->state.style->Bold;
+ render_priv->state.italic = render_priv->state.style->Italic;
+ update_font(render_priv);
+
+ change_border(render_priv, -1.);
+ render_priv->state.scale_x = render_priv->state.style->ScaleX;
+ render_priv->state.scale_y = render_priv->state.style->ScaleY;
+ render_priv->state.hspacing = render_priv->state.style->Spacing;
+ render_priv->state.be = 0;
+ render_priv->state.blur = 0.0;
+ render_priv->state.shadow = render_priv->state.style->Shadow;
+ render_priv->state.frx = render_priv->state.fry = 0.;
+ render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.;
+
+ // FIXME: does not reset unsupported attributes.
}
/**
* \brief Start new event. Reset render_priv->state.
*/
-static void init_render_context(ass_renderer_t* render_priv, ass_event_t* event)
+static void
+init_render_context(ass_renderer_t *render_priv, ass_event_t *event)
{
- render_priv->state.event = event;
- render_priv->state.style = render_priv->track->styles + event->Style;
-
- reset_render_context(render_priv);
-
- render_priv->state.evt_type = EVENT_NORMAL;
- render_priv->state.alignment = render_priv->state.style->Alignment;
- render_priv->state.pos_x = 0;
- render_priv->state.pos_y = 0;
- render_priv->state.org_x = 0;
- render_priv->state.org_y = 0;
- render_priv->state.have_origin = 0;
- render_priv->state.clip_x0 = 0;
- render_priv->state.clip_y0 = 0;
- render_priv->state.clip_x1 = render_priv->track->PlayResX;
- render_priv->state.clip_y1 = render_priv->track->PlayResY;
- render_priv->state.detect_collisions = 1;
- render_priv->state.fade = 0;
- render_priv->state.drawing_mode = 0;
- render_priv->state.effect_type = EF_NONE;
- render_priv->state.effect_timing = 0;
- render_priv->state.effect_skip_timing = 0;
-
- apply_transition_effects(render_priv, event);
+ render_priv->state.event = event;
+ render_priv->state.style = render_priv->track->styles + event->Style;
+
+ reset_render_context(render_priv);
+
+ render_priv->state.evt_type = EVENT_NORMAL;
+ render_priv->state.alignment = render_priv->state.style->Alignment;
+ render_priv->state.pos_x = 0;
+ render_priv->state.pos_y = 0;
+ render_priv->state.org_x = 0;
+ render_priv->state.org_y = 0;
+ render_priv->state.have_origin = 0;
+ render_priv->state.clip_x0 = 0;
+ render_priv->state.clip_y0 = 0;
+ render_priv->state.clip_x1 = render_priv->track->PlayResX;
+ render_priv->state.clip_y1 = render_priv->track->PlayResY;
+ render_priv->state.detect_collisions = 1;
+ render_priv->state.fade = 0;
+ render_priv->state.drawing_mode = 0;
+ render_priv->state.effect_type = EF_NONE;
+ render_priv->state.effect_timing = 0;
+ render_priv->state.effect_skip_timing = 0;
+
+ apply_transition_effects(render_priv, event);
}
static void free_render_context(void)
}
// Calculate the cbox of a series of points
-static void get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end) {
+static void
+get_contour_cbox(FT_BBox * box, FT_Vector * points, int start, int end)
+{
box->xMin = box->yMin = INT_MAX;
box->xMax = box->yMax = INT_MIN;
int i;
- for (i=start; i<end; i++) {
- box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin;
- box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax;
- box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin;
- box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax;
+ for (i = start; i < end; i++) {
+ box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin;
+ box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax;
+ box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin;
+ box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax;
}
}
* \brief Fix-up stroker result for huge borders by removing the contours from
* the outline that are harmful.
*/
-static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border) {
+static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border)
+{
int nc = glyph->outline.n_contours;
int begin, stop;
char modified = 0;
int i, j;
// Create a list of cboxes of the contours
- for (i=0; i < nc; i++) {
+ for (i = 0; i < nc; i++) {
start = end + 1;
end = glyph->outline.contours[i];
get_contour_cbox(&boxes[i], glyph->outline.points, start, end);
// b) contour's height or width is smaller than the border*2
// the contour can be safely removed.
valid_cont = calloc(1, nc);
- for (i=0; i < nc; i++) {
+ for (i = 0; i < nc; i++) {
valid_cont[i] = 1;
- for (j=0; j < nc; j++) {
- if (i == j) continue;
+ for (j = 0; j < nc; j++) {
+ if (i == j)
+ continue;
if (boxes[i].xMin >= boxes[j].xMin &&
boxes[i].xMax <= boxes[j].xMax &&
boxes[i].yMin >= boxes[j].yMin &&
boxes[i].yMax <= boxes[j].yMax) {
- int width = boxes[i].xMax - boxes[i].xMin;
- int height = boxes[i].yMax - boxes[i].yMin;
- if (width < border*2 || height < border*2) {
- valid_cont[i] = 0;
- modified = 1;
- break;
- }
+ int width = boxes[i].xMax - boxes[i].xMin;
+ int height = boxes[i].yMax - boxes[i].yMin;
+ if (width < border * 2 || height < border * 2) {
+ valid_cont[i] = 0;
+ modified = 1;
+ break;
}
+ }
}
}
// Zero-out contours that can be removed; much simpler than copying
if (modified) {
- for (i=0; i<nc; i++) {
- if (valid_cont[i]) continue;
- begin = (i == 0) ? 0 : glyph->outline.contours[i-1]+1;
+ for (i = 0; i < nc; i++) {
+ if (valid_cont[i])
+ continue;
+ begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1;
stop = glyph->outline.contours[i];
- for (j=begin; j<=stop; j++) {
+ for (j = begin; j <= stop; j++) {
glyph->outline.points[j].x = 0;
glyph->outline.points[j].y = 0;
glyph->outline.tags[j] = 0;
* and add them to cache.
* The glyphs are returned in info->glyph and info->outline_glyph
*/
-static void get_outline_glyph(ass_renderer_t* render_priv, int symbol, glyph_info_t* info, FT_Vector* advance)
+static void
+get_outline_glyph(ass_renderer_t *render_priv, 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_priv->state.font;
- key.size = render_priv->state.font_size;
- key.ch = symbol;
- key.scale_x = (render_priv->state.scale_x * 0xFFFF);
- key.scale_y = (render_priv->state.scale_y * 0xFFFF);
- key.advance = *advance;
- key.bold = render_priv->state.bold;
- key.italic = render_priv->state.italic;
- key.outline = render_priv->state.border * 0xFFFF;
-
- memset(info, 0, sizeof(glyph_info_t));
-
- val = cache_find_glyph(render_priv->cache.glyph_cache, &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(render_priv->fontconfig_priv, render_priv->state.font, symbol, render_priv->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_priv->state.stroker) {
- FT_Glyph_Copy(info->glyph, &info->outline_glyph);
- fix_freetype_stroker((FT_OutlineGlyph) info->outline_glyph, double_to_d6(render_priv->state.border * render_priv->border_scale));
- error = FT_Glyph_StrokeBorder( &(info->outline_glyph), render_priv->state.stroker, 0 , 1);
- 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(render_priv->cache.glyph_cache, &key, &v);
- }
+ int error;
+ glyph_hash_val_t *val;
+ glyph_hash_key_t key;
+ memset(&key, 0, sizeof(key));
+ key.font = render_priv->state.font;
+ key.size = render_priv->state.font_size;
+ key.ch = symbol;
+ key.scale_x = (render_priv->state.scale_x * 0xFFFF);
+ key.scale_y = (render_priv->state.scale_y * 0xFFFF);
+ key.advance = *advance;
+ key.bold = render_priv->state.bold;
+ key.italic = render_priv->state.italic;
+ key.outline = render_priv->state.border * 0xFFFF;
+
+ memset(info, 0, sizeof(glyph_info_t));
+
+ val = cache_find_glyph(render_priv->cache.glyph_cache, &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(render_priv->fontconfig_priv,
+ render_priv->state.font, symbol,
+ render_priv->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_priv->state.stroker) {
+ FT_Glyph_Copy(info->glyph, &info->outline_glyph);
+ fix_freetype_stroker((FT_OutlineGlyph) info->outline_glyph,
+ double_to_d6(render_priv->state.
+ border *
+ render_priv->border_scale));
+ error =
+ FT_Glyph_StrokeBorder(&(info->outline_glyph),
+ render_priv->state.stroker, 0, 1);
+ 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(render_priv->cache.glyph_cache, &key, &v);
+ }
}
-static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, double frx, double fry, double frz);
+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
* 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(ass_renderer_t* render_priv, glyph_info_t* info)
+static void
+get_bitmap_glyph(ass_renderer_t *render_priv, glyph_info_t *info)
{
- bitmap_hash_val_t* val;
- bitmap_hash_key_t* key = &info->hash_key;
-
- val = cache_find_bitmap(render_priv->cache.bitmap_cache, 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(render_priv->synth_priv,
- info->glyph, info->outline_glyph,
- &info->bm, &info->bm_o,
- &info->bm_s, info->be, info->blur * render_priv->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(render_priv->cache.bitmap_cache, &(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);
+ bitmap_hash_val_t *val;
+ bitmap_hash_key_t *key = &info->hash_key;
+
+ val = cache_find_bitmap(render_priv->cache.bitmap_cache, 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(render_priv->synth_priv,
+ info->glyph, info->outline_glyph,
+ &info->bm, &info->bm_o,
+ &info->bm_s, info->be,
+ info->blur * render_priv->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(render_priv->cache.bitmap_cache,
+ &(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);
}
/**
* lines[].asc
* lines[].desc
*/
-static void measure_text(ass_renderer_t* render_priv)
+static void measure_text(ass_renderer_t *render_priv)
{
- text_info_t* text_info = &render_priv->text_info;
- 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(render_priv->settings.line_spacing);
+ text_info_t *text_info = &render_priv->text_info;
+ 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(render_priv->settings.line_spacing);
}
/**
* the difference in lengths between this two lines.
* The result may not be optimal, but usually is good enough.
*/
-static void wrap_lines_smart(ass_renderer_t* render_priv, int max_text_width)
+static void
+wrap_lines_smart(ass_renderer_t *render_priv, 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;
- text_info_t* text_info = &render_priv->text_info;
-
- 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) && (render_priv->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 >= text_info->max_lines) {
- // Raise maximum number of lines
- text_info->max_lines *= 2;
- text_info->lines = realloc(text_info->lines,
- sizeof(line_info_t) * text_info->max_lines);
- }
- 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--;
- }
+ 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;
+ text_info_t *text_info = &render_priv->text_info;
+
+ 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)
+ && (render_priv->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 >= text_info->max_lines) {
+ // Raise maximum number of lines
+ text_info->max_lines *= 2;
+ text_info->lines = realloc(text_info->lines,
+ sizeof(line_info_t) *
+ text_info->max_lines);
+ }
+ 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);
+ 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(render_priv);
-
- 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(render_priv->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;
- }
+ measure_text(render_priv);
+
+ 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(render_priv->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;
+ }
}
/**
* 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(ass_renderer_t* render_priv)
+static void process_karaoke_effects(ass_renderer_t *render_priv)
{
- 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 = render_priv->time - render_priv->state.event->Start;
- timing = 0;
- s1 = s2 = 0;
- for (i = 0; i <= render_priv->text_info.length; ++i) {
- cur = render_priv->text_info.glyphs + i;
- if ((i == render_priv->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;
- }
- }
- }
- }
+ 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 = render_priv->time - render_priv->state.event->Start;
+ timing = 0;
+ s1 = s2 = 0;
+ for (i = 0; i <= render_priv->text_info.length; ++i) {
+ cur = render_priv->text_info.glyphs + i;
+ if ((i == render_priv->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;
+ }
+ }
+ }
+ }
}
/**
* \param alignment alignment
* \param bx, by out: base point coordinates
*/
-static void get_base_point(FT_BBox bbox, int alignment, int* bx, int* by)
+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;
- }
+ 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;
+ }
}
/**
* 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; i<outline->n_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;
- }
+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; i < outline->n_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;
+ }
}
/**
* \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)
+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);
- }
+ 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);
+ }
}
* \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_renderer_t* render_priv, ass_event_t* event, event_images_t* event_images)
+static int
+ass_render_event(ass_renderer_t *render_priv, 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;
- ass_settings_t* settings_priv = &render_priv->settings;
- text_info_t* text_info = &render_priv->text_info;
-
- if (event->Style >= render_priv->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(render_priv, 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(render_priv, &p);
- } while (code && render_priv->state.drawing_mode); // skip everything in drawing mode
-
- // face could have been changed in get_next_char
- if (!render_priv->state.font) {
- free_render_context();
- return 1;
- }
-
- if (code == 0)
- break;
-
- if (text_info->length >= text_info->max_glyphs) {
- // Raise maximum number of glyphs
- text_info->max_glyphs *= 2;
- text_info->glyphs = realloc(text_info->glyphs, sizeof(glyph_info_t) * text_info->max_glyphs);
- }
-
- if ( previous && code ) {
- FT_Vector delta;
- delta = ass_font_get_kerning(render_priv->state.font, previous, code);
- pen.x += delta.x * render_priv->state.scale_x;
- pen.y += delta.y * render_priv->state.scale_y;
- }
-
- shift.x = pen.x & SUBPIXEL_MASK;
- shift.y = pen.y & SUBPIXEL_MASK;
-
- if (render_priv->state.evt_type == EVENT_POSITIONED) {
- shift.x += double_to_d6(x2scr_pos(render_priv, render_priv->state.pos_x)) & SUBPIXEL_MASK;
- shift.y -= double_to_d6(y2scr_pos(render_priv, render_priv->state.pos_y)) & SUBPIXEL_MASK;
- }
-
- ass_font_set_transform(render_priv->state.font,
- render_priv->state.scale_x * render_priv->font_scale_x,
- render_priv->state.scale_y,
- &shift );
-
- get_outline_glyph(render_priv, 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_priv->state.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_priv->state.c[i];
- change_alpha(&clr, mult_alpha(_a(clr), render_priv->state.fade), 1.);
- text_info->glyphs[text_info->length].c[i] = clr;
- }
- text_info->glyphs[text_info->length].effect_type = render_priv->state.effect_type;
- text_info->glyphs[text_info->length].effect_timing = render_priv->state.effect_timing;
- text_info->glyphs[text_info->length].effect_skip_timing = render_priv->state.effect_skip_timing;
- text_info->glyphs[text_info->length].be = render_priv->state.be;
- text_info->glyphs[text_info->length].blur = render_priv->state.blur;
- text_info->glyphs[text_info->length].shadow = render_priv->state.shadow;
- text_info->glyphs[text_info->length].frx = render_priv->state.frx;
- text_info->glyphs[text_info->length].fry = render_priv->state.fry;
- text_info->glyphs[text_info->length].frz = render_priv->state.frz;
- ass_font_get_asc_desc(render_priv->state.font, code,
- &text_info->glyphs[text_info->length].asc,
- &text_info->glyphs[text_info->length].desc);
- text_info->glyphs[text_info->length].asc *= render_priv->state.scale_y;
- text_info->glyphs[text_info->length].desc *= render_priv->state.scale_y;
-
- // fill bitmap_hash_key
- text_info->glyphs[text_info->length].hash_key.font = render_priv->state.font;
- text_info->glyphs[text_info->length].hash_key.size = render_priv->state.font_size;
- text_info->glyphs[text_info->length].hash_key.outline = render_priv->state.border * 0xFFFF;
- text_info->glyphs[text_info->length].hash_key.scale_x = render_priv->state.scale_x * 0xFFFF;
- text_info->glyphs[text_info->length].hash_key.scale_y = render_priv->state.scale_y * 0xFFFF;
- text_info->glyphs[text_info->length].hash_key.frx = render_priv->state.frx * 0xFFFF;
- text_info->glyphs[text_info->length].hash_key.fry = render_priv->state.fry * 0xFFFF;
- text_info->glyphs[text_info->length].hash_key.frz = render_priv->state.frz * 0xFFFF;
- text_info->glyphs[text_info->length].hash_key.bold = render_priv->state.bold;
- text_info->glyphs[text_info->length].hash_key.italic = render_priv->state.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_priv->state.be;
- text_info->glyphs[text_info->length].hash_key.blur = render_priv->state.blur;
-
- text_info->length++;
-
- render_priv->state.effect_type = EF_NONE;
- render_priv->state.effect_timing = 0;
- render_priv->state.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(render_priv);
-
- // alignments
- alignment = render_priv->state.alignment;
- halign = alignment & 3;
- valign = alignment & 12;
-
- MarginL = (event->MarginL) ? event->MarginL : render_priv->state.style->MarginL;
- MarginR = (event->MarginR) ? event->MarginR : render_priv->state.style->MarginR;
- MarginV = (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV;
-
- if (render_priv->state.evt_type != EVENT_HSCROLL) {
- int max_text_width;
-
- // calculate max length of a line
- max_text_width = x2scr(render_priv, render_priv->track->PlayResX - MarginR) - x2scr(render_priv, MarginL);
-
- // rearrange text in several lines
- wrap_lines_smart(render_priv, 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_priv->state.evt_type == EVENT_HSCROLL
- measure_text(render_priv);
- }
-
- // determing text bounding box
- compute_string_bbox(text_info, &bbox);
-
- // determine device coordinates for text
-
- // x coordinate for everything except positioned events
- if (render_priv->state.evt_type == EVENT_NORMAL ||
- render_priv->state.evt_type == EVENT_VSCROLL) {
- device_x = x2scr(render_priv, MarginL);
- } else if (render_priv->state.evt_type == EVENT_HSCROLL) {
- if (render_priv->state.scroll_direction == SCROLL_RL)
- device_x = x2scr(render_priv, render_priv->track->PlayResX - render_priv->state.scroll_shift);
- else if (render_priv->state.scroll_direction == SCROLL_LR)
- device_x = x2scr(render_priv, render_priv->state.scroll_shift) - (bbox.xMax - bbox.xMin);
- }
-
- // y coordinate for everything except positioned events
- if (render_priv->state.evt_type == EVENT_NORMAL ||
- render_priv->state.evt_type == EVENT_HSCROLL) {
- if (valign == VALIGN_TOP) { // toptitle
- device_y = y2scr_top(render_priv, MarginV) + d6_to_int(text_info->lines[0].asc);
- } else if (valign == VALIGN_CENTER) { // midtitle
- int scr_y = y2scr(render_priv, render_priv->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(render_priv, render_priv->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_priv->state.evt_type == EVENT_VSCROLL) {
- if (render_priv->state.scroll_direction == SCROLL_TB)
- device_y = y2scr(render_priv, render_priv->state.clip_y0 + render_priv->state.scroll_shift) - (bbox.yMax - bbox.yMin);
- else if (render_priv->state.scroll_direction == SCROLL_BT)
- device_y = y2scr(render_priv, render_priv->state.clip_y1 - render_priv->state.scroll_shift);
- }
-
- // positioned events are totally different
- if (render_priv->state.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_priv->state.pos_x, render_priv->state.pos_y);
- get_base_point(bbox, alignment, &base_x, &base_y);
- device_x = x2scr_pos(render_priv, render_priv->state.pos_x) - base_x;
- device_y = y2scr_pos(render_priv, render_priv->state.pos_y) - base_y;
- }
-
- // fix clip coordinates (they depend on alignment)
- if (render_priv->state.evt_type == EVENT_NORMAL ||
- render_priv->state.evt_type == EVENT_HSCROLL ||
- render_priv->state.evt_type == EVENT_VSCROLL) {
- render_priv->state.clip_x0 = x2scr(render_priv, render_priv->state.clip_x0);
- render_priv->state.clip_x1 = x2scr(render_priv, render_priv->state.clip_x1);
- if (valign == VALIGN_TOP) {
- render_priv->state.clip_y0 = y2scr_top(render_priv, render_priv->state.clip_y0);
- render_priv->state.clip_y1 = y2scr_top(render_priv, render_priv->state.clip_y1);
- } else if (valign == VALIGN_CENTER) {
- render_priv->state.clip_y0 = y2scr(render_priv, render_priv->state.clip_y0);
- render_priv->state.clip_y1 = y2scr(render_priv, render_priv->state.clip_y1);
- } else if (valign == VALIGN_SUB) {
- render_priv->state.clip_y0 = y2scr_sub(render_priv, render_priv->state.clip_y0);
- render_priv->state.clip_y1 = y2scr_sub(render_priv, render_priv->state.clip_y1);
- }
- } else if (render_priv->state.evt_type == EVENT_POSITIONED) {
- render_priv->state.clip_x0 = x2scr_pos(render_priv, render_priv->state.clip_x0);
- render_priv->state.clip_x1 = x2scr_pos(render_priv, render_priv->state.clip_x1);
- render_priv->state.clip_y0 = y2scr_pos(render_priv, render_priv->state.clip_y0);
- render_priv->state.clip_y1 = y2scr_pos(render_priv, render_priv->state.clip_y1);
- }
-
- // calculate rotation parameters
- {
- FT_Vector center;
-
- if (render_priv->state.have_origin) {
- center.x = x2scr(render_priv, render_priv->state.org_x);
- center.y = y2scr(render_priv, render_priv->state.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(render_priv, 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_priv->state.detect_collisions;
- event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1;
- event_images->event = event;
- event_images->imgs = render_text(render_priv, device_x, device_y);
-
- free_render_context();
-
- return 0;
+ 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;
+ ass_settings_t *settings_priv = &render_priv->settings;
+ text_info_t *text_info = &render_priv->text_info;
+
+ if (event->Style >= render_priv->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(render_priv, 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(render_priv, &p);
+ } while (code && render_priv->state.drawing_mode); // skip everything in drawing mode
+
+ // face could have been changed in get_next_char
+ if (!render_priv->state.font) {
+ free_render_context();
+ return 1;
+ }
+
+ if (code == 0)
+ break;
+
+ if (text_info->length >= text_info->max_glyphs) {
+ // Raise maximum number of glyphs
+ text_info->max_glyphs *= 2;
+ text_info->glyphs =
+ realloc(text_info->glyphs,
+ sizeof(glyph_info_t) * text_info->max_glyphs);
+ }
+
+ if (previous && code) {
+ FT_Vector delta;
+ delta =
+ ass_font_get_kerning(render_priv->state.font, previous,
+ code);
+ pen.x += delta.x * render_priv->state.scale_x;
+ pen.y += delta.y * render_priv->state.scale_y;
+ }
+
+ shift.x = pen.x & SUBPIXEL_MASK;
+ shift.y = pen.y & SUBPIXEL_MASK;
+
+ if (render_priv->state.evt_type == EVENT_POSITIONED) {
+ shift.x +=
+ double_to_d6(x2scr_pos
+ (render_priv,
+ render_priv->state.pos_x)) & SUBPIXEL_MASK;
+ shift.y -=
+ double_to_d6(y2scr_pos
+ (render_priv,
+ render_priv->state.pos_y)) & SUBPIXEL_MASK;
+ }
+
+ ass_font_set_transform(render_priv->state.font,
+ render_priv->state.scale_x *
+ render_priv->font_scale_x,
+ render_priv->state.scale_y, &shift);
+
+ get_outline_glyph(render_priv, 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_priv->state.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_priv->state.c[i];
+ change_alpha(&clr,
+ mult_alpha(_a(clr), render_priv->state.fade), 1.);
+ text_info->glyphs[text_info->length].c[i] = clr;
+ }
+ text_info->glyphs[text_info->length].effect_type =
+ render_priv->state.effect_type;
+ text_info->glyphs[text_info->length].effect_timing =
+ render_priv->state.effect_timing;
+ text_info->glyphs[text_info->length].effect_skip_timing =
+ render_priv->state.effect_skip_timing;
+ text_info->glyphs[text_info->length].be = render_priv->state.be;
+ text_info->glyphs[text_info->length].blur = render_priv->state.blur;
+ text_info->glyphs[text_info->length].shadow =
+ render_priv->state.shadow;
+ text_info->glyphs[text_info->length].frx = render_priv->state.frx;
+ text_info->glyphs[text_info->length].fry = render_priv->state.fry;
+ text_info->glyphs[text_info->length].frz = render_priv->state.frz;
+ ass_font_get_asc_desc(render_priv->state.font, code,
+ &text_info->glyphs[text_info->length].asc,
+ &text_info->glyphs[text_info->length].desc);
+ text_info->glyphs[text_info->length].asc *=
+ render_priv->state.scale_y;
+ text_info->glyphs[text_info->length].desc *=
+ render_priv->state.scale_y;
+
+ // fill bitmap_hash_key
+ text_info->glyphs[text_info->length].hash_key.font =
+ render_priv->state.font;
+ text_info->glyphs[text_info->length].hash_key.size =
+ render_priv->state.font_size;
+ text_info->glyphs[text_info->length].hash_key.outline =
+ render_priv->state.border * 0xFFFF;
+ text_info->glyphs[text_info->length].hash_key.scale_x =
+ render_priv->state.scale_x * 0xFFFF;
+ text_info->glyphs[text_info->length].hash_key.scale_y =
+ render_priv->state.scale_y * 0xFFFF;
+ text_info->glyphs[text_info->length].hash_key.frx =
+ render_priv->state.frx * 0xFFFF;
+ text_info->glyphs[text_info->length].hash_key.fry =
+ render_priv->state.fry * 0xFFFF;
+ text_info->glyphs[text_info->length].hash_key.frz =
+ render_priv->state.frz * 0xFFFF;
+ text_info->glyphs[text_info->length].hash_key.bold =
+ render_priv->state.bold;
+ text_info->glyphs[text_info->length].hash_key.italic =
+ render_priv->state.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_priv->state.be;
+ text_info->glyphs[text_info->length].hash_key.blur =
+ render_priv->state.blur;
+
+ text_info->length++;
+
+ render_priv->state.effect_type = EF_NONE;
+ render_priv->state.effect_timing = 0;
+ render_priv->state.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(render_priv);
+
+ // alignments
+ alignment = render_priv->state.alignment;
+ halign = alignment & 3;
+ valign = alignment & 12;
+
+ MarginL =
+ (event->MarginL) ? event->MarginL : render_priv->state.style->
+ MarginL;
+ MarginR =
+ (event->MarginR) ? event->MarginR : render_priv->state.style->
+ MarginR;
+ MarginV =
+ (event->MarginV) ? event->MarginV : render_priv->state.style->
+ MarginV;
+
+ if (render_priv->state.evt_type != EVENT_HSCROLL) {
+ int max_text_width;
+
+ // calculate max length of a line
+ max_text_width =
+ x2scr(render_priv,
+ render_priv->track->PlayResX - MarginR) -
+ x2scr(render_priv, MarginL);
+
+ // rearrange text in several lines
+ wrap_lines_smart(render_priv, 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_priv->state.evt_type == EVENT_HSCROLL
+ measure_text(render_priv);
+ }
+
+ // determing text bounding box
+ compute_string_bbox(text_info, &bbox);
+
+ // determine device coordinates for text
+
+ // x coordinate for everything except positioned events
+ if (render_priv->state.evt_type == EVENT_NORMAL ||
+ render_priv->state.evt_type == EVENT_VSCROLL) {
+ device_x = x2scr(render_priv, MarginL);
+ } else if (render_priv->state.evt_type == EVENT_HSCROLL) {
+ if (render_priv->state.scroll_direction == SCROLL_RL)
+ device_x =
+ x2scr(render_priv,
+ render_priv->track->PlayResX -
+ render_priv->state.scroll_shift);
+ else if (render_priv->state.scroll_direction == SCROLL_LR)
+ device_x =
+ x2scr(render_priv,
+ render_priv->state.scroll_shift) - (bbox.xMax -
+ bbox.xMin);
+ }
+ // y coordinate for everything except positioned events
+ if (render_priv->state.evt_type == EVENT_NORMAL ||
+ render_priv->state.evt_type == EVENT_HSCROLL) {
+ if (valign == VALIGN_TOP) { // toptitle
+ device_y =
+ y2scr_top(render_priv,
+ MarginV) + d6_to_int(text_info->lines[0].asc);
+ } else if (valign == VALIGN_CENTER) { // midtitle
+ int scr_y =
+ y2scr(render_priv, render_priv->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(render_priv,
+ render_priv->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_priv->state.evt_type == EVENT_VSCROLL) {
+ if (render_priv->state.scroll_direction == SCROLL_TB)
+ device_y =
+ y2scr(render_priv,
+ render_priv->state.clip_y0 +
+ render_priv->state.scroll_shift) - (bbox.yMax -
+ bbox.yMin);
+ else if (render_priv->state.scroll_direction == SCROLL_BT)
+ device_y =
+ y2scr(render_priv,
+ render_priv->state.clip_y1 -
+ render_priv->state.scroll_shift);
+ }
+ // positioned events are totally different
+ if (render_priv->state.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_priv->state.pos_x, render_priv->state.pos_y);
+ get_base_point(bbox, alignment, &base_x, &base_y);
+ device_x =
+ x2scr_pos(render_priv, render_priv->state.pos_x) - base_x;
+ device_y =
+ y2scr_pos(render_priv, render_priv->state.pos_y) - base_y;
+ }
+ // fix clip coordinates (they depend on alignment)
+ if (render_priv->state.evt_type == EVENT_NORMAL ||
+ render_priv->state.evt_type == EVENT_HSCROLL ||
+ render_priv->state.evt_type == EVENT_VSCROLL) {
+ render_priv->state.clip_x0 =
+ x2scr(render_priv, render_priv->state.clip_x0);
+ render_priv->state.clip_x1 =
+ x2scr(render_priv, render_priv->state.clip_x1);
+ if (valign == VALIGN_TOP) {
+ render_priv->state.clip_y0 =
+ y2scr_top(render_priv, render_priv->state.clip_y0);
+ render_priv->state.clip_y1 =
+ y2scr_top(render_priv, render_priv->state.clip_y1);
+ } else if (valign == VALIGN_CENTER) {
+ render_priv->state.clip_y0 =
+ y2scr(render_priv, render_priv->state.clip_y0);
+ render_priv->state.clip_y1 =
+ y2scr(render_priv, render_priv->state.clip_y1);
+ } else if (valign == VALIGN_SUB) {
+ render_priv->state.clip_y0 =
+ y2scr_sub(render_priv, render_priv->state.clip_y0);
+ render_priv->state.clip_y1 =
+ y2scr_sub(render_priv, render_priv->state.clip_y1);
+ }
+ } else if (render_priv->state.evt_type == EVENT_POSITIONED) {
+ render_priv->state.clip_x0 =
+ x2scr_pos(render_priv, render_priv->state.clip_x0);
+ render_priv->state.clip_x1 =
+ x2scr_pos(render_priv, render_priv->state.clip_x1);
+ render_priv->state.clip_y0 =
+ y2scr_pos(render_priv, render_priv->state.clip_y0);
+ render_priv->state.clip_y1 =
+ y2scr_pos(render_priv, render_priv->state.clip_y1);
+ }
+ // calculate rotation parameters
+ {
+ FT_Vector center;
+
+ if (render_priv->state.have_origin) {
+ center.x = x2scr(render_priv, render_priv->state.org_x);
+ center.y = y2scr(render_priv, render_priv->state.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(render_priv, 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_priv->state.detect_collisions;
+ event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1;
+ event_images->event = event;
+ event_images->imgs = render_text(render_priv, 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)
+static void ass_free_images(ass_image_t *img)
{
- while (img) {
- ass_image_t* next = img->next;
- free(img);
- img = next;
- }
+ while (img) {
+ ass_image_t *next = img->next;
+ free(img);
+ img = next;
+ }
}
-static void ass_reconfigure(ass_renderer_t* priv)
+static void ass_reconfigure(ass_renderer_t *priv)
{
- priv->render_id = ++last_render_id;
- priv->cache.glyph_cache = ass_glyph_cache_reset(priv->cache.glyph_cache);
- priv->cache.bitmap_cache = ass_bitmap_cache_reset(priv->cache.bitmap_cache);
- priv->cache.composite_cache = ass_composite_cache_reset(priv->cache.composite_cache);
- ass_free_images(priv->prev_images_root);
- priv->prev_images_root = 0;
+ priv->render_id = ++last_render_id;
+ priv->cache.glyph_cache =
+ ass_glyph_cache_reset(priv->cache.glyph_cache);
+ priv->cache.bitmap_cache =
+ ass_bitmap_cache_reset(priv->cache.bitmap_cache);
+ priv->cache.composite_cache =
+ ass_composite_cache_reset(priv->cache.composite_cache);
+ 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)
+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);
- }
+ 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)
+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);
- }
+ 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)
+void ass_set_use_margins(ass_renderer_t *priv, int use)
{
- priv->settings.use_margins = use;
+ priv->settings.use_margins = use;
}
-void ass_set_aspect_ratio(ass_renderer_t* priv, double ar)
+void ass_set_aspect_ratio(ass_renderer_t *priv, double ar)
{
- if (priv->settings.aspect != ar) {
- priv->settings.aspect = ar;
- ass_reconfigure(priv);
- }
+ if (priv->settings.aspect != ar) {
+ priv->settings.aspect = ar;
+ ass_reconfigure(priv);
+ }
}
-void ass_set_font_scale(ass_renderer_t* priv, double font_scale)
+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);
- }
+ 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)
+void ass_set_hinting(ass_renderer_t *priv, ass_hinting_t ht)
{
- if (priv->settings.hinting != ht) {
- priv->settings.hinting = ht;
- ass_reconfigure(priv);
- }
+ if (priv->settings.hinting != ht) {
+ priv->settings.hinting = ht;
+ ass_reconfigure(priv);
+ }
}
-void ass_set_line_spacing(ass_renderer_t* priv, double line_spacing)
+void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing)
{
- priv->settings.line_spacing = 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)
+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;
+ 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)
+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);
+ 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)
+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);
+ return ass_set_fonts_(priv, default_font, default_family, 0);
}
/**
* \brief Start a new frame
*/
-static int ass_start_frame(ass_renderer_t *render_priv, ass_track_t* track, long long now)
+static int
+ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track,
+ long long now)
{
- ass_settings_t* settings_priv = &render_priv->settings;
-
- if (!render_priv->settings.frame_width && !render_priv->settings.frame_height)
- return 1; // library not initialized
-
- if (track->n_events == 0)
- return 1; // nothing to do
-
- render_priv->width = settings_priv->frame_width;
- render_priv->height = settings_priv->frame_height;
- render_priv->orig_width = settings_priv->frame_width - settings_priv->left_margin - settings_priv->right_margin;
- render_priv->orig_height = settings_priv->frame_height - settings_priv->top_margin - settings_priv->bottom_margin;
- render_priv->orig_width_nocrop = settings_priv->frame_width -
- FFMAX(settings_priv->left_margin, 0) -
- FFMAX(settings_priv->right_margin, 0);
- render_priv->orig_height_nocrop = settings_priv->frame_height -
- FFMAX(settings_priv->top_margin, 0) -
- FFMAX(settings_priv->bottom_margin, 0);
- render_priv->track = track;
- render_priv->time = now;
-
- ass_lazy_track_init(render_priv);
-
- render_priv->font_scale = settings_priv->font_size_coeff *
- render_priv->orig_height / render_priv->track->PlayResY;
- if (render_priv->track->ScaledBorderAndShadow)
- render_priv->border_scale = ((double)render_priv->orig_height) / render_priv->track->PlayResY;
- else
- render_priv->border_scale = 1.;
-
- render_priv->font_scale_x = 1.;
-
- render_priv->prev_images_root = render_priv->images_root;
- render_priv->images_root = 0;
-
- return 0;
+ ass_settings_t *settings_priv = &render_priv->settings;
+
+ if (!render_priv->settings.frame_width
+ && !render_priv->settings.frame_height)
+ return 1; // library not initialized
+
+ if (track->n_events == 0)
+ return 1; // nothing to do
+
+ render_priv->width = settings_priv->frame_width;
+ render_priv->height = settings_priv->frame_height;
+ render_priv->orig_width =
+ settings_priv->frame_width - settings_priv->left_margin -
+ settings_priv->right_margin;
+ render_priv->orig_height =
+ settings_priv->frame_height - settings_priv->top_margin -
+ settings_priv->bottom_margin;
+ render_priv->orig_width_nocrop =
+ settings_priv->frame_width - FFMAX(settings_priv->left_margin,
+ 0) -
+ FFMAX(settings_priv->right_margin, 0);
+ render_priv->orig_height_nocrop =
+ settings_priv->frame_height - FFMAX(settings_priv->top_margin,
+ 0) -
+ FFMAX(settings_priv->bottom_margin, 0);
+ render_priv->track = track;
+ render_priv->time = now;
+
+ ass_lazy_track_init(render_priv);
+
+ render_priv->font_scale = settings_priv->font_size_coeff *
+ render_priv->orig_height / render_priv->track->PlayResY;
+ if (render_priv->track->ScaledBorderAndShadow)
+ render_priv->border_scale =
+ ((double) render_priv->orig_height) /
+ render_priv->track->PlayResY;
+ else
+ render_priv->border_scale = 1.;
+
+ render_priv->font_scale_x = 1.;
+
+ render_priv->prev_images_root = render_priv->images_root;
+ render_priv->images_root = 0;
+
+ return 0;
}
-static int cmp_event_layer(const void* p1, const void* p2)
+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;
+ 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_renderer_t* render_priv, ass_event_t* event)
+static render_priv_t *get_render_priv(ass_renderer_t *render_priv,
+ ass_event_t *event)
{
- if (!event->render_priv)
- event->render_priv = calloc(1, sizeof(render_priv_t));
- // FIXME: check render_id
- if (render_priv->render_id != event->render_priv->render_id) {
- memset(event->render_priv, 0, sizeof(render_priv_t));
- event->render_priv->render_id = render_priv->render_id;
- }
- return event->render_priv;
+ if (!event->render_priv)
+ event->render_priv = calloc(1, sizeof(render_priv_t));
+ // FIXME: check render_id
+ if (render_priv->render_id != event->render_priv->render_id) {
+ memset(event->render_priv, 0, sizeof(render_priv_t));
+ event->render_priv->render_id = render_priv->render_id;
+ }
+ return event->render_priv;
}
typedef struct segment_s {
- int a, b; // top and height
+ int a, b; // top and height
} segment_t;
-static int overlap(segment_t* s1, segment_t* s2)
+static int overlap(segment_t *s1, segment_t *s2)
{
- if (s1->a >= s2->b || s2->a >= s1->b)
- return 0;
- return 1;
+ if (s1->a >= s2->b || s2->a >= s1->b)
+ return 0;
+ return 1;
}
-static int cmp_segment(const void* p1, const void* p2)
+static int cmp_segment(const void *p1, const void *p2)
{
- return ((segment_t*)p1)->a - ((segment_t*)p2)->a;
+ return ((segment_t *) p1)->a - ((segment_t *) p2)->a;
}
-static void shift_event(ass_renderer_t* render_priv, event_images_t* ei, int shift)
+static void
+shift_event(ass_renderer_t *render_priv, 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 >= render_priv->height) {
- int clip = cur->dst_y + cur->h - render_priv->height;
- cur->h -= clip;
- }
- if (cur->h <= 0) {
- cur->h = 0;
- cur->dst_y = 0;
- }
- cur = cur->next;
- }
- ei->top += 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 >= render_priv->height) {
+ int clip = cur->dst_y + cur->h - render_priv->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)
+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;
+ 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(ass_renderer_t* render_priv, event_images_t* imgs, int cnt)
+static void
+fix_collisions(ass_renderer_t *render_priv, 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(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(render_priv, 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(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(render_priv, imgs + i, shift);
- // make it fixed
- priv->top = imgs[i].top;
- priv->height = imgs[i].height;
- }
-
- }
+ 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(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(render_priv, 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(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(render_priv, imgs + i, shift);
+ // make it fixed
+ priv->top = imgs[i].top;
+ priv->height = imgs[i].height;
+ }
+
+ }
}
/**
*/
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;
+ 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;
}
/**
*/
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;
+ 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;
}
/**
* 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)
+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(priv, 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(priv, last, priv->eimg + i - last);
- last = priv->eimg + i;
- }
- if (cnt > 0)
- fix_collisions(priv, last, priv->eimg + cnt - last);
-
- // concat lists
- tail = &priv->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 priv->images_root;
-}
+ 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(priv, 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(priv, last, priv->eimg + i - last);
+ last = priv->eimg + i;
+ }
+ if (cnt > 0)
+ fix_collisions(priv, last, priv->eimg + cnt - last);
+
+ // concat lists
+ tail = &priv->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 priv->images_root;
+}
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
/// 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;
+ 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;
+ 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;
+ 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;
/// 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
+ 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
+ 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;
+ 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;
+ // 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
+ 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_library_t *library;
+ parser_priv_t *parser_priv;
} ass_track_t;
-#endif /* LIBASS_TYPES_H */
+#endif /* LIBASS_TYPES_H */
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#include "mputils.h"
#include "ass_utils.h"
-int mystrtoi(char** p, int* res)
+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;
+ // 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)
+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;
+ 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)
+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;
+ char *start = *p;
+ *res = strtoll(*p, p, base);
+ if (*p != start)
+ return 1;
+ else
+ return 0;
}
-int mystrtod(char** p, double* res)
+int mystrtod(char **p, double *res)
{
- char* start = *p;
- *res = strtod(*p, p);
- if (*p != start) return 1;
- else return 0;
+ char *start = *p;
+ *res = strtod(*p, p);
+ if (*p != start)
+ return 1;
+ else
+ return 0;
}
-int strtocolor(char** q, uint32_t* res)
+int strtocolor(char **q, uint32_t *res)
{
- uint32_t color = 0;
- int result;
- char* p = *q;
+ 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 == '&')
+ ++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);
- }
+ 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;
+ {
+ 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;
+ *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;
+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)
+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;
+ 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));
- }
+ 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
-// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
-// vim:ts=8:sw=8:noet:ai:
/*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
*
#include <stdint.h>
-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);
+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 d6_to_int(int x)
+{
+ return (x + 32) >> 6;
}
-static inline int d16_to_int(int x) {
- return (x + 32768) >> 16;
+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_d6(int x)
+{
+ return x << 6;
}
-static inline int int_to_d16(int x) {
- return x << 16;
+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 d16_to_d6(int x)
+{
+ return (x + 512) >> 10;
}
-static inline int d6_to_d16(int x) {
- return x << 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 double d6_to_double(int x)
+{
+ return x / 64.;
}
-static inline int double_to_d6(double x) {
- return (int)(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 double d16_to_double(int x)
+{
+ return ((double) x) / 0x10000;
}
-static inline int double_to_d16(double x) {
- return (int)(x * 0x10000);
+static inline int double_to_d16(double x)
+{
+ return (int) (x * 0x10000);
}
-#endif /* LIBASS_UTILS_H */
+#endif /* LIBASS_UTILS_H */
#define MSGTR_LIBASS_NoCharmaps "[ass] font face with no charmaps\n"
#define MSGTR_LIBASS_NoCharmapAutodetected "[ass] no charmap autodetected, trying the first one\n"
#endif
-
#include <enca.h>
#endif
-void my_mp_msg(int lvl, char *lvl_str, char *fmt, ...) {
- va_list va;
- if(lvl > MSGL_INFO) return;
- va_start(va, fmt);
- vprintf(fmt, va);
- va_end(va);
+void my_mp_msg(int lvl, char *lvl_str, char *fmt, ...)
+{
+ va_list va;
+ if (lvl > MSGL_INFO)
+ return;
+ va_start(va, fmt);
+ vprintf(fmt, va);
+ va_end(va);
}
-unsigned utf8_get_char(char **str) {
- uint8_t *strp = (uint8_t *)*str;
- unsigned c = *strp++;
- unsigned mask = 0x80;
- int len = -1;
- while (c & mask) {
- mask >>= 1;
- len++;
- }
- if (len <= 0 || len > 4)
- goto no_utf8;
- c &= mask - 1;
- while ((*strp & 0xc0) == 0x80) {
- if (len-- <= 0)
- goto no_utf8;
- c = (c << 6) | (*strp++ & 0x3f);
- }
- if (len)
- goto no_utf8;
- *str = (char *)strp;
- return c;
-
-no_utf8:
- strp = (uint8_t *)*str;
- c = *strp++;
- *str = (char *)strp;
- return c;
+unsigned utf8_get_char(char **str)
+{
+ uint8_t *strp = (uint8_t *) * str;
+ unsigned c = *strp++;
+ unsigned mask = 0x80;
+ int len = -1;
+ while (c & mask) {
+ mask >>= 1;
+ len++;
+ }
+ if (len <= 0 || len > 4)
+ goto no_utf8;
+ c &= mask - 1;
+ while ((*strp & 0xc0) == 0x80) {
+ if (len-- <= 0)
+ goto no_utf8;
+ c = (c << 6) | (*strp++ & 0x3f);
+ }
+ if (len)
+ goto no_utf8;
+ *str = (char *) strp;
+ return c;
+
+ no_utf8:
+ strp = (uint8_t *) * str;
+ c = *strp++;
+ *str = (char *) strp;
+ return c;
}
// gaussian blur
-void blur(
- unsigned char *buffer,
- unsigned short *tmp2,
- int width,
- int height,
- int stride,
- int *m2,
- int r,
- int mwidth) {
+void blur(unsigned char *buffer,
+ unsigned short *tmp2,
+ int width, int height, int stride, int *m2, int r, int mwidth)
+{
int x, y;
- unsigned char *s = buffer;
- unsigned short *t = tmp2+1;
- for(y=0; y<height; y++){
- memset(t-1, 0, (width+1)*sizeof(short));
-
- for(x=0; x<r; x++){
- const int src= s[x];
- if(src){
- register unsigned short *dstp= t + x-r;
- int mx;
- unsigned *m3= m2 + src*mwidth;
- for(mx=r-x; mx<mwidth; mx++){
- dstp[mx]+= m3[mx];
- }
- }
- }
-
- for(; x<width-r; x++){
- const int src= s[x];
- if(src){
- register unsigned short *dstp= t + x-r;
- int mx;
- unsigned *m3= m2 + src*mwidth;
- for(mx=0; mx<mwidth; mx++){
- dstp[mx]+= m3[mx];
- }
- }
- }
-
- for(; x<width; x++){
- const int src= s[x];
- if(src){
- register unsigned short *dstp= t + x-r;
- int mx;
- const int x2= r+width -x;
- unsigned *m3= m2 + src*mwidth;
- for(mx=0; mx<x2; mx++){
- dstp[mx]+= m3[mx];
- }
- }
- }
-
- s+= stride;
- t+= width + 1;
+ unsigned char *s = buffer;
+ unsigned short *t = tmp2 + 1;
+ for (y = 0; y < height; y++) {
+ memset(t - 1, 0, (width + 1) * sizeof(short));
+
+ for (x = 0; x < r; x++) {
+ const int src = s[x];
+ if (src) {
+ register unsigned short *dstp = t + x - r;
+ int mx;
+ unsigned *m3 = m2 + src * mwidth;
+ for (mx = r - x; mx < mwidth; mx++) {
+ dstp[mx] += m3[mx];
+ }
+ }
+ }
+
+ for (; x < width - r; x++) {
+ const int src = s[x];
+ if (src) {
+ register unsigned short *dstp = t + x - r;
+ int mx;
+ unsigned *m3 = m2 + src * mwidth;
+ for (mx = 0; mx < mwidth; mx++) {
+ dstp[mx] += m3[mx];
+ }
+ }
+ }
+
+ for (; x < width; x++) {
+ const int src = s[x];
+ if (src) {
+ register unsigned short *dstp = t + x - r;
+ int mx;
+ const int x2 = r + width - x;
+ unsigned *m3 = m2 + src * mwidth;
+ for (mx = 0; mx < x2; mx++) {
+ dstp[mx] += m3[mx];
+ }
+ }
+ }
+
+ s += stride;
+ t += width + 1;
}
t = tmp2;
- for(x=0; x<width; x++){
- for(y=0; y<r; y++){
- unsigned short *srcp= t + y*(width+1) + 1;
- int src= *srcp;
- if(src){
- register unsigned short *dstp= srcp - 1 + width+1;
- const int src2= (src + 128)>>8;
- unsigned *m3= m2 + src2*mwidth;
-
- int mx;
- *srcp= 128;
- for(mx=r-1; mx<mwidth; mx++){
- *dstp += m3[mx];
- dstp+= width+1;
- }
- }
- }
- for(; y<height-r; y++){
- unsigned short *srcp= t + y*(width+1) + 1;
- int src= *srcp;
- if(src){
- register unsigned short *dstp= srcp - 1 - r*(width+1);
- const int src2= (src + 128)>>8;
- unsigned *m3= m2 + src2*mwidth;
-
- int mx;
- *srcp= 128;
- for(mx=0; mx<mwidth; mx++){
- *dstp += m3[mx];
- dstp+= width+1;
- }
- }
- }
- for(; y<height; y++){
- unsigned short *srcp= t + y*(width+1) + 1;
- int src= *srcp;
- if(src){
- const int y2=r+height-y;
- register unsigned short *dstp= srcp - 1 - r*(width+1);
- const int src2= (src + 128)>>8;
- unsigned *m3= m2 + src2*mwidth;
-
- int mx;
- *srcp= 128;
- for(mx=0; mx<y2; mx++){
- *dstp += m3[mx];
- dstp+= width+1;
- }
- }
- }
- t++;
+ for (x = 0; x < width; x++) {
+ for (y = 0; y < r; y++) {
+ unsigned short *srcp = t + y * (width + 1) + 1;
+ int src = *srcp;
+ if (src) {
+ register unsigned short *dstp = srcp - 1 + width + 1;
+ const int src2 = (src + 128) >> 8;
+ unsigned *m3 = m2 + src2 * mwidth;
+
+ int mx;
+ *srcp = 128;
+ for (mx = r - 1; mx < mwidth; mx++) {
+ *dstp += m3[mx];
+ dstp += width + 1;
+ }
+ }
+ }
+ for (; y < height - r; y++) {
+ unsigned short *srcp = t + y * (width + 1) + 1;
+ int src = *srcp;
+ if (src) {
+ register unsigned short *dstp = srcp - 1 - r * (width + 1);
+ const int src2 = (src + 128) >> 8;
+ unsigned *m3 = m2 + src2 * mwidth;
+
+ int mx;
+ *srcp = 128;
+ for (mx = 0; mx < mwidth; mx++) {
+ *dstp += m3[mx];
+ dstp += width + 1;
+ }
+ }
+ }
+ for (; y < height; y++) {
+ unsigned short *srcp = t + y * (width + 1) + 1;
+ int src = *srcp;
+ if (src) {
+ const int y2 = r + height - y;
+ register unsigned short *dstp = srcp - 1 - r * (width + 1);
+ const int src2 = (src + 128) >> 8;
+ unsigned *m3 = m2 + src2 * mwidth;
+
+ int mx;
+ *srcp = 128;
+ for (mx = 0; mx < y2; mx++) {
+ *dstp += m3[mx];
+ dstp += width + 1;
+ }
+ }
+ }
+ t++;
}
t = tmp2;
s = buffer;
- for(y=0; y<height; y++){
- for(x=0; x<width; x++){
- s[x]= t[x]>>8;
- }
- s+= stride;
- t+= width + 1;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ s[x] = t[x] >> 8;
+ }
+ s += stride;
+ t += width + 1;
}
}
#ifdef CONFIG_ENCA
-void* guess_buffer_cp(unsigned char* buffer, int buflen, char *preferred_language, char *fallback)
+void *guess_buffer_cp(unsigned char *buffer, int buflen,
+ char *preferred_language, char *fallback)
{
const char **languages;
size_t langcnt;
languages = enca_get_languages(&langcnt);
mp_msg(MSGT_ASS, MSGL_V, "ENCA supported languages: ");
for (i = 0; i < langcnt; i++) {
- mp_msg(MSGT_ASS, MSGL_V, "%s ", languages[i]);
+ mp_msg(MSGT_ASS, MSGL_V, "%s ", languages[i]);
}
mp_msg(MSGT_ASS, MSGL_V, "\n");
-
+
for (i = 0; i < langcnt; i++) {
- const char *tmp;
-
- if (strcasecmp(languages[i], preferred_language) != 0) continue;
- analyser = enca_analyser_alloc(languages[i]);
- encoding = enca_analyse_const(analyser, buffer, buflen);
- tmp = enca_charset_name(encoding.charset, ENCA_NAME_STYLE_ICONV);
- if (tmp && encoding.charset != ENCA_CS_UNKNOWN) {
- detected_sub_cp = strdup(tmp);
- mp_msg(MSGT_ASS, MSGL_INFO, "ENCA detected charset: %s\n", tmp);
- }
- enca_analyser_free(analyser);
+ const char *tmp;
+
+ if (strcasecmp(languages[i], preferred_language) != 0)
+ continue;
+ analyser = enca_analyser_alloc(languages[i]);
+ encoding = enca_analyse_const(analyser, buffer, buflen);
+ tmp = enca_charset_name(encoding.charset, ENCA_NAME_STYLE_ICONV);
+ if (tmp && encoding.charset != ENCA_CS_UNKNOWN) {
+ detected_sub_cp = strdup(tmp);
+ mp_msg(MSGT_ASS, MSGL_INFO, "ENCA detected charset: %s\n", tmp);
+ }
+ enca_analyser_free(analyser);
}
-
+
free(languages);
if (!detected_sub_cp) {
- detected_sub_cp = strdup(fallback);
- mp_msg(MSGT_ASS, MSGL_INFO, "ENCA detection failed: fallback to %s\n", fallback);
+ detected_sub_cp = strdup(fallback);
+ mp_msg(MSGT_ASS, MSGL_INFO,
+ "ENCA detection failed: fallback to %s\n", fallback);
}
return detected_sub_cp;
void my_mp_msg(int lvl, char *lvl_str, char *fmt, ...);
#ifdef __VISUALC__
-static void mp_msg(int mod, int level, const char *fmt, ...) {
- // MSVC doesn't like the # used all around for mp_msg, so it breaks va_arg
+static void mp_msg(int mod, int level, const char *fmt, ...)
+{
+ // MSVC doesn't like the # used all around for mp_msg, so it breaks va_arg
}
#else
#define mp_msg(mod, level, args...) my_mp_msg(level, #level, args)
#define MSGL_V 6
#define MSGL_DBG2 7
-void blur(unsigned char *buffer, unsigned short *tmp2, int width, int height,
- int stride, int *m2, int r, int mwidth);
+void blur(unsigned char *buffer, unsigned short *tmp2, int width,
+ int height, int stride, int *m2, int r, int mwidth);
-void* guess_buffer_cp(unsigned char* buffer, int buflen, char *preferred_language, char *fallback);
+void *guess_buffer_cp(unsigned char *buffer, int buflen,
+ char *preferred_language, char *fallback);
#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))