Update libass to 15df37.
Originally committed to SVN as r3102.
This commit is contained in:
parent
06259dfb1e
commit
df2b0b5b67
22 changed files with 6208 additions and 4345 deletions
|
@ -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
|
||||||
|
|
1738
aegisub/libass/ass.c
1738
aegisub/libass/ass.c
File diff suppressed because it is too large
Load diff
|
@ -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>
|
||||||
*
|
*
|
||||||
|
@ -31,74 +29,75 @@ typedef struct ass_renderer_s ass_renderer_t;
|
||||||
|
|
||||||
/// a linked list of images produced by ass renderer
|
/// a linked list of images produced by ass renderer
|
||||||
typedef struct ass_image_s {
|
typedef struct ass_image_s {
|
||||||
int w, h; // bitmap width/height
|
int w, h; // bitmap width/height
|
||||||
int stride; // bitmap stride
|
int stride; // bitmap stride
|
||||||
unsigned char* bitmap; // 1bpp stride*h alpha buffer
|
unsigned char *bitmap; // 1bpp stride*h alpha buffer
|
||||||
uint32_t color; // RGBA
|
uint32_t color; // RGBA
|
||||||
int dst_x, dst_y; // bitmap placement inside the video frame
|
int dst_x, dst_y; // bitmap placement inside the video frame
|
||||||
|
|
||||||
struct ass_image_s* next; // linked list
|
struct ass_image_s *next; // linked list
|
||||||
} ass_image_t;
|
} ass_image_t;
|
||||||
|
|
||||||
/// Hinting type
|
/// Hinting type
|
||||||
typedef enum {ASS_HINTING_NONE = 0,
|
typedef enum { ASS_HINTING_NONE = 0,
|
||||||
ASS_HINTING_LIGHT,
|
ASS_HINTING_LIGHT,
|
||||||
ASS_HINTING_NORMAL,
|
ASS_HINTING_NORMAL,
|
||||||
ASS_HINTING_NATIVE
|
ASS_HINTING_NATIVE
|
||||||
} ass_hinting_t;
|
} ass_hinting_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief initialize the library
|
* \brief initialize the library
|
||||||
* \return library handle or NULL if failed
|
* \return library handle or NULL if failed
|
||||||
*/
|
*/
|
||||||
ass_library_t* ass_library_init(void);
|
ass_library_t *ass_library_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief finalize the library
|
* \brief finalize the library
|
||||||
* \param priv library handle
|
* \param priv library handle
|
||||||
*/
|
*/
|
||||||
void ass_library_done(ass_library_t*);
|
void ass_library_done(ass_library_t *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief set private font directory
|
* \brief set private font directory
|
||||||
* It is used for saving embedded fonts and also in font lookup.
|
* It is used for saving embedded fonts and also in font lookup.
|
||||||
*/
|
*/
|
||||||
void ass_set_fonts_dir(ass_library_t* priv, const char* fonts_dir);
|
void ass_set_fonts_dir(ass_library_t *priv, const char *fonts_dir);
|
||||||
|
|
||||||
void ass_set_extract_fonts(ass_library_t* priv, int extract);
|
void ass_set_extract_fonts(ass_library_t *priv, int extract);
|
||||||
|
|
||||||
void ass_set_style_overrides(ass_library_t* priv, char** list);
|
void ass_set_style_overrides(ass_library_t *priv, char **list);
|
||||||
|
|
||||||
|
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
|
||||||
* \return renderer handle or NULL if failed
|
* \return renderer handle or NULL if failed
|
||||||
*/
|
*/
|
||||||
ass_renderer_t* ass_renderer_init(ass_library_t*);
|
ass_renderer_t *ass_renderer_init(ass_library_t *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief finalize the renderer
|
* \brief finalize the renderer
|
||||||
* \param priv renderer handle
|
* \param priv renderer handle
|
||||||
*/
|
*/
|
||||||
void ass_renderer_done(ass_renderer_t* priv);
|
void ass_renderer_done(ass_renderer_t *priv);
|
||||||
|
|
||||||
void ass_set_frame_size(ass_renderer_t* priv, int w, int h);
|
void ass_set_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 //
|
||||||
|
@ -115,27 +115,27 @@ ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long lon
|
||||||
* \brief allocate a new empty track object
|
* \brief allocate a new empty track object
|
||||||
* \return pointer to empty track
|
* \return pointer to empty track
|
||||||
*/
|
*/
|
||||||
ass_track_t* ass_new_track(ass_library_t*);
|
ass_track_t *ass_new_track(ass_library_t *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief deallocate track and all its child objects (styles and events)
|
* \brief deallocate track and all its child objects (styles and events)
|
||||||
* \param track track to deallocate
|
* \param track track to deallocate
|
||||||
*/
|
*/
|
||||||
void ass_free_track(ass_track_t* track);
|
void ass_free_track(ass_track_t *track);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief allocate new style
|
* \brief allocate new style
|
||||||
* \param track track
|
* \param track track
|
||||||
* \return newly allocated style id
|
* \return newly allocated style id
|
||||||
*/
|
*/
|
||||||
int ass_alloc_style(ass_track_t* track);
|
int ass_alloc_style(ass_track_t *track);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief allocate new event
|
* \brief allocate new event
|
||||||
* \param track track
|
* \param track track
|
||||||
* \return newly allocated event id
|
* \return newly allocated event id
|
||||||
*/
|
*/
|
||||||
int ass_alloc_event(ass_track_t* track);
|
int ass_alloc_event(ass_track_t *track);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief delete a style
|
* \brief delete a style
|
||||||
|
@ -143,7 +143,7 @@ int ass_alloc_event(ass_track_t* track);
|
||||||
* \param sid style id
|
* \param sid style id
|
||||||
* Deallocates style data. Does not modify track->n_styles.
|
* Deallocates style data. Does not modify track->n_styles.
|
||||||
*/
|
*/
|
||||||
void ass_free_style(ass_track_t* track, int sid);
|
void ass_free_style(ass_track_t *track, int sid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief delete an event
|
* \brief delete an event
|
||||||
|
@ -151,7 +151,7 @@ void ass_free_style(ass_track_t* track, int sid);
|
||||||
* \param eid event id
|
* \param eid event id
|
||||||
* Deallocates event data. Does not modify track->n_events.
|
* Deallocates event data. Does not modify track->n_events.
|
||||||
*/
|
*/
|
||||||
void ass_free_event(ass_track_t* track, int eid);
|
void ass_free_event(ass_track_t *track, int eid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Parse a chunk of subtitle stream data.
|
* \brief Parse a chunk of subtitle stream data.
|
||||||
|
@ -159,7 +159,7 @@ void ass_free_event(ass_track_t* track, int eid);
|
||||||
* \param data string to parse
|
* \param data string to parse
|
||||||
* \param size length of data
|
* \param size length of data
|
||||||
*/
|
*/
|
||||||
void ass_process_data(ass_track_t* track, char* data, int size);
|
void ass_process_data(ass_track_t *track, char *data, int size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Parse Codec Private section of subtitle stream
|
* \brief Parse Codec Private section of subtitle stream
|
||||||
|
@ -167,7 +167,7 @@ void ass_process_data(ass_track_t* track, char* data, int size);
|
||||||
* \param data string to parse
|
* \param data string to parse
|
||||||
* \param size length of data
|
* \param size length of data
|
||||||
*/
|
*/
|
||||||
void ass_process_codec_private(ass_track_t* track, char *data, int size);
|
void ass_process_codec_private(ass_track_t *track, char *data, int size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Parse a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary).
|
* \brief Parse a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary).
|
||||||
|
@ -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.
|
||||||
|
@ -195,13 +195,14 @@ ass_track_t* ass_read_file(ass_library_t* library, char* fname, char* codepage);
|
||||||
* \param bufsize size of buffer
|
* \param bufsize size of buffer
|
||||||
* \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
|
||||||
*/
|
*/
|
||||||
int ass_read_styles(ass_track_t* track, char* fname, char* codepage);
|
int ass_read_styles(ass_track_t *track, char *fname, char *codepage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Add a memory font.
|
* \brief Add a memory font.
|
||||||
|
@ -209,12 +210,13 @@ 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
|
||||||
*/
|
*/
|
||||||
void ass_clear_fonts(ass_library_t* library);
|
void ass_clear_fonts(ass_library_t *library);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Calculates timeshift from now to the start of some other subtitle event, depending on movement parameter
|
* \brief Calculates timeshift from now to the start of some other subtitle event, depending on movement parameter
|
||||||
|
@ -224,6 +226,6 @@ void ass_clear_fonts(ass_library_t* library);
|
||||||
* +2 means "the one after the next", -1 means "previous"
|
* +2 means "the one after the next", -1 means "previous"
|
||||||
* \return timeshift, ms
|
* \return timeshift, ms
|
||||||
*/
|
*/
|
||||||
long long ass_step_sub(ass_track_t* track, long long now, int movement);
|
long long ass_step_sub(ass_track_t *track, long long now, int movement);
|
||||||
|
|
||||||
#endif /* LIBASS_ASS_H */
|
#endif /* LIBASS_ASS_H */
|
||||||
|
|
|
@ -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,195 +25,207 @@
|
||||||
#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 {
|
||||||
int tmp_w, tmp_h;
|
int tmp_w, tmp_h;
|
||||||
unsigned short* tmp;
|
unsigned short *tmp;
|
||||||
|
|
||||||
int g_r;
|
int g_r;
|
||||||
int g_w;
|
int g_w;
|
||||||
|
|
||||||
unsigned *g;
|
unsigned *g;
|
||||||
unsigned *gt2;
|
unsigned *gt2;
|
||||||
|
|
||||||
double radius;
|
double radius;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned int maxcolor = 255;
|
static const unsigned int maxcolor = 255;
|
||||||
static const unsigned base = 256;
|
static const unsigned base = 256;
|
||||||
|
|
||||||
static int generate_tables(ass_synth_priv_t* priv, double radius)
|
static int generate_tables(ass_synth_priv_t *priv, double radius)
|
||||||
{
|
{
|
||||||
double A = log(1.0/base)/(radius*radius*2);
|
double A = log(1.0 / base) / (radius * radius * 2);
|
||||||
int mx, i;
|
int mx, i;
|
||||||
double volume_diff, volume_factor = 0;
|
double volume_diff, volume_factor = 0;
|
||||||
unsigned volume;
|
unsigned volume;
|
||||||
|
|
||||||
if (priv->radius == radius)
|
if (priv->radius == radius)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
priv->radius = radius;
|
priv->radius = radius;
|
||||||
|
|
||||||
priv->g_r = ceil(radius);
|
priv->g_r = ceil(radius);
|
||||||
priv->g_w = 2*priv->g_r+1;
|
priv->g_w = 2 * priv->g_r + 1;
|
||||||
|
|
||||||
if (priv->g_r) {
|
if (priv->g_r) {
|
||||||
priv->g = realloc(priv->g, priv->g_w * sizeof(unsigned));
|
priv->g = realloc(priv->g, priv->g_w * sizeof(unsigned));
|
||||||
priv->gt2 = realloc(priv->gt2, 256 * priv->g_w * sizeof(unsigned));
|
priv->gt2 = realloc(priv->gt2, 256 * priv->g_w * sizeof(unsigned));
|
||||||
if (priv->g==NULL || priv->gt2==NULL) {
|
if (priv->g == NULL || priv->gt2 == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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_factor+= volume_diff;
|
volume_diff *= 0.5) {
|
||||||
volume=0;
|
volume_factor += volume_diff;
|
||||||
for (i = 0; i<priv->g_w; ++i) {
|
volume = 0;
|
||||||
priv->g[i] = (unsigned)(exp(A * (i-priv->g_r)*(i-priv->g_r)) * volume_factor + .5);
|
for (i = 0; i < priv->g_w; ++i) {
|
||||||
volume+= priv->g[i];
|
priv->g[i] =
|
||||||
}
|
(unsigned) (exp(A * (i - priv->g_r) * (i - priv->g_r)) *
|
||||||
if(volume>256) volume_factor-= volume_diff;
|
volume_factor + .5);
|
||||||
}
|
volume += priv->g[i];
|
||||||
volume=0;
|
}
|
||||||
for (i = 0; i<priv->g_w; ++i) {
|
if (volume > 256)
|
||||||
priv->g[i] = (unsigned)(exp(A * (i-priv->g_r)*(i-priv->g_r)) * volume_factor + .5);
|
volume_factor -= volume_diff;
|
||||||
volume+= priv->g[i];
|
}
|
||||||
}
|
volume = 0;
|
||||||
|
for (i = 0; i < priv->g_w; ++i) {
|
||||||
|
priv->g[i] =
|
||||||
|
(unsigned) (exp(A * (i - priv->g_r) * (i - priv->g_r)) *
|
||||||
|
volume_factor + .5);
|
||||||
|
volume += priv->g[i];
|
||||||
|
}
|
||||||
|
|
||||||
// gauss table:
|
// gauss table:
|
||||||
for(mx=0;mx<priv->g_w;mx++){
|
for (mx = 0; mx < priv->g_w; mx++) {
|
||||||
for(i=0;i<256;i++){
|
for (i = 0; i < 256; i++) {
|
||||||
priv->gt2[mx+i*priv->g_w] = i*priv->g[mx];
|
priv->gt2[mx + i * priv->g_w] = i * priv->g[mx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resize_tmp(ass_synth_priv_t* priv, int w, int h)
|
static void resize_tmp(ass_synth_priv_t *priv, int w, int h)
|
||||||
{
|
{
|
||||||
if (priv->tmp_w >= w && priv->tmp_h >= h)
|
if (priv->tmp_w >= w && priv->tmp_h >= h)
|
||||||
return;
|
return;
|
||||||
if (priv->tmp_w == 0)
|
if (priv->tmp_w == 0)
|
||||||
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;
|
||||||
if (priv->tmp)
|
while (priv->tmp_h < h)
|
||||||
free(priv->tmp);
|
priv->tmp_h *= 2;
|
||||||
priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short));
|
if (priv->tmp)
|
||||||
|
free(priv->tmp);
|
||||||
|
priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short));
|
||||||
}
|
}
|
||||||
|
|
||||||
ass_synth_priv_t* ass_synth_init(double radius)
|
ass_synth_priv_t *ass_synth_init(double radius)
|
||||||
{
|
{
|
||||||
ass_synth_priv_t* priv = calloc(1, sizeof(ass_synth_priv_t));
|
ass_synth_priv_t *priv = calloc(1, sizeof(ass_synth_priv_t));
|
||||||
generate_tables(priv, radius);
|
generate_tables(priv, radius);
|
||||||
return priv;
|
return priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ass_synth_done(ass_synth_priv_t* priv)
|
void ass_synth_done(ass_synth_priv_t *priv)
|
||||||
{
|
{
|
||||||
if (priv->tmp)
|
if (priv->tmp)
|
||||||
free(priv->tmp);
|
free(priv->tmp);
|
||||||
if (priv->g)
|
if (priv->g)
|
||||||
free(priv->g);
|
free(priv->g);
|
||||||
if (priv->gt2)
|
if (priv->gt2)
|
||||||
free(priv->gt2);
|
free(priv->gt2);
|
||||||
free(priv);
|
free(priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bitmap_t* alloc_bitmap(int w, int h)
|
static bitmap_t *alloc_bitmap(int w, int h)
|
||||||
{
|
{
|
||||||
bitmap_t* bm;
|
bitmap_t *bm;
|
||||||
bm = calloc(1, sizeof(bitmap_t));
|
bm = calloc(1, sizeof(bitmap_t));
|
||||||
bm->buffer = malloc(w*h);
|
bm->buffer = malloc(w * h);
|
||||||
bm->w = w;
|
bm->w = w;
|
||||||
bm->h = h;
|
bm->h = h;
|
||||||
bm->left = bm->top = 0;
|
bm->left = bm->top = 0;
|
||||||
return bm;
|
return bm;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
free(bm->buffer);
|
||||||
}
|
free(bm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bitmap_t* copy_bitmap(const bitmap_t* src)
|
static bitmap_t *copy_bitmap(const bitmap_t *src)
|
||||||
{
|
{
|
||||||
bitmap_t* dst = alloc_bitmap(src->w, src->h);
|
bitmap_t *dst = alloc_bitmap(src->w, src->h);
|
||||||
dst->left = src->left;
|
dst->left = src->left;
|
||||||
dst->top = src->top;
|
dst->top = src->top;
|
||||||
memcpy(dst->buffer, src->buffer, src->w * src->h);
|
memcpy(dst->buffer, src->buffer, src->w * src->h);
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int check_glyph_area(FT_Glyph glyph)
|
static int check_glyph_area(FT_Glyph glyph)
|
||||||
{
|
{
|
||||||
FT_BBox bbox;
|
FT_BBox bbox;
|
||||||
long long dx, dy;
|
long long dx, dy;
|
||||||
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox);
|
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox);
|
||||||
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,
|
||||||
return 1;
|
(int) dx, (int) dy);
|
||||||
} else
|
return 1;
|
||||||
return 0;
|
} else
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bitmap_t* glyph_to_bitmap_internal(FT_Glyph glyph, int bord)
|
static bitmap_t *glyph_to_bitmap_internal(FT_Glyph glyph, int bord)
|
||||||
{
|
{
|
||||||
FT_BitmapGlyph bg;
|
FT_BitmapGlyph bg;
|
||||||
FT_Bitmap* bit;
|
FT_Bitmap *bit;
|
||||||
bitmap_t* bm;
|
bitmap_t *bm;
|
||||||
int w, h;
|
int w, h;
|
||||||
unsigned char* src;
|
unsigned char *src;
|
||||||
unsigned char* dst;
|
unsigned char *dst;
|
||||||
int i;
|
int i;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (check_glyph_area(glyph))
|
if (check_glyph_area(glyph))
|
||||||
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,
|
||||||
return 0;
|
error);
|
||||||
}
|
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,
|
||||||
FT_Done_Glyph(glyph);
|
(int) (bit->pixel_mode));
|
||||||
return 0;
|
FT_Done_Glyph(glyph);
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
w = bit->width;
|
w = bit->width;
|
||||||
h = bit->rows;
|
h = bit->rows;
|
||||||
bm = alloc_bitmap(w + 2*bord, h + 2*bord);
|
bm = alloc_bitmap(w + 2 * bord, h + 2 * bord);
|
||||||
memset(bm->buffer, 0, bm->w * bm->h);
|
memset(bm->buffer, 0, bm->w * bm->h);
|
||||||
bm->left = bg->left - bord;
|
bm->left = bg->left - bord;
|
||||||
bm->top = - bg->top - bord;
|
bm->top = -bg->top - bord;
|
||||||
|
|
||||||
src = bit->buffer;
|
src = bit->buffer;
|
||||||
dst = bm->buffer + bord + bm->w * bord;
|
dst = bm->buffer + bord + bm->w * bord;
|
||||||
for (i = 0; i < h; ++i) {
|
for (i = 0; i < h; ++i) {
|
||||||
memcpy(dst, src, w);
|
memcpy(dst, src, w);
|
||||||
src += bit->pitch;
|
src += bit->pitch;
|
||||||
dst += bm->w;
|
dst += bm->w;
|
||||||
}
|
}
|
||||||
|
|
||||||
FT_Done_Glyph(glyph);
|
FT_Done_Glyph(glyph);
|
||||||
return bm;
|
return bm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -224,114 +234,183 @@ static bitmap_t* glyph_to_bitmap_internal(FT_Glyph glyph, int bord)
|
||||||
* 1. Glyph bitmap is subtracted from outline bitmap. This way looks much better in some cases.
|
* 1. Glyph bitmap is subtracted from outline bitmap. This way looks much better in some cases.
|
||||||
* 2. Shadow bitmap is created as a sum of glyph and outline bitmaps.
|
* 2. Shadow bitmap is created as a sum of glyph and outline bitmaps.
|
||||||
*/
|
*/
|
||||||
static bitmap_t* fix_outline_and_shadow(bitmap_t* bm_g, bitmap_t* bm_o)
|
static bitmap_t *fix_outline_and_shadow(bitmap_t *bm_g, bitmap_t *bm_o)
|
||||||
{
|
{
|
||||||
int x, y;
|
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);
|
||||||
for (y = 0; y < b - t; ++y) {
|
unsigned char *s =
|
||||||
for (x = 0; x < r - l; ++x) {
|
bm_s->buffer + (t - bm_s->top) * bm_s->w + (l - bm_s->left);
|
||||||
unsigned char c_g, c_o;
|
|
||||||
c_g = g[x];
|
|
||||||
c_o = o[x];
|
|
||||||
o[x] = (c_o > c_g) ? c_o - (c_g/2) : 0;
|
|
||||||
s[x] = (c_o < 0xFF - c_g) ? c_o + c_g : 0xFF;
|
|
||||||
}
|
|
||||||
g += bm_g->w;
|
|
||||||
o += bm_o->w;
|
|
||||||
s += bm_s->w;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(bm_s);
|
for (y = 0; y < b - t; ++y) {
|
||||||
return bm_s;
|
for (x = 0; x < r - l; ++x) {
|
||||||
|
unsigned char c_g, c_o;
|
||||||
|
c_g = g[x];
|
||||||
|
c_o = o[x];
|
||||||
|
o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0;
|
||||||
|
s[x] = (c_o < 0xFF - c_g) ? c_o + c_g : 0xFF;
|
||||||
|
}
|
||||||
|
g += bm_g->w;
|
||||||
|
o += bm_o->w;
|
||||||
|
s += bm_s->w;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(bm_s);
|
||||||
|
return bm_s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief 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 old_sum, new_sum;
|
|
||||||
|
|
||||||
for (y=0; y<h; y++) {
|
|
||||||
old_sum = 2 * buf[y*w];
|
|
||||||
for (x=0; x<w-1; x++) {
|
|
||||||
new_sum = buf[y*w+x] + buf[y*w+x+1];
|
|
||||||
buf[y*w+x] = (old_sum + new_sum) >> 2;
|
|
||||||
old_sum = new_sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (x=0; x<w; x++) {
|
|
||||||
old_sum = 2 * buf[x];
|
|
||||||
for (y=0; y<h-1; y++) {
|
|
||||||
new_sum = buf[y*w+x] + buf[(y+1)*w+x];
|
|
||||||
buf[y*w+x] = (old_sum + new_sum) >> 2;
|
|
||||||
old_sum = new_sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int glyph_to_bitmap(ass_synth_priv_t* priv_blur,
|
|
||||||
FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g,
|
|
||||||
bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius)
|
|
||||||
{
|
{
|
||||||
int bord = be ? (be/4+1) : 0;
|
unsigned int x, y;
|
||||||
blur_radius *= 2;
|
unsigned int old_sum, new_sum;
|
||||||
bord = (blur_radius > 0.0) ? blur_radius : bord;
|
|
||||||
|
|
||||||
assert(bm_g && bm_o && bm_s);
|
for (y = 0; y < h; y++) {
|
||||||
|
old_sum = 2 * buf[y * w];
|
||||||
|
for (x = 0; x < w - 1; x++) {
|
||||||
|
new_sum = buf[y * w + x] + buf[y * w + x + 1];
|
||||||
|
buf[y * w + x] = (old_sum + new_sum) >> 2;
|
||||||
|
old_sum = new_sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*bm_g = *bm_o = *bm_s = 0;
|
for (x = 0; x < w; x++) {
|
||||||
|
old_sum = 2 * buf[x];
|
||||||
if (glyph)
|
for (y = 0; y < h - 1; y++) {
|
||||||
*bm_g = glyph_to_bitmap_internal(glyph, bord);
|
new_sum = buf[y * w + x] + buf[(y + 1) * w + x];
|
||||||
if (!*bm_g)
|
buf[y * w + x] = (old_sum + new_sum) >> 2;
|
||||||
return 1;
|
old_sum = new_sum;
|
||||||
|
}
|
||||||
if (outline_glyph) {
|
}
|
||||||
*bm_o = glyph_to_bitmap_internal(outline_glyph, bord);
|
|
||||||
if (!*bm_o) {
|
|
||||||
ass_free_bitmap(*bm_g);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*bm_o)
|
|
||||||
resize_tmp(priv_blur, (*bm_o)->w, (*bm_o)->h);
|
|
||||||
resize_tmp(priv_blur, (*bm_g)->w, (*bm_g)->h);
|
|
||||||
|
|
||||||
if (be) {
|
|
||||||
while (be--) {
|
|
||||||
if (*bm_o)
|
|
||||||
be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h);
|
|
||||||
else
|
|
||||||
be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (blur_radius > 0.0) {
|
|
||||||
generate_tables(priv_blur, blur_radius);
|
|
||||||
if (*bm_o)
|
|
||||||
blur((*bm_o)->buffer, priv_blur->tmp, (*bm_o)->w, (*bm_o)->h, (*bm_o)->w, (int*)priv_blur->gt2, priv_blur->g_r, priv_blur->g_w);
|
|
||||||
else
|
|
||||||
blur((*bm_g)->buffer, priv_blur->tmp, (*bm_g)->w, (*bm_g)->h, (*bm_g)->w, (int*)priv_blur->gt2, priv_blur->g_r, priv_blur->g_w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*bm_o)
|
|
||||||
*bm_s = fix_outline_and_shadow(*bm_g, *bm_o);
|
|
||||||
else
|
|
||||||
*bm_s = copy_bitmap(*bm_g);
|
|
||||||
|
|
||||||
assert(bm_s);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int 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)
|
||||||
|
{
|
||||||
|
int bord = be ? (be / 4 + 1) : 0;
|
||||||
|
blur_radius *= 2;
|
||||||
|
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);
|
||||||
|
|
||||||
|
*bm_g = *bm_o = *bm_s = 0;
|
||||||
|
|
||||||
|
if (glyph)
|
||||||
|
*bm_g = glyph_to_bitmap_internal(glyph, bord);
|
||||||
|
if (!*bm_g)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (outline_glyph) {
|
||||||
|
*bm_o = glyph_to_bitmap_internal(outline_glyph, bord);
|
||||||
|
if (!*bm_o) {
|
||||||
|
ass_free_bitmap(*bm_g);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*bm_o)
|
||||||
|
resize_tmp(priv_blur, (*bm_o)->w, (*bm_o)->h);
|
||||||
|
resize_tmp(priv_blur, (*bm_g)->w, (*bm_g)->h);
|
||||||
|
|
||||||
|
if (be) {
|
||||||
|
while (be--) {
|
||||||
|
if (*bm_o)
|
||||||
|
be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h);
|
||||||
|
else
|
||||||
|
be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (blur_radius > 0.0) {
|
||||||
|
generate_tables(priv_blur, blur_radius);
|
||||||
|
if (*bm_o)
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
*bm_s = fix_outline_and_shadow(*bm_g, *bm_o);
|
||||||
|
else
|
||||||
|
*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);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
*
|
*
|
||||||
|
@ -28,13 +26,13 @@
|
||||||
|
|
||||||
typedef struct ass_synth_priv_s ass_synth_priv_t;
|
typedef struct ass_synth_priv_s ass_synth_priv_t;
|
||||||
|
|
||||||
ass_synth_priv_t* ass_synth_init(double);
|
ass_synth_priv_t *ass_synth_init(double);
|
||||||
void ass_synth_done(ass_synth_priv_t* priv);
|
void ass_synth_done(ass_synth_priv_t *priv);
|
||||||
|
|
||||||
typedef struct bitmap_s {
|
typedef struct bitmap_s {
|
||||||
int left, top;
|
int left, top;
|
||||||
int w, h; // width, height
|
int w, h; // width, height
|
||||||
unsigned char* buffer; // w x h buffer
|
unsigned char *buffer; // w x h buffer
|
||||||
} bitmap_t;
|
} bitmap_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,8 +44,11 @@ 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);
|
||||||
|
|
||||||
#endif /* LIBASS_BITMAP_H */
|
#endif /* LIBASS_BITMAP_H */
|
||||||
|
|
|
@ -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,205 +27,170 @@
|
||||||
|
|
||||||
#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"
|
||||||
|
|
||||||
|
static unsigned hashmap_hash(void *buf, size_t len)
|
||||||
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;
|
return fnv_32a_buf(buf, len, FNV1_32A_INIT);
|
||||||
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 int hashmap_key_compare(void *a, void *b, size_t size)
|
||||||
{
|
{
|
||||||
return fnv_32a_buf(buf, len, FNV1_32A_INIT);
|
return memcmp(a, b, size) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hashmap_key_compare(void* a, void* b, size_t size)
|
static void hashmap_item_dtor(void *key, size_t key_size, void *value,
|
||||||
|
size_t value_size)
|
||||||
{
|
{
|
||||||
return memcmp(a, b, size) == 0;
|
free(key);
|
||||||
|
free(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hashmap_item_dtor(void* key, size_t key_size, void* value, size_t value_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)
|
||||||
{
|
{
|
||||||
free(key);
|
hashmap_t *map = calloc(1, sizeof(hashmap_t));
|
||||||
free(value);
|
map->nbuckets = nbuckets;
|
||||||
|
map->key_size = key_size;
|
||||||
|
map->value_size = value_size;
|
||||||
|
map->root = calloc(nbuckets, sizeof(hashmap_item_p));
|
||||||
|
map->item_dtor = item_dtor ? item_dtor : hashmap_item_dtor;
|
||||||
|
map->key_compare = key_compare ? key_compare : hashmap_key_compare;
|
||||||
|
map->hash = hash ? hash : hashmap_hash;
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
hashmap_t* hashmap_init(size_t key_size, size_t value_size, int nbuckets,
|
void hashmap_done(hashmap_t *map)
|
||||||
hashmap_item_dtor_t item_dtor, hashmap_key_compare_t key_compare,
|
|
||||||
hashmap_hash_t hash)
|
|
||||||
{
|
{
|
||||||
hashmap_t* map = calloc(1, sizeof(hashmap_t));
|
int i;
|
||||||
map->nbuckets = nbuckets;
|
// print stats
|
||||||
map->key_size = key_size;
|
if (map->count > 0 || map->hit_count + map->miss_count > 0)
|
||||||
map->value_size = value_size;
|
ass_msg(MSGL_V,
|
||||||
map->root = calloc(nbuckets, sizeof(hashmap_item_p));
|
"cache statistics: \n total accesses: %d\n hits: %d\n misses: %d\n object count: %d\n",
|
||||||
map->item_dtor = item_dtor ? item_dtor : hashmap_item_dtor;
|
map->hit_count + map->miss_count, map->hit_count,
|
||||||
map->key_compare = key_compare ? key_compare : hashmap_key_compare;
|
map->miss_count, map->count);
|
||||||
map->hash = hash ? hash : hashmap_hash;
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hashmap_done(hashmap_t* map)
|
for (i = 0; i < map->nbuckets; ++i) {
|
||||||
{
|
hashmap_item_t *item = map->root[i];
|
||||||
int i;
|
while (item) {
|
||||||
// print stats
|
hashmap_item_t *next = item->next;
|
||||||
if (map->count > 0 || map->hit_count + map->miss_count > 0)
|
map->item_dtor(item->key, map->key_size, item->value,
|
||||||
mp_msg(MSGT_ASS, MSGL_V, "cache statistics: \n total accesses: %d\n hits: %d\n misses: %d\n object count: %d\n",
|
map->value_size);
|
||||||
map->hit_count + map->miss_count, map->hit_count, map->miss_count, map->count);
|
free(item);
|
||||||
|
item = next;
|
||||||
for (i = 0; i < map->nbuckets; ++i) {
|
}
|
||||||
hashmap_item_t* item = map->root[i];
|
}
|
||||||
while (item) {
|
free(map->root);
|
||||||
hashmap_item_t* next = item->next;
|
free(map);
|
||||||
map->item_dtor(item->key, map->key_size, item->value, map->value_size);
|
|
||||||
free(item);
|
|
||||||
item = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(map->root);
|
|
||||||
free(map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// does nothing if key already exists
|
// does nothing if key already exists
|
||||||
void* hashmap_insert(hashmap_t* map, void* key, void* value)
|
void *hashmap_insert(hashmap_t *map, void *key, void *value)
|
||||||
{
|
{
|
||||||
unsigned hash = map->hash(key, map->key_size);
|
unsigned hash = map->hash(key, map->key_size);
|
||||||
hashmap_item_t** next = map->root + (hash % map->nbuckets);
|
hashmap_item_t **next = map->root + (hash % map->nbuckets);
|
||||||
while (*next) {
|
while (*next) {
|
||||||
if (map->key_compare(key, (*next)->key, map->key_size))
|
if (map->key_compare(key, (*next)->key, map->key_size))
|
||||||
return (*next)->value;
|
return (*next)->value;
|
||||||
next = &((*next)->next);
|
next = &((*next)->next);
|
||||||
assert(next);
|
assert(next);
|
||||||
}
|
}
|
||||||
(*next) = malloc(sizeof(hashmap_item_t));
|
(*next) = malloc(sizeof(hashmap_item_t));
|
||||||
(*next)->key = malloc(map->key_size);
|
(*next)->key = malloc(map->key_size);
|
||||||
(*next)->value = malloc(map->value_size);
|
(*next)->value = malloc(map->value_size);
|
||||||
memcpy((*next)->key, key, map->key_size);
|
memcpy((*next)->key, key, map->key_size);
|
||||||
memcpy((*next)->value, value, map->value_size);
|
memcpy((*next)->value, value, map->value_size);
|
||||||
(*next)->next = 0;
|
(*next)->next = 0;
|
||||||
|
|
||||||
map->count ++;
|
map->count++;
|
||||||
return (*next)->value;
|
return (*next)->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* hashmap_find(hashmap_t* map, void* key)
|
void *hashmap_find(hashmap_t *map, void *key)
|
||||||
{
|
{
|
||||||
unsigned hash = map->hash(key, map->key_size);
|
unsigned hash = map->hash(key, map->key_size);
|
||||||
hashmap_item_t* item = map->root[hash % map->nbuckets];
|
hashmap_item_t *item = map->root[hash % map->nbuckets];
|
||||||
while (item) {
|
while (item) {
|
||||||
if (map->key_compare(key, item->key, map->key_size)) {
|
if (map->key_compare(key, item->key, map->key_size)) {
|
||||||
map->hit_count++;
|
map->hit_count++;
|
||||||
return item->value;
|
return item->value;
|
||||||
}
|
}
|
||||||
item = item->next;
|
item = item->next;
|
||||||
}
|
}
|
||||||
map->miss_count++;
|
map->miss_count++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
// 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;
|
||||||
unsigned hval;
|
unsigned hval;
|
||||||
hval = fnv_32a_str(desc->family, FNV1_32A_INIT);
|
hval = fnv_32a_str(desc->family, FNV1_32A_INIT);
|
||||||
hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval);
|
hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval);
|
||||||
hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval);
|
hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval);
|
||||||
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* b = key2;
|
ass_font_desc_t *a = key1;
|
||||||
if (strcmp(a->family, b->family) != 0)
|
ass_font_desc_t *b = key2;
|
||||||
return 0;
|
if (strcmp(a->family, b->family) != 0)
|
||||||
if (a->bold != b->bold)
|
return 0;
|
||||||
return 0;
|
if (a->bold != b->bold)
|
||||||
if (a->italic != b->italic)
|
return 0;
|
||||||
return 0;
|
if (a->italic != b->italic)
|
||||||
if (a->treat_family_as_pattern != b->treat_family_as_pattern)
|
return 0;
|
||||||
return 0;
|
if (a->treat_family_as_pattern != b->treat_family_as_pattern)
|
||||||
return 1;
|
return 0;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void font_hash_dtor(void* key, size_t key_size, void* value, size_t value_size)
|
static void font_hash_dtor(void *key, size_t key_size, void *value,
|
||||||
|
size_t value_size)
|
||||||
{
|
{
|
||||||
ass_font_free(value);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \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)
|
||||||
{
|
{
|
||||||
font_cache = hashmap_init(sizeof(ass_font_desc_t),
|
hashmap_t *font_cache;
|
||||||
sizeof(ass_font_t),
|
font_cache = hashmap_init(sizeof(ass_font_desc_t),
|
||||||
1000,
|
sizeof(ass_font_t),
|
||||||
font_hash_dtor, font_compare, font_desc_hash);
|
1000,
|
||||||
|
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,119 +203,131 @@ 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)
|
||||||
free(key);
|
ass_free_bitmap(v->bm_o);
|
||||||
free(value);
|
if (v->bm_s)
|
||||||
|
ass_free_bitmap(v->bm_s);
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* cache_add_bitmap(bitmap_hash_key_t* key, bitmap_hash_val_t* val)
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Get a bitmap from bitmap cache.
|
* \brief Get a bitmap from bitmap cache.
|
||||||
* \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)
|
||||||
{
|
{
|
||||||
bitmap_cache = hashmap_init(sizeof(bitmap_hash_key_t),
|
hashmap_t *bitmap_cache;
|
||||||
sizeof(bitmap_hash_val_t),
|
bitmap_cache = hashmap_init(sizeof(bitmap_hash_key_t),
|
||||||
0xFFFF + 13,
|
sizeof(bitmap_hash_val_t),
|
||||||
bitmap_hash_dtor, bitmap_compare,
|
0xFFFF + 13,
|
||||||
bitmap_hash);
|
bitmap_hash_dtor, bitmap_compare,
|
||||||
|
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);
|
||||||
free(key);
|
if (v->outline_glyph)
|
||||||
free(value);
|
FT_Done_Glyph(v->outline_glyph);
|
||||||
|
free(key);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Get a glyph from glyph cache.
|
* \brief Get a glyph from glyph cache.
|
||||||
* \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)
|
||||||
{
|
{
|
||||||
glyph_cache = hashmap_init(sizeof(glyph_hash_key_t),
|
hashmap_t *glyph_cache;
|
||||||
sizeof(glyph_hash_val_t),
|
glyph_cache = hashmap_init(sizeof(glyph_hash_key_t),
|
||||||
0xFFFF + 13,
|
sizeof(glyph_hash_val_t),
|
||||||
glyph_hash_dtor, glyph_compare, glyph_hash);
|
0xFFFF + 13,
|
||||||
|
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);
|
||||||
free(v->b);
|
free(v->b);
|
||||||
free(key);
|
free(key);
|
||||||
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)
|
||||||
{
|
{
|
||||||
composite_cache = hashmap_init(sizeof(composite_hash_key_t),
|
hashmap_t *composite_cache;
|
||||||
sizeof(composite_hash_val_t),
|
composite_cache = hashmap_init(sizeof(composite_hash_key_t),
|
||||||
0xFFFF + 13,
|
sizeof(composite_hash_val_t),
|
||||||
composite_hash_dtor, NULL, NULL);
|
0xFFFF + 13,
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,72 +25,92 @@
|
||||||
#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
|
||||||
#include "ass_cache_template.c"
|
#include "ass_cache_template.c"
|
||||||
|
|
||||||
typedef struct bitmap_hash_val_s {
|
typedef struct bitmap_hash_val_s {
|
||||||
bitmap_t* bm; // the actual bitmaps
|
bitmap_t *bm; // the actual bitmaps
|
||||||
bitmap_t* bm_o;
|
bitmap_t *bm_o;
|
||||||
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 {
|
||||||
FT_Glyph glyph;
|
FT_Glyph glyph;
|
||||||
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);
|
||||||
|
void ass_glyph_cache_done(hashmap_t *glyph_cache);
|
||||||
|
|
||||||
typedef struct hashmap_s hashmap_t;
|
#endif /* LIBASS_CACHE_H */
|
||||||
typedef void (*hashmap_item_dtor_t)(void* key, size_t key_size, void* value, size_t value_size);
|
|
||||||
typedef int (*hashmap_key_compare_t)(void* key1, void* key2, size_t key_size);
|
|
||||||
typedef unsigned (*hashmap_hash_t)(void* key, size_t key_size);
|
|
||||||
|
|
||||||
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 */
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
477
aegisub/libass/ass_drawing.c
Normal file
477
aegisub/libass/ass_drawing.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
76
aegisub/libass/ass_drawing.h
Normal file
76
aegisub/libass/ass_drawing.h
Normal 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 */
|
|
@ -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.
|
||||||
|
@ -44,200 +41,218 @@
|
||||||
*/
|
*/
|
||||||
static void charmap_magic(FT_Face face)
|
static void charmap_magic(FT_Face face)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < face->num_charmaps; ++i) {
|
for (i = 0; i < face->num_charmaps; ++i) {
|
||||||
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 */
|
||||||
FT_Set_Charmap(face, cmap);
|
&& (eid == 1 /*unicode bmp */
|
||||||
return;
|
|| eid == 10 /*full unicode */ )) {
|
||||||
}
|
FT_Set_Charmap(face, cmap);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_transform(ass_font_t* font)
|
static void update_transform(ass_font_t *font)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
FT_Matrix m;
|
FT_Matrix m;
|
||||||
m.xx = double_to_d16(font->scale_x);
|
m.xx = double_to_d16(font->scale_x);
|
||||||
m.yy = double_to_d16(font->scale_y);
|
m.yy = double_to_d16(font->scale_y);
|
||||||
m.xy = m.yx = 0;
|
m.xy = m.yx = 0;
|
||||||
for (i = 0; i < font->n_faces; ++i)
|
for (i = 0; i < font->n_faces; ++i)
|
||||||
FT_Set_Transform(font->faces[i], &m, &font->v);
|
FT_Set_Transform(font->faces[i], &m, &font->v);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief find a memory font by name
|
* \brief find a memory font by name
|
||||||
*/
|
*/
|
||||||
static int find_font(ass_library_t* library, char* name)
|
static int find_font(ass_library_t *library, char *name)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < library->num_fontdata; ++i)
|
for (i = 0; i < library->num_fontdata; ++i)
|
||||||
if (strcasecmp(name, library->fontdata[i].name) == 0)
|
if (strcasecmp(name, library->fontdata[i].name) == 0)
|
||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void face_set_size(FT_Face face, double size);
|
static void face_set_size(FT_Face face, double size);
|
||||||
|
|
||||||
static void buggy_font_workaround(FT_Face face)
|
static void buggy_font_workaround(FT_Face face)
|
||||||
{
|
{
|
||||||
// Some fonts have zero Ascender/Descender fields in 'hhea' table.
|
// Some fonts have zero Ascender/Descender fields in 'hhea' table.
|
||||||
// In this case, get the information from 'os2' table or, as
|
// In this case, get the information from 'os2' table or, as
|
||||||
// a last resort, from face.bbox.
|
// a last resort, from face.bbox.
|
||||||
if (face->ascender + face->descender == 0 || face->height == 0) {
|
if (face->ascender + face->descender == 0 || face->height == 0) {
|
||||||
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
|
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
|
||||||
if (os2) {
|
if (os2) {
|
||||||
face->ascender = os2->sTypoAscender;
|
face->ascender = os2->sTypoAscender;
|
||||||
face->descender = os2->sTypoDescender;
|
face->descender = os2->sTypoDescender;
|
||||||
face->height = face->ascender - face->descender;
|
face->height = face->ascender - face->descender;
|
||||||
} else {
|
} else {
|
||||||
face->ascender = face->bbox.yMax;
|
face->ascender = face->bbox.yMax;
|
||||||
face->descender = face->bbox.yMin;
|
face->descender = face->bbox.yMin;
|
||||||
face->height = face->ascender - face->descender;
|
face->height = face->ascender - face->descender;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Select a face with the given charcode and add it to ass_font_t
|
* \brief Select a face with the given charcode and add it to ass_font_t
|
||||||
* \return index of the new face in font->faces, -1 if failed
|
* \return index of the new face in font->faces, -1 if failed
|
||||||
*/
|
*/
|
||||||
static int add_face(void* fc_priv, ass_font_t* font, uint32_t ch)
|
static int add_face(void *fc_priv, ass_font_t *font, uint32_t ch)
|
||||||
{
|
{
|
||||||
char* path;
|
char *path;
|
||||||
int index;
|
int index;
|
||||||
FT_Face face;
|
FT_Face face;
|
||||||
int error;
|
int error;
|
||||||
int mem_idx;
|
int mem_idx;
|
||||||
|
|
||||||
if (font->n_faces == ASS_FONT_MAX_FACES)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
path = fontconfig_select(fc_priv, font->desc.family, font->desc.treat_family_as_pattern, font->desc.bold,
|
|
||||||
font->desc.italic, &index, ch);
|
|
||||||
|
|
||||||
mem_idx = find_font(font->library, path);
|
if (font->n_faces == ASS_FONT_MAX_FACES)
|
||||||
if (mem_idx >= 0) {
|
return -1;
|
||||||
error = FT_New_Memory_Face(font->ftlibrary, (unsigned char*)font->library->fontdata[mem_idx].data,
|
|
||||||
font->library->fontdata[mem_idx].size, 0, &face);
|
path =
|
||||||
if (error) {
|
fontconfig_select(fc_priv, font->desc.family,
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, path);
|
font->desc.treat_family_as_pattern,
|
||||||
return -1;
|
font->desc.bold, font->desc.italic, &index, ch);
|
||||||
}
|
if (!path)
|
||||||
} else {
|
return -1;
|
||||||
error = FT_New_Face(font->ftlibrary, path, index, &face);
|
|
||||||
if (error) {
|
mem_idx = find_font(font->library, path);
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningFont, path, index);
|
if (mem_idx >= 0) {
|
||||||
return -1;
|
error =
|
||||||
}
|
FT_New_Memory_Face(font->ftlibrary,
|
||||||
}
|
(unsigned char *) font->library->
|
||||||
charmap_magic(face);
|
fontdata[mem_idx].data,
|
||||||
buggy_font_workaround(face);
|
font->library->fontdata[mem_idx].size, 0,
|
||||||
|
&face);
|
||||||
font->faces[font->n_faces++] = face;
|
if (error) {
|
||||||
update_transform(font);
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont,
|
||||||
face_set_size(face, font->size);
|
path);
|
||||||
return font->n_faces - 1;
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error = FT_New_Face(font->ftlibrary, path, index, &face);
|
||||||
|
if (error) {
|
||||||
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningFont, path,
|
||||||
|
index);
|
||||||
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
charmap_magic(face);
|
||||||
|
buggy_font_workaround(face);
|
||||||
|
|
||||||
|
font->faces[font->n_faces++] = face;
|
||||||
|
update_transform(font);
|
||||||
|
face_set_size(face, font->size);
|
||||||
|
free(path);
|
||||||
|
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;
|
||||||
|
|
||||||
font.library = library;
|
|
||||||
font.ftlibrary = ftlibrary;
|
|
||||||
font.n_faces = 0;
|
|
||||||
font.desc.family = strdup(desc->family);
|
|
||||||
font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
|
|
||||||
font.desc.bold = desc->bold;
|
|
||||||
font.desc.italic = desc->italic;
|
|
||||||
|
|
||||||
font.scale_x = font.scale_y = 1.;
|
font.library = library;
|
||||||
font.v.x = font.v.y = 0;
|
font.ftlibrary = ftlibrary;
|
||||||
font.size = 0.;
|
font.n_faces = 0;
|
||||||
|
font.desc.family = strdup(desc->family);
|
||||||
|
font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
|
||||||
|
font.desc.bold = desc->bold;
|
||||||
|
font.desc.italic = desc->italic;
|
||||||
|
|
||||||
error = add_face(fc_priv, &font, 0);
|
font.scale_x = font.scale_y = 1.;
|
||||||
if (error == -1) {
|
font.v.x = font.v.y = 0;
|
||||||
free(font.desc.family);
|
font.size = 0.;
|
||||||
return 0;
|
|
||||||
} else
|
error = add_face(fc_priv, &font, 0);
|
||||||
return ass_font_cache_add(&font);
|
if (error == -1) {
|
||||||
|
free(font.desc.family);
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
return ass_font_cache_add((hashmap_t *) font_cache, &font);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Set font transformation matrix and shift vector
|
* \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;
|
||||||
font->v.x = v->x;
|
font->v.x = v->x;
|
||||||
font->v.y = v->y;
|
font->v.y = v->y;
|
||||||
update_transform(font);
|
update_transform(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void face_set_size(FT_Face face, double size)
|
static void face_set_size(FT_Face face, double size)
|
||||||
{
|
{
|
||||||
#if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1))
|
#if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1))
|
||||||
TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
|
TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
|
||||||
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
|
TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
|
||||||
double mscale = 1.;
|
double mscale = 1.;
|
||||||
FT_Size_RequestRec rq;
|
FT_Size_RequestRec rq;
|
||||||
FT_Size_Metrics *m = &face->size->metrics;
|
FT_Size_Metrics *m = &face->size->metrics;
|
||||||
// VSFilter uses metrics from TrueType OS/2 table
|
// VSFilter uses metrics from TrueType OS/2 table
|
||||||
// The idea was borrowed from asa (http://asa.diac24.net)
|
// The idea was borrowed from asa (http://asa.diac24.net)
|
||||||
if (hori && os2) {
|
if (hori && os2) {
|
||||||
int hori_height = hori->Ascender - hori->Descender;
|
int hori_height = hori->Ascender - hori->Descender;
|
||||||
int os2_height = os2->usWinAscent + os2->usWinDescent;
|
int os2_height = os2->usWinAscent + os2->usWinDescent;
|
||||||
if (hori_height && os2_height)
|
if (hori_height && os2_height)
|
||||||
mscale = (double)hori_height / os2_height;
|
mscale = (double) hori_height / os2_height;
|
||||||
}
|
}
|
||||||
memset(&rq, 0, sizeof(rq));
|
memset(&rq, 0, sizeof(rq));
|
||||||
rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
|
rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM;
|
||||||
rq.width = 0;
|
rq.width = 0;
|
||||||
rq.height = double_to_d6(size * mscale);
|
rq.height = double_to_d6(size * mscale);
|
||||||
rq.horiResolution = rq.vertResolution = 0;
|
rq.horiResolution = rq.vertResolution = 0;
|
||||||
FT_Request_Size(face, &rq);
|
FT_Request_Size(face, &rq);
|
||||||
m->ascender /= mscale;
|
m->ascender /= mscale;
|
||||||
m->descender /= mscale;
|
m->descender /= mscale;
|
||||||
m->height /= mscale;
|
m->height /= mscale;
|
||||||
#else
|
#else
|
||||||
FT_Set_Char_Size(face, 0, double_to_d6(size), 0, 0);
|
FT_Set_Char_Size(face, 0, double_to_d6(size), 0, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Set font size
|
* \brief Set font size
|
||||||
**/
|
**/
|
||||||
void ass_font_set_size(ass_font_t* font, double size)
|
void ass_font_set_size(ass_font_t *font, double size)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
if (font->size != size) {
|
if (font->size != size) {
|
||||||
font->size = size;
|
font->size = size;
|
||||||
for (i = 0; i < font->n_faces; ++i)
|
for (i = 0; i < font->n_faces; ++i)
|
||||||
face_set_size(font->faces[i], size);
|
face_set_size(font->faces[i], size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -245,125 +260,222 @@ 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) {
|
||||||
FT_Face face = font->faces[i];
|
FT_Face face = font->faces[i];
|
||||||
if (FT_Get_Char_Index(face, ch)) {
|
if (FT_Get_Char_Index(face, ch)) {
|
||||||
*asc = face->size->metrics.ascender;
|
*asc = face->size->metrics.ascender;
|
||||||
*desc = - face->size->metrics.descender;
|
*desc = -face->size->metrics.descender;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*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;
|
||||||
int i;
|
int i;
|
||||||
FT_Glyph glyph;
|
FT_Glyph glyph;
|
||||||
FT_Face face = 0;
|
FT_Face face = 0;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
|
|
||||||
if (ch < 0x20)
|
if (ch < 0x20)
|
||||||
return 0;
|
return 0;
|
||||||
if (font->n_faces == 0)
|
if (font->n_faces == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < font->n_faces; ++i) {
|
for (i = 0; i < font->n_faces; ++i) {
|
||||||
face = font->faces[i];
|
face = font->faces[i];
|
||||||
index = FT_Get_Char_Index(face, ch);
|
index = FT_Get_Char_Index(face, ch);
|
||||||
if (index)
|
if (index)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#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,
|
||||||
face_idx = add_face(fontconfig_priv, font, ch);
|
font->desc.family, font->desc.bold, font->desc.italic);
|
||||||
if (face_idx >= 0) {
|
face_idx = add_face(fontconfig_priv, font, ch);
|
||||||
face = font->faces[face_idx];
|
if (face_idx >= 0) {
|
||||||
index = FT_Get_Char_Index(face, ch);
|
face = font->faces[face_idx];
|
||||||
if (index == 0) {
|
index = FT_Get_Char_Index(face, ch);
|
||||||
mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_GlyphNotFound,
|
if (index == 0) {
|
||||||
ch, font->desc.family, font->desc.bold, font->desc.italic);
|
ass_msg(MSGL_ERR, MSGTR_LIBASS_GlyphNotFound,
|
||||||
}
|
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;
|
||||||
error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags);
|
case ASS_HINTING_NORMAL:
|
||||||
if (error) {
|
flags = FT_LOAD_FORCE_AUTOHINT;
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph);
|
break;
|
||||||
return 0;
|
case ASS_HINTING_NATIVE:
|
||||||
}
|
flags = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags);
|
||||||
|
if (error) {
|
||||||
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph);
|
||||||
|
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))
|
||||||
// FreeType >= 2.1.10 required
|
// FreeType >= 2.1.10 required
|
||||||
if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) &&
|
if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) &&
|
||||||
(font->desc.italic > 55)) {
|
(font->desc.italic > 55)) {
|
||||||
FT_GlyphSlot_Oblique(face->glyph);
|
FT_GlyphSlot_Oblique(face->glyph);
|
||||||
}
|
}
|
||||||
#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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return glyph;
|
ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE,
|
||||||
|
deco & DECO_STRIKETHROUGH);
|
||||||
|
|
||||||
|
return glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Get kerning for the pair of glyphs.
|
* \brief Get kerning for the pair of glyphs.
|
||||||
**/
|
**/
|
||||||
FT_Vector ass_font_get_kerning(ass_font_t* font, uint32_t c1, uint32_t c2)
|
FT_Vector ass_font_get_kerning(ass_font_t *font, uint32_t c1, uint32_t c2)
|
||||||
{
|
{
|
||||||
FT_Vector v = {0, 0};
|
FT_Vector v = { 0, 0 };
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < font->n_faces; ++i) {
|
for (i = 0; i < font->n_faces; ++i) {
|
||||||
FT_Face face = font->faces[i];
|
FT_Face face = font->faces[i];
|
||||||
int i1 = FT_Get_Char_Index(face, c1);
|
int i1 = FT_Get_Char_Index(face, c1);
|
||||||
int i2 = FT_Get_Char_Index(face, c2);
|
int i2 = FT_Get_Char_Index(face, c2);
|
||||||
if (i1 && i2) {
|
if (i1 && i2) {
|
||||||
if (FT_HAS_KERNING(face))
|
if (FT_HAS_KERNING(face))
|
||||||
FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v);
|
FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
if (i1 || i2) // these glyphs are from different font faces, no kerning information
|
if (i1 || i2) // these glyphs are from different font faces, no kerning information
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Deallocate ass_font_t
|
* \brief Deallocate ass_font_t
|
||||||
**/
|
**/
|
||||||
void ass_font_free(ass_font_t* font)
|
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]);
|
||||||
free(font);
|
if (font->desc.family)
|
||||||
|
free(font->desc.family);
|
||||||
|
free(font);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,32 +27,40 @@
|
||||||
#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;
|
||||||
unsigned italic;
|
unsigned italic;
|
||||||
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;
|
||||||
FT_Library ftlibrary;
|
FT_Library ftlibrary;
|
||||||
FT_Face faces[ASS_FONT_MAX_FACES];
|
FT_Face faces[ASS_FONT_MAX_FACES];
|
||||||
int n_faces;
|
int n_faces;
|
||||||
double scale_x, scale_y; // current transform
|
double scale_x, scale_y; // current transform
|
||||||
FT_Vector v; // current shift
|
FT_Vector v; // current shift
|
||||||
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,
|
||||||
void ass_font_set_size(ass_font_t* font, double size);
|
FT_Library ftlibrary, void *fc_priv,
|
||||||
void ass_font_get_asc_desc(ass_font_t* font, uint32_t ch, int* asc, int* desc);
|
ass_font_desc_t *desc);
|
||||||
FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch, ass_hinting_t hinting);
|
void ass_font_set_transform(ass_font_t *font, double scale_x,
|
||||||
FT_Vector ass_font_get_kerning(ass_font_t* font, uint32_t c1, uint32_t c2);
|
double scale_y, FT_Vector * v);
|
||||||
void ass_font_free(ass_font_t* font);
|
void ass_font_set_size(ass_font_t *font, double size);
|
||||||
|
void ass_font_get_asc_desc(ass_font_t *font, uint32_t ch, int *asc,
|
||||||
|
int *desc);
|
||||||
|
FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font,
|
||||||
|
uint32_t ch, ass_hinting_t hinting, int flags);
|
||||||
|
FT_Vector ass_font_get_kerning(ass_font_t *font, uint32_t c1, uint32_t c2);
|
||||||
|
void ass_font_free(ass_font_t *font);
|
||||||
|
|
||||||
#endif /* LIBASS_FONT_H */
|
#endif /* LIBASS_FONT_H */
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -49,11 +42,11 @@
|
||||||
|
|
||||||
struct fc_instance_s {
|
struct fc_instance_s {
|
||||||
#ifdef CONFIG_FONTCONFIG
|
#ifdef CONFIG_FONTCONFIG
|
||||||
FcConfig* config;
|
FcConfig *config;
|
||||||
#endif
|
#endif
|
||||||
char* family_default;
|
char *family_default;
|
||||||
char* path_default;
|
char *path_default;
|
||||||
int index_default;
|
int index_default;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_FONTCONFIG
|
#ifdef CONFIG_FONTCONFIG
|
||||||
|
@ -77,157 +70,163 @@ struct fc_instance_s {
|
||||||
* \param index out: font index inside a file
|
* \param index out: font index inside a file
|
||||||
* \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;
|
||||||
FcPattern *pat = NULL, *rpat = NULL;
|
FcPattern *pat = NULL, *rpat = NULL;
|
||||||
int r_index, r_slant, r_weight;
|
int r_index, r_slant, r_weight;
|
||||||
FcChar8 *r_family, *r_style, *r_file, *r_fullname;
|
FcChar8 *r_family, *r_style, *r_file, *r_fullname;
|
||||||
FcBool r_outline, r_embolden;
|
FcBool r_outline, r_embolden;
|
||||||
FcCharSet* r_charset;
|
FcCharSet *r_charset;
|
||||||
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;
|
|
||||||
|
|
||||||
if (treat_family_as_pattern)
|
*index = 0;
|
||||||
pat = FcNameParse((const FcChar8*)family);
|
|
||||||
else
|
|
||||||
pat = FcPatternCreate();
|
|
||||||
|
|
||||||
if (!pat)
|
if (treat_family_as_pattern)
|
||||||
goto error;
|
pat = FcNameParse((const FcChar8 *) family);
|
||||||
|
else
|
||||||
if (!treat_family_as_pattern) {
|
pat = FcPatternCreate();
|
||||||
FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)family);
|
|
||||||
|
|
||||||
// In SSA/ASS fonts are sometimes referenced by their "full name",
|
if (!pat)
|
||||||
// which is usually a concatenation of family name and font
|
goto error;
|
||||||
// style (ex. Ottawa Bold). Full name is available from
|
|
||||||
// FontConfig pattern element FC_FULLNAME, but it is never
|
|
||||||
// used for font matching.
|
|
||||||
// Therefore, I'm removing words from the end of the name one
|
|
||||||
// by one, and adding shortened names to the pattern. It seems
|
|
||||||
// that the first value (full name in this case) has
|
|
||||||
// precedence in matching.
|
|
||||||
// An alternative approach could be to reimplement FcFontSort
|
|
||||||
// using FC_FULLNAME instead of FC_FAMILY.
|
|
||||||
family_cnt = 1;
|
|
||||||
{
|
|
||||||
char* s = strdup(family);
|
|
||||||
char* p = s + strlen(s);
|
|
||||||
while (--p > s)
|
|
||||||
if (*p == ' ' || *p == '-') {
|
|
||||||
*p = '\0';
|
|
||||||
FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)s);
|
|
||||||
++ family_cnt;
|
|
||||||
}
|
|
||||||
free(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
|
|
||||||
FcPatternAddInteger(pat, FC_SLANT, italic);
|
|
||||||
FcPatternAddInteger(pat, FC_WEIGHT, bold);
|
|
||||||
|
|
||||||
FcDefaultSubstitute(pat);
|
if (!treat_family_as_pattern) {
|
||||||
|
FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) family);
|
||||||
rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern);
|
|
||||||
if (!rc)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result);
|
// In SSA/ASS fonts are sometimes referenced by their "full name",
|
||||||
if (!fset)
|
// which is usually a concatenation of family name and font
|
||||||
goto error;
|
// style (ex. Ottawa Bold). Full name is available from
|
||||||
|
// FontConfig pattern element FC_FULLNAME, but it is never
|
||||||
|
// used for font matching.
|
||||||
|
// Therefore, I'm removing words from the end of the name one
|
||||||
|
// by one, and adding shortened names to the pattern. It seems
|
||||||
|
// that the first value (full name in this case) has
|
||||||
|
// precedence in matching.
|
||||||
|
// An alternative approach could be to reimplement FcFontSort
|
||||||
|
// using FC_FULLNAME instead of FC_FAMILY.
|
||||||
|
family_cnt = 1;
|
||||||
|
{
|
||||||
|
char *s = strdup(family);
|
||||||
|
char *p = s + strlen(s);
|
||||||
|
while (--p > s)
|
||||||
|
if (*p == ' ' || *p == '-') {
|
||||||
|
*p = '\0';
|
||||||
|
FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) s);
|
||||||
|
++family_cnt;
|
||||||
|
}
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
|
||||||
|
FcPatternAddInteger(pat, FC_SLANT, italic);
|
||||||
|
FcPatternAddInteger(pat, FC_WEIGHT, bold);
|
||||||
|
|
||||||
for (curf = 0; curf < fset->nfont; ++curf) {
|
FcDefaultSubstitute(pat);
|
||||||
FcPattern* curp = fset->fonts[curf];
|
|
||||||
|
|
||||||
result = FcPatternGetBool(curp, FC_OUTLINE, 0, &r_outline);
|
rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern);
|
||||||
if (result != FcResultMatch)
|
if (!rc)
|
||||||
continue;
|
goto error;
|
||||||
if (r_outline != FcTrue)
|
|
||||||
continue;
|
|
||||||
if (!code)
|
|
||||||
break;
|
|
||||||
result = FcPatternGetCharSet(curp, FC_CHARSET, 0, &r_charset);
|
|
||||||
if (result != FcResultMatch)
|
|
||||||
continue;
|
|
||||||
if (FcCharSetHasChar(r_charset, code))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curf >= fset->nfont)
|
fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result);
|
||||||
goto error;
|
if (!fset)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
for (curf = 0; curf < fset->nfont; ++curf) {
|
||||||
|
FcPattern *curp = fset->fonts[curf];
|
||||||
|
|
||||||
|
result = FcPatternGetBool(curp, FC_OUTLINE, 0, &r_outline);
|
||||||
|
if (result != FcResultMatch)
|
||||||
|
continue;
|
||||||
|
if (r_outline != FcTrue)
|
||||||
|
continue;
|
||||||
|
if (!code)
|
||||||
|
break;
|
||||||
|
result = FcPatternGetCharSet(curp, FC_CHARSET, 0, &r_charset);
|
||||||
|
if (result != FcResultMatch)
|
||||||
|
continue;
|
||||||
|
if (FcCharSetHasChar(r_charset, code))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curf >= fset->nfont)
|
||||||
|
goto error;
|
||||||
|
|
||||||
#if (FC_VERSION >= 20297)
|
#if (FC_VERSION >= 20297)
|
||||||
if (!treat_family_as_pattern) {
|
if (!treat_family_as_pattern) {
|
||||||
// Remove all extra family names from original pattern.
|
// Remove all extra family names from original pattern.
|
||||||
// After this, FcFontRenderPrepare will select the most relevant family
|
// After this, FcFontRenderPrepare will select the most relevant family
|
||||||
// name in case there are more than one of them.
|
// name in case there are more than one of them.
|
||||||
for (; family_cnt > 1; --family_cnt)
|
for (; family_cnt > 1; --family_cnt)
|
||||||
FcPatternRemove(pat, FC_FAMILY, family_cnt - 1);
|
FcPatternRemove(pat, FC_FAMILY, family_cnt - 1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rpat = FcFontRenderPrepare(priv->config, pat, fset->fonts[curf]);
|
rpat = FcFontRenderPrepare(priv->config, pat, fset->fonts[curf]);
|
||||||
if (!rpat)
|
if (!rpat)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
result = FcPatternGetInteger(rpat, FC_INDEX, 0, &r_index);
|
result = FcPatternGetInteger(rpat, FC_INDEX, 0, &r_index);
|
||||||
if (result != FcResultMatch)
|
if (result != FcResultMatch)
|
||||||
goto error;
|
goto error;
|
||||||
*index = r_index;
|
*index = r_index;
|
||||||
|
|
||||||
result = FcPatternGetString(rpat, FC_FILE, 0, &r_file);
|
result = FcPatternGetString(rpat, FC_FILE, 0, &r_file);
|
||||||
if (result != FcResultMatch)
|
if (result != FcResultMatch)
|
||||||
goto error;
|
goto error;
|
||||||
retval = strdup((const char*)r_file);
|
retval = strdup((const char *) r_file);
|
||||||
|
|
||||||
result = FcPatternGetString(rpat, FC_FAMILY, 0, &r_family);
|
result = FcPatternGetString(rpat, FC_FAMILY, 0, &r_family);
|
||||||
if (result != FcResultMatch)
|
if (result != FcResultMatch)
|
||||||
r_family = NULL;
|
r_family = NULL;
|
||||||
|
|
||||||
result = FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname);
|
result = FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname);
|
||||||
if (result != FcResultMatch)
|
if (result != FcResultMatch)
|
||||||
r_fullname = NULL;
|
r_fullname = NULL;
|
||||||
|
|
||||||
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,
|
||||||
(const char*)(r_fullname ? r_fullname : r_family), family);
|
MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne,
|
||||||
|
(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);
|
||||||
if (result != FcResultMatch)
|
if (result != FcResultMatch)
|
||||||
r_style = NULL;
|
r_style = NULL;
|
||||||
|
|
||||||
result = FcPatternGetInteger(rpat, FC_SLANT, 0, &r_slant);
|
result = FcPatternGetInteger(rpat, FC_SLANT, 0, &r_slant);
|
||||||
if (result != FcResultMatch)
|
if (result != FcResultMatch)
|
||||||
r_slant = 0;
|
r_slant = 0;
|
||||||
|
|
||||||
result = FcPatternGetInteger(rpat, FC_WEIGHT, 0, &r_weight);
|
result = FcPatternGetInteger(rpat, FC_WEIGHT, 0, &r_weight);
|
||||||
if (result != FcResultMatch)
|
if (result != FcResultMatch)
|
||||||
r_weight = 0;
|
r_weight = 0;
|
||||||
|
|
||||||
result = FcPatternGetBool(rpat, FC_EMBOLDEN, 0, &r_embolden);
|
result = FcPatternGetBool(rpat, FC_EMBOLDEN, 0, &r_embolden);
|
||||||
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)
|
||||||
return retval;
|
FcPatternDestroy(rpat);
|
||||||
|
if (fset)
|
||||||
|
FcFontSetDestroy(fset);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -240,76 +239,79 @@ static char* _select_font(fc_instance_t* priv, const char* family, int treat_fam
|
||||||
* \param index out: font index inside a file
|
* \param index out: font index inside a file
|
||||||
* \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) {
|
||||||
*index = priv->index_default;
|
*index = priv->index_default;
|
||||||
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 =
|
||||||
if (!res && priv->family_default) {
|
_select_font(priv, family, treat_family_as_pattern, bold,
|
||||||
res = _select_font(priv, priv->family_default, 0, bold, italic, index, code);
|
italic, index, code);
|
||||||
if (res)
|
if (!res && priv->family_default) {
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily,
|
res =
|
||||||
family, bold, italic, res, *index);
|
_select_font(priv, priv->family_default, 0, bold, italic, index,
|
||||||
}
|
code);
|
||||||
if (!res && priv->path_default) {
|
if (res)
|
||||||
res = priv->path_default;
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily,
|
||||||
*index = priv->index_default;
|
family, bold, italic, res, *index);
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont,
|
}
|
||||||
family, bold, italic, res, *index);
|
if (!res && priv->path_default) {
|
||||||
}
|
res = priv->path_default;
|
||||||
if (!res) {
|
*index = priv->index_default;
|
||||||
res = _select_font(priv, "Arial", 0, bold, italic, index, code);
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont,
|
||||||
if (res)
|
family, bold, italic, res, *index);
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily,
|
}
|
||||||
family, bold, italic, res, *index);
|
if (!res) {
|
||||||
}
|
res = _select_font(priv, "Arial", 0, bold, italic, index, code);
|
||||||
if (res)
|
if (res)
|
||||||
mp_msg(MSGT_ASS, MSGL_V, "fontconfig_select: (%s, %d, %d) -> %s, %d\n",
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily,
|
||||||
family, bold, italic, res, *index);
|
family, bold, italic, res, *index);
|
||||||
return res;
|
}
|
||||||
|
if (res)
|
||||||
|
ass_msg(MSGL_V,
|
||||||
|
"fontconfig_select: (%s, %d, %d) -> %s, %d\n", family, bold,
|
||||||
|
italic, res, *index);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (FC_VERSION < 20402)
|
#if (FC_VERSION < 20402)
|
||||||
static char* validate_fname(char* name)
|
static char *validate_fname(char *name)
|
||||||
{
|
{
|
||||||
char* fname;
|
char *fname;
|
||||||
char* p;
|
char *p;
|
||||||
char* q;
|
char *q;
|
||||||
unsigned code;
|
unsigned code;
|
||||||
int sz = strlen(name);
|
int sz = strlen(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) ||
|
||||||
(code == '\\') ||
|
(code == '\\') ||
|
||||||
(code == '/') ||
|
(code == '/') ||
|
||||||
(code == ':') ||
|
(code == ':') ||
|
||||||
(code == '*') ||
|
(code == '*') ||
|
||||||
(code == '?') ||
|
(code == '?') ||
|
||||||
(code == '<') ||
|
(code == '<') ||
|
||||||
(code == '>') ||
|
(code == '>') || (code == '|') || (code == 0)) {
|
||||||
(code == '|') ||
|
*q++ = '_';
|
||||||
(code == 0))
|
} else {
|
||||||
{
|
*q++ = code;
|
||||||
*q++ = '_';
|
}
|
||||||
} else {
|
if (p - name > sz)
|
||||||
*q++ = code;
|
break;
|
||||||
}
|
}
|
||||||
if (p - name > sz)
|
*q = 0;
|
||||||
break;
|
return fname;
|
||||||
}
|
|
||||||
*q = 0;
|
|
||||||
return fname;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -321,87 +323,97 @@ static char* validate_fname(char* name)
|
||||||
* \param idx index of the processed font in library->fontdata
|
* \param idx index of the processed font in library->fontdata
|
||||||
* With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
|
* With 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;
|
||||||
const char* data = library->fontdata[idx].data;
|
const char *data = library->fontdata[idx].data;
|
||||||
int data_size = library->fontdata[idx].size;
|
int data_size = library->fontdata[idx].size;
|
||||||
|
|
||||||
#if (FC_VERSION < 20402)
|
#if (FC_VERSION < 20402)
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char* fname;
|
char *fname;
|
||||||
const char* fonts_dir = library->fonts_dir;
|
const char *fonts_dir = library->fonts_dir;
|
||||||
char buf[1000];
|
char buf[1000];
|
||||||
FILE* fp = NULL;
|
FILE *fp = NULL;
|
||||||
|
|
||||||
if (!fonts_dir)
|
if (!fonts_dir)
|
||||||
return;
|
return;
|
||||||
rc = stat(fonts_dir, &st);
|
rc = stat(fonts_dir, &st);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
int res;
|
int res;
|
||||||
#ifndef __MINGW32__
|
#ifndef __MINGW32__
|
||||||
res = mkdir(fonts_dir, 0700);
|
res = mkdir(fonts_dir, 0700);
|
||||||
#else
|
#else
|
||||||
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)) {
|
}
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir);
|
} else if (!S_ISDIR(st.st_mode)) {
|
||||||
}
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir);
|
||||||
|
}
|
||||||
fname = validate_fname((char*)name);
|
|
||||||
|
|
||||||
snprintf(buf, 1000, "%s/%s", fonts_dir, fname);
|
fname = validate_fname((char *) name);
|
||||||
free(fname);
|
|
||||||
|
|
||||||
fp = fopen(buf, "wb");
|
snprintf(buf, 1000, "%s/%s", fonts_dir, fname);
|
||||||
if (!fp) return;
|
free(fname);
|
||||||
|
|
||||||
fwrite(data, data_size, 1, fp);
|
fp = fopen(buf, "wb");
|
||||||
fclose(fp);
|
if (!fp)
|
||||||
|
return;
|
||||||
|
|
||||||
#else // (FC_VERSION >= 20402)
|
fwrite(data, data_size, 1, fp);
|
||||||
FT_Face face;
|
fclose(fp);
|
||||||
FcPattern* pattern;
|
|
||||||
FcFontSet* fset;
|
|
||||||
FcBool res;
|
|
||||||
int face_index, num_faces = 1;
|
|
||||||
|
|
||||||
for (face_index = 0; face_index < num_faces; ++face_index) {
|
#else // (FC_VERSION >= 20402)
|
||||||
rc = FT_New_Memory_Face(ftlibrary, (unsigned char*)data, data_size, face_index, &face);
|
FT_Face face;
|
||||||
if (rc) {
|
FcPattern *pattern;
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, name);
|
FcFontSet *fset;
|
||||||
return;
|
FcBool res;
|
||||||
}
|
int face_index, num_faces = 1;
|
||||||
num_faces = face->num_faces;
|
|
||||||
|
|
||||||
pattern = FcFreeTypeQueryFace(face, (unsigned char*)name, 0, FcConfigGetBlanks(priv->config));
|
for (face_index = 0; face_index < num_faces; ++face_index) {
|
||||||
if (!pattern) {
|
rc = FT_New_Memory_Face(ftlibrary, (unsigned char *) data,
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFreeTypeQueryFace");
|
data_size, face_index, &face);
|
||||||
FT_Done_Face(face);
|
if (rc) {
|
||||||
return;
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont,
|
||||||
}
|
name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
num_faces = face->num_faces;
|
||||||
|
|
||||||
fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication
|
pattern =
|
||||||
if (!fset) {
|
FcFreeTypeQueryFace(face, (unsigned char *) name, 0,
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcConfigGetFonts");
|
FcConfigGetBlanks(priv->config));
|
||||||
FT_Done_Face(face);
|
if (!pattern) {
|
||||||
return;
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed,
|
||||||
}
|
"FcFreeTypeQueryFace");
|
||||||
|
FT_Done_Face(face);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
res = FcFontSetAdd(fset, pattern);
|
fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication
|
||||||
if (!res) {
|
if (!fset) {
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFontSetAdd");
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed,
|
||||||
FT_Done_Face(face);
|
"FcConfigGetFonts");
|
||||||
return;
|
FT_Done_Face(face);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FT_Done_Face(face);
|
res = FcFontSetAdd(fset, pattern);
|
||||||
}
|
if (!res) {
|
||||||
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed,
|
||||||
|
"FcFontSetAdd");
|
||||||
|
FT_Done_Face(face);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FT_Done_Face(face);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,128 +424,131 @@ static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Lib
|
||||||
* \param family default font family
|
* \param family default font family
|
||||||
* \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
|
int i;
|
||||||
char config_path[MAXPATHLEN];
|
|
||||||
char *config_dir;
|
|
||||||
#endif
|
|
||||||
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();
|
priv->config = FcConfigCreate();
|
||||||
snprintf(config_path, MAXPATHLEN, "%s/etc/fonts/fonts.conf", config_dir);
|
rc = FcConfigParseAndLoad(priv->config, (unsigned char *)config,
|
||||||
free(config_dir);
|
FcTrue);
|
||||||
priv->config = FcConfigCreate();
|
FcConfigBuildFonts(priv->config);
|
||||||
rc = FcConfigParseAndLoad(priv->config, config_path, FcTrue);
|
FcConfigSetCurrent(priv->config);
|
||||||
FcConfigBuildFonts(priv->config);
|
} else {
|
||||||
FcConfigSetCurrent(priv->config);
|
rc = FcInit();
|
||||||
|
assert(rc);
|
||||||
|
priv->config = FcConfigGetCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
if (!rc) {
|
if (!rc || !priv->config) {
|
||||||
#else
|
ass_msg(MSGL_FATAL,
|
||||||
rc = FcInit();
|
MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed);
|
||||||
assert(rc);
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
priv->config = FcConfigGetCurrent();
|
for (i = 0; i < library->num_fontdata; ++i)
|
||||||
if (!priv->config) {
|
process_fontdata(priv, library, ftlibrary, i);
|
||||||
#endif
|
|
||||||
mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed);
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < library->num_fontdata; ++i)
|
if (dir) {
|
||||||
process_fontdata(priv, library, ftlibrary, i);
|
if (FcDirCacheValid((const FcChar8 *) dir) == FcFalse) {
|
||||||
|
ass_msg(MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache);
|
||||||
|
if (FcGetVersion() >= 20390 && FcGetVersion() < 20400)
|
||||||
|
ass_msg(MSGL_WARN,
|
||||||
|
MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported);
|
||||||
|
// FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir()
|
||||||
|
if (FcGetVersion() < 20390) {
|
||||||
|
FcFontSet *fcs;
|
||||||
|
FcStrSet *fss;
|
||||||
|
fcs = FcFontSetCreate();
|
||||||
|
fss = FcStrSetCreate();
|
||||||
|
rc = FcStrSetAdd(fss, (const FcChar8 *) dir);
|
||||||
|
if (!rc) {
|
||||||
|
ass_msg(MSGL_WARN,
|
||||||
|
MSGTR_LIBASS_FcStrSetAddFailed);
|
||||||
|
goto ErrorFontCache;
|
||||||
|
}
|
||||||
|
|
||||||
if (dir) {
|
rc = FcDirScan(fcs, fss, NULL,
|
||||||
if (FcDirCacheValid((const FcChar8 *)dir) == FcFalse)
|
FcConfigGetBlanks(priv->config),
|
||||||
{
|
(const FcChar8 *) dir, FcFalse);
|
||||||
mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache);
|
if (!rc) {
|
||||||
if (FcGetVersion() >= 20390 && FcGetVersion() < 20400)
|
ass_msg(MSGL_WARN,
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN,
|
MSGTR_LIBASS_FcDirScanFailed);
|
||||||
MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported);
|
goto ErrorFontCache;
|
||||||
// FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir()
|
}
|
||||||
if (FcGetVersion() < 20390) {
|
|
||||||
FcFontSet* fcs;
|
|
||||||
FcStrSet* fss;
|
|
||||||
fcs = FcFontSetCreate();
|
|
||||||
fss = FcStrSetCreate();
|
|
||||||
rc = FcStrSetAdd(fss, (const FcChar8*)dir);
|
|
||||||
if (!rc) {
|
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcStrSetAddFailed);
|
|
||||||
goto ErrorFontCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = FcDirScan(fcs, fss, NULL, FcConfigGetBlanks(priv->config),
|
rc = FcDirSave(fcs, fss, (const FcChar8 *) dir);
|
||||||
(const FcChar8 *)dir, FcFalse);
|
if (!rc) {
|
||||||
if (!rc) {
|
ass_msg(MSGL_WARN, MSGTR_LIBASS_FcDirSave);
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirScanFailed);
|
goto ErrorFontCache;
|
||||||
goto ErrorFontCache;
|
}
|
||||||
}
|
ErrorFontCache:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc = FcDirSave(fcs, fss, (const FcChar8 *)dir);
|
rc = FcConfigAppFontAddDir(priv->config, (const FcChar8 *) dir);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirSave);
|
ass_msg(MSGL_WARN,
|
||||||
goto ErrorFontCache;
|
MSGTR_LIBASS_FcConfigAppFontAddDirFailed);
|
||||||
}
|
}
|
||||||
ErrorFontCache:
|
}
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = FcConfigAppFontAddDir(priv->config, (const FcChar8*)dir);
|
priv->family_default = family ? strdup(family) : NULL;
|
||||||
if (!rc) {
|
exit:
|
||||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcConfigAppFontAddDirFailed);
|
priv->path_default = path ? strdup(path) : NULL;
|
||||||
}
|
priv->index_default = 0;
|
||||||
}
|
|
||||||
|
|
||||||
priv->family_default = family ? strdup(family) : NULL;
|
return priv;
|
||||||
exit:
|
|
||||||
priv->path_default = path ? strdup(path) : NULL;
|
|
||||||
priv->index_default = 0;
|
|
||||||
|
|
||||||
return priv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* CONFIG_FONTCONFIG */
|
#else /* CONFIG_FONTCONFIG */
|
||||||
|
|
||||||
char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern,
|
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));
|
||||||
priv->path_default = strdup(path);
|
|
||||||
priv->index_default = 0;
|
priv->path_default = strdup(path);
|
||||||
return priv;
|
priv->index_default = 0;
|
||||||
|
return priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void fontconfig_done(fc_instance_t* priv)
|
void fontconfig_done(fc_instance_t *priv)
|
||||||
{
|
{
|
||||||
// don't call FcFini() here, library can still be used by some code
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
void fontconfig_done(fc_instance_t* priv);
|
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);
|
||||||
|
|
||||||
#endif /* LIBASS_FONTCONFIG_H */
|
#endif /* LIBASS_FONTCONFIG_H */
|
||||||
|
|
|
@ -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,87 +27,90 @@
|
||||||
#include "ass_library.h"
|
#include "ass_library.h"
|
||||||
|
|
||||||
|
|
||||||
ass_library_t* ass_library_init(void)
|
ass_library_t *ass_library_init(void)
|
||||||
{
|
{
|
||||||
return calloc(1, sizeof(ass_library_t));
|
return calloc(1, sizeof(ass_library_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ass_library_done(ass_library_t* priv)
|
void ass_library_done(ass_library_t *priv)
|
||||||
{
|
{
|
||||||
if (priv) {
|
if (priv) {
|
||||||
ass_set_fonts_dir(priv, NULL);
|
ass_set_fonts_dir(priv, NULL);
|
||||||
ass_set_style_overrides(priv, NULL);
|
ass_set_style_overrides(priv, NULL);
|
||||||
ass_clear_fonts(priv);
|
ass_clear_fonts(priv);
|
||||||
free(priv);
|
free(priv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ass_set_fonts_dir(ass_library_t* priv, const char* fonts_dir)
|
void ass_set_fonts_dir(ass_library_t *priv, const char *fonts_dir)
|
||||||
{
|
{
|
||||||
if (priv->fonts_dir)
|
if (priv->fonts_dir)
|
||||||
free(priv->fonts_dir);
|
free(priv->fonts_dir);
|
||||||
|
|
||||||
priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0;
|
priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ass_set_extract_fonts(ass_library_t* priv, int extract)
|
void ass_set_extract_fonts(ass_library_t *priv, int extract)
|
||||||
{
|
{
|
||||||
priv->extract_fonts = !!extract;
|
priv->extract_fonts = !!extract;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ass_set_style_overrides(ass_library_t* priv, char** list)
|
void ass_set_style_overrides(ass_library_t *priv, char **list)
|
||||||
{
|
{
|
||||||
char** p;
|
char **p;
|
||||||
char** q;
|
char **q;
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
if (priv->style_overrides) {
|
|
||||||
for (p = priv->style_overrides; *p; ++p)
|
|
||||||
free(*p);
|
|
||||||
free(priv->style_overrides);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!list) return;
|
|
||||||
|
|
||||||
for (p = list, cnt = 0; *p; ++p, ++cnt) {}
|
if (priv->style_overrides) {
|
||||||
|
for (p = priv->style_overrides; *p; ++p)
|
||||||
|
free(*p);
|
||||||
|
free(priv->style_overrides);
|
||||||
|
}
|
||||||
|
|
||||||
priv->style_overrides = malloc((cnt + 1) * sizeof(char*));
|
if (!list)
|
||||||
for (p = list, q = priv->style_overrides; *p; ++p, ++q)
|
return;
|
||||||
*q = strdup(*p);
|
|
||||||
priv->style_overrides[cnt] = NULL;
|
for (p = list, cnt = 0; *p; ++p, ++cnt) {
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->style_overrides = malloc((cnt + 1) * sizeof(char *));
|
||||||
|
for (p = list, q = priv->style_overrides; *p; ++p, ++q)
|
||||||
|
*q = strdup(*p);
|
||||||
|
priv->style_overrides[cnt] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void grow_array(void **array, int nelem, size_t elsize)
|
static void grow_array(void **array, int nelem, size_t elsize)
|
||||||
{
|
{
|
||||||
if (!(nelem & 31))
|
if (!(nelem & 31))
|
||||||
*array = realloc(*array, (nelem + 32) * elsize);
|
*array = realloc(*array, (nelem + 32) * elsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ass_add_font(ass_library_t* priv, char* name, char* data, int size)
|
void ass_add_font(ass_library_t *priv, char *name, char *data, int size)
|
||||||
{
|
{
|
||||||
int idx = priv->num_fontdata;
|
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);
|
||||||
priv->fontdata[idx].data = malloc(size);
|
|
||||||
memcpy(priv->fontdata[idx].data, data, size);
|
priv->fontdata[idx].data = malloc(size);
|
||||||
|
memcpy(priv->fontdata[idx].data, data, size);
|
||||||
priv->fontdata[idx].size = size;
|
|
||||||
|
priv->fontdata[idx].size = size;
|
||||||
priv->num_fontdata ++;
|
|
||||||
|
priv->num_fontdata++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ass_clear_fonts(ass_library_t* priv)
|
void ass_clear_fonts(ass_library_t *priv)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < priv->num_fontdata; ++i) {
|
for (i = 0; i < priv->num_fontdata; ++i) {
|
||||||
free(priv->fontdata[i].name);
|
free(priv->fontdata[i].name);
|
||||||
free(priv->fontdata[i].data);
|
free(priv->fontdata[i].data);
|
||||||
}
|
}
|
||||||
free(priv->fontdata);
|
free(priv->fontdata);
|
||||||
priv->fontdata = NULL;
|
priv->fontdata = NULL;
|
||||||
priv->num_fontdata = 0;
|
priv->num_fontdata = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
*
|
*
|
||||||
|
@ -24,18 +22,18 @@
|
||||||
#define LIBASS_LIBRARY_H
|
#define LIBASS_LIBRARY_H
|
||||||
|
|
||||||
typedef struct ass_fontdata_s {
|
typedef struct ass_fontdata_s {
|
||||||
char* name;
|
char *name;
|
||||||
char* data;
|
char *data;
|
||||||
int size;
|
int size;
|
||||||
} ass_fontdata_t;
|
} ass_fontdata_t;
|
||||||
|
|
||||||
struct ass_library_s {
|
struct ass_library_s {
|
||||||
char* fonts_dir;
|
char *fonts_dir;
|
||||||
int extract_fonts;
|
int extract_fonts;
|
||||||
char** style_overrides;
|
char **style_overrides;
|
||||||
|
|
||||||
ass_fontdata_t* fontdata;
|
ass_fontdata_t *fontdata;
|
||||||
int num_fontdata;
|
int num_fontdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* LIBASS_LIBRARY_H */
|
#endif /* LIBASS_LIBRARY_H */
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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,31 +32,31 @@
|
||||||
|
|
||||||
/// ass Style: line
|
/// ass Style: line
|
||||||
typedef struct ass_style_s {
|
typedef struct ass_style_s {
|
||||||
char* Name;
|
char *Name;
|
||||||
char* FontName;
|
char *FontName;
|
||||||
double FontSize;
|
double FontSize;
|
||||||
uint32_t PrimaryColour;
|
uint32_t PrimaryColour;
|
||||||
uint32_t SecondaryColour;
|
uint32_t SecondaryColour;
|
||||||
uint32_t OutlineColour;
|
uint32_t OutlineColour;
|
||||||
uint32_t BackColour;
|
uint32_t BackColour;
|
||||||
int Bold;
|
int Bold;
|
||||||
int Italic;
|
int Italic;
|
||||||
int Underline;
|
int Underline;
|
||||||
int StrikeOut;
|
int StrikeOut;
|
||||||
double ScaleX;
|
double ScaleX;
|
||||||
double ScaleY;
|
double ScaleY;
|
||||||
double Spacing;
|
double Spacing;
|
||||||
int Angle;
|
int Angle;
|
||||||
int BorderStyle;
|
int BorderStyle;
|
||||||
double Outline;
|
double Outline;
|
||||||
double Shadow;
|
double Shadow;
|
||||||
int Alignment;
|
int Alignment;
|
||||||
int MarginL;
|
int MarginL;
|
||||||
int MarginR;
|
int MarginR;
|
||||||
int MarginV;
|
int MarginV;
|
||||||
// int AlphaLevel;
|
// int AlphaLevel;
|
||||||
int Encoding;
|
int Encoding;
|
||||||
int treat_fontname_as_pattern;
|
int treat_fontname_as_pattern;
|
||||||
} ass_style_t;
|
} ass_style_t;
|
||||||
|
|
||||||
typedef struct render_priv_s render_priv_t;
|
typedef struct render_priv_s render_priv_t;
|
||||||
|
@ -66,20 +64,20 @@ typedef struct render_priv_s render_priv_t;
|
||||||
/// ass_event_t corresponds to a single Dialogue line
|
/// ass_event_t corresponds to a single Dialogue line
|
||||||
/// Text is stored as-is, style overrides will be parsed later
|
/// Text is stored as-is, style overrides will be parsed later
|
||||||
typedef struct ass_event_s {
|
typedef struct ass_event_s {
|
||||||
long long Start; // ms
|
long long Start; // ms
|
||||||
long long Duration; // ms
|
long long Duration; // ms
|
||||||
|
|
||||||
int ReadOrder;
|
int ReadOrder;
|
||||||
int Layer;
|
int Layer;
|
||||||
int Style;
|
int Style;
|
||||||
char* Name;
|
char *Name;
|
||||||
int MarginL;
|
int MarginL;
|
||||||
int MarginR;
|
int MarginR;
|
||||||
int MarginV;
|
int MarginV;
|
||||||
char* Effect;
|
char *Effect;
|
||||||
char* Text;
|
char *Text;
|
||||||
|
|
||||||
render_priv_t* render_priv;
|
render_priv_t *render_priv;
|
||||||
} ass_event_t;
|
} ass_event_t;
|
||||||
|
|
||||||
typedef struct parser_priv_s parser_priv_t;
|
typedef struct parser_priv_s parser_priv_t;
|
||||||
|
@ -89,31 +87,32 @@ typedef struct ass_library_s ass_library_t;
|
||||||
/// ass track represent either an external script or a matroska subtitle stream (no real difference between them)
|
/// ass track represent either an external script or a matroska subtitle stream (no real difference between them)
|
||||||
/// it can be used in rendering after the headers are parsed (i.e. events format line read)
|
/// it can be used in rendering after the headers are parsed (i.e. events format line read)
|
||||||
typedef struct ass_track_s {
|
typedef struct ass_track_s {
|
||||||
int n_styles; // amount used
|
int n_styles; // amount used
|
||||||
int max_styles; // amount allocated
|
int max_styles; // amount allocated
|
||||||
int n_events;
|
int n_events;
|
||||||
int max_events;
|
int max_events;
|
||||||
ass_style_t* styles; // array of styles, max_styles length, n_styles used
|
ass_style_t *styles; // array of styles, max_styles length, n_styles used
|
||||||
ass_event_t* events; // the same as styles
|
ass_event_t *events; // the same as styles
|
||||||
|
|
||||||
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
|
|
||||||
int PlayResX;
|
|
||||||
int PlayResY;
|
|
||||||
double Timer;
|
|
||||||
int WrapStyle;
|
|
||||||
char ScaledBorderAndShadow;
|
|
||||||
|
|
||||||
|
// script header fields
|
||||||
int default_style; // index of default style
|
int PlayResX;
|
||||||
char* name; // file name in case of external subs, 0 for streams
|
int PlayResY;
|
||||||
|
double Timer;
|
||||||
|
int WrapStyle;
|
||||||
|
char ScaledBorderAndShadow;
|
||||||
|
|
||||||
ass_library_t* library;
|
|
||||||
parser_priv_t* parser_priv;
|
int default_style; // index of default style
|
||||||
|
char *name; // file name in case of external subs, 0 for streams
|
||||||
|
|
||||||
|
ass_library_t *library;
|
||||||
|
parser_priv_t *parser_priv;
|
||||||
} ass_track_t;
|
} ass_track_t;
|
||||||
|
|
||||||
#endif /* LIBASS_TYPES_H */
|
#endif /* LIBASS_TYPES_H */
|
||||||
|
|
|
@ -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,109 +25,301 @@
|
||||||
#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)
|
||||||
{
|
{
|
||||||
// NOTE: base argument is ignored, but not used in libass anyway
|
// NOTE: base argument is ignored, but not used in libass anyway
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
uint32_t color = 0;
|
uint32_t color = 0;
|
||||||
int result;
|
int result;
|
||||||
char* p = *q;
|
char *p = *q;
|
||||||
|
|
||||||
if (*p == '&') ++p;
|
|
||||||
else mp_msg(MSGT_ASS, MSGL_DBG2, "suspicious color format: \"%s\"\n", p);
|
|
||||||
|
|
||||||
if (*p == 'H' || *p == 'h') {
|
|
||||||
++p;
|
|
||||||
result = mystrtou32(&p, 16, &color);
|
|
||||||
} else {
|
|
||||||
result = mystrtou32(&p, 0, &color);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
unsigned char* tmp = (unsigned char*)(&color);
|
|
||||||
unsigned char b;
|
|
||||||
b = tmp[0]; tmp[0] = tmp[3]; tmp[3] = b;
|
|
||||||
b = tmp[1]; tmp[1] = tmp[2]; tmp[2] = b;
|
|
||||||
}
|
|
||||||
if (*p == '&') ++p;
|
|
||||||
*q = p;
|
|
||||||
|
|
||||||
*res = color;
|
if (*p == '&')
|
||||||
return result;
|
++p;
|
||||||
|
else
|
||||||
|
ass_msg(MSGL_DBG2, "suspicious color format: \"%s\"\n", p);
|
||||||
|
|
||||||
|
if (*p == 'H' || *p == 'h') {
|
||||||
|
++p;
|
||||||
|
result = mystrtou32(&p, 16, &color);
|
||||||
|
} else {
|
||||||
|
result = mystrtou32(&p, 0, &color);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned char *tmp = (unsigned char *) (&color);
|
||||||
|
unsigned char b;
|
||||||
|
b = tmp[0];
|
||||||
|
tmp[0] = tmp[3];
|
||||||
|
tmp[3] = b;
|
||||||
|
b = tmp[1];
|
||||||
|
tmp[1] = tmp[2];
|
||||||
|
tmp[2] = b;
|
||||||
|
}
|
||||||
|
if (*p == '&')
|
||||||
|
++p;
|
||||||
|
*q = p;
|
||||||
|
|
||||||
|
*res = color;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a boolean value for a string
|
// Return a boolean value for a string
|
||||||
char parse_bool(char* str) {
|
char parse_bool(char *str)
|
||||||
while (*str == ' ' || *str == '\t')
|
{
|
||||||
str++;
|
while (*str == ' ' || *str == '\t')
|
||||||
if (!strncasecmp(str, "yes", 3))
|
str++;
|
||||||
return 1;
|
if (!strncasecmp(str, "yes", 3))
|
||||||
else if (strtol(str, NULL, 10) > 0)
|
return 1;
|
||||||
return 1;
|
else if (strtol(str, NULL, 10) > 0)
|
||||||
return 0;
|
return 1;
|
||||||
|
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);
|
if (len <= 0 || len > 4)
|
||||||
for (i = 0; i < o->n_points; ++i) {
|
goto no_utf8;
|
||||||
printf(" point %f, %f \n", d6_to_double(o->points[i].x), d6_to_double(o->points[i].y));
|
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
|
||||||
|
|
|
@ -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,44 +21,108 @@
|
||||||
#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>
|
||||||
|
|
||||||
int mystrtoi(char** p, int* res);
|
#ifdef CONFIG_ENCA
|
||||||
int mystrtoll(char** p, long long* res);
|
#include <enca.h>
|
||||||
int mystrtou32(char** p, int base, uint32_t* res);
|
#endif
|
||||||
int mystrtod(char** p, double* res);
|
|
||||||
int strtocolor(char** q, uint32_t* res);
|
|
||||||
char parse_bool(char* str);
|
|
||||||
|
|
||||||
static inline int d6_to_int(int x) {
|
#include "help_mp.h"
|
||||||
return (x + 32) >> 6;
|
|
||||||
|
#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 mystrtoll(char **p, long long *res);
|
||||||
|
int mystrtou32(char **p, int base, uint32_t *res);
|
||||||
|
int mystrtod(char **p, double *res);
|
||||||
|
int strtocolor(char **q, uint32_t *res);
|
||||||
|
char parse_bool(char *str);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* LIBASS_UTILS_H */
|
#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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue