Update libass to 15df37.

Originally committed to SVN as r3102.
This commit is contained in:
Amar Takhar 2009-07-11 15:48:50 +00:00
parent 1ec77b7576
commit 516db6c901
22 changed files with 6208 additions and 4345 deletions

View file

@ -10,24 +10,16 @@ libass_aegisub_a_SOURCES = \
ass.c \ ass.c \
ass_bitmap.c \ ass_bitmap.c \
ass_cache.c \ ass_cache.c \
ass_drawing.c \
ass_font.c \ ass_font.c \
ass_fontconfig.c \ ass_fontconfig.c \
ass_library.c \ ass_library.c \
ass_render.c \ ass_render.c \
ass_utils.c \ ass_utils.c \
mputils.c \ mputils.c
ass.h \
ass_bitmap.h \ libass_aegisub_a_SOURCES += \
ass_cache.h \ *.h
ass_font.h \
ass_fontconfig.h \
ass_library.h \
ass_types.h \
ass_utils.h \
help_mp.h \
libass_msvc.patch \
mputils.h \
msvc.h
EXTRA_DIST= \ EXTRA_DIST= \
ass_cache_template.c ass_cache_template.c

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -39,9 +37,9 @@
#include "ass.h" #include "ass.h"
#include "ass_utils.h" #include "ass_utils.h"
#include "ass_library.h" #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 { struct parser_priv_s {
parser_state_t state; parser_state_t state;
@ -54,7 +52,8 @@ struct parser_priv_s {
#define ASS_STYLES_ALLOC 20 #define ASS_STYLES_ALLOC 20
#define ASS_EVENTS_ALLOC 200 #define ASS_EVENTS_ALLOC 200
void ass_free_track(ass_track_t* track) { void ass_free_track(ass_track_t *track)
{
int i; int i;
if (track->parser_priv) { if (track->parser_priv) {
@ -83,14 +82,18 @@ void ass_free_track(ass_track_t* track) {
/// \brief Allocate a new style struct /// \brief Allocate a new style struct
/// \param track track /// \param track track
/// \return style id /// \return style id
int ass_alloc_style(ass_track_t* track) { int ass_alloc_style(ass_track_t *track)
{
int sid; int sid;
assert(track->n_styles <= track->max_styles); assert(track->n_styles <= track->max_styles);
if (track->n_styles == track->max_styles) { if (track->n_styles == track->max_styles) {
track->max_styles += ASS_STYLES_ALLOC; track->max_styles += ASS_STYLES_ALLOC;
track->styles = (ass_style_t*)realloc(track->styles, sizeof(ass_style_t)*track->max_styles); track->styles =
(ass_style_t *) realloc(track->styles,
sizeof(ass_style_t) *
track->max_styles);
} }
sid = track->n_styles++; sid = track->n_styles++;
@ -101,14 +104,18 @@ int ass_alloc_style(ass_track_t* track) {
/// \brief Allocate a new event struct /// \brief Allocate a new event struct
/// \param track track /// \param track track
/// \return event id /// \return event id
int ass_alloc_event(ass_track_t* track) { int ass_alloc_event(ass_track_t *track)
{
int eid; int eid;
assert(track->n_events <= track->max_events); assert(track->n_events <= track->max_events);
if (track->n_events == track->max_events) { if (track->n_events == track->max_events) {
track->max_events += ASS_EVENTS_ALLOC; track->max_events += ASS_EVENTS_ALLOC;
track->events = (ass_event_t*)realloc(track->events, sizeof(ass_event_t)*track->max_events); track->events =
(ass_event_t *) realloc(track->events,
sizeof(ass_event_t) *
track->max_events);
} }
eid = track->n_events++; eid = track->n_events++;
@ -116,7 +123,8 @@ int ass_alloc_event(ass_track_t* track) {
return eid; return eid;
} }
void ass_free_event(ass_track_t* track, int eid) { void ass_free_event(ass_track_t *track, int eid)
{
ass_event_t *event = track->events + eid; ass_event_t *event = track->events + eid;
if (event->Name) if (event->Name)
free(event->Name); free(event->Name);
@ -128,7 +136,8 @@ void ass_free_event(ass_track_t* track, int eid) {
free(event->render_priv); free(event->render_priv);
} }
void ass_free_style(ass_track_t* track, int sid) { void ass_free_style(ass_track_t *track, int sid)
{
ass_style_t *style = track->styles + sid; ass_style_t *style = track->styles + sid;
if (style->Name) if (style->Name)
free(style->Name); free(style->Name);
@ -138,14 +147,16 @@ void ass_free_style(ass_track_t* track, int sid) {
// ============================================================================================== // ==============================================================================================
static void skip_spaces(char** str) { static void skip_spaces(char **str)
{
char *p = *str; char *p = *str;
while ((*p == ' ') || (*p == '\t')) while ((*p == ' ') || (*p == '\t'))
++p; ++p;
*str = p; *str = p;
} }
static void rskip_spaces(char** str, char* limit) { static void rskip_spaces(char **str, char *limit)
{
char *p = *str; char *p = *str;
while ((p >= limit) && ((*p == ' ') || (*p == '\t'))) while ((p >= limit) && ((*p == ' ') || (*p == '\t')))
--p; --p;
@ -160,31 +171,36 @@ static void rskip_spaces(char** str, char* limit) {
* Returnes 0 if no styles found => expects at least 1 style. * Returnes 0 if no styles found => expects at least 1 style.
* Parsing code always adds "Default" style in the end. * Parsing code always adds "Default" style in the end.
*/ */
static int lookup_style(ass_track_t* track, char* name) { static int lookup_style(ass_track_t *track, char *name)
{
int i; int i;
if (*name == '*') ++name; // FIXME: what does '*' really mean ? if (*name == '*')
++name; // FIXME: what does '*' really mean ?
for (i = track->n_styles - 1; i >= 0; --i) { for (i = track->n_styles - 1; i >= 0; --i) {
// FIXME: mb strcasecmp ? // FIXME: mb strcasecmp ?
if (strcmp(track->styles[i].Name, name) == 0) if (strcmp(track->styles[i].Name, name) == 0)
return i; return i;
} }
i = track->default_style; i = track->default_style;
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoStyleNamedXFoundUsingY, track, name, track->styles[i].Name); ass_msg(MSGL_WARN, MSGTR_LIBASS_NoStyleNamedXFoundUsingY,
track, name, track->styles[i].Name);
return i; // use the first style return i; // use the first style
} }
static uint32_t string2color(char* p) { static uint32_t string2color(char *p)
{
uint32_t tmp; uint32_t tmp;
(void) strtocolor(&p, &tmp); (void) strtocolor(&p, &tmp);
return tmp; return tmp;
} }
static long long string2timecode(char* p) { static long long string2timecode(char *p)
{
unsigned h, m, s, ms; unsigned h, m, s, ms;
long long tm; long long tm;
int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms); int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms);
if (res < 4) { if (res < 4) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_BadTimestamp); ass_msg(MSGL_WARN, MSGTR_LIBASS_BadTimestamp);
return 0; return 0;
} }
tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10; tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10;
@ -194,10 +210,12 @@ static long long string2timecode(char* p) {
/** /**
* \brief converts numpad-style align to align. * \brief converts numpad-style align to align.
*/ */
static int numpad2align(int val) { static int numpad2align(int val)
{
int res, v; int res, v;
v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment
if (v != 0) v = 3 - v; if (v != 0)
v = 3 - v;
res = ((val - 1) % 3) + 1; // horizontal alignment res = ((val - 1) % 3) + 1; // horizontal alignment
res += v * 4; res += v * 4;
return res; return res;
@ -210,13 +228,13 @@ static int numpad2align(int val) {
#define ANYVAL(name,func) \ #define ANYVAL(name,func) \
} else if (strcasecmp(tname, #name) == 0) { \ } else if (strcasecmp(tname, #name) == 0) { \
target->name = func(token); \ target->name = func(token); \
mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); ass_msg(MSGL_DBG2, "%s = %s\n", #name, token);
#define STRVAL(name) \ #define STRVAL(name) \
} else if (strcasecmp(tname, #name) == 0) { \ } else if (strcasecmp(tname, #name) == 0) { \
if (target->name != NULL) free(target->name); \ if (target->name != NULL) free(target->name); \
target->name = strdup(token); \ target->name = strdup(token); \
mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); ass_msg(MSGL_DBG2, "%s = %s\n", #name, token);
#define COLORVAL(name) ANYVAL(name,string2color) #define COLORVAL(name) ANYVAL(name,string2color)
#define INTVAL(name) ANYVAL(name,atoi) #define INTVAL(name) ANYVAL(name,atoi)
@ -225,12 +243,13 @@ static int numpad2align(int val) {
#define STYLEVAL(name) \ #define STYLEVAL(name) \
} else if (strcasecmp(tname, #name) == 0) { \ } else if (strcasecmp(tname, #name) == 0) { \
target->name = lookup_style(track, token); \ target->name = lookup_style(track, token); \
mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); ass_msg(MSGL_DBG2, "%s = %s\n", #name, token);
#define ALIAS(alias,name) \ #define ALIAS(alias,name) \
if (strcasecmp(tname, #alias) == 0) {tname = #name;} if (strcasecmp(tname, #alias) == 0) {tname = #name;}
static char* next_token(char** str) { static char *next_token(char **str)
{
char *p = *str; char *p = *str;
char *start; char *start;
skip_spaces(&p); skip_spaces(&p);
@ -239,7 +258,8 @@ static char* next_token(char** str) {
return 0; return 0;
} }
start = p; // start of the token start = p; // start of the token
for (; (*p != '\0') && (*p != ','); ++p) {} for (; (*p != '\0') && (*p != ','); ++p) {
}
if (*p == '\0') { if (*p == '\0') {
*str = p; // eos found, str will point to '\0' at exit *str = p; // eos found, str will point to '\0' at exit
} else { } else {
@ -255,6 +275,7 @@ static char* next_token(char** str) {
*p = '\0'; *p = '\0';
return start; return start;
} }
/** /**
* \brief Parse the tail of Dialogue line * \brief Parse the tail of Dialogue line
* \param track track * \param track track
@ -262,7 +283,8 @@ static char* next_token(char** str) {
* \param str string to parse, zero-terminated * \param str string to parse, zero-terminated
* \param n_ignored number of format options to skip at the beginning * \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 *token;
char *tname; char *tname;
@ -295,7 +317,7 @@ static int process_event_tail(ass_track_t* track, ass_event_t* event, char* str,
if (last >= event->Text && *last == '\r') if (last >= event->Text && *last == '\r')
*last = 0; *last = 0;
} }
mp_msg(MSGT_ASS, MSGL_DBG2, "Text = %s\n", event->Text); ass_msg(MSGL_DBG2, "Text = %s\n", event->Text);
event->Duration -= event->Start; event->Duration -= event->Start;
free(format); free(format);
return 0; // "Text" is always the last return 0; // "Text" is always the last
@ -324,13 +346,15 @@ static int process_event_tail(ass_track_t* track, ass_event_t* event, char* str,
* \param track track to apply overrides to * \param track track to apply overrides to
* The format for overrides is [StyleName.]Field=Value * The format for overrides is [StyleName.]Field=Value
*/ */
void process_force_style(ass_track_t* track) { void ass_process_force_style(ass_track_t *track)
{
char **fs, *eq, *dt, *style, *tname, *token; char **fs, *eq, *dt, *style, *tname, *token;
ass_style_t *target; ass_style_t *target;
int sid; int sid;
char **list = track->library->style_overrides; char **list = track->library->style_overrides;
if (!list) return; if (!list)
return;
for (fs = list; *fs; ++fs) { for (fs = list; *fs; ++fs) {
eq = strrchr(*fs, '='); eq = strrchr(*fs, '=');
@ -360,7 +384,8 @@ void process_force_style(ass_track_t* track) {
tname = *fs; tname = *fs;
} }
for (sid = 0; sid < track->n_styles; ++sid) { for (sid = 0; sid < track->n_styles; ++sid) {
if (style == NULL || strcasecmp(track->styles[sid].Name, style) == 0) { if (style == NULL
|| strcasecmp(track->styles[sid].Name, style) == 0) {
target = track->styles + sid; target = track->styles + sid;
if (0) { if (0) {
STRVAL(FontName) STRVAL(FontName)
@ -389,7 +414,8 @@ void process_force_style(ass_track_t* track) {
} }
} }
*eq = '='; *eq = '=';
if (dt) *dt = '.'; if (dt)
*dt = '.';
} }
} }
@ -415,11 +441,15 @@ static int process_style(ass_track_t* track, char *str)
// no style format header // no style format header
// probably an ancient script version // probably an ancient script version
if (track->track_type == TRACK_TYPE_SSA) if (track->track_type == TRACK_TYPE_SSA)
track->style_format = strdup("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," track->style_format =
strdup
("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,"
"TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline," "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline,"
"Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"); "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding");
else else
track->style_format = strdup("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," track->style_format =
strdup
("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour,"
"OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut," "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut,"
"ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow," "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow,"
"Alignment, MarginL, MarginR, MarginV, Encoding"); "Alignment, MarginL, MarginR, MarginV, Encoding");
@ -427,7 +457,7 @@ static int process_style(ass_track_t* track, char *str)
q = format = strdup(track->style_format); q = format = strdup(track->style_format);
mp_msg(MSGT_ASS, MSGL_V, "[%p] Style: %s\n", track, str); ass_msg(MSGL_V, "[%p] Style: %s\n", track, str);
sid = ass_alloc_style(track); sid = ass_alloc_style(track);
@ -445,7 +475,8 @@ static int process_style(ass_track_t* track, char *str)
if (0) { // cool ;) if (0) { // cool ;)
STRVAL(Name) STRVAL(Name)
if ((strcmp(target->Name, "Default")==0) || (strcmp(target->Name, "*Default")==0)) if ((strcmp(target->Name, "Default") == 0)
|| (strcmp(target->Name, "*Default") == 0))
track->default_style = sid; track->default_style = sid;
STRVAL(FontName) STRVAL(FontName)
COLORVAL(PrimaryColour) COLORVAL(PrimaryColour)
@ -503,7 +534,8 @@ static int process_styles_line(ass_track_t* track, char *str)
char *p = str + 7; char *p = str + 7;
skip_spaces(&p); skip_spaces(&p);
track->style_format = strdup(p); track->style_format = strdup(p);
mp_msg(MSGT_ASS, MSGL_DBG2, "Style format: %s\n", track->style_format); ass_msg(MSGL_DBG2, "Style format: %s\n",
track->style_format);
} else if (!strncmp(str, "Style:", 6)) { } else if (!strncmp(str, "Style:", 6)) {
char *p = str + 6; char *p = str + 6;
skip_spaces(&p); skip_spaces(&p);
@ -528,13 +560,28 @@ static int process_info_line(ass_track_t* track, char *str)
return 0; return 0;
} }
static void event_format_fallback(ass_track_t *track)
{
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");
ass_msg(MSGL_V, "No event format found, using fallback.\n");
}
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)) { if (!strncmp(str, "Format:", 7)) {
char *p = str + 7; char *p = str + 7;
skip_spaces(&p); skip_spaces(&p);
track->event_format = strdup(p); track->event_format = strdup(p);
mp_msg(MSGT_ASS, MSGL_DBG2, "Event format: %s\n", track->event_format); ass_msg(MSGL_DBG2, "Event format: %s\n",
track->event_format);
} else if (!strncmp(str, "Dialogue:", 9)) { } else if (!strncmp(str, "Dialogue:", 9)) {
// This should never be reached for embedded subtitles. // This should never be reached for embedded subtitles.
// They have slightly different format and are parsed in ass_process_chunk, // They have slightly different format and are parsed in ass_process_chunk,
@ -548,22 +595,29 @@ static int process_events_line(ass_track_t* track, char *str)
eid = ass_alloc_event(track); eid = ass_alloc_event(track);
event = track->events + eid; event = track->events + eid;
// We can't parse events with event_format
if (!track->event_format)
event_format_fallback(track);
process_event_tail(track, event, str, 0); process_event_tail(track, event, str, 0);
} else { } else {
mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str); ass_msg(MSGL_V, "Not understood: %s \n", str);
} }
return 0; return 0;
} }
// Copied from mkvtoolnix // Copied from mkvtoolnix
static unsigned char *decode_chars(unsigned char c1, unsigned char c2, static unsigned char *decode_chars(unsigned char c1, unsigned char c2,
unsigned char c3, unsigned char c4, unsigned char* dst, int cnt) unsigned char c3, unsigned char c4,
unsigned char *dst, int cnt)
{ {
uint32_t value; uint32_t value;
unsigned char bytes[3]; unsigned char bytes[3];
int i; int i;
value = ((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 - 33); value =
((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 -
33);
bytes[2] = value & 0xff; bytes[2] = value & 0xff;
bytes[1] = (value & 0xff00) >> 8; bytes[1] = (value & 0xff00) >> 8;
bytes[0] = (value & 0xff0000) >> 16; bytes[0] = (value & 0xff0000) >> 16;
@ -582,15 +636,17 @@ static int decode_font(ass_track_t* track)
int dsize; // decoded size int dsize; // decoded size
unsigned char *buf = 0; unsigned char *buf = 0;
mp_msg(MSGT_ASS, MSGL_V, "font: %d bytes encoded data \n", track->parser_priv->fontdata_used); ass_msg(MSGL_V, "font: %d bytes encoded data \n",
track->parser_priv->fontdata_used);
size = track->parser_priv->fontdata_used; size = track->parser_priv->fontdata_used;
if (size % 4 == 1) { if (size % 4 == 1) {
mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_BadEncodedDataSize); ass_msg(MSGL_ERR, MSGTR_LIBASS_BadEncodedDataSize);
goto error_decode_font; goto error_decode_font;
} }
buf = malloc(size / 4 * 3 + 2); buf = malloc(size / 4 * 3 + 2);
q = buf; q = buf;
for (i = 0, p = (unsigned char*)track->parser_priv->fontdata; i < size / 4; i++, p+=4) { 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); q = decode_chars(p[0], p[1], p[2], p[3], q, 3);
} }
if (size % 4 == 2) { if (size % 4 == 2) {
@ -602,12 +658,14 @@ static int decode_font(ass_track_t* track)
assert(dsize <= size / 4 * 3 + 2); assert(dsize <= size / 4 * 3 + 2);
if (track->library->extract_fonts) { if (track->library->extract_fonts) {
ass_add_font(track->library, track->parser_priv->fontname, (char*)buf, dsize); ass_add_font(track->library, track->parser_priv->fontname,
(char *) buf, dsize);
buf = 0; buf = 0;
} }
error_decode_font: error_decode_font:
if (buf) free(buf); if (buf)
free(buf);
free(track->parser_priv->fontname); free(track->parser_priv->fontname);
free(track->parser_priv->fontdata); free(track->parser_priv->fontdata);
track->parser_priv->fontname = 0; track->parser_priv->fontname = 0;
@ -628,25 +686,30 @@ static int process_fonts_line(ass_track_t* track, char *str)
decode_font(track); decode_font(track);
} }
track->parser_priv->fontname = strdup(p); track->parser_priv->fontname = strdup(p);
mp_msg(MSGT_ASS, MSGL_V, "fontname: %s\n", track->parser_priv->fontname); ass_msg(MSGL_V, "fontname: %s\n",
track->parser_priv->fontname);
return 0; return 0;
} }
if (!track->parser_priv->fontname) { if (!track->parser_priv->fontname) {
mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str); ass_msg(MSGL_V, "Not understood: %s \n", str);
return 0; return 0;
} }
len = strlen(str); len = strlen(str);
if (len > 80) { if (len > 80) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontLineTooLong, len, str); ass_msg(MSGL_WARN, MSGTR_LIBASS_FontLineTooLong, len, str);
return 0; return 0;
} }
if (track->parser_priv->fontdata_used + len > track->parser_priv->fontdata_size) { if (track->parser_priv->fontdata_used + len >
track->parser_priv->fontdata_size) {
track->parser_priv->fontdata_size += 100 * 1024; track->parser_priv->fontdata_size += 100 * 1024;
track->parser_priv->fontdata = realloc(track->parser_priv->fontdata, track->parser_priv->fontdata_size); 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); memcpy(track->parser_priv->fontdata + track->parser_priv->fontdata_used,
str, len);
track->parser_priv->fontdata_used += len; track->parser_priv->fontdata_used += len;
return 0; return 0;
@ -691,7 +754,8 @@ static int process_line(ass_track_t* track, char *str)
} }
// there is no explicit end-of-font marker in ssa/ass // there is no explicit end-of-font marker in ssa/ass
if ((track->parser_priv->state != PST_FONTS) && (track->parser_priv->fontname)) if ((track->parser_priv->state != PST_FONTS)
&& (track->parser_priv->fontname))
decode_font(track); decode_font(track);
return 0; return 0;
@ -703,11 +767,15 @@ static int process_text(ass_track_t* track, char* str)
while (1) { while (1) {
char *q; char *q;
while (1) { while (1) {
if ((*p=='\r')||(*p=='\n')) ++p; if ((*p == '\r') || (*p == '\n'))
else if (p[0]=='\xef' && p[1]=='\xbb' && p[2]=='\xbf') p+=3; // U+FFFE (BOM) ++p;
else break; 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) {}; for (q = p; ((*q != '\0') && (*q != '\r') && (*q != '\n')); ++q) {
};
if (q == p) if (q == p)
break; break;
if (*q != '\0') if (*q != '\0')
@ -733,7 +801,7 @@ void ass_process_data(ass_track_t* track, char* data, int size)
memcpy(str, data, size); memcpy(str, data, size);
str[size] = '\0'; str[size] = '\0';
mp_msg(MSGT_ASS, MSGL_V, "event: %s\n", str); ass_msg(MSGL_V, "event: %s\n", str);
process_text(track, str); process_text(track, str);
free(str); free(str);
} }
@ -749,17 +817,12 @@ void ass_process_codec_private(ass_track_t* track, char *data, int size)
{ {
ass_process_data(track, data, size); ass_process_data(track, data, size);
if (!track->event_format) {
// probably an mkv produced by ancient mkvtoolnix // probably an mkv produced by ancient mkvtoolnix
// such files don't have [Events] and Format: headers // such files don't have [Events] and Format: headers
track->parser_priv->state = PST_EVENTS; if (!track->event_format)
if (track->track_type == TRACK_TYPE_SSA) event_format_fallback(track);
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_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)
@ -779,7 +842,8 @@ static int check_duplicate_event(ass_track_t* track, int ReadOrder)
* \param timecode starting time of the event (milliseconds) * \param timecode starting time of the event (milliseconds)
* \param duration duration 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; char *str;
int eid; int eid;
@ -788,14 +852,15 @@ void ass_process_chunk(ass_track_t* track, char *data, int size, long long timec
ass_event_t *event; ass_event_t *event;
if (!track->event_format) { if (!track->event_format) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EventFormatHeaderMissing); ass_msg(MSGL_WARN, MSGTR_LIBASS_EventFormatHeaderMissing);
return; return;
} }
str = malloc(size + 1); str = malloc(size + 1);
memcpy(str, data, size); memcpy(str, data, size);
str[size] = '\0'; str[size] = '\0';
mp_msg(MSGT_ASS, MSGL_V, "event at %" PRId64 ", +%" PRId64 ": %s \n", (int64_t)timecode, (int64_t)duration, str); ass_msg(MSGL_V, "event at %" PRId64 ", +%" PRId64 ": %s \n",
(int64_t) timecode, (int64_t) duration, str);
eid = ass_alloc_event(track); eid = ass_alloc_event(track);
event = track->events + eid; event = track->events + eid;
@ -835,7 +900,7 @@ void ass_process_chunk(ass_track_t* track, char *data, int size, long long timec
**/ **/
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); iconv_t icdsc;
char *tocp = "UTF-8"; char *tocp = "UTF-8";
char *outbuf; char *outbuf;
assert(codepage); assert(codepage);
@ -845,14 +910,18 @@ static char* sub_recode(char* data, size_t size, char* codepage)
#ifdef CONFIG_ENCA #ifdef CONFIG_ENCA
char enca_lang[3], enca_fallback[100]; char enca_lang[3], enca_fallback[100];
if (sscanf(codepage, "enca:%2s:%99s", enca_lang, enca_fallback) == 2 if (sscanf(codepage, "enca:%2s:%99s", enca_lang, enca_fallback) == 2
|| sscanf(codepage, "ENCA:%2s:%99s", enca_lang, enca_fallback) == 2) { || sscanf(codepage, "ENCA:%2s:%99s", enca_lang,
cp_tmp = guess_buffer_cp((unsigned char*)data, size, enca_lang, enca_fallback); enca_fallback) == 2) {
cp_tmp =
ass_guess_buffer_cp((unsigned char *) data, size, enca_lang,
enca_fallback);
} }
#endif #endif
if ((icdsc = iconv_open(tocp, cp_tmp)) != (iconv_t) (-1)) { if ((icdsc = iconv_open(tocp, cp_tmp)) != (iconv_t) (-1)) {
mp_msg(MSGT_ASS,MSGL_V,"LIBSUB: opened iconv descriptor.\n"); ass_msg(MSGL_V, "LIBSUB: opened iconv descriptor.\n");
} else } else
mp_msg(MSGT_ASS,MSGL_ERR,MSGTR_LIBASS_ErrorOpeningIconvDescriptor); ass_msg(MSGL_ERR,
MSGTR_LIBASS_ErrorOpeningIconvDescriptor);
} }
{ {
@ -883,11 +952,11 @@ static char* sub_recode(char* data, size_t size, char* codepage)
osize += size; osize += size;
oleft += size; oleft += size;
} else { } else {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorRecodingFile); ass_msg(MSGL_WARN,
MSGTR_LIBASS_ErrorRecodingFile);
return NULL; return NULL;
} }
} else } else if (clear)
if (clear)
break; break;
} }
outbuf[osize - oleft - 1] = 0; outbuf[osize - oleft - 1] = 0;
@ -896,7 +965,7 @@ static char* sub_recode(char* data, size_t size, char* codepage)
if (icdsc != (iconv_t) (-1)) { if (icdsc != (iconv_t) (-1)) {
(void) iconv_close(icdsc); (void) iconv_close(icdsc);
icdsc = (iconv_t) (-1); icdsc = (iconv_t) (-1);
mp_msg(MSGT_ASS,MSGL_V,"LIBSUB: closed iconv descriptor.\n"); ass_msg(MSGL_V, "LIBSUB: closed iconv descriptor.\n");
} }
return outbuf; return outbuf;
@ -918,12 +987,12 @@ static char* read_file(char* fname, size_t *bufsize)
FILE *fp = fopen(fname, "rb"); FILE *fp = fopen(fname, "rb");
if (!fp) { if (!fp) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FopenFailed, fname); ass_msg(MSGL_WARN, MSGTR_LIBASS_FopenFailed, fname);
return 0; return 0;
} }
res = fseek(fp, 0, SEEK_END); res = fseek(fp, 0, SEEK_END);
if (res == -1) { if (res == -1) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FseekFailed, fname); ass_msg(MSGL_WARN, MSGTR_LIBASS_FseekFailed, fname);
fclose(fp); fclose(fp);
return 0; return 0;
} }
@ -932,12 +1001,13 @@ static char* read_file(char* fname, size_t *bufsize)
rewind(fp); rewind(fp);
if (sz > 10 * 1024 * 1024) { if (sz > 10 * 1024 * 1024) {
mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_RefusingToLoadSubtitlesLargerThan10M, fname); ass_msg(MSGL_INFO,
MSGTR_LIBASS_RefusingToLoadSubtitlesLargerThan10M, fname);
fclose(fp); fclose(fp);
return 0; return 0;
} }
mp_msg(MSGT_ASS, MSGL_V, "file size: %ld\n", sz); ass_msg(MSGL_V, "file size: %ld\n", sz);
buf = malloc(sz + 1); buf = malloc(sz + 1);
assert(buf); assert(buf);
@ -945,7 +1015,8 @@ static char* read_file(char* fname, size_t *bufsize)
do { do {
res = fread(buf + bytes_read, 1, sz - bytes_read, fp); res = fread(buf + bytes_read, 1, sz - bytes_read, fp);
if (res <= 0) { if (res <= 0) {
mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_ReadFailed, errno, strerror(errno)); ass_msg(MSGL_INFO, MSGTR_LIBASS_ReadFailed, errno,
strerror(errno));
fclose(fp); fclose(fp);
free(buf); free(buf);
return 0; return 0;
@ -986,7 +1057,7 @@ static ass_track_t* parse_memory(ass_library_t* library, char* buf)
return 0; return 0;
} }
process_force_style(track); ass_process_force_style(track);
return track; return track;
} }
@ -999,7 +1070,8 @@ static ass_track_t* parse_memory(ass_library_t* library, char* buf)
* \param codepage recode buffer contents from given codepage * \param codepage recode buffer contents from given codepage
* \return newly allocated track * \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; ass_track_t *track;
int need_free = 0; int need_free = 0;
@ -1021,11 +1093,12 @@ ass_track_t* ass_read_memory(ass_library_t* library, char* buf, size_t bufsize,
if (!track) if (!track)
return 0; return 0;
mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileMemory, track->n_styles, track->n_events); ass_msg(MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileMemory,
track->n_styles, track->n_events);
return track; return track;
} }
char* read_file_recode(char* fname, char* codepage, size_t* size) static char *read_file_recode(char *fname, char *codepage, size_t *size)
{ {
char *buf; char *buf;
size_t bufsize; size_t bufsize;
@ -1053,7 +1126,8 @@ char* read_file_recode(char* fname, char* codepage, size_t* size)
* \param codepage recode buffer contents from given codepage * \param codepage recode buffer contents from given codepage
* \return newly allocated track * \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; char *buf;
ass_track_t *track; ass_track_t *track;
@ -1069,7 +1143,8 @@ ass_track_t* ass_read_file(ass_library_t* library, char* fname, char* codepage)
track->name = strdup(fname); track->name = strdup(fname);
mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname, track->n_styles, track->n_events); ass_msg(MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname,
track->n_styles, track->n_events);
// dump_events(forced_tid); // dump_events(forced_tid);
return track; return track;
@ -1106,30 +1181,43 @@ int ass_read_styles(ass_track_t* track, char* fname, char* codepage)
return 0; return 0;
} }
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)
{
int i; int i;
if (movement == 0) return 0; if (movement == 0)
if (track->n_events == 0) return 0; return 0;
if (track->n_events == 0)
return 0;
if (movement < 0) if (movement < 0)
for (i = 0; (i < track->n_events) && ((long long)(track->events[i].Start + track->events[i].Duration) <= now); ++i) {} for (i = 0;
else (i < track->n_events)
for (i = track->n_events - 1; (i >= 0) && ((long long)(track->events[i].Start) > now); --i) {} &&
((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 // -1 and n_events are ok
assert(i >= -1); assert(i <= track->n_events); assert(i >= -1);
assert(i <= track->n_events);
i += movement; i += movement;
if (i < 0) i = 0; if (i < 0)
if (i >= track->n_events) i = track->n_events - 1; i = 0;
if (i >= track->n_events)
i = track->n_events - 1;
return ((long long) track->events[i].Start) - now; return ((long long) track->events[i].Start) - now;
} }
ass_track_t* ass_new_track(ass_library_t* library) { ass_track_t *ass_new_track(ass_library_t *library)
{
ass_track_t *track = calloc(1, sizeof(ass_track_t)); ass_track_t *track = calloc(1, sizeof(ass_track_t));
track->library = library; track->library = library;
track->ScaledBorderAndShadow = 1; track->ScaledBorderAndShadow = 1;
track->parser_priv = calloc(1, sizeof(parser_priv_t)); track->parser_priv = calloc(1, sizeof(parser_priv_t));
return track; return track;
} }

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -69,6 +67,8 @@ 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);
void ass_process_force_style(ass_track_t *track);
/** /**
* \brief initialize the renderer * \brief initialize the renderer
* \param priv library handle * \param priv library handle
@ -85,20 +85,19 @@ void ass_renderer_done(ass_renderer_t* priv);
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);
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);
void ass_set_use_margins(ass_renderer_t *priv, int use); 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_aspect_ratio(ass_renderer_t *priv, double ar, double par);
void ass_set_font_scale(ass_renderer_t *priv, double font_scale); 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_hinting(ass_renderer_t *priv, ass_hinting_t ht);
void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing); void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing);
/** /**
* \brief set font lookup defaults * \brief set font lookup defaults
* \param fc bool, use fontconfig?
* \param config path to fontconfig configuration file, or NULL. Only matters
* if fontconfig is used
*/ */
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, int fc, const char *config);
/**
* \brief set font lookup defaults, don't use fontconfig even if it is available
*/
int ass_set_fonts_nofc(ass_renderer_t* priv, const char* default_font, const char* default_family);
/** /**
* \brief render a frame, producing a list of ass_image_t * \brief render a frame, producing a list of ass_image_t
@ -106,7 +105,8 @@ int ass_set_fonts_nofc(ass_renderer_t* priv, const char* default_font, const ch
* \param track subtitle track * \param track subtitle track
* \param now video timestamp in milliseconds * \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 // // The following functions operate on track objects and do not need an ass_renderer //
@ -177,16 +177,16 @@ void ass_process_codec_private(ass_track_t* track, char *data, int size);
* \param timecode starting time of the event (milliseconds) * \param timecode starting time of the event (milliseconds)
* \param duration duration 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);
/** /**
* \brief Read subtitles from file. * \brief Read subtitles from file.
* \param fname file name * \param fname file name
* \return newly allocated track * \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. * \brief Read subtitles from memory.
@ -196,7 +196,8 @@ ass_track_t* ass_read_file(ass_library_t* library, char* fname, char* codepage);
* \param codepage recode buffer contents from given codepage * \param codepage recode buffer contents from given codepage
* \return newly allocated track * \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 * \brief read styles from file into already initialized track
* \return 0 on success * \return 0 on success
@ -209,7 +210,8 @@ int ass_read_styles(ass_track_t* track, char* fname, char* codepage);
* \param data binary font data * \param data binary font data
* \param data_size data size * \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 * \brief Remove all fonts stored in ass_library object

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -27,7 +25,7 @@
#include <ft2build.h> #include <ft2build.h>
#include FT_GLYPH_H #include FT_GLYPH_H
#include "mputils.h" #include "ass_utils.h"
#include "ass_bitmap.h" #include "ass_bitmap.h"
struct ass_synth_priv_s { struct ass_synth_priv_s {
@ -71,18 +69,24 @@ static int generate_tables(ass_synth_priv_t* priv, double radius)
if (priv->g_r) { if (priv->g_r) {
// gaussian curve with volume = 256 // gaussian curve with volume = 256
for (volume_diff=10000000; volume_diff>0.0000001; volume_diff*=0.5){ for (volume_diff = 10000000; volume_diff > 0.0000001;
volume_diff *= 0.5) {
volume_factor += volume_diff; volume_factor += volume_diff;
volume = 0; volume = 0;
for (i = 0; i < priv->g_w; ++i) { 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); priv->g[i] =
(unsigned) (exp(A * (i - priv->g_r) * (i - priv->g_r)) *
volume_factor + .5);
volume += priv->g[i]; volume += priv->g[i];
} }
if(volume>256) volume_factor-= volume_diff; if (volume > 256)
volume_factor -= volume_diff;
} }
volume = 0; volume = 0;
for (i = 0; i < priv->g_w; ++i) { 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); priv->g[i] =
(unsigned) (exp(A * (i - priv->g_r) * (i - priv->g_r)) *
volume_factor + .5);
volume += priv->g[i]; volume += priv->g[i];
} }
@ -105,8 +109,10 @@ static void resize_tmp(ass_synth_priv_t* priv, int w, int h)
priv->tmp_w = 64; priv->tmp_w = 64;
if (priv->tmp_h == 0) if (priv->tmp_h == 0)
priv->tmp_h = 64; priv->tmp_h = 64;
while (priv->tmp_w < w) priv->tmp_w *= 2; while (priv->tmp_w < w)
while (priv->tmp_h < h) priv->tmp_h *= 2; priv->tmp_w *= 2;
while (priv->tmp_h < h)
priv->tmp_h *= 2;
if (priv->tmp) if (priv->tmp)
free(priv->tmp); free(priv->tmp);
priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short)); priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short));
@ -144,7 +150,8 @@ static bitmap_t* alloc_bitmap(int w, int h)
void ass_free_bitmap(bitmap_t *bm) void ass_free_bitmap(bitmap_t *bm)
{ {
if (bm) { if (bm) {
if (bm->buffer) free(bm->buffer); if (bm->buffer)
free(bm->buffer);
free(bm); free(bm);
} }
} }
@ -166,7 +173,8 @@ static int check_glyph_area(FT_Glyph glyph)
dx = bbox.xMax - bbox.xMin; dx = bbox.xMax - bbox.xMin;
dy = bbox.yMax - bbox.yMin; dy = bbox.yMax - bbox.yMin;
if (dx * dy > 8000000) { if (dx * dy > 8000000) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_GlyphBBoxTooLarge, (int)dx, (int)dy); ass_msg(MSGL_WARN, MSGTR_LIBASS_GlyphBBoxTooLarge,
(int) dx, (int) dy);
return 1; return 1;
} else } else
return 0; return 0;
@ -187,14 +195,16 @@ static bitmap_t* glyph_to_bitmap_internal(FT_Glyph glyph, int bord)
return 0; return 0;
error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0); error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0);
if (error) { if (error) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FT_Glyph_To_BitmapError, error); ass_msg(MSGL_WARN, MSGTR_LIBASS_FT_Glyph_To_BitmapError,
error);
return 0; return 0;
} }
bg = (FT_BitmapGlyph) glyph; bg = (FT_BitmapGlyph) glyph;
bit = &(bg->bitmap); bit = &(bg->bitmap);
if (bit->pixel_mode != FT_PIXEL_MODE_GRAY) { if (bit->pixel_mode != FT_PIXEL_MODE_GRAY) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UnsupportedPixelMode, (int)(bit->pixel_mode)); ass_msg(MSGL_WARN, MSGTR_LIBASS_UnsupportedPixelMode,
(int) (bit->pixel_mode));
FT_Done_Glyph(glyph); FT_Done_Glyph(glyph);
return 0; return 0;
} }
@ -229,14 +239,21 @@ static bitmap_t* fix_outline_and_shadow(bitmap_t* bm_g, bitmap_t* bm_o)
int x, y; int x, y;
const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left; 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 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 r =
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; 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); 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 *g =
unsigned char* o = bm_o->buffer + (t - bm_o->top) * bm_o->w + (l - bm_o->left); bm_g->buffer + (t - bm_g->top) * bm_g->w + (l - bm_g->left);
unsigned char* s = bm_s->buffer + (t - bm_s->top) * bm_s->w + (l - bm_s->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 (y = 0; y < b - t; ++y) {
for (x = 0; x < r - l; ++x) { for (x = 0; x < r - l; ++x) {
@ -255,11 +272,62 @@ static bitmap_t* fix_outline_and_shadow(bitmap_t* bm_g, bitmap_t* bm_o)
return bm_s; return bm_s;
} }
/**
* \brief Shift a bitmap by the fraction of a pixel in x and y direction
* expressed in 26.6 fixed point
*/
static void shift_bitmap(unsigned char *buf, int w, int h, int shift_x,
int shift_y)
{
int x, y, b;
// Shift in x direction
if (shift_x > 0) {
for (y = 0; y < h; y++) {
for (x = w - 1; x > 0; x--) {
b = (buf[x + y * w - 1] * shift_x) >> 6;
buf[x + y * w - 1] -= b;
buf[x + y * w] += b;
}
}
} else if (shift_x < 0) {
shift_x = -shift_x;
for (y = 0; y < h; y++) {
for (x = 0; x < w - 1; x++) {
b = (buf[x + y * w + 1] * shift_x) >> 6;
buf[x + y * w + 1] -= b;
buf[x + y * w] += b;
}
}
}
// Shift in y direction
if (shift_y > 0) {
for (x = 0; x < w; x++) {
for (y = h - 1; y > 0; y--) {
b = (buf[x + (y - 1) * w] * shift_y) >> 6;
buf[x + (y - 1) * w] -= b;
buf[x + y * w] += b;
}
}
} else if (shift_y < 0) {
shift_y = -shift_y;
for (x = 0; x < w; x++) {
for (y = 0; y < h - 1; y++) {
b = (buf[x + (y + 1) * w] * shift_y) >> 6;
buf[x + (y + 1) * w] -= b;
buf[x + y * w] += b;
}
}
}
}
/** /**
* \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
* This blur is the same as the one employed by vsfilter. * This blur is the same as the one employed by vsfilter.
*/ */
static void be_blur(unsigned char *buf, int w, int h) { static void be_blur(unsigned char *buf, int w, int h)
{
unsigned int x, y; unsigned int x, y;
unsigned int old_sum, new_sum; unsigned int old_sum, new_sum;
@ -283,12 +351,15 @@ static void be_blur(unsigned char *buf, int w, int h) {
} }
int glyph_to_bitmap(ass_synth_priv_t *priv_blur, int glyph_to_bitmap(ass_synth_priv_t *priv_blur,
FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g, FT_Glyph glyph, FT_Glyph outline_glyph,
bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius) bitmap_t **bm_g, bitmap_t **bm_o, bitmap_t **bm_s,
int be, double blur_radius, FT_Vector shadow_offset)
{ {
int bord = be ? (be / 4 + 1) : 0; int bord = be ? (be / 4 + 1) : 0;
blur_radius *= 2; blur_radius *= 2;
bord = (blur_radius > 0.0) ? blur_radius : bord; bord = (blur_radius > 0.0) ? blur_radius + 1 : bord;
if (bord == 0 && (shadow_offset.x || shadow_offset.y))
bord = 1;
assert(bm_g && bm_o && bm_s); assert(bm_g && bm_o && bm_s);
@ -321,9 +392,15 @@ int glyph_to_bitmap(ass_synth_priv_t* priv_blur,
if (blur_radius > 0.0) { if (blur_radius > 0.0) {
generate_tables(priv_blur, blur_radius); generate_tables(priv_blur, blur_radius);
if (*bm_o) 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); ass_gauss_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 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); ass_gauss_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) if (*bm_o)
@ -331,7 +408,9 @@ int glyph_to_bitmap(ass_synth_priv_t* priv_blur,
else else
*bm_s = copy_bitmap(*bm_g); *bm_s = copy_bitmap(*bm_g);
shift_bitmap((*bm_s)->buffer, (*bm_s)->w,(*bm_s)->h,
shadow_offset.x, shadow_offset.y);
assert(bm_s); assert(bm_s);
return 0; return 0;
} }

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -46,7 +44,10 @@ typedef struct bitmap_s {
* \param bm_g out: pointer to the bitmap of glyph shadow is returned here * \param bm_g out: pointer to the bitmap of glyph shadow is returned here
* \param be 1 = produces blurred bitmaps, 0 = normal bitmaps * \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, FT_Vector shadow_offset);
void ass_free_bitmap(bitmap_t *bm); void ass_free_bitmap(bitmap_t *bm);

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -29,56 +27,13 @@
#include <assert.h> #include <assert.h>
#include "mputils.h" #include "ass_utils.h"
#include "ass.h" #include "ass.h"
#include "ass_fontconfig.h" #include "ass_fontconfig.h"
#include "ass_font.h" #include "ass_font.h"
#include "ass_bitmap.h" #include "ass_bitmap.h"
#include "ass_cache.h" #include "ass_cache.h"
typedef struct hashmap_item_s {
void* key;
void* value;
struct hashmap_item_s* next;
} hashmap_item_t;
typedef hashmap_item_t* hashmap_item_p;
struct hashmap_s {
int nbuckets;
size_t key_size, value_size;
hashmap_item_p* root;
hashmap_item_dtor_t item_dtor; // a destructor for hashmap key/value pairs
hashmap_key_compare_t key_compare;
hashmap_hash_t hash;
// stats
int hit_count;
int miss_count;
int count;
};
#define FNV1_32A_INIT (unsigned)0x811c9dc5
static inline unsigned fnv_32a_buf(void* buf, size_t len, unsigned hval)
{
unsigned char *bp = buf;
unsigned char *be = bp + len;
while (bp < be) {
hval ^= (unsigned)*bp++;
hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
}
return hval;
}
static inline unsigned fnv_32a_str(char* str, unsigned hval)
{
unsigned char* s = (unsigned char*)str;
while (*s) {
hval ^= (unsigned)*s++;
hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
}
return hval;
}
static unsigned hashmap_hash(void *buf, size_t len) 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);
@ -89,14 +44,16 @@ 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(key);
free(value); free(value);
} }
hashmap_t *hashmap_init(size_t key_size, size_t value_size, int nbuckets, 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_item_dtor_t item_dtor,
hashmap_key_compare_t key_compare,
hashmap_hash_t hash) hashmap_hash_t hash)
{ {
hashmap_t *map = calloc(1, sizeof(hashmap_t)); hashmap_t *map = calloc(1, sizeof(hashmap_t));
@ -115,14 +72,17 @@ void hashmap_done(hashmap_t* map)
int i; int i;
// print stats // print stats
if (map->count > 0 || map->hit_count + map->miss_count > 0) 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", ass_msg(MSGL_V,
map->hit_count + map->miss_count, map->hit_count, map->miss_count, map->count); "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) { for (i = 0; i < map->nbuckets; ++i) {
hashmap_item_t *item = map->root[i]; hashmap_item_t *item = map->root[i];
while (item) { while (item) {
hashmap_item_t *next = item->next; hashmap_item_t *next = item->next;
map->item_dtor(item->key, map->key_size, item->value, map->value_size); map->item_dtor(item->key, map->key_size, item->value,
map->value_size);
free(item); free(item);
item = next; item = next;
} }
@ -171,8 +131,6 @@ void* hashmap_find(hashmap_t* map, void* key)
//--------------------------------- //---------------------------------
// font cache // font cache
hashmap_t* 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; ass_font_desc_t *desc = buf;
@ -183,7 +141,8 @@ static unsigned font_desc_hash(void* buf, size_t len)
return hval; return hval;
} }
static int font_compare(void* key1, void* key2, size_t key_size) { static int font_compare(void *key1, void *key2, size_t key_size)
{
ass_font_desc_t *a = key1; ass_font_desc_t *a = key1;
ass_font_desc_t *b = key2; ass_font_desc_t *b = key2;
if (strcmp(a->family, b->family) != 0) if (strcmp(a->family, b->family) != 0)
@ -197,13 +156,15 @@ static int font_compare(void* key1, void* key2, size_t key_size) {
return 1; 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); ass_font_free(value);
free(key); free(key);
} }
ass_font_t* ass_font_cache_find(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);
} }
@ -212,20 +173,22 @@ ass_font_t* ass_font_cache_find(ass_font_desc_t* desc)
* \brief Add a face struct to cache. * \brief Add a face struct to cache.
* \param font font struct * \param font font struct
*/ */
void* ass_font_cache_add(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);
} }
void 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), font_cache = hashmap_init(sizeof(ass_font_desc_t),
sizeof(ass_font_t), sizeof(ass_font_t),
1000, 1000,
font_hash_dtor, font_compare, font_desc_hash); font_hash_dtor, font_compare, font_desc_hash);
return font_cache;
} }
void ass_font_cache_done(void) void ass_font_cache_done(hashmap_t *font_cache)
{ {
hashmap_done(font_cache); hashmap_done(font_cache);
} }
@ -240,19 +203,22 @@ void ass_font_cache_done(void)
//--------------------------------- //---------------------------------
// bitmap cache // bitmap cache
hashmap_t* 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; bitmap_hash_val_t *v = value;
if (v->bm) ass_free_bitmap(v->bm); if (v->bm)
if (v->bm_o) ass_free_bitmap(v->bm_o); ass_free_bitmap(v->bm);
if (v->bm_s) ass_free_bitmap(v->bm_s); if (v->bm_o)
ass_free_bitmap(v->bm_o);
if (v->bm_s)
ass_free_bitmap(v->bm_s);
free(key); free(key);
free(value); free(value);
} }
void* cache_add_bitmap(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);
} }
@ -262,46 +228,51 @@ void* cache_add_bitmap(bitmap_hash_key_t* key, bitmap_hash_val_t* val)
* \param key hash key * \param key hash key
* \return requested hash val or 0 if not found * \return requested hash val or 0 if not found
*/ */
bitmap_hash_val_t* cache_find_bitmap(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);
} }
void 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), bitmap_cache = hashmap_init(sizeof(bitmap_hash_key_t),
sizeof(bitmap_hash_val_t), sizeof(bitmap_hash_val_t),
0xFFFF + 13, 0xFFFF + 13,
bitmap_hash_dtor, bitmap_compare, bitmap_hash_dtor, bitmap_compare,
bitmap_hash); bitmap_hash);
return bitmap_cache;
} }
void ass_bitmap_cache_done(void) void ass_bitmap_cache_done(hashmap_t *bitmap_cache)
{ {
hashmap_done(bitmap_cache); hashmap_done(bitmap_cache);
} }
void ass_bitmap_cache_reset(void) hashmap_t *ass_bitmap_cache_reset(hashmap_t *bitmap_cache)
{ {
ass_bitmap_cache_done(); ass_bitmap_cache_done(bitmap_cache);
ass_bitmap_cache_init(); return ass_bitmap_cache_init();
} }
//--------------------------------- //---------------------------------
// glyph cache // glyph cache
hashmap_t* 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; glyph_hash_val_t *v = value;
if (v->glyph) FT_Done_Glyph(v->glyph); if (v->glyph)
if (v->outline_glyph) FT_Done_Glyph(v->outline_glyph); FT_Done_Glyph(v->glyph);
if (v->outline_glyph)
FT_Done_Glyph(v->outline_glyph);
free(key); free(key);
free(value); free(value);
} }
void* cache_add_glyph(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);
} }
@ -311,37 +282,39 @@ void* cache_add_glyph(glyph_hash_key_t* key, glyph_hash_val_t* val)
* \param key hash key * \param key hash key
* \return requested hash val or 0 if not found * \return requested hash val or 0 if not found
*/ */
glyph_hash_val_t* cache_find_glyph(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);
} }
void 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), glyph_cache = hashmap_init(sizeof(glyph_hash_key_t),
sizeof(glyph_hash_val_t), sizeof(glyph_hash_val_t),
0xFFFF + 13, 0xFFFF + 13,
glyph_hash_dtor, glyph_compare, glyph_hash); glyph_hash_dtor, glyph_compare, glyph_hash);
return glyph_cache;
} }
void ass_glyph_cache_done(void) void ass_glyph_cache_done(hashmap_t *glyph_cache)
{ {
hashmap_done(glyph_cache); hashmap_done(glyph_cache);
} }
void ass_glyph_cache_reset(void) hashmap_t *ass_glyph_cache_reset(hashmap_t *glyph_cache)
{ {
ass_glyph_cache_done(); ass_glyph_cache_done(glyph_cache);
ass_glyph_cache_init(); return ass_glyph_cache_init();
} }
//--------------------------------- //---------------------------------
// composite cache // composite cache
hashmap_t* 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; composite_hash_val_t *v = value;
free(v->a); free(v->a);
@ -350,7 +323,9 @@ static void composite_hash_dtor(void* key, size_t key_size, void* value, size_t
free(value); free(value);
} }
void* cache_add_composite(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);
} }
@ -360,27 +335,30 @@ void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val)
* \param key hash key * \param key hash key
* \return requested hash val or 0 if not found * \return requested hash val or 0 if not found
*/ */
composite_hash_val_t* cache_find_composite(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);
} }
void 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), composite_cache = hashmap_init(sizeof(composite_hash_key_t),
sizeof(composite_hash_val_t), sizeof(composite_hash_val_t),
0xFFFF + 13, 0xFFFF + 13,
composite_hash_dtor, NULL, NULL); composite_hash_dtor, composite_compare,
composite_hash);
return composite_cache;
} }
void ass_composite_cache_done(void) void ass_composite_cache_done(hashmap_t *composite_cache)
{ {
hashmap_done(composite_cache); hashmap_done(composite_cache);
} }
void ass_composite_cache_reset(void) hashmap_t *ass_composite_cache_reset(hashmap_t *composite_cache)
{ {
ass_composite_cache_done(); ass_composite_cache_done(composite_cache);
ass_composite_cache_init(); return ass_composite_cache_init();
} }

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -27,11 +25,44 @@
#include "ass_font.h" #include "ass_font.h"
#include "ass_bitmap.h" #include "ass_bitmap.h"
void ass_font_cache_init(void); typedef void (*hashmap_item_dtor_t) (void *key, size_t key_size,
ass_font_t* ass_font_cache_find(ass_font_desc_t* desc); void *value, size_t value_size);
void* ass_font_cache_add(ass_font_t* font); typedef int (*hashmap_key_compare_t) (void *key1, void *key2,
void ass_font_cache_done(void); 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;
} hashmap_item_t;
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;
} 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 *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 // Create definitions for bitmap_hash_key and glyph_hash_key
#define CREATE_STRUCT_DEFINITIONS #define CREATE_STRUCT_DEFINITIONS
@ -43,31 +74,27 @@ typedef struct bitmap_hash_val_s {
bitmap_t *bm_s; bitmap_t *bm_s;
} bitmap_hash_val_t; } bitmap_hash_val_t;
void ass_bitmap_cache_init(void); hashmap_t *ass_bitmap_cache_init(void);
void* cache_add_bitmap(bitmap_hash_key_t* key, bitmap_hash_val_t* val); void *cache_add_bitmap(hashmap_t *, bitmap_hash_key_t *key,
bitmap_hash_val_t* cache_find_bitmap(bitmap_hash_key_t* key); bitmap_hash_val_t *val);
void ass_bitmap_cache_reset(void); bitmap_hash_val_t *cache_find_bitmap(hashmap_t *bitmap_cache,
void ass_bitmap_cache_done(void); 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;
} composite_hash_key_t;
typedef struct composite_hash_val_s { typedef struct composite_hash_val_s {
unsigned char *a; unsigned char *a;
unsigned char *b; unsigned char *b;
} composite_hash_val_t; } composite_hash_val_t;
void ass_composite_cache_init(void); hashmap_t *ass_composite_cache_init(void);
void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val); void *cache_add_composite(hashmap_t *, composite_hash_key_t *key,
composite_hash_val_t* cache_find_composite(composite_hash_key_t* key); composite_hash_val_t *val);
void ass_composite_cache_reset(void); composite_hash_val_t *cache_find_composite(hashmap_t *composite_cache,
void ass_composite_cache_done(void); 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 { typedef struct glyph_hash_val_s {
@ -75,24 +102,15 @@ typedef struct glyph_hash_val_s {
FT_Glyph outline_glyph; FT_Glyph outline_glyph;
FT_BBox bbox_scaled; // bbox after scaling, but before rotation FT_BBox bbox_scaled; // bbox after scaling, but before rotation
FT_Vector advance; // 26.6, advance distance to the next bitmap in line FT_Vector advance; // 26.6, advance distance to the next bitmap in line
int asc, desc; // ascender/descender of a drawing
} glyph_hash_val_t; } glyph_hash_val_t;
void ass_glyph_cache_init(void); hashmap_t *ass_glyph_cache_init(void);
void* cache_add_glyph(glyph_hash_key_t* key, glyph_hash_val_t* val); void *cache_add_glyph(hashmap_t *, glyph_hash_key_t *key,
glyph_hash_val_t* cache_find_glyph(glyph_hash_key_t* key); glyph_hash_val_t *val);
void ass_glyph_cache_reset(void); glyph_hash_val_t *cache_find_glyph(hashmap_t *glyph_cache,
void ass_glyph_cache_done(void); glyph_hash_key_t *key);
hashmap_t *ass_glyph_cache_reset(hashmap_t *glyph_cache);
typedef struct hashmap_s hashmap_t; void ass_glyph_cache_done(hashmap_t *glyph_cache);
typedef void (*hashmap_item_dtor_t)(void* key, size_t key_size, void* value, size_t value_size);
typedef int (*hashmap_key_compare_t)(void* key1, void* key2, size_t key_size);
typedef unsigned (*hashmap_hash_t)(void* key, size_t key_size);
hashmap_t* hashmap_init(size_t key_size, size_t value_size, int nbuckets,
hashmap_item_dtor_t item_dtor, hashmap_key_compare_t key_compare,
hashmap_hash_t hash);
void hashmap_done(hashmap_t* map);
void* hashmap_insert(hashmap_t* map, void* key, void* value);
void* hashmap_find(hashmap_t* map, void* key);
#endif /* LIBASS_CACHE_H */ #endif /* LIBASS_CACHE_H */

View file

@ -6,6 +6,8 @@
type member; type member;
#define FTVECTOR(member) \ #define FTVECTOR(member) \
FT_Vector member; FT_Vector member;
#define BITMAPHASHKEY(member) \
bitmap_hash_key_t member;
#define END(typedefnamename) \ #define END(typedefnamename) \
} typedefnamename; } typedefnamename;
@ -21,6 +23,8 @@
a->member == b->member && a->member == b->member &&
#define FTVECTOR(member) \ #define FTVECTOR(member) \
a->member.x == b->member.x && a->member.y == b->member.y && a->member.x == b->member.x && a->member.y == b->member.y &&
#define BITMAPHASHKEY(member) \
bitmap_compare(&a->member, &b->member, sizeof(a->member)) &&
#define END(typedefname) \ #define END(typedefname) \
1; \ 1; \
} }
@ -35,6 +39,10 @@
#define GENERIC(type, member) \ #define GENERIC(type, member) \
hval = fnv_32a_buf(&p->member, sizeof(p->member), hval); hval = fnv_32a_buf(&p->member, sizeof(p->member), hval);
#define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y); #define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y);
#define BITMAPHASHKEY(member) { \
unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \
hval = fnv_32a_buf(&temp, sizeof(temp), hval); \
}
#define END(typedefname) \ #define END(typedefname) \
return hval; \ return hval; \
} }
@ -51,7 +59,7 @@ START(bitmap, bipmap_hash_key_s)
GENERIC(ass_font_t *, font) GENERIC(ass_font_t *, font)
GENERIC(double, size) // font size GENERIC(double, size) // font size
GENERIC(uint32_t, ch) // character code GENERIC(uint32_t, ch) // character code
GENERIC(unsigned, outline) // border width, 16.16 fixed point value FTVECTOR(outline) // border width, 16.16 fixed point value
GENERIC(int, bold) GENERIC(int, bold)
GENERIC(int, italic) GENERIC(int, italic)
GENERIC(char, be) // blur edges GENERIC(char, be) // blur edges
@ -61,12 +69,16 @@ START(bitmap, bipmap_hash_key_s)
GENERIC(int, frx) // signed 16.16 GENERIC(int, frx) // signed 16.16
GENERIC(int, fry) // signed 16.16 GENERIC(int, fry) // signed 16.16
GENERIC(int, frz) // signed 16.16 GENERIC(int, frz) // signed 16.16
GENERIC(int, fax) // signed 16.16
GENERIC(int, fay) // signed 16.16
// shift vector that was added to glyph before applying rotation // shift vector that was added to glyph before applying rotation
// = 0, if frx = fry = frx = 0 // = 0, if frx = fry = frx = 0
// = (glyph base point) - (rotation origin), otherwise // = (glyph base point) - (rotation origin), otherwise
GENERIC(int, shift_x) GENERIC(int, shift_x)
GENERIC(int, shift_y) GENERIC(int, shift_y)
FTVECTOR(advance) // subpixel shift vector FTVECTOR(advance) // subpixel shift vector
FTVECTOR(shadow_offset) // shadow subpixel shift
GENERIC(unsigned, drawing_hash) // hashcode of a drawing
END(bitmap_hash_key_t) END(bitmap_hash_key_t)
// describes an outline glyph // describes an outline glyph
@ -79,10 +91,28 @@ START(glyph, glyph_hash_key_s)
GENERIC(unsigned, scale_x) // 16.16 GENERIC(unsigned, scale_x) // 16.16
GENERIC(unsigned, scale_y) // 16.16 GENERIC(unsigned, scale_y) // 16.16
FTVECTOR(advance) // subpixel shift vector FTVECTOR(advance) // subpixel shift vector
GENERIC(unsigned, outline) // border width, 16.16 FTVECTOR(outline) // border width, 16.16
GENERIC(unsigned, drawing_hash) // hashcode of a drawing
GENERIC(unsigned, flags) // glyph decoration flags
END(glyph_hash_key_t) END(glyph_hash_key_t)
// Cache for composited bitmaps
START(composite, composite_hash_key_s)
GENERIC(int, aw)
GENERIC(int, ah)
GENERIC(int, bw)
GENERIC(int, bh)
GENERIC(int, ax)
GENERIC(int, ay)
GENERIC(int, bx)
GENERIC(int, by)
BITMAPHASHKEY(a)
BITMAPHASHKEY(b)
END(composite_hash_key_t)
#undef START #undef START
#undef GENERIC #undef GENERIC
#undef FTVECTOR #undef FTVECTOR
#undef BITMAPHASHKEY
#undef END #undef END

View file

@ -0,0 +1,477 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <ft2build.h>
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include FT_BBOX_H
#include <math.h>
#include "ass_utils.h"
#include "ass_font.h"
#include "ass_drawing.h"
#define CURVE_ACCURACY 64.0
#define GLYPH_INITIAL_POINTS 100
#define GLYPH_INITIAL_CONTOURS 5
/*
* \brief Get and prepare a FreeType glyph
*/
static void drawing_make_glyph(ass_drawing_t *drawing, void *fontconfig_priv,
ass_font_t *font, ass_hinting_t hint)
{
FT_OutlineGlyph glyph;
// This is hacky...
glyph = (FT_OutlineGlyph) ass_font_get_glyph(fontconfig_priv, font,
(uint32_t) ' ', hint, 0);
FT_Outline_Done(drawing->ftlibrary, &glyph->outline);
FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS,
GLYPH_INITIAL_CONTOURS, &glyph->outline);
glyph->outline.n_contours = 0;
glyph->outline.n_points = 0;
glyph->root.advance.x = glyph->root.advance.y = 0;
drawing->glyph = glyph;
}
/*
* \brief Add a single point to a contour.
*/
static inline void drawing_add_point(ass_drawing_t *drawing,
FT_Vector *point)
{
FT_Outline *ol = &drawing->glyph->outline;
if (ol->n_points >= drawing->max_points) {
drawing->max_points *= 2;
ol->points = realloc(ol->points, sizeof(FT_Vector) *
drawing->max_points);
ol->tags = realloc(ol->tags, drawing->max_points);
}
ol->points[ol->n_points].x = point->x;
ol->points[ol->n_points].y = point->y;
ol->tags[ol->n_points] = 1;
ol->n_points++;
}
/*
* \brief Close a contour and check glyph size overflow.
*/
static inline void drawing_close_shape(ass_drawing_t *drawing)
{
FT_Outline *ol = &drawing->glyph->outline;
if (ol->n_contours >= drawing->max_contours) {
drawing->max_contours *= 2;
ol->contours = realloc(ol->contours, sizeof(short) *
drawing->max_contours);
}
ol->contours[ol->n_contours] = ol->n_points - 1;
ol->n_contours++;
}
/*
* \brief Prepare drawing for parsing. This just sets a few parameters.
*/
static void drawing_prepare(ass_drawing_t *drawing)
{
// Scaling parameters
drawing->point_scale_x = drawing->scale_x *
64.0 / (1 << (drawing->scale - 1));
drawing->point_scale_y = drawing->scale_y *
64.0 / (1 << (drawing->scale - 1));
}
/*
* \brief Finish a drawing. This only sets the horizontal advance according
* to the glyph's bbox at the moment.
*/
static void drawing_finish(ass_drawing_t *drawing)
{
int i, offset;
FT_BBox bbox;
FT_Outline *ol = &drawing->glyph->outline;
// Close the last contour
drawing_close_shape(drawing);
#if 0
// Dump points
for (i = 0; i < ol->n_points; i++) {
printf("point (%d, %d)\n", (int) ol->points[i].x,
(int) ol->points[i].y);
}
// Dump contours
for (i = 0; i < ol->n_contours; i++)
printf("contour %d\n", ol->contours[i]);
#endif
FT_Outline_Get_CBox(&drawing->glyph->outline, &bbox);
drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin);
drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y);
drawing->asc = bbox.yMax - bbox.yMin + drawing->desc;
// Place it onto the baseline
offset = (bbox.yMax - bbox.yMin) + double_to_d6(-drawing->pbo *
drawing->scale_y);
for (i = 0; i < ol->n_points; i++)
ol->points[i].y += offset;
}
/*
* \brief Check whether a number of items on the list is available
*/
static int token_check_values(ass_drawing_token_t *token, int i, int type)
{
int j;
for (j = 0; j < i; j++) {
if (!token || token->type != type) return 0;
token = token->next;
}
return 1;
}
/*
* \brief Tokenize a drawing string into a list of ass_drawing_token_t
* This also expands points for closing b-splines
*/
static ass_drawing_token_t *drawing_tokenize(char *str)
{
char *p = str;
int i, val, type = -1, is_set = 0;
FT_Vector point = {0, 0};
ass_drawing_token_t *root = NULL, *tail = NULL, *spline_start = NULL;
while (*p) {
if (*p == 'c' && spline_start) {
// Close b-splines: add the first three points of the b-spline
// back to the end
if (token_check_values(spline_start->next, 2, TOKEN_B_SPLINE)) {
for (i = 0; i < 3; i++) {
tail->next = calloc(1, sizeof(ass_drawing_token_t));
tail->next->prev = tail;
tail = tail->next;
tail->type = TOKEN_B_SPLINE;
tail->point = spline_start->point;
spline_start = spline_start->next;
}
spline_start = NULL;
}
} else if (!is_set && mystrtoi(&p, &val)) {
point.x = val;
is_set = 1;
p--;
} else if (is_set == 1 && mystrtoi(&p, &val)) {
point.y = val;
is_set = 2;
p--;
} else if (*p == 'm')
type = TOKEN_MOVE;
else if (*p == 'n')
type = TOKEN_MOVE_NC;
else if (*p == 'l')
type = TOKEN_LINE;
else if (*p == 'b')
type = TOKEN_CUBIC_BEZIER;
else if (*p == 'q')
type = TOKEN_CONIC_BEZIER;
else if (*p == 's')
type = TOKEN_B_SPLINE;
// We're simply ignoring TOKEN_EXTEND_B_SPLINE here.
// This is not harmful at all, since it can be ommitted with
// similar result (the spline is extended anyway).
if (type != -1 && is_set == 2) {
if (root) {
tail->next = calloc(1, sizeof(ass_drawing_token_t));
tail->next->prev = tail;
tail = tail->next;
} else
root = tail = calloc(1, sizeof(ass_drawing_token_t));
tail->type = type;
tail->point = point;
is_set = 0;
if (type == TOKEN_B_SPLINE && !spline_start)
spline_start = tail->prev;
}
p++;
}
#if 0
// Check tokens
ass_drawing_token_t *t = root;
while(t) {
printf("token %d point (%d, %d)\n", t->type, t->point.x, t->point.y);
t = t->next;
}
#endif
return root;
}
/*
* \brief Free a list of tokens
*/
static void drawing_free_tokens(ass_drawing_token_t *token)
{
while (token) {
ass_drawing_token_t *at = token;
token = token->next;
free(at);
}
}
/*
* \brief Translate and scale a point coordinate according to baseline
* offset and scale.
*/
static inline void translate_point(ass_drawing_t *drawing, FT_Vector *point)
{
point->x = drawing->point_scale_x * point->x;
point->y = drawing->point_scale_y * -point->y;
}
/*
* \brief Evaluate a curve into lines
* This curve evaluator is also used in VSFilter (RTS.cpp); it's a simple
* implementation of the De Casteljau algorithm.
*/
static void drawing_evaluate_curve(ass_drawing_t *drawing,
ass_drawing_token_t *token, char spline,
int started)
{
double cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0;
double t, h, max_accel, max_accel1, max_accel2;
FT_Vector cur = {0, 0};
cur = token->point;
translate_point(drawing, &cur);
int x0 = cur.x;
int y0 = cur.y;
token = token->next;
cur = token->point;
translate_point(drawing, &cur);
int x1 = cur.x;
int y1 = cur.y;
token = token->next;
cur = token->point;
translate_point(drawing, &cur);
int x2 = cur.x;
int y2 = cur.y;
token = token->next;
cur = token->point;
translate_point(drawing, &cur);
int x3 = cur.x;
int y3 = cur.y;
if (spline) {
// 1 [-1 +3 -3 +1]
// - * [+3 -6 +3 0]
// 6 [-3 0 +3 0]
// [+1 +4 +1 0]
double div6 = 1.0/6.0;
cx3 = div6*(- x0+3*x1-3*x2+x3);
cx2 = div6*( 3*x0-6*x1+3*x2);
cx1 = div6*(-3*x0 +3*x2);
cx0 = div6*( x0+4*x1+1*x2);
cy3 = div6*(- y0+3*y1-3*y2+y3);
cy2 = div6*( 3*y0-6*y1+3*y2);
cy1 = div6*(-3*y0 +3*y2);
cy0 = div6*( y0+4*y1+1*y2);
} else {
// [-1 +3 -3 +1]
// [+3 -6 +3 0]
// [-3 +3 0 0]
// [+1 0 0 0]
cx3 = - x0+3*x1-3*x2+x3;
cx2 = 3*x0-6*x1+3*x2;
cx1 = -3*x0+3*x1;
cx0 = x0;
cy3 = - y0+3*y1-3*y2+y3;
cy2 = 3*y0-6*y1+3*y2;
cy1 = -3*y0+3*y1;
cy0 = y0;
}
max_accel1 = fabs(2 * cy2) + fabs(6 * cy3);
max_accel2 = fabs(2 * cx2) + fabs(6 * cx3);
max_accel = FFMAX(max_accel1, max_accel2);
h = 1.0;
if (max_accel > CURVE_ACCURACY)
h = sqrt(CURVE_ACCURACY / max_accel);
if (!started) {
cur.x = cx0;
cur.y = cy0;
drawing_add_point(drawing, &cur);
}
for (t = 0; t < 1.0; t += h) {
cur.x = cx0 + t * (cx1 + t * (cx2 + t * cx3));
cur.y = cy0 + t * (cy1 + t * (cy2 + t * cy3));
drawing_add_point(drawing, &cur);
}
cur.x = cx0 + cx1 + cx2 + cx3;
cur.y = cy0 + cy1 + cy2 + cy3;
drawing_add_point(drawing, &cur);
}
/*
* \brief Create and initialize a new drawing and return it
*/
ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
ass_hinting_t hint, FT_Library lib)
{
ass_drawing_t* drawing;
drawing = calloc(1, sizeof(*drawing));
drawing->text = malloc(DRAWING_INITIAL_SIZE);
drawing->size = DRAWING_INITIAL_SIZE;
drawing->ftlibrary = lib;
drawing_make_glyph(drawing, fontconfig_priv, font, hint);
drawing->scale_x = 1.;
drawing->scale_y = 1.;
drawing->max_contours = GLYPH_INITIAL_CONTOURS;
drawing->max_points = GLYPH_INITIAL_POINTS;
return drawing;
}
/*
* \brief Free a drawing
*/
void ass_drawing_free(ass_drawing_t* drawing)
{
free(drawing->text);
free(drawing);
}
/*
* \brief Add one ASCII character to the drawing text buffer
*/
void ass_drawing_add_char(ass_drawing_t* drawing, char symbol)
{
drawing->text[drawing->i++] = symbol;
drawing->text[drawing->i] = 0;
if (drawing->i + 1 >= drawing->size) {
drawing->size *= 2;
drawing->text = realloc(drawing->text, drawing->size);
}
}
/*
* \brief Create a hashcode for the drawing
* XXX: To avoid collisions a better hash algorithm might be useful.
*/
void ass_drawing_hash(ass_drawing_t* drawing)
{
drawing->hash = fnv_32a_str(drawing->text, FNV1_32A_INIT);
}
/*
* \brief Convert token list to outline. Calls the line and curve evaluators.
*/
FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing)
{
int started = 0;
ass_drawing_token_t *token;
FT_Vector pen = {0, 0};
drawing->tokens = drawing_tokenize(drawing->text);
drawing_prepare(drawing);
token = drawing->tokens;
while (token) {
// Draw something according to current command
switch (token->type) {
case TOKEN_MOVE_NC:
pen = token->point;
translate_point(drawing, &pen);
token = token->next;
break;
case TOKEN_MOVE:
pen = token->point;
translate_point(drawing, &pen);
if (started) {
drawing_close_shape(drawing);
started = 0;
}
token = token->next;
break;
case TOKEN_LINE: {
FT_Vector to;
to = token->point;
translate_point(drawing, &to);
if (!started) drawing_add_point(drawing, &pen);
drawing_add_point(drawing, &to);
started = 1;
token = token->next;
break;
}
case TOKEN_CUBIC_BEZIER:
if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) &&
token->prev) {
drawing_evaluate_curve(drawing, token->prev, 0, started);
token = token->next;
token = token->next;
token = token->next;
started = 1;
} else
token = token->next;
break;
case TOKEN_B_SPLINE:
if (token_check_values(token, 3, TOKEN_B_SPLINE) &&
token->prev) {
drawing_evaluate_curve(drawing, token->prev, 1, started);
token = token->next;
started = 1;
} else
token = token->next;
break;
default:
token = token->next;
break;
}
}
drawing_finish(drawing);
drawing_free_tokens(drawing->tokens);
return &drawing->glyph;
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2009 Grigori Goronzy <greg@geekmind.org>
*
* This file is part of libass.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIBASS_DRAWING_H
#define LIBASS_DRAWING_H
#include <ft2build.h>
#include FT_GLYPH_H
#include "ass.h"
#define DRAWING_INITIAL_SIZE 256
enum ass_token_type {
TOKEN_MOVE,
TOKEN_MOVE_NC,
TOKEN_LINE,
TOKEN_CUBIC_BEZIER,
TOKEN_CONIC_BEZIER,
TOKEN_B_SPLINE,
TOKEN_EXTEND_SPLINE,
TOKEN_CLOSE
};
typedef struct ass_drawing_token_s {
enum ass_token_type type;
FT_Vector point;
struct ass_drawing_token_s *next;
struct ass_drawing_token_s *prev;
} ass_drawing_token_t;
typedef struct ass_drawing_s {
char *text; // drawing string
int i; // text index
int scale; // scale (1-64) for subpixel accuracy
double pbo; // drawing will be shifted in y direction by this amount
double scale_x; // FontScaleX
double scale_y; // FontScaleY
int asc; // ascender
int desc; // descender
FT_OutlineGlyph glyph; // the "fake" glyph created for later rendering
int hash; // hash value (for caching)
// private
FT_Library ftlibrary; // FT library instance, needed for font ops
int size; // current buffer size
ass_drawing_token_t *tokens; // tokenized drawing
int max_points; // current maximum size
int max_contours;
double point_scale_x;
double point_scale_y;
} ass_drawing_t;
ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
ass_hinting_t hint, FT_Library lib);
void ass_drawing_free(ass_drawing_t* drawing);
void ass_drawing_add_char(ass_drawing_t* drawing, char symbol);
void ass_drawing_hash(ass_drawing_t* drawing);
FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing);
#endif /* LIBASS_DRAWING_H */

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -36,7 +34,6 @@
#include "ass_cache.h" #include "ass_cache.h"
#include "ass_fontconfig.h" #include "ass_fontconfig.h"
#include "ass_utils.h" #include "ass_utils.h"
#include "mputils.h"
/** /**
* Select Microfost Unicode CharMap, if the font has one. * Select Microfost Unicode CharMap, if the font has one.
@ -49,7 +46,9 @@ static void charmap_magic(FT_Face face)
FT_CharMap cmap = face->charmaps[i]; FT_CharMap cmap = face->charmaps[i];
unsigned pid = cmap->platform_id; unsigned pid = cmap->platform_id;
unsigned eid = cmap->encoding_id; unsigned eid = cmap->encoding_id;
if (pid == 3 /*microsoft*/ && (eid == 1 /*unicode bmp*/ || eid == 10 /*full unicode*/)) { if (pid == 3 /*microsoft */
&& (eid == 1 /*unicode bmp */
|| eid == 10 /*full unicode */ )) {
FT_Set_Charmap(face, cmap); FT_Set_Charmap(face, cmap);
return; return;
} }
@ -57,10 +56,10 @@ static void charmap_magic(FT_Face face)
if (!face->charmap) { if (!face->charmap) {
if (face->num_charmaps == 0) { if (face->num_charmaps == 0) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoCharmaps); ass_msg(MSGL_WARN, MSGTR_LIBASS_NoCharmaps);
return; return;
} }
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoCharmapAutodetected); ass_msg(MSGL_WARN, MSGTR_LIBASS_NoCharmapAutodetected);
FT_Set_Charmap(face, face->charmaps[0]); FT_Set_Charmap(face, face->charmaps[0]);
return; return;
} }
@ -125,21 +124,33 @@ static int add_face(void* fc_priv, ass_font_t* font, uint32_t ch)
if (font->n_faces == ASS_FONT_MAX_FACES) if (font->n_faces == ASS_FONT_MAX_FACES)
return -1; return -1;
path = fontconfig_select(fc_priv, font->desc.family, font->desc.treat_family_as_pattern, font->desc.bold, path =
font->desc.italic, &index, ch); 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); mem_idx = find_font(font->library, path);
if (mem_idx >= 0) { if (mem_idx >= 0) {
error = FT_New_Memory_Face(font->ftlibrary, (unsigned char*)font->library->fontdata[mem_idx].data, error =
font->library->fontdata[mem_idx].size, 0, &face); FT_New_Memory_Face(font->ftlibrary,
(unsigned char *) font->library->
fontdata[mem_idx].data,
font->library->fontdata[mem_idx].size, 0,
&face);
if (error) { if (error) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, path); ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont,
path);
free(path);
return -1; return -1;
} }
} else { } else {
error = FT_New_Face(font->ftlibrary, path, index, &face); error = FT_New_Face(font->ftlibrary, path, index, &face);
if (error) { if (error) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningFont, path, index); ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningFont, path,
index);
free(path);
return -1; return -1;
} }
} }
@ -149,19 +160,22 @@ static int add_face(void* fc_priv, ass_font_t* font, uint32_t ch)
font->faces[font->n_faces++] = face; font->faces[font->n_faces++] = face;
update_transform(font); update_transform(font);
face_set_size(face, font->size); face_set_size(face, font->size);
free(path);
return font->n_faces - 1; return font->n_faces - 1;
} }
/** /**
* \brief Create a new ass_font_t according to "desc" argument * \brief Create a new ass_font_t according to "desc" argument
*/ */
ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_priv, ass_font_desc_t* desc) 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; int error;
ass_font_t *fontp; ass_font_t *fontp;
ass_font_t font; ass_font_t font;
fontp = ass_font_cache_find(desc); fontp = ass_font_cache_find((hashmap_t *) font_cache, desc);
if (fontp) if (fontp)
return fontp; return fontp;
@ -182,13 +196,14 @@ ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_
free(font.desc.family); free(font.desc.family);
return 0; return 0;
} else } else
return ass_font_cache_add(&font); return ass_font_cache_add((hashmap_t *) font_cache, &font);
} }
/** /**
* \brief Set font transformation matrix and shift vector * \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_x = scale_x;
font->scale_y = scale_y; font->scale_y = scale_y;
@ -245,7 +260,8 @@ void ass_font_set_size(ass_font_t* font, double size)
* \param ch character code * \param ch character code
* The values are extracted from the font face that provides glyphs for the given character * 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; int i;
for (i = 0; i < font->n_faces; ++i) { for (i = 0; i < font->n_faces; ++i) {
@ -260,11 +276,93 @@ void ass_font_get_asc_desc(ass_font_t* font, uint32_t ch, int* asc, int* desc)
*asc = *desc = 0; *asc = *desc = 0;
} }
/*
* Strike a glyph with a horizontal line; it's possible to underline it
* and/or strike through it. For the line's position and size, truetype
* tables are consulted. Obviously this relies on the data in the tables
* being accurate.
*
*/
static int ass_strike_outline_glyph(FT_Face face, ass_font_t *font,
FT_Glyph glyph, int under, int through)
{
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post);
FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline;
int bear, advance, y_scale, i;
// Grow outline
i = (under ? 4 : 0) + (through ? 4 : 0);
ol->points = realloc(ol->points, sizeof(FT_Vector) *
(ol->n_points + i));
ol->tags = realloc(ol->tags, ol->n_points + i);
i = !!under + !!through;
ol->contours = realloc(ol->contours, sizeof(short) *
(ol->n_contours + i));
// If the bearing is negative, the glyph starts left of the current
// pen position
bear = FFMIN(face->glyph->metrics.horiBearingX, 0);
// We're adding half a pixel to avoid small gaps
advance = d16_to_d6(glyph->advance.x) + 32;
y_scale = face->size->metrics.y_scale;
// Add points to the outline
if (under) {
int pos, size;
pos = FT_MulFix(ps->underlinePosition, y_scale * font->scale_y);
size = FT_MulFix(ps->underlineThickness,
y_scale * font->scale_y / 2);
if (pos > 0 || size <= 0)
return 0;
FT_Vector points[4] = {
{.x = bear, .y = pos + size},
{.x = advance, .y = pos + size},
{.x = advance, .y = pos - size},
{.x = bear, .y = pos - size},
};
for (i = 0; i < 4; i++) {
ol->points[ol->n_points] = points[i];
ol->tags[ol->n_points++] = 1;
}
ol->contours[ol->n_contours++] = ol->n_points - 1;
}
if (through) {
int pos, size;
pos = FT_MulFix(os2->yStrikeoutPosition, y_scale * font->scale_y);
size = FT_MulFix(os2->yStrikeoutSize, y_scale * font->scale_y / 2);
if (pos < 0 || size <= 0)
return 0;
FT_Vector points[4] = {
{.x = bear, .y = pos + size},
{.x = advance, .y = pos + size},
{.x = advance, .y = pos - size},
{.x = bear, .y = pos - size},
};
for (i = 0; i < 4; i++) {
ol->points[ol->n_points] = points[i];
ol->tags[ol->n_points++] = 1;
}
ol->contours[ol->n_contours++] = ol->n_points - 1;
}
return 1;
}
/** /**
* \brief Get a glyph * \brief Get a glyph
* \param ch character code * \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 deco)
{ {
int error; int error;
int index = 0; int index = 0;
@ -288,33 +386,42 @@ FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch
#ifdef CONFIG_FONTCONFIG #ifdef CONFIG_FONTCONFIG
if (index == 0) { if (index == 0) {
int face_idx; int face_idx;
mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_GlyphNotFoundReselectingFont, ass_msg(MSGL_INFO,
ch, font->desc.family, font->desc.bold, font->desc.italic); MSGTR_LIBASS_GlyphNotFoundReselectingFont, ch,
font->desc.family, font->desc.bold, font->desc.italic);
face_idx = add_face(fontconfig_priv, font, ch); face_idx = add_face(fontconfig_priv, font, ch);
if (face_idx >= 0) { if (face_idx >= 0) {
face = font->faces[face_idx]; face = font->faces[face_idx];
index = FT_Get_Char_Index(face, ch); index = FT_Get_Char_Index(face, ch);
if (index == 0) { if (index == 0) {
mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_GlyphNotFound, ass_msg(MSGL_ERR, MSGTR_LIBASS_GlyphNotFound,
ch, font->desc.family, font->desc.bold, font->desc.italic); ch, font->desc.family, font->desc.bold,
font->desc.italic);
} }
} }
} }
#endif #endif
switch (hinting) { switch (hinting) {
case ASS_HINTING_NONE: flags = FT_LOAD_NO_HINTING; break; case ASS_HINTING_NONE:
case ASS_HINTING_LIGHT: flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; break; flags = FT_LOAD_NO_HINTING;
case ASS_HINTING_NORMAL: flags = FT_LOAD_FORCE_AUTOHINT; break; break;
case ASS_HINTING_NATIVE: flags = 0; 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); error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags);
if (error) { if (error) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph); ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph);
return 0; return 0;
} }
#if (FREETYPE_MAJOR > 2) || \ #if (FREETYPE_MAJOR > 2) || \
((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR >= 2)) || \ ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR >= 2)) || \
((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR == 1) && (FREETYPE_PATCH >= 10)) ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR == 1) && (FREETYPE_PATCH >= 10))
@ -326,10 +433,13 @@ FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch
#endif #endif
error = FT_Get_Glyph(face->glyph, &glyph); error = FT_Get_Glyph(face->glyph, &glyph);
if (error) { if (error) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph); ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph);
return 0; return 0;
} }
ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE,
deco & DECO_STRIKETHROUGH);
return glyph; return glyph;
} }
@ -363,7 +473,9 @@ void ass_font_free(ass_font_t* font)
{ {
int i; int i;
for (i = 0; i < font->n_faces; ++i) for (i = 0; i < font->n_faces; ++i)
if (font->faces[i]) FT_Done_Face(font->faces[i]); if (font->faces[i])
if (font->desc.family) free(font->desc.family); FT_Done_Face(font->faces[i]);
if (font->desc.family)
free(font->desc.family);
free(font); free(font);
} }

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -29,6 +27,10 @@
#include "ass.h" #include "ass.h"
#include "ass_types.h" #include "ass_types.h"
#define ASS_FONT_MAX_FACES 10
#define DECO_UNDERLINE 1
#define DECO_STRIKETHROUGH 2
typedef struct ass_font_desc_s { typedef struct ass_font_desc_s {
char *family; char *family;
unsigned bold; unsigned bold;
@ -36,8 +38,6 @@ typedef struct ass_font_desc_s {
int treat_family_as_pattern; int treat_family_as_pattern;
} ass_font_desc_t; } ass_font_desc_t;
#define ASS_FONT_MAX_FACES 10
typedef struct ass_font_s { typedef struct ass_font_s {
ass_font_desc_t desc; ass_font_desc_t desc;
ass_library_t *library; ass_library_t *library;
@ -49,11 +49,17 @@ typedef struct ass_font_s {
double size; double size;
} ass_font_t; } ass_font_t;
ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_priv, ass_font_desc_t* desc); // FIXME: passing the hashmap via a void pointer is very ugly.
void ass_font_set_transform(ass_font_t* font, double scale_x, double scale_y, FT_Vector* v); 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_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); void ass_font_get_asc_desc(ass_font_t *font, uint32_t ch, int *asc,
FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch, ass_hinting_t hinting); int *desc);
FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font,
uint32_t ch, ass_hinting_t hinting, int flags);
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);
void ass_font_free(ass_font_t *font); void ass_font_free(ass_font_t *font);

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -32,16 +30,11 @@
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
#include "mputils.h" #include "ass_utils.h"
#include "ass.h" #include "ass.h"
#include "ass_library.h" #include "ass_library.h"
#include "ass_fontconfig.h" #include "ass_fontconfig.h"
#ifdef BUILD_DARWIN
#include "../src/libosxutil/libosxutil.h"
#include <sys/param.h>
#endif
#ifdef CONFIG_FONTCONFIG #ifdef CONFIG_FONTCONFIG
#include <fontconfig/fontconfig.h> #include <fontconfig/fontconfig.h>
#include <fontconfig/fcfreetype.h> #include <fontconfig/fcfreetype.h>
@ -78,8 +71,9 @@ struct fc_instance_s {
* \param code: the character that should be present in the font, can be 0 * \param code: the character that should be present in the font, can be 0
* \return font file path * \return font file path
*/ */
static char* _select_font(fc_instance_t* priv, const char* family, int treat_family_as_pattern, static char *_select_font(fc_instance_t *priv, const char *family,
unsigned bold, unsigned italic, int* index, uint32_t code) int treat_family_as_pattern, unsigned bold,
unsigned italic, int *index, uint32_t code)
{ {
FcBool rc; FcBool rc;
FcResult result; FcResult result;
@ -91,7 +85,7 @@ static char* _select_font(fc_instance_t* priv, const char* family, int treat_fam
FcFontSet *fset = NULL; FcFontSet *fset = NULL;
int curf; int curf;
char *retval = NULL; char *retval = NULL;
int family_cnt; int family_cnt = 0;
*index = 0; *index = 0;
@ -199,7 +193,8 @@ static char* _select_font(fc_instance_t* priv, const char* family, int treat_fam
if (!treat_family_as_pattern && if (!treat_family_as_pattern &&
!(r_family && strcasecmp((const char *) r_family, family) == 0) && !(r_family && strcasecmp((const char *) r_family, family) == 0) &&
!(r_fullname && strcasecmp((const char *) r_fullname, family) == 0)) !(r_fullname && strcasecmp((const char *) r_fullname, family) == 0))
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne, ass_msg(MSGL_WARN,
MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne,
(const char *) (r_fullname ? r_fullname : r_family), family); (const char *) (r_fullname ? r_fullname : r_family), family);
result = FcPatternGetString(rpat, FC_STYLE, 0, &r_style); result = FcPatternGetString(rpat, FC_STYLE, 0, &r_style);
@ -218,15 +213,19 @@ static char* _select_font(fc_instance_t* priv, const char* family, int treat_fam
if (result != FcResultMatch) if (result != FcResultMatch)
r_embolden = 0; r_embolden = 0;
mp_msg(MSGT_ASS, MSGL_V, "[ass] Font info: family '%s', style '%s', fullname '%s'," ass_msg(MSGL_V,
" slant %d, weight %d%s\n", "[ass] Font info: family '%s', style '%s', fullname '%s',"
(const char*)r_family, (const char*)r_style, (const char*)r_fullname, " slant %d, weight %d%s\n", (const char *) r_family,
r_slant, r_weight, r_embolden ? ", embolden" : ""); (const char *) r_style, (const char *) r_fullname, r_slant,
r_weight, r_embolden ? ", embolden" : "");
error: error:
if (pat) FcPatternDestroy(pat); if (pat)
if (rpat) FcPatternDestroy(rpat); FcPatternDestroy(pat);
if (fset) FcFontSetDestroy(fset); if (rpat)
FcPatternDestroy(rpat);
if (fset)
FcFontSetDestroy(fset);
return retval; return retval;
} }
@ -241,8 +240,9 @@ static char* _select_font(fc_instance_t* priv, const char* family, int treat_fam
* \param code: the character that should be present in the font, can be 0 * \param code: the character that should be present in the font, can be 0
* \return font file path * \return font file path
*/ */
char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, char *fontconfig_select(fc_instance_t *priv, const char *family,
unsigned bold, unsigned italic, int* index, uint32_t code) int treat_family_as_pattern, unsigned bold,
unsigned italic, int *index, uint32_t code)
{ {
char *res = 0; char *res = 0;
if (!priv->config) { if (!priv->config) {
@ -250,28 +250,33 @@ char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_famil
return priv->path_default; return priv->path_default;
} }
if (family && *family) if (family && *family)
res = _select_font(priv, family, treat_family_as_pattern, bold, italic, index, code); res =
_select_font(priv, family, treat_family_as_pattern, bold,
italic, index, code);
if (!res && priv->family_default) { if (!res && priv->family_default) {
res = _select_font(priv, priv->family_default, 0, bold, italic, index, code); res =
_select_font(priv, priv->family_default, 0, bold, italic, index,
code);
if (res) if (res)
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily, ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily,
family, bold, italic, res, *index); family, bold, italic, res, *index);
} }
if (!res && priv->path_default) { if (!res && priv->path_default) {
res = priv->path_default; res = priv->path_default;
*index = priv->index_default; *index = priv->index_default;
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont, ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont,
family, bold, italic, res, *index); family, bold, italic, res, *index);
} }
if (!res) { if (!res) {
res = _select_font(priv, "Arial", 0, bold, italic, index, code); res = _select_font(priv, "Arial", 0, bold, italic, index, code);
if (res) if (res)
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily, ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily,
family, bold, italic, res, *index); family, bold, italic, res, *index);
} }
if (res) if (res)
mp_msg(MSGT_ASS, MSGL_V, "fontconfig_select: (%s, %d, %d) -> %s, %d\n", ass_msg(MSGL_V,
family, bold, italic, res, *index); "fontconfig_select: (%s, %d, %d) -> %s, %d\n", family, bold,
italic, res, *index);
return res; return res;
} }
@ -287,7 +292,7 @@ static char* validate_fname(char* name)
q = fname = malloc(sz + 1); q = fname = malloc(sz + 1);
p = name; p = name;
while (*p) { while (*p) {
code = utf8_get_char(&p); code = ass_utf8_get_char(&p);
if (code == 0) if (code == 0)
break; break;
if ((code > 0x7F) || if ((code > 0x7F) ||
@ -297,10 +302,7 @@ static char* validate_fname(char* name)
(code == '*') || (code == '*') ||
(code == '?') || (code == '?') ||
(code == '<') || (code == '<') ||
(code == '>') || (code == '>') || (code == '|') || (code == 0)) {
(code == '|') ||
(code == 0))
{
*q++ = '_'; *q++ = '_';
} else { } else {
*q++ = code; *q++ = code;
@ -322,7 +324,8 @@ static char* validate_fname(char* name)
* With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace. * 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. * 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; int rc;
const char *name = library->fontdata[idx].name; const char *name = library->fontdata[idx].name;
@ -347,10 +350,11 @@ static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Lib
res = mkdir(fonts_dir); res = mkdir(fonts_dir);
#endif #endif
if (res) { if (res) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FailedToCreateDirectory, fonts_dir); ass_msg(MSGL_WARN,
MSGTR_LIBASS_FailedToCreateDirectory, fonts_dir);
} }
} else if (!S_ISDIR(st.st_mode)) { } else if (!S_ISDIR(st.st_mode)) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir); ass_msg(MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir);
} }
fname = validate_fname((char *) name); fname = validate_fname((char *) name);
@ -359,7 +363,8 @@ static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Lib
free(fname); free(fname);
fp = fopen(buf, "wb"); fp = fopen(buf, "wb");
if (!fp) return; if (!fp)
return;
fwrite(data, data_size, 1, fp); fwrite(data, data_size, 1, fp);
fclose(fp); fclose(fp);
@ -372,30 +377,37 @@ static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Lib
int face_index, num_faces = 1; int face_index, num_faces = 1;
for (face_index = 0; face_index < num_faces; ++face_index) { for (face_index = 0; face_index < num_faces; ++face_index) {
rc = FT_New_Memory_Face(ftlibrary, (unsigned char*)data, data_size, face_index, &face); rc = FT_New_Memory_Face(ftlibrary, (unsigned char *) data,
data_size, face_index, &face);
if (rc) { if (rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, name); ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont,
name);
return; return;
} }
num_faces = face->num_faces; num_faces = face->num_faces;
pattern = FcFreeTypeQueryFace(face, (unsigned char*)name, 0, FcConfigGetBlanks(priv->config)); pattern =
FcFreeTypeQueryFace(face, (unsigned char *) name, 0,
FcConfigGetBlanks(priv->config));
if (!pattern) { if (!pattern) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFreeTypeQueryFace"); ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed,
"FcFreeTypeQueryFace");
FT_Done_Face(face); FT_Done_Face(face);
return; return;
} }
fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication
if (!fset) { if (!fset) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcConfigGetFonts"); ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed,
"FcConfigGetFonts");
FT_Done_Face(face); FT_Done_Face(face);
return; return;
} }
res = FcFontSetAdd(fset, pattern); res = FcFontSetAdd(fset, pattern);
if (!res) { if (!res) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFontSetAdd"); ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed,
"FcFontSetAdd");
FT_Done_Face(face); FT_Done_Face(face);
return; return;
} }
@ -413,41 +425,36 @@ static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Lib
* \param path default font path * \param path default font path
* \return pointer to fontconfig private data * \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, const char *config)
{ {
int rc; int rc;
fc_instance_t *priv = calloc(1, sizeof(fc_instance_t)); fc_instance_t *priv = calloc(1, sizeof(fc_instance_t));
const char *dir = library->fonts_dir; const char *dir = library->fonts_dir;
#ifdef BUILD_DARWIN
char config_path[MAXPATHLEN];
char *config_dir;
#endif
int i; int i;
if (!fc) { if (!fc) {
mp_msg(MSGT_ASS, MSGL_WARN, ass_msg(MSGL_WARN,
MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed); MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed);
goto exit; goto exit;
} }
#ifdef BUILD_DARWIN if (config) {
config_dir = OSX_GetBundleResourcesDirectory();
snprintf(config_path, MAXPATHLEN, "%s/etc/fonts/fonts.conf", config_dir);
free(config_dir);
priv->config = FcConfigCreate(); priv->config = FcConfigCreate();
rc = FcConfigParseAndLoad(priv->config, config_path, FcTrue); rc = FcConfigParseAndLoad(priv->config, (unsigned char *)config,
FcTrue);
FcConfigBuildFonts(priv->config); FcConfigBuildFonts(priv->config);
FcConfigSetCurrent(priv->config); FcConfigSetCurrent(priv->config);
} else {
if (!rc) {
#else
rc = FcInit(); rc = FcInit();
assert(rc); assert(rc);
priv->config = FcConfigGetCurrent(); priv->config = FcConfigGetCurrent();
if (!priv->config) { }
#endif
mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed); if (!rc || !priv->config) {
ass_msg(MSGL_FATAL,
MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed);
goto exit; goto exit;
} }
@ -455,11 +462,10 @@ fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, con
process_fontdata(priv, library, ftlibrary, i); process_fontdata(priv, library, ftlibrary, i);
if (dir) { if (dir) {
if (FcDirCacheValid((const FcChar8 *)dir) == FcFalse) if (FcDirCacheValid((const FcChar8 *) dir) == FcFalse) {
{ ass_msg(MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache);
mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache);
if (FcGetVersion() >= 20390 && FcGetVersion() < 20400) if (FcGetVersion() >= 20390 && FcGetVersion() < 20400)
mp_msg(MSGT_ASS, MSGL_WARN, ass_msg(MSGL_WARN,
MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported); MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported);
// FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir() // FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir()
if (FcGetVersion() < 20390) { if (FcGetVersion() < 20390) {
@ -469,20 +475,23 @@ fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, con
fss = FcStrSetCreate(); fss = FcStrSetCreate();
rc = FcStrSetAdd(fss, (const FcChar8 *) dir); rc = FcStrSetAdd(fss, (const FcChar8 *) dir);
if (!rc) { if (!rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcStrSetAddFailed); ass_msg(MSGL_WARN,
MSGTR_LIBASS_FcStrSetAddFailed);
goto ErrorFontCache; goto ErrorFontCache;
} }
rc = FcDirScan(fcs, fss, NULL, FcConfigGetBlanks(priv->config), rc = FcDirScan(fcs, fss, NULL,
FcConfigGetBlanks(priv->config),
(const FcChar8 *) dir, FcFalse); (const FcChar8 *) dir, FcFalse);
if (!rc) { if (!rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirScanFailed); ass_msg(MSGL_WARN,
MSGTR_LIBASS_FcDirScanFailed);
goto ErrorFontCache; goto ErrorFontCache;
} }
rc = FcDirSave(fcs, fss, (const FcChar8 *) dir); rc = FcDirSave(fcs, fss, (const FcChar8 *) dir);
if (!rc) { if (!rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirSave); ass_msg(MSGL_WARN, MSGTR_LIBASS_FcDirSave);
goto ErrorFontCache; goto ErrorFontCache;
} }
ErrorFontCache: ErrorFontCache:
@ -492,7 +501,8 @@ fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, con
rc = FcConfigAppFontAddDir(priv->config, (const FcChar8 *) dir); rc = FcConfigAppFontAddDir(priv->config, (const FcChar8 *) dir);
if (!rc) { if (!rc) {
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcConfigAppFontAddDirFailed); ass_msg(MSGL_WARN,
MSGTR_LIBASS_FcConfigAppFontAddDirFailed);
} }
} }
@ -506,18 +516,22 @@ exit:
#else /* CONFIG_FONTCONFIG */ #else /* CONFIG_FONTCONFIG */
char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, char *fontconfig_select(fc_instance_t *priv, const char *family,
unsigned bold, unsigned italic, int* index, uint32_t code) int treat_family_as_pattern, unsigned bold,
unsigned italic, int *index, uint32_t code)
{ {
*index = priv->index_default; *index = priv->index_default;
return priv->path_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); ass_msg(MSGL_WARN,
MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed);
priv = calloc(1, sizeof(fc_instance_t)); priv = calloc(1, sizeof(fc_instance_t));
@ -531,9 +545,10 @@ fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, con
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 // 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->path_default)
if (priv && priv->family_default) free(priv->family_default); free(priv->path_default);
if (priv) free(priv); if (priv && priv->family_default)
free(priv->family_default);
if (priv)
free(priv);
} }

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -34,8 +32,12 @@
typedef struct fc_instance_s fc_instance_t; 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); fc_instance_t *fontconfig_init(ass_library_t *library,
char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, unsigned bold, unsigned italic, int* index, uint32_t code); FT_Library ftlibrary, const char *family,
const char *path, int fc, const char *config);
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); void fontconfig_done(fc_instance_t *priv);
#endif /* LIBASS_FONTCONFIG_H */ #endif /* LIBASS_FONTCONFIG_H */

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -69,9 +67,11 @@ void ass_set_style_overrides(ass_library_t* priv, char** list)
free(priv->style_overrides); free(priv->style_overrides);
} }
if (!list) return; if (!list)
return;
for (p = list, cnt = 0; *p; ++p, ++cnt) {} for (p = list, cnt = 0; *p; ++p, ++cnt) {
}
priv->style_overrides = malloc((cnt + 1) * sizeof(char *)); priv->style_overrides = malloc((cnt + 1) * sizeof(char *));
for (p = list, q = priv->style_overrides; *p; ++p, ++q) for (p = list, q = priv->style_overrides; *p; ++p, ++q)
@ -90,7 +90,8 @@ void ass_add_font(ass_library_t* priv, char* name, char* data, int size)
int idx = priv->num_fontdata; int idx = priv->num_fontdata;
if (!name || !data || !size) if (!name || !data || !size)
return; return;
grow_array((void**)&priv->fontdata, priv->num_fontdata, sizeof(*priv->fontdata)); grow_array((void **) &priv->fontdata, priv->num_fontdata,
sizeof(*priv->fontdata));
priv->fontdata[idx].name = strdup(name); priv->fontdata[idx].name = strdup(name);

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -99,7 +97,8 @@ typedef struct ass_track_s {
char *style_format; // style format line (everything after "Format: ") char *style_format; // style format line (everything after "Format: ")
char *event_format; // event format line 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 // script header fields
int PlayResX; int PlayResX;

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -27,7 +25,6 @@
#include <ft2build.h> #include <ft2build.h>
#include FT_GLYPH_H #include FT_GLYPH_H
#include "mputils.h"
#include "ass_utils.h" #include "ass_utils.h"
int mystrtoi(char **p, int *res) int mystrtoi(char **p, int *res)
@ -36,9 +33,11 @@ int mystrtoi(char** p, int* res)
double temp_res; double temp_res;
char *start = *p; char *start = *p;
temp_res = strtod(*p, p); temp_res = strtod(*p, p);
*res = (int) (temp_res + 0.5); *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5));
if (*p != start) return 1; if (*p != start)
else return 0; return 1;
else
return 0;
} }
int mystrtoll(char **p, long long *res) int mystrtoll(char **p, long long *res)
@ -46,25 +45,31 @@ int mystrtoll(char** p, long long* res)
double temp_res; double temp_res;
char *start = *p; char *start = *p;
temp_res = strtod(*p, p); temp_res = strtod(*p, p);
*res = (long long) (temp_res + 0.5); *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5));
if (*p != start) return 1; if (*p != start)
else return 0; 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; char *start = *p;
*res = strtoll(*p, p, base); *res = strtoll(*p, p, base);
if (*p != start) return 1; if (*p != start)
else return 0; return 1;
else
return 0;
} }
int mystrtod(char **p, double *res) int mystrtod(char **p, double *res)
{ {
char *start = *p; char *start = *p;
*res = strtod(*p, p); *res = strtod(*p, p);
if (*p != start) return 1; if (*p != start)
else return 0; return 1;
else
return 0;
} }
int strtocolor(char **q, uint32_t *res) int strtocolor(char **q, uint32_t *res)
@ -73,8 +78,10 @@ int strtocolor(char** q, uint32_t* res)
int result; int result;
char *p = *q; char *p = *q;
if (*p == '&') ++p; if (*p == '&')
else mp_msg(MSGT_ASS, MSGL_DBG2, "suspicious color format: \"%s\"\n", p); ++p;
else
ass_msg(MSGL_DBG2, "suspicious color format: \"%s\"\n", p);
if (*p == 'H' || *p == 'h') { if (*p == 'H' || *p == 'h') {
++p; ++p;
@ -86,10 +93,15 @@ int strtocolor(char** q, uint32_t* res)
{ {
unsigned char *tmp = (unsigned char *) (&color); unsigned char *tmp = (unsigned char *) (&color);
unsigned char b; unsigned char b;
b = tmp[0]; tmp[0] = tmp[3]; tmp[3] = b; b = tmp[0];
b = tmp[1]; tmp[1] = tmp[2]; tmp[2] = b; tmp[0] = tmp[3];
tmp[3] = b;
b = tmp[1];
tmp[1] = tmp[2];
tmp[2] = b;
} }
if (*p == '&') ++p; if (*p == '&')
++p;
*q = p; *q = p;
*res = color; *res = color;
@ -97,7 +109,8 @@ int strtocolor(char** q, uint32_t* res)
} }
// Return a boolean value for a string // Return a boolean value for a string
char parse_bool(char* str) { char parse_bool(char *str)
{
while (*str == ' ' || *str == '\t') while (*str == ' ' || *str == '\t')
str++; str++;
if (!strncasecmp(str, "yes", 3)) if (!strncasecmp(str, "yes", 3))
@ -107,29 +120,206 @@ char parse_bool(char* str) {
return 0; return 0;
} }
#if 0 void ass_msg(int lvl, char *fmt, ...)
static void sprint_tag(uint32_t tag, char* dst)
{ {
dst[0] = (tag >> 24) & 0xFF; va_list va;
dst[1] = (tag >> 16) & 0xFF; if (lvl > MSGL_INFO)
dst[2] = (tag >> 8) & 0xFF; return;
dst[3] = tag & 0xFF; va_start(va, fmt);
dst[4] = 0; vprintf(fmt, va);
va_end(va);
} }
void dump_glyph(FT_Glyph g) unsigned ass_utf8_get_char(char **str)
{ {
char tag[5]; uint8_t *strp = (uint8_t *) * str;
int i; unsigned c = *strp++;
FT_OutlineGlyph og = (FT_OutlineGlyph)g; unsigned mask = 0x80;
FT_Outline* o = &(og->outline); int len = -1;
sprint_tag(g->format, tag); while (c & mask) {
printf("glyph: %p \n", g); mask >>= 1;
printf("format: %s \n", tag); len++;
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));
} }
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 ass_gauss_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 = (unsigned *) (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 = (unsigned *) (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 = (unsigned *) (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 = (unsigned *) (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 = (unsigned *) (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 = (unsigned *) (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;
}
}
#ifdef CONFIG_ENCA
void *ass_guess_buffer_cp(unsigned char *buffer, int buflen,
char *preferred_language, char *fallback)
{
const char **languages;
size_t langcnt;
EncaAnalyser analyser;
EncaEncoding encoding;
char *detected_sub_cp = NULL;
int i;
languages = enca_get_languages(&langcnt);
ass_msg(MSGL_V, "ENCA supported languages: ");
for (i = 0; i < langcnt; i++) {
ass_msg(MSGL_V, "%s ", languages[i]);
}
ass_msg(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);
ass_msg(MSGL_INFO, "ENCA detected charset: %s\n", tmp);
}
enca_analyser_free(analyser);
}
free(languages);
if (!detected_sub_cp) {
detected_sub_cp = strdup(fallback);
ass_msg(MSGL_INFO,
"ENCA detection failed: fallback to %s\n", fallback);
}
return detected_sub_cp;
} }
#endif #endif

View file

@ -1,5 +1,3 @@
// -*- c-basic-offset: 8; indent-tabs-mode: t -*-
// vim:ts=8:sw=8:noet:ai:
/* /*
* Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com> * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
* *
@ -23,7 +21,28 @@
#ifndef LIBASS_UTILS_H #ifndef LIBASS_UTILS_H
#define LIBASS_UTILS_H #define LIBASS_UTILS_H
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef CONFIG_ENCA
#include <enca.h>
#endif
#include "help_mp.h"
#define MSGL_FATAL 0
#define MSGL_ERR 1
#define MSGL_WARN 2
#define MSGL_INFO 4
#define MSGL_V 6
#define MSGL_DBG2 7
#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
int mystrtoi(char **p, int *res); int mystrtoi(char **p, int *res);
int mystrtoll(char **p, long long *res); int mystrtoll(char **p, long long *res);
@ -31,36 +50,79 @@ int mystrtou32(char** p, int base, uint32_t* res);
int mystrtod(char **p, double *res); int mystrtod(char **p, double *res);
int strtocolor(char **q, uint32_t *res); int strtocolor(char **q, uint32_t *res);
char parse_bool(char *str); char parse_bool(char *str);
unsigned ass_utf8_get_char(char **str);
void ass_msg(int lvl, char *fmt, ...);
void ass_gauss_blur(unsigned char *buffer, unsigned short *tmp2,
int width, int height, int stride, int *m2,
int r, int mwidth);
void *ass_guess_buffer_cp(unsigned char *buffer, int buflen,
char *preferred_language, char *fallback);
static inline int d6_to_int(int x) { static inline int d6_to_int(int x)
{
return (x + 32) >> 6; return (x + 32) >> 6;
} }
static inline int d16_to_int(int x) { static inline int d16_to_int(int x)
{
return (x + 32768) >> 16; return (x + 32768) >> 16;
} }
static inline int int_to_d6(int x) { static inline int int_to_d6(int x)
{
return x << 6; return x << 6;
} }
static inline int int_to_d16(int x) { static inline int int_to_d16(int x)
{
return x << 16; return x << 16;
} }
static inline int d16_to_d6(int x) { static inline int d16_to_d6(int x)
{
return (x + 512) >> 10; return (x + 512) >> 10;
} }
static inline int d6_to_d16(int x) { static inline int d6_to_d16(int x)
{
return x << 10; return x << 10;
} }
static inline double d6_to_double(int x) { static inline double d6_to_double(int x)
{
return x / 64.; return x / 64.;
} }
static inline int double_to_d6(double x) { static inline int double_to_d6(double x)
{
return (int) (x * 64); return (int) (x * 64);
} }
static inline double d16_to_double(int x) { static inline double d16_to_double(int x)
{
return ((double) x) / 0x10000; return ((double) x) / 0x10000;
} }
static inline int double_to_d16(double x) { static inline int double_to_d16(double x)
{
return (int) (x * 0x10000); return (int) (x * 0x10000);
} }
#define FNV1_32A_INIT (unsigned)0x811c9dc5
static inline unsigned fnv_32a_buf(void *buf, size_t len, unsigned hval)
{
unsigned char *bp = buf;
unsigned char *be = bp + len;
while (bp < be) {
hval ^= (unsigned) *bp++;
hval +=
(hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) +
(hval << 24);
}
return hval;
}
static inline unsigned fnv_32a_str(char *str, unsigned hval)
{
unsigned char *s = (unsigned char *) str;
while (*s) {
hval ^= (unsigned) *s++;
hval +=
(hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) +
(hval << 24);
}
return hval;
}
#endif /* LIBASS_UTILS_H */ #endif /* LIBASS_UTILS_H */

View file

@ -2,6 +2,7 @@
#define __LIBASS_HELP_MP_H__ #define __LIBASS_HELP_MP_H__
#define MSGTR_LIBASS_FT_Glyph_To_BitmapError "[ass] FT_Glyph_To_Bitmap error %d \n" #define MSGTR_LIBASS_FT_Glyph_To_BitmapError "[ass] FT_Glyph_To_Bitmap error %d \n"
#define MSGTR_LIBASS_UnsupportedPixelMode "[ass] Unsupported pixel mode: %d\n" #define MSGTR_LIBASS_UnsupportedPixelMode "[ass] Unsupported pixel mode: %d\n"
#define MSGTR_LIBASS_GlyphBBoxTooLarge "[ass] Glyph bounding box too large: %dx%dpx\n"
#define MSGTR_LIBASS_NoStyleNamedXFoundUsingY "[ass] [%p] Warning: no style named '%s' found, using '%s'\n" #define MSGTR_LIBASS_NoStyleNamedXFoundUsingY "[ass] [%p] Warning: no style named '%s' found, using '%s'\n"
#define MSGTR_LIBASS_BadTimestamp "[ass] bad timestamp\n" #define MSGTR_LIBASS_BadTimestamp "[ass] bad timestamp\n"
#define MSGTR_LIBASS_BadEncodedDataSize "[ass] bad encoded data size\n" #define MSGTR_LIBASS_BadEncodedDataSize "[ass] bad encoded data size\n"
@ -19,7 +20,7 @@
#define MSGTR_LIBASS_NotADirectory "[ass] Not a directory: %s\n" #define MSGTR_LIBASS_NotADirectory "[ass] Not a directory: %s\n"
#define MSGTR_LIBASS_TooManyFonts "[ass] Too many fonts\n" #define MSGTR_LIBASS_TooManyFonts "[ass] Too many fonts\n"
#define MSGTR_LIBASS_ErrorOpeningFont "[ass] Error opening font: %s, %d\n" #define MSGTR_LIBASS_ErrorOpeningFont "[ass] Error opening font: %s, %d\n"
#define MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne "[ass] fontconfig: Selected font family is not the requested one: '%s' != '%s'\n" #define MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne "[ass] fontconfig: Selected font is not the requested one: '%s' != '%s'\n"
#define MSGTR_LIBASS_UsingDefaultFontFamily "[ass] fontconfig_select: Using default font family: (%s, %d, %d) -> %s, %d\n" #define MSGTR_LIBASS_UsingDefaultFontFamily "[ass] fontconfig_select: Using default font family: (%s, %d, %d) -> %s, %d\n"
#define MSGTR_LIBASS_UsingDefaultFont "[ass] fontconfig_select: Using default font: (%s, %d, %d) -> %s, %d\n" #define MSGTR_LIBASS_UsingDefaultFont "[ass] fontconfig_select: Using default font: (%s, %d, %d) -> %s, %d\n"
#define MSGTR_LIBASS_UsingArialFontFamily "[ass] fontconfig_select: Using 'Arial' font family: (%s, %d, %d) -> %s, %d\n" #define MSGTR_LIBASS_UsingArialFontFamily "[ass] fontconfig_select: Using 'Arial' font family: (%s, %d, %d) -> %s, %d\n"
@ -46,11 +47,9 @@
#define MSGTR_LIBASS_EmptyEvent "[ass] Empty event!\n" #define MSGTR_LIBASS_EmptyEvent "[ass] Empty event!\n"
#define MSGTR_LIBASS_MAX_GLYPHS_Reached "[ass] MAX_GLYPHS reached: event %d, start = %llu, duration = %llu\n Text = %s\n" #define MSGTR_LIBASS_MAX_GLYPHS_Reached "[ass] MAX_GLYPHS reached: event %d, start = %llu, duration = %llu\n Text = %s\n"
#define MSGTR_LIBASS_EventHeightHasChanged "[ass] Warning! Event height has changed! \n" #define MSGTR_LIBASS_EventHeightHasChanged "[ass] Warning! Event height has changed! \n"
#define MSGTR_LIBASS_GlyphNotFoundReselectingFont "[ass] Glyph 0x%X not found, reselecting font for (%s, %d, %d)\n" #define MSGTR_LIBASS_GlyphNotFoundReselectingFont "[ass] Glyph 0x%X not found, selecting one more font for (%s, %d, %d)\n"
#define MSGTR_LIBASS_GlyphNotFound "[ass] Glyph 0x%X not found in font for (%s, %d, %d)\n" #define MSGTR_LIBASS_GlyphNotFound "[ass] Glyph 0x%X not found in font for (%s, %d, %d)\n"
#define MSGTR_LIBASS_ErrorOpeningMemoryFont "[ass] Error opening memory font: %s\n" #define MSGTR_LIBASS_ErrorOpeningMemoryFont "[ass] Error opening memory font: %s\n"
#define MSGTR_LIBASS_NoCharmaps "[ass] font face with no charmaps\n" #define MSGTR_LIBASS_NoCharmaps "[ass] font face with no charmaps\n"
#define MSGTR_LIBASS_NoCharmapAutodetected "[ass] no charmap autodetected, trying the first one\n" #define MSGTR_LIBASS_NoCharmapAutodetected "[ass] no charmap autodetected, trying the first one\n"
#define MSGTR_LIBASS_GlyphBBoxTooLarge "[ass] Glyph bounding box too large: %dx%dpx\n"
#endif #endif

View file

@ -73,7 +73,7 @@ LibassSubtitlesProvider::LibassSubtitlesProvider() {
ass_renderer = ass_renderer_init(ass_library); ass_renderer = ass_renderer_init(ass_library);
if (!ass_renderer) throw _T("ass_renderer_init failed"); if (!ass_renderer) throw _T("ass_renderer_init failed");
ass_set_font_scale(ass_renderer, 1.); ass_set_font_scale(ass_renderer, 1.);
ass_set_fonts(ass_renderer, NULL, "Sans"); ass_set_fonts(ass_renderer, NULL, "Sans", 1, NULL);
} }