osd: use libass for OSD rendering

The OSD will now be rendered with libass. The old rendering code, which
used freetype/fontconfig and did text layout manually, is disabled. To
re-enable the old code, use the --disable-libass-osd configure switch.

Some switches do nothing with the new code enabled, such as -subalign,
-sub-bg-alpha, -sub-bg-color, and many more. (The reason is mostly that
the code for rendering unstyled subtitles with libass doesn't make any
attempts to support them. Some of them could be supported in theory.)

Teletext rendering is not implemented in the new OSD rendering code. I
don't have any teletext sources for testing, and since teletext is
being phased out world-wide, the need for this is questionable.

Note that rendering is extremely inefficient, mostly because the libass
output is blended with the extremely strange mplayer OSD format. This
could be improved at a later point.

Remove most OSD rendering from vo_aa.c, because that was extremely
hacky, can't be made work with osd_libass, and didn't work anyway in
my tests.

Internally, some cleanup is done. Subtitle and OSD related variable
declarations were literally all over the place. Move them to sub.h and
sub.c, which were hoarding most of these declarations already. Make the
player core in mplayer.c free of concerns like bitmap font loading.

The old OSD rendering code has been moved to osd_ft.c. The font_load.c
and font_load_ft.c are only needed and compiled if the old OSD
rendering code is configured.
This commit is contained in:
wm4 2012-03-22 06:26:37 +01:00
parent 7a06095dc3
commit 74e7a1e937
23 changed files with 1508 additions and 1110 deletions

View File

@ -213,6 +213,10 @@ SRCS_COMMON-$(WIN32DLL) += libmpcodecs/ad_acm.c \
SRCS_COMMON-$(XANIM_CODECS) += libmpcodecs/vd_xanim.c
SRCS_COMMON-$(XMMS_PLUGINS) += libmpdemux/demux_xmms.c
SRCS_COMMON-$(XVID4) += libmpcodecs/vd_xvid4.c
SRCS_COMMON-$(OLD_OSD) += sub/osd_ft.c
SRCS_COMMON-$(LIBASS_OSD) += sub/osd_libass.c
SRCS_COMMON = asxparser.c \
av_log.c \
av_opts.c \

View File

@ -680,14 +680,12 @@ const m_option_t common_opts[] = {
{"spualign", &spu_alignment, CONF_TYPE_INT, CONF_RANGE, -1, 2, NULL},
{"spuaa", &spu_aamode, CONF_TYPE_INT, CONF_RANGE, 0, 31, NULL},
{"spugauss", &spu_gaussvar, CONF_TYPE_FLOAT, CONF_RANGE, 0.0, 3.0, NULL},
#ifdef CONFIG_FREETYPE
{"subfont-encoding", &subtitle_font_encoding, CONF_TYPE_STRING, 0, 0, 0, NULL},
{"subfont-text-scale", &text_font_scale_factor, CONF_TYPE_FLOAT, CONF_RANGE, 0, 100, NULL},
{"subfont-osd-scale", &osd_font_scale_factor, CONF_TYPE_FLOAT, CONF_RANGE, 0, 100, NULL},
{"subfont-blur", &subtitle_font_radius, CONF_TYPE_FLOAT, CONF_RANGE, 0, 8, NULL},
{"subfont-outline", &subtitle_font_thickness, CONF_TYPE_FLOAT, CONF_RANGE, 0, 8, NULL},
{"subfont-autoscale", &subtitle_autoscale, CONF_TYPE_INT, CONF_RANGE, 0, 3, NULL},
#endif
OPT_START_CONDITIONAL(CONFIG_ASS, "libass"),
OPT_MAKE_FLAGS("ass", ass_enabled, 0),
OPT_FLOATRANGE("ass-font-scale", ass_font_scale, 0, 0, 100),

View File

@ -43,7 +43,6 @@
#include "mp_osd.h"
#include "libvo/video_out.h"
#include "libvo/csputils.h"
#include "sub/font_load.h"
#include "playtree.h"
#include "libao2/audio_out.h"
#include "mpcommon.h"
@ -2062,7 +2061,6 @@ static int mp_property_sub_forced_only(m_option_t *prop, int action,
}
#ifdef CONFIG_FREETYPE
/// Subtitle scale (RW)
static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
MPContext *mpctx)
@ -2081,8 +2079,7 @@ static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
}
#endif
text_font_scale_factor = *(float *) arg;
force_load_font = 1;
vo_osd_changed(OSDTYPE_SUBTITLE);
vo_osd_resized();
return M_PROPERTY_OK;
case M_PROPERTY_STEP_UP:
case M_PROPERTY_STEP_DOWN:
@ -2097,8 +2094,7 @@ static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
text_font_scale_factor += (arg ? *(float *) arg : 0.1) *
(action == M_PROPERTY_STEP_UP ? 1.0 : -1.0);
M_PROPERTY_CLAMP(prop, text_font_scale_factor);
force_load_font = 1;
vo_osd_changed(OSDTYPE_SUBTITLE);
vo_osd_resized();
return M_PROPERTY_OK;
default:
#ifdef CONFIG_ASS
@ -2109,7 +2105,6 @@ static int mp_property_sub_scale(m_option_t *prop, int action, void *arg,
return m_property_float_ro(prop, action, arg, text_font_scale_factor);
}
}
#endif
#ifdef CONFIG_TV
@ -2378,10 +2373,8 @@ static const m_option_t mp_properties[] = {
M_OPT_RANGE, 0, 1, NULL },
{ "sub_forced_only", mp_property_sub_forced_only, CONF_TYPE_FLAG,
M_OPT_RANGE, 0, 1, NULL },
#ifdef CONFIG_FREETYPE
{ "sub_scale", mp_property_sub_scale, CONF_TYPE_FLOAT,
M_OPT_RANGE, 0, 100, NULL },
#endif
#ifdef CONFIG_ASS
{ "ass_use_margins", mp_property_ass_use_margins, CONF_TYPE_FLAG,
M_OPT_RANGE, 0, 1, NULL },
@ -2497,9 +2490,7 @@ static struct property_osd_display {
{ "sub_delay", 0, OSD_MSG_SUB_DELAY, _("Sub delay: %s") },
{ "sub_visibility", 0, -1, _("Subtitles: %s") },
{ "sub_forced_only", 0, -1, _("Forced sub only: %s") },
#ifdef CONFIG_FREETYPE
{ "sub_scale", 0, -1, _("Sub Scale: %s")},
#endif
{ "ass_vsfilter_aspect_compat", 0, -1,
_("Subtitle VSFilter aspect compat: %s")},
#ifdef CONFIG_TV
@ -2624,9 +2615,7 @@ static struct {
{ "sub_delay", MP_CMD_SUB_DELAY, 0},
{ "sub_visibility", MP_CMD_SUB_VISIBILITY, 1},
{ "sub_forced_only", MP_CMD_SUB_FORCED_ONLY, 1},
#ifdef CONFIG_FREETYPE
{ "sub_scale", MP_CMD_SUB_SCALE, 0},
#endif
#ifdef CONFIG_ASS
{ "ass_use_margins", MP_CMD_ASS_USE_MARGINS, 1},
#endif

53
configure vendored
View File

@ -361,6 +361,7 @@ Optional features:
--disable-pthreads disable Posix threads support [autodetect]
--disable-w32threads disable Win32 threads support [autodetect]
--disable-libass disable subtitle rendering with libass [autodetect]
--disable-libass-osd disable OSD rendering with libass [autodetect]
--enable-rpath enable runtime linker path for extra libs [disabled]
--disable-libpostproc disable postprocess filter (vf_pp) [autodetect]
@ -657,6 +658,7 @@ _vstream=auto
_pthreads=auto
_w32threads=auto
_ass=auto
_libass_osd=auto
_rpath=no
libpostproc=auto
_asmalign_pot=auto
@ -991,6 +993,8 @@ for ac_option do
--disable-w32threads) _w32threads=no ;;
--enable-libass) _ass=yes ;;
--disable-libass) _ass=no ;;
--enable-libass-osd) _libass_osd=yes ;;
--disable-libass-osd) _libass_osd=no ;;
--enable-rpath) _rpath=yes ;;
--disable-rpath) _rpath=no ;;
--enable-libpostproc) libpostproc=yes ;;
@ -4542,6 +4546,39 @@ else
noinputmodules="cddb $noinputmodules"
fi
echocheck "SSA/ASS support"
if test "$_ass" = auto ; then
if pkg_config_add libass ; then
_ass=yes
def_ass='#define CONFIG_ASS 1'
else
die "Unable to find development files for libass. Aborting. If you really mean to compile without libass support use --disable-libass."
fi
else
def_ass='#undef CONFIG_ASS'
fi
echores "$_ass"
echocheck "libass OSD support"
_old_osd=yes
if test "$_libass_osd" = auto ; then
_libass_osd=no
if test "$_ass" = yes ; then
_libass_osd=yes
_old_osd=no
_bitmap_font=no
# disable unneeded dependencies
_freetype=no
_fontconfig=no
# this should always be forced, because libass does bidi itself
_fribidi=no
fi
fi
echores "$_libass_osd"
echocheck "bitmap font support"
if test "$_bitmap_font" = yes ; then
def_bitmap_font="#define CONFIG_BITMAP_FONT 1"
@ -4593,20 +4630,6 @@ fi
echores "$_fontconfig"
echocheck "SSA/ASS support"
if test "$_ass" = auto ; then
if pkg_config_add libass ; then
_ass=yes
def_ass='#define CONFIG_ASS 1'
else
die "Unable to find development files for libass. Aborting. If you really mean to compile without libass support use --disable-libass."
fi
else
def_ass='#undef CONFIG_ASS'
fi
echores "$_ass"
echocheck "fribidi with charsets"
if test "$_fribidi" = auto ; then
_fribidi=no
@ -5724,6 +5747,8 @@ JPEG = $_jpeg
LADSPA = $_ladspa
LIBA52 = $_liba52
LIBASS = $_ass
LIBASS_OSD = $_libass_osd
OLD_OSD = $_old_osd
LIBBLURAY = $_bluray
LIBBS2B = $_libbs2b
LIBDCA = $_libdca

View File

@ -72,8 +72,6 @@ static const struct vf_priv_s {
} *line_limits;
} vf_priv_dflt;
extern float sub_delay;
static int config(struct vf_instance *vf,
int width, int height, int d_width, int d_height,
unsigned int flags, unsigned int outfmt)

View File

@ -33,8 +33,6 @@
#include "sub/ass_mp.h"
#include "sub/sub.h"
extern float sub_delay;
struct vf_priv_s {
struct vo *vo;
#ifdef CONFIG_ASS

View File

@ -50,8 +50,7 @@
#include "parse_es.h"
#include "stheader.h"
#include "sub/sub_cc.h"
extern int sub_justify;
#include "sub/sub.h"
// 2/c0: audio data
// 3/c0: audio packet header (PES header)

View File

@ -40,7 +40,6 @@
#include "aspect.h"
#include "w32_common.h"
#include "libavutil/common.h"
#include "sub/font_load.h"
#include "sub/sub.h"
#include "eosd_packer.h"
@ -893,12 +892,7 @@ static bool resize_d3d(d3d_priv *priv)
calc_fs_rect(priv);
#ifdef CONFIG_FREETYPE
// font needs to be adjusted
force_load_font = 1;
#endif
// OSD needs to be drawn fresh for new size
vo_osd_changed(OSDTYPE_OSD);
vo_osd_resized();
priv->vo->want_redraw = true;

View File

@ -36,7 +36,6 @@
#include "libmpcodecs/mp_image.h"
#include "geometry.h"
#include "osd.h"
#include "sub/font_load.h"
#include "sub/sub.h"
#include "eosd_packer.h"
@ -171,13 +170,7 @@ static void resize(struct vo *vo, int x, int y)
gl->MatrixMode(GL_MODELVIEW);
gl->LoadIdentity();
if (!p->scaled_osd) {
#ifdef CONFIG_FREETYPE
// adjust font size to display size
force_load_font = 1;
#endif
vo_osd_changed(OSDTYPE_OSD);
}
vo_osd_resized();
gl->Clear(GL_COLOR_BUFFER_BIT);
vo->want_redraw = true;
}

View File

@ -45,7 +45,6 @@
#include "libmpcodecs/mp_image.h"
#include "geometry.h"
#include "osd.h"
#include "sub/font_load.h"
#include "sub/sub.h"
#include "eosd_packer.h"
@ -1186,12 +1185,7 @@ static void resize(struct gl_priv *p)
update_window_sized_objects(p);
update_all_uniforms(p);
#ifdef CONFIG_FREETYPE
// adjust font size to display size
force_load_font = 1;
#endif
vo_osd_changed(OSDTYPE_OSD);
vo_osd_resized();
gl->Clear(GL_COLOR_BUFFER_BIT);
vo->want_redraw = true;
}

View File

@ -50,8 +50,6 @@
#include "libavcodec/vdpau.h"
#include "sub/font_load.h"
#include "libavutil/common.h"
#include "libavutil/mathematics.h"
@ -384,11 +382,7 @@ static void resize(struct vo *vo)
vc->src_rect_vid.y1 = vc->flip ? src_rect.top : src_rect.bottom;
vc->border_x = borders.left;
vc->border_y = borders.top;
#ifdef CONFIG_FREETYPE
// adjust font size to display size
force_load_font = 1;
#endif
vo_osd_changed(OSDTYPE_OSD);
vo_osd_resized();
int flip_offset_ms = vo_fs ? vc->flip_offset_fs : vc->flip_offset_window;
vo->flip_queue_offset = flip_offset_ms / 1000.;

View File

@ -80,12 +80,12 @@
#include "sub/subreader.h"
#include "sub/find_subfiles.h"
#include "sub/dec_sub.h"
#include "sub/sub_cc.h"
#include "mp_osd.h"
#include "libvo/video_out.h"
#include "screenshot.h"
#include "sub/font_load.h"
#include "sub/sub.h"
#include "sub/av_sub.h"
#include "libmpcodecs/dec_teletext.h"
@ -319,16 +319,6 @@ int frame_dropping = 0; // option 0=no drop 1= drop vo 2= drop decode
static int play_n_frames = -1;
static int play_n_frames_mf = -1;
// sub:
char *font_name = NULL;
char *sub_font_name = NULL;
extern int font_fontconfig;
float font_factor = 0.75;
float sub_delay = 0;
float sub_fps = 0;
int subcc_enabled = 0;
int suboverlap_enabled = 1;
#include "sub/ass_mp.h"
char *current_module; // for debugging
@ -708,14 +698,6 @@ void exit_player_with_rc(struct MPContext *mpctx, enum exit_reason how, int rc)
current_module = "uninit_input";
mp_input_uninit(mpctx->input);
#ifdef CONFIG_FREETYPE
current_module = "uninit_font";
if (mpctx->osd && mpctx->osd->sub_font != vo_font)
free_font_desc(mpctx->osd->sub_font);
free_font_desc(vo_font);
vo_font = NULL;
done_freetype();
#endif
osd_free(mpctx->osd);
#ifdef CONFIG_ASS
@ -1652,7 +1634,6 @@ static void update_osd_msg(struct MPContext *mpctx)
struct MPOpts *opts = &mpctx->opts;
mp_osd_msg_t *msg;
struct osd_state *osd = mpctx->osd;
char osd_text_timer[128];
if (mpctx->add_osd_seek_info) {
double percentage = get_percent_pos(mpctx);
@ -1682,6 +1663,7 @@ static void update_osd_msg(struct MPContext *mpctx)
if (mpctx->sh_video && opts->term_osd != 1) {
// fallback on the timer
char osd_text_timer[128] = {0};
if (opts->osd_level >= 2) {
int len = get_time_length(mpctx);
int percentage = -1;
@ -1723,19 +1705,22 @@ static void update_osd_msg(struct MPContext *mpctx)
fractions_text[0] = 0;
}
osd_get_function_sym(osd_text_timer, sizeof(osd_text_timer),
mpctx->osd_function);
size_t blen = strlen(osd_text_timer);
if (opts->osd_level == 3)
snprintf(osd_text_timer, sizeof(osd_text_timer),
"%c %02d:%02d:%02d%s / %02d:%02d:%02d%s",
mpctx->osd_function, pts / 3600, (pts / 60) % 60, pts % 60,
fractions_text, len / 3600, (len / 60) % 60, len % 60,
snprintf(osd_text_timer + blen, sizeof(osd_text_timer) - blen,
" %02d:%02d:%02d%s / %02d:%02d:%02d%s",
pts / 3600, (pts / 60) % 60, pts % 60, fractions_text,
len / 3600, (len / 60) % 60, len % 60,
percentage_text);
else
snprintf(osd_text_timer, sizeof(osd_text_timer),
"%c %02d:%02d:%02d%s%s",
mpctx->osd_function, pts / 3600, (pts / 60) % 60,
pts % 60, fractions_text, percentage_text);
} else
osd_text_timer[0] = 0;
snprintf(osd_text_timer + blen, sizeof(osd_text_timer) - blen,
" %02d:%02d:%02d%s%s",
pts / 3600, (pts / 60) % 60, pts % 60, fractions_text,
percentage_text);
}
if (strcmp(osd->osd_text, osd_text_timer)) {
osd_set_text(osd, osd_text_timer);
@ -4090,45 +4075,12 @@ int main(int argc, char *argv[])
//------ load global data first ------
mpctx->osd = osd_create();
// check font
#ifdef CONFIG_FREETYPE
init_freetype();
#endif
#ifdef CONFIG_FONTCONFIG
if (font_fontconfig <= 0) {
#endif
#ifdef CONFIG_BITMAP_FONT
if (font_name) {
vo_font = read_font_desc(font_name, font_factor, verbose > 1);
if (!vo_font)
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Cannot load bitmap font: %s\n",
filename_recode(font_name));
} else {
// try default:
vo_font = read_font_desc(mem_ptr = get_path("font/font.desc"),
font_factor, verbose > 1);
free(mem_ptr); // release the buffer created by get_path()
if (!vo_font)
vo_font = read_font_desc(MPLAYER_DATADIR "/font/font.desc",
font_factor, verbose > 1);
}
if (sub_font_name)
mpctx->osd->sub_font = read_font_desc(sub_font_name, font_factor,
verbose > 1);
else
mpctx->osd->sub_font = vo_font;
#endif
#ifdef CONFIG_FONTCONFIG
}
#endif
#ifdef CONFIG_ASS
mpctx->ass_library = mp_ass_init(opts);
mpctx->osd->ass_library = mpctx->ass_library;
#endif
mpctx->osd = osd_create(opts, mpctx->ass_library);
#ifdef HAVE_RTC
if (opts->rtc) {
char *rtc_device = opts->rtc_device;
@ -4792,9 +4744,7 @@ goto_enable_cache:
if (mpctx->sh_video) {
if (mpctx->sh_video->output_flags & VFCAP_SPU && vo_spudec)
spudec_set_hw_spu(vo_spudec, mpctx->video_out);
#ifdef CONFIG_FREETYPE
force_load_font = 1;
#endif
osd_font_invalidate();
} else if (!mpctx->sh_audio)
goto goto_next_file;

View File

@ -31,16 +31,8 @@ extern char ** video_driver_list;
extern char ** audio_driver_list;
extern float audio_delay;
extern unsigned int osd_visible;
extern char * font_name;
extern char * sub_font_name;
extern float font_factor;
extern double force_fps;
extern float sub_delay;
extern float sub_fps;
extern int stream_cache_size;
extern int frame_dropping;

View File

@ -34,22 +34,11 @@
#include "path.h"
#include "ass_mp.h"
#include "subreader.h"
#include "sub/sub.h"
#include "stream/stream.h"
#include "options.h"
#ifdef CONFIG_FONTCONFIG
extern int font_fontconfig;
#else
static int font_fontconfig = -1;
#endif
extern char *font_name;
extern char *sub_font_name;
extern float text_font_scale_factor;
extern int subtitle_autoscale;
#ifdef CONFIG_ICONV
extern char *sub_cp;
#else
#ifndef CONFIG_ICONV
static char *sub_cp = 0;
#endif

View File

@ -83,13 +83,6 @@ typedef struct font_desc {
extern font_desc_t* vo_font;
extern char *subtitle_font_encoding;
extern float text_font_scale_factor;
extern float osd_font_scale_factor;
extern float subtitle_font_radius;
extern float subtitle_font_thickness;
extern int subtitle_autoscale;
extern int vo_image_width;
extern int vo_image_height;

View File

@ -48,23 +48,14 @@
#include "mp_msg.h"
#include "mplayer.h"
#include "path.h"
#include "sub/sub.h"
#include "osd_font.h"
#if (FREETYPE_MAJOR > 2) || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 1)
#define HAVE_FREETYPE21
#endif
char *subtitle_font_encoding = NULL;
float text_font_scale_factor = 3.5;
float osd_font_scale_factor = 4.0;
float subtitle_font_radius = 2.0;
float subtitle_font_thickness = 2.0;
// 0 = no autoscale
// 1 = video height
// 2 = video width
// 3 = diagonal
int subtitle_autoscale = 3;
int vo_image_width = 0;
int vo_image_height = 0;
int force_load_font;

View File

@ -19,7 +19,7 @@
#ifndef MPLAYER_OSD_FONT_H
#define MPLAYER_OSD_FONT_H
const unsigned char osd_font_pfb[] = {
static const unsigned char osd_font_pfb[] = {
0x80,0x01,0x02,0x17,0x00,0x00,0x25,0x21,0x50,0x53,0x2d,0x41,0x64,0x6f,0x62,0x65,
0x46,0x6f,0x6e,0x74,0x2d,0x31,0x2e,0x30,0x3a,0x20,0x4f,0x53,0x44,0x20,0x31,0x2e,
0x30,0x30,0x0a,0x25,0x25,0x43,0x72,0x65,0x61,0x74,0x69,0x6f,0x6e,0x44,0x61,0x74,

969
sub/osd_ft.c Normal file
View File

@ -0,0 +1,969 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "stream/stream.h"
#include "stream/stream_dvdnav.h"
#define OSD_NAV_BOX_ALPHA 0x7f
#include "libmpcodecs/dec_teletext.h"
#include "osdep/timer.h"
#include "talloc.h"
#include "mplayer.h"
#include "path.h"
#include "mp_msg.h"
#include "libvo/video_out.h"
#include "sub.h"
#include "sub/font_load.h"
#include "spudec.h"
#include "libavutil/common.h"
#define FONT_LOAD_DEFER 6
#define NEW_SPLITTING
//static int vo_font_loaded=-1;
font_desc_t* vo_font=NULL;
// Structures needed for the new splitting algorithm.
// osd_text_t contains the single subtitle word.
// osd_text_p is used to mark the lines of subtitles
struct osd_text_t {
int osd_kerning, //kerning with the previous word
osd_length, //orizontal length inside the bbox
text_length, //number of characters
*text; //characters
struct osd_text_t *prev,
*next;
};
struct osd_text_p {
int value;
struct osd_text_t *ott;
struct osd_text_p *prev,
*next;
};
//^
// return the real height of a char:
static inline int get_height(int c,int h){
int font;
if ((font=vo_font->font[c])>=0)
if(h<vo_font->pic_a[font]->h) h=vo_font->pic_a[font]->h;
return h;
}
void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t *obj)
{
const char *cp = osd->osd_text;
int x=20;
int h=0;
int font;
obj->bbox.x1=obj->x=x;
obj->bbox.y1=obj->y=10;
while (*cp){
uint16_t c=utf8_get_char(&cp);
render_one_glyph(vo_font, c);
x+=vo_font->width[c]+vo_font->charspace;
h=get_height(c,h);
}
obj->bbox.x2=x-vo_font->charspace;
obj->bbox.y2=obj->bbox.y1+h;
obj->flags|=OSDFLAG_BBOX;
osd_alloc_buf(obj);
cp = osd->osd_text;
x = obj->x;
while (*cp){
uint16_t c=utf8_get_char(&cp);
if ((font=vo_font->font[c])>=0)
draw_alpha_buf(obj,x,obj->y,
vo_font->width[c],
vo_font->pic_a[font]->h,
vo_font->pic_b[font]->bmp+vo_font->start[c],
vo_font->pic_a[font]->bmp+vo_font->start[c],
vo_font->pic_a[font]->w);
x+=vo_font->width[c]+vo_font->charspace;
}
}
static int vo_osd_teletext_scale=0;
// renders char to a big per-object buffer where alpha and bitmap are separated
static void tt_draw_alpha_buf(mp_osd_obj_t* obj, int x0,int y0, int w,int h, unsigned char* src, int stride,int fg,int bg,int alpha)
{
int dststride = obj->stride;
int dstskip = obj->stride-w;
int srcskip = stride-w;
int i, j;
unsigned char *b = obj->bitmap_buffer + (y0-obj->bbox.y1)*dststride + (x0-obj->bbox.x1);
unsigned char *a = obj->alpha_buffer + (y0-obj->bbox.y1)*dststride + (x0-obj->bbox.x1);
unsigned char *bs = src;
if (x0 < obj->bbox.x1 || x0+w > obj->bbox.x2 || y0 < obj->bbox.y1 || y0+h > obj->bbox.y2) {
mp_msg(MSGT_OSD,MSGL_ERR,"tt osd text out of range: bbox [%d %d %d %d], txt [%d %d %d %d]\n",
obj->bbox.x1, obj->bbox.x2, obj->bbox.y1, obj->bbox.y2,
x0, x0+w, y0, y0+h);
return;
}
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++, b++, a++, bs++) {
*b=(fg-bg)*(*bs)/255+bg;
*a=alpha;
}
b+= dstskip;
a+= dstskip;
bs+= srcskip;
}
}
void vo_update_text_teletext(struct osd_state *osd, mp_osd_obj_t *obj)
{
int h=0,w=0,i,j,font,flashon;
int wm,hm;
int color;
int x,y,x0,y0;
int cols,rows;
int wm12;
int hm13;
int hm23;
int start_row,max_rows;
int b,ax[6],ay[6],aw[6],ah[6];
tt_char tc;
tt_char* tdp=vo_osd_teletext_page;
static const uint8_t colors[8]={1,85,150,226,70,105,179,254};
unsigned char* buf[9];
int dxs = osd->w, dys = osd->h;
obj->flags|=OSDFLAG_CHANGED|OSDFLAG_VISIBLE;
if (!tdp || !vo_osd_teletext_mode) {
obj->flags&=~OSDFLAG_VISIBLE;
return;
}
flashon=(GetTimer()/1000000)%2;
switch(vo_osd_teletext_half){
case TT_ZOOM_TOP_HALF:
start_row=0;
max_rows=VBI_ROWS/2;
break;
case TT_ZOOM_BOTTOM_HALF:
start_row=VBI_ROWS/2;
max_rows=VBI_ROWS/2;
break;
default:
start_row=0;
max_rows=VBI_ROWS;
break;
}
wm=0;
for(i=start_row;i<max_rows;i++){
for(j=0;j<VBI_COLUMNS;j++){
tc=tdp[i*VBI_COLUMNS+j];
if(!tc.ctl && !tc.gfx)
{
render_one_glyph(vo_font, tc.unicode);
if (wm<vo_font->width[tc.unicode])
wm=vo_font->width[tc.unicode];
}
}
}
hm=vo_font->height+1;
wm=dxs*hm*max_rows/(dys*VBI_COLUMNS);
#ifdef CONFIG_FREETYPE
//very simple teletext font auto scaling
if(!vo_osd_teletext_scale && hm*(max_rows+1)>dys){
osd_font_scale_factor*=1.0*(dys)/((max_rows+1)*hm);
force_load_font=1;
vo_osd_teletext_scale=osd_font_scale_factor;
obj->flags&=~OSDFLAG_VISIBLE;
return;
}
#endif
cols=dxs/wm;
rows=dys/hm;
if(cols>VBI_COLUMNS)
cols=VBI_COLUMNS;
if(rows>max_rows)
rows=max_rows;
w=cols*wm-vo_font->charspace;
h=rows*hm-vo_font->charspace;
if(w<dxs)
x0=(dxs-w)/2;
else
x0=0;
if(h<dys)
y0=(dys-h)/2;
else
y0=0;
wm12=wm>>1;
hm13=(hm+1)/3;
hm23=hm13<<1;
for(i=0;i<6;i+=2){
ax[i+0]=0;
aw[i+0]=wm12;
ax[i+1]=wm12;
aw[i+1]=wm-wm12;
}
for(i=0;i<2;i++){
ay[i+0]=0;
ah[i+0]=hm13;
ay[i+2]=hm13;
ah[i+2]=hm-hm23;
ay[i+4]=hm-hm13;
ah[i+4]=hm13;
}
obj->x = 0;
obj->y = 0;
obj->bbox.x1 = x0;
obj->bbox.y1 = y0;
obj->bbox.x2 = x0+w;
obj->bbox.y2 = y0+h;
obj->flags |= OSDFLAG_BBOX;
osd_alloc_buf(obj);
for(i=0;i<9;i++)
buf[i]=malloc(wm*hm);
//alpha
if(vo_osd_teletext_format==TT_FORMAT_OPAQUE ||vo_osd_teletext_format==TT_FORMAT_OPAQUE_INV)
color=1;
else
color=200;
memset(buf[8],color,wm*hm);
//colors
if(vo_osd_teletext_format==TT_FORMAT_OPAQUE ||vo_osd_teletext_format==TT_FORMAT_TRANSPARENT){
for(i=0;i<8;i++){
memset(buf[i],(unsigned char)(1.0*(255-color)*colors[i]/255),wm*hm);
}
}else{
for(i=0;i<8;i++)
memset(buf[i],(unsigned char)(1.0*(255-color)*colors[7-i]/255),wm*hm);
}
y=y0;
for(i=0;i<rows;i++){
x=x0;
for(j=0;j<cols;j++){
tc=tdp[(i+start_row)*VBI_COLUMNS+j];
if (tc.hidden) { x+=wm; continue;}
if(!tc.gfx || (tc.flh && !flashon)){
/* Rendering one text character */
draw_alpha_buf(obj,x,y,wm,hm,buf[tc.bg],buf[8],wm);
if(tc.unicode!=0x20 && tc.unicode!=0x00 && !tc.ctl &&
(!tc.flh || flashon) &&
(font=vo_font->font[tc.unicode])>=0 && y+hm<dys){
tt_draw_alpha_buf(obj,x,y,vo_font->width[tc.unicode],vo_font->height,
vo_font->pic_b[font]->bmp+vo_font->start[tc.unicode]-vo_font->charspace*vo_font->pic_a[font]->w,
vo_font->pic_b[font]->w,
buf[tc.fg][0],buf[tc.bg][0],buf[8][0]);
}
}else{
/*
Rendering one graphics character
TODO: support for separated graphics symbols (where six rectangles does not touch each other)
+--+ +--+ 87654321
|01| |12| --------
|10| <= |34| <= 00100110 <= 0x26
|01| |56|
+--+ +--+
(0:wm/2) (wm/2:wm-wm/2)
********** *********** (0:hm/3)
*** **** **** ****
*** 1 **** **** 2 ****
*** **** **** ****
********** ***********
********** ***********
********** *********** (hm/3:hm-2*hm/3)
********** ***********
*** **** **** ****
*** 3 **** **** 4 ****
*** **** **** ****
********** ***********
********** ***********
********** ***********
********** *********** (hm-hm/3:hm/3)
*** **** **** ****
*** 5 **** **** 6 ****
*** **** **** ****
********** ***********
********** ***********
*/
if(tc.gfx>1){ //separated gfx
for(b=0;b<6;b++){
color=(tc.unicode>>b)&1?tc.fg:tc.bg;
draw_alpha_buf(obj,x+ax[b]+1,y+ay[b]+1,aw[b]-2,ah[b]-2,buf[color],buf[8],wm);
}
//separated gfx (background borders)
//vertical
draw_alpha_buf(obj,x ,y,1,hm,buf[tc.bg],buf[8],wm);
draw_alpha_buf(obj,x+ax[1]-1,y,2,hm,buf[tc.bg],buf[8],wm);
draw_alpha_buf(obj,x+ax[1]+aw[1]-1,y,wm-ax[1]-aw[1]+1,hm,buf[tc.bg],buf[8],wm);
//horizontal
draw_alpha_buf(obj,x,y ,wm,1,buf[tc.bg],buf[8],wm);
draw_alpha_buf(obj,x,y+ay[0]+ah[0]-1,wm,2,buf[tc.bg],buf[8],wm);
draw_alpha_buf(obj,x,y+ay[2]+ah[2]-1,wm,2,buf[tc.bg],buf[8],wm);
draw_alpha_buf(obj,x,y+ay[4]+ah[4]-1,wm,hm-ay[4]-ah[4]+1,buf[tc.bg],buf[8],wm);
}else{
for(b=0;b<6;b++){
color=(tc.unicode>>b)&1?tc.fg:tc.bg;
draw_alpha_buf(obj,x+ax[b],y+ay[b],aw[b],ah[b],buf[color],buf[8],wm);
}
}
}
x+=wm;
}
y+=hm;
}
for(i=0;i<9;i++)
free(buf[i]);
}
// if we have n=256 bars then OSD progbar looks like below
//
// 0 1 2 3 ... 256 <= vo_osd_progbar_value
// | | | | |
// [ === === === ... === ]
//
// the above schema is rescalled to n=elems bars
void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t *obj){
obj->flags|=OSDFLAG_CHANGED|OSDFLAG_VISIBLE;
if(vo_osd_progbar_type<0 || !vo_font){
obj->flags&=~OSDFLAG_VISIBLE;
return;
}
render_one_glyph(vo_font, OSD_PB_START);
render_one_glyph(vo_font, OSD_PB_END);
render_one_glyph(vo_font, OSD_PB_0);
render_one_glyph(vo_font, OSD_PB_1);
render_one_glyph(vo_font, vo_osd_progbar_type);
// calculate bbox corners:
{ int h=0;
int y=(osd->h-vo_font->height)/2;
int delimw=vo_font->width[OSD_PB_START]
+vo_font->width[OSD_PB_END]
+vo_font->charspace;
int width=(2*osd->w-3*delimw)/3;
int charw=vo_font->width[OSD_PB_0]+vo_font->charspace;
int elems=width/charw;
int x=(osd->w-elems*charw-delimw)/2;
int delta = 0;
h=get_height(OSD_PB_START,h);
h=get_height(OSD_PB_END,h);
h=get_height(OSD_PB_0,h);
h=get_height(OSD_PB_1,h);
if (vo_osd_progbar_type>0 && vo_font->font[vo_osd_progbar_type]>=0){
delta = vo_font->width[vo_osd_progbar_type]+vo_font->spacewidth;
delta = (x-delta > 0) ? delta : x;
h=get_height(vo_osd_progbar_type,h);
}
obj->bbox.x1=obj->x=x;
obj->bbox.y1=obj->y=y;
obj->bbox.x2=x+width+delimw;
obj->bbox.y2=y+h; //vo_font->height;
obj->flags|=OSDFLAG_BBOX;
obj->params.progbar.elems=elems;
obj->bbox.x1-=delta; // space for an icon
}
osd_alloc_buf(obj);
{
int minw = vo_font->width[OSD_PB_START]+vo_font->width[OSD_PB_END]+vo_font->width[OSD_PB_0];
if (vo_osd_progbar_type>0 && vo_font->font[vo_osd_progbar_type]>=0){
minw += vo_font->width[vo_osd_progbar_type]+vo_font->charspace+vo_font->spacewidth;
}
if (obj->bbox.x2 - obj->bbox.x1 < minw) return; // space too small, don't render anything
}
// render it:
{ unsigned char *s;
unsigned char *sa;
int i,w,h,st,mark;
int x=obj->x;
int y=obj->y;
int c,font;
int charw=vo_font->width[OSD_PB_0]+vo_font->charspace;
int elems=obj->params.progbar.elems;
if (vo_osd_progbar_value<=0)
mark=0;
else {
int ev=vo_osd_progbar_value*elems;
mark=ev>>8;
if (ev & 0xFF) mark++;
if (mark>elems) mark=elems;
}
// printf("osd.progbar width=%d xpos=%d\n",width,x);
c=vo_osd_progbar_type;
if(vo_osd_progbar_type>0 && (font=vo_font->font[c])>=0) {
int xp=x-vo_font->width[c]-vo_font->spacewidth;
draw_alpha_buf(obj,(xp<0?0:xp),y,
vo_font->width[c],
vo_font->pic_a[font]->h,
vo_font->pic_b[font]->bmp+vo_font->start[c],
vo_font->pic_a[font]->bmp+vo_font->start[c],
vo_font->pic_a[font]->w);
}
c=OSD_PB_START;
if ((font=vo_font->font[c])>=0)
draw_alpha_buf(obj,x,y,
vo_font->width[c],
vo_font->pic_a[font]->h,
vo_font->pic_b[font]->bmp+vo_font->start[c],
vo_font->pic_a[font]->bmp+vo_font->start[c],
vo_font->pic_a[font]->w);
x+=vo_font->width[c]+vo_font->charspace;
c=OSD_PB_0;
if ((font=vo_font->font[c])>=0){
w=vo_font->width[c];
h=vo_font->pic_a[font]->h;
s=vo_font->pic_b[font]->bmp+vo_font->start[c];
sa=vo_font->pic_a[font]->bmp+vo_font->start[c];
st=vo_font->pic_a[font]->w;
if ((i=mark)) do {
draw_alpha_buf(obj,x,y,w,h,s,sa,st);
x+=charw;
} while(--i);
}
c=OSD_PB_1;
if ((font=vo_font->font[c])>=0){
w=vo_font->width[c];
h=vo_font->pic_a[font]->h;
s =vo_font->pic_b[font]->bmp+vo_font->start[c];
sa=vo_font->pic_a[font]->bmp+vo_font->start[c];
st=vo_font->pic_a[font]->w;
if ((i=elems-mark)) do {
draw_alpha_buf(obj,x,y,w,h,s,sa,st);
x+=charw;
} while(--i);
}
c=OSD_PB_END;
if ((font=vo_font->font[c])>=0)
draw_alpha_buf(obj,x,y,
vo_font->width[c],
vo_font->pic_a[font]->h,
vo_font->pic_b[font]->bmp+vo_font->start[c],
vo_font->pic_a[font]->bmp+vo_font->start[c],
vo_font->pic_a[font]->w);
// x+=vo_font->width[c]+vo_font->charspace;
}
// vo_osd_progbar_value=(vo_osd_progbar_value+1)&0xFF;
}
void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t *obj){
unsigned char *t;
int c,i,j,l,x,y,font,prevc,counter;
int k;
int xsize;
int dxs = osd->w, dys = osd->h;
int xmin=dxs,xmax=0;
int h,lasth;
int xtblc, utblc;
struct font_desc *sub_font = osd->sub_font;
obj->flags|=OSDFLAG_CHANGED|OSDFLAG_VISIBLE;
if(!vo_sub || !osd->sub_font || !sub_visibility || (sub_font->font[40]<0)){
obj->flags&=~OSDFLAG_VISIBLE;
return;
}
obj->bbox.y2=obj->y=dys;
obj->params.subtitle.lines=0;
// too long lines divide into a smaller ones
i=k=lasth=0;
h=sub_font->height;
l=vo_sub->lines;
{
struct osd_text_t *osl, *cp_ott, *tmp_ott, *tmp;
struct osd_text_p *otp_sub = NULL, *otp_sub_tmp = NULL, // these are used to store the whole sub text osd
*otp, *tmp_otp, *pmt; // these are used to manage sub text osd coming from a single sub line
int *char_seq, char_position, xlimit = dxs * sub_width_p / 100, counter;
while (l) {
xsize = -sub_font->charspace;
l--;
t=vo_sub->text[i++];
char_position = 0;
char_seq = calloc(strlen(t), sizeof(int));
prevc = -1;
otp = NULL;
osl = NULL;
x = 1;
// reading the subtitle words from vo_sub->text[]
while (*t) {
if (sub_utf8)
c = utf8_get_char((const char **)&t);
else if ((c = *t++) >= 0x80 && sub_unicode)
c = (c<<8) + *t++;
if (k==MAX_UCS){
t += strlen(t); // end here
mp_msg(MSGT_OSD,MSGL_WARN,"\nMAX_UCS exceeded!\n");
}
if (!c) c++; // avoid UCS 0
render_one_glyph(sub_font, c);
if (c == ' ') {
struct osd_text_t *tmp_ott = calloc(1, sizeof(struct osd_text_t));
if (osl == NULL) {
osl = cp_ott = tmp_ott;
} else {
tmp_ott->prev = cp_ott;
cp_ott->next = tmp_ott;
tmp_ott->osd_kerning =
sub_font->charspace + sub_font->width[' '];
cp_ott = tmp_ott;
}
tmp_ott->osd_length = xsize;
tmp_ott->text_length = char_position;
tmp_ott->text = malloc(char_position * sizeof(int));
for (counter = 0; counter < char_position; ++counter)
tmp_ott->text[counter] = char_seq[counter];
char_position = 0;
xsize = 0;
prevc = c;
} else {
int delta_xsize = sub_font->width[c] + sub_font->charspace + kerning(sub_font, prevc, c);
if (xsize + delta_xsize <= dxs) {
if (!x) x = 1;
prevc = c;
char_seq[char_position++] = c;
xsize += delta_xsize;
if ((!suboverlap_enabled) && ((font = sub_font->font[c]) >= 0)) {
if (sub_font->pic_a[font]->h > h) {
h = sub_font->pic_a[font]->h;
}
}
} else {
if (x) {
mp_msg(MSGT_OSD, MSGL_WARN, "\nSubtitle word '%s' too long!\n", t);
x = 0;
}
}
}
}// for len (all words from subtitle line read)
// osl holds an ordered (as they appear in the lines) chain of the subtitle words
{
struct osd_text_t *tmp_ott = calloc(1, sizeof(struct osd_text_t));
if (osl == NULL) {
osl = cp_ott = tmp_ott;
} else {
tmp_ott->prev = cp_ott;
cp_ott->next = tmp_ott;
tmp_ott->osd_kerning =
sub_font->charspace + sub_font->width[' '];
cp_ott = tmp_ott;
}
tmp_ott->osd_length = xsize;
tmp_ott->text_length = char_position;
tmp_ott->text = malloc(char_position * sizeof(int));
for (counter = 0; counter < char_position; ++counter)
tmp_ott->text[counter] = char_seq[counter];
char_position = 0;
xsize = -sub_font->charspace;
}
free(char_seq);
if (osl != NULL) {
int value = 0, exit = 0, minimum = 0;
// otp will contain the chain of the osd subtitle lines coming from the single vo_sub line.
otp = tmp_otp = calloc(1, sizeof(struct osd_text_p));
tmp_otp->ott = osl;
for (tmp_ott = tmp_otp->ott; exit == 0; ) {
do {
value += tmp_ott->osd_kerning + tmp_ott->osd_length;
tmp_ott = tmp_ott->next;
} while ((tmp_ott != NULL) && (value + tmp_ott->osd_kerning + tmp_ott->osd_length <= xlimit));
if (tmp_ott != NULL) {
struct osd_text_p *tmp = calloc(1, sizeof(struct osd_text_p));
tmp_otp->value = value;
tmp_otp->next = tmp;
tmp->prev = tmp_otp;
tmp_otp = tmp;
tmp_otp->ott = tmp_ott;
value = -2 * sub_font->charspace - sub_font->width[' '];
} else {
tmp_otp->value = value;
exit = 1;
}
}
#ifdef NEW_SPLITTING
// minimum holds the 'sum of the differences in length among the lines',
// a measure of the evenness of the lengths of the lines
for (tmp_otp = otp; tmp_otp->next != NULL; tmp_otp = tmp_otp->next) {
pmt = tmp_otp->next;
while (pmt != NULL) {
minimum += abs(tmp_otp->value - pmt->value);
pmt = pmt->next;
}
}
if (otp->next != NULL) {
int mem1, mem2;
struct osd_text_p *mem, *hold;
exit = 0;
// until the last word of a line can be moved to the beginning of following line
// reducing the 'sum of the differences in length among the lines', it is done
while (exit == 0) {
hold = NULL;
exit = 1;
for (tmp_otp = otp; tmp_otp->next != NULL; tmp_otp = tmp_otp->next) {
pmt = tmp_otp->next;
for (tmp = tmp_otp->ott; tmp->next != pmt->ott; tmp = tmp->next);
if (pmt->value + tmp->osd_length + pmt->ott->osd_kerning <= xlimit) {
mem1 = tmp_otp->value;
mem2 = pmt->value;
tmp_otp->value = mem1 - tmp->osd_length - tmp->osd_kerning;
pmt->value = mem2 + tmp->osd_length + pmt->ott->osd_kerning;
value = 0;
for (mem = otp; mem->next != NULL; mem = mem->next) {
pmt = mem->next;
while (pmt != NULL) {
value += abs(mem->value - pmt->value);
pmt = pmt->next;
}
}
if (value < minimum) {
minimum = value;
hold = tmp_otp;
exit = 0;
}
tmp_otp->value = mem1;
tmp_otp->next->value = mem2;
}
}
// merging
if (exit == 0) {
tmp_otp = hold;
pmt = tmp_otp->next;
for (tmp = tmp_otp->ott; tmp->next != pmt->ott; tmp = tmp->next);
mem1 = tmp_otp->value;
mem2 = pmt->value;
tmp_otp->value = mem1 - tmp->osd_length - tmp->osd_kerning;
pmt->value = mem2 + tmp->osd_length + pmt->ott->osd_kerning;
pmt->ott = tmp;
}//~merging
}//~while(exit == 0)
}//~if(otp->next!=NULL)
#endif
// adding otp (containing splitted lines) to otp chain
if (otp_sub == NULL) {
otp_sub = otp;
for (otp_sub_tmp = otp_sub; otp_sub_tmp->next != NULL; otp_sub_tmp = otp_sub_tmp->next);
} else {
//updating ott chain
tmp = otp_sub->ott;
while (tmp->next != NULL) tmp = tmp->next;
tmp->next = otp->ott;
otp->ott->prev = tmp;
//attaching new subtitle line at the end
otp_sub_tmp->next = otp;
otp->prev = otp_sub_tmp;
do
otp_sub_tmp = otp_sub_tmp->next;
while (otp_sub_tmp->next != NULL);
}
}//~ if(osl != NULL)
} // while
// write lines into utbl
xtblc = 0;
utblc = 0;
obj->y = dys;
obj->params.subtitle.lines = 0;
for (tmp_otp = otp_sub; tmp_otp != NULL; tmp_otp = tmp_otp->next) {
if ((obj->params.subtitle.lines++) >= MAX_UCSLINES)
break;
if (h > obj->y) { // out of the screen so end parsing
obj->y -= lasth - sub_font->height; // correct the y position
break;
}
xsize = tmp_otp->value;
obj->params.subtitle.xtbl[xtblc++] = (dxs - xsize) / 2;
if (xmin > (dxs - xsize) / 2)
xmin = (dxs - xsize) / 2;
if (xmax < (dxs + xsize) / 2)
xmax = (dxs + xsize) / 2;
tmp = (tmp_otp->next == NULL) ? NULL : tmp_otp->next->ott;
for (tmp_ott = tmp_otp->ott; tmp_ott != tmp; tmp_ott = tmp_ott->next) {
for (counter = 0; counter < tmp_ott->text_length; ++counter) {
if (utblc > MAX_UCS) {
break;
}
c = tmp_ott->text[counter];
render_one_glyph(sub_font, c);
obj->params.subtitle.utbl[utblc++] = c;
k++;
}
obj->params.subtitle.utbl[utblc++] = ' ';
}
obj->params.subtitle.utbl[utblc - 1] = 0;
obj->y -= sub_font->height;
}
if(obj->params.subtitle.lines)
obj->y = dys - ((obj->params.subtitle.lines - 1) * sub_font->height + sub_font->pic_a[sub_font->font[40]]->h);
// free memory
if (otp_sub != NULL) {
for (tmp = otp_sub->ott; tmp->next != NULL; free(tmp->prev)) {
free(tmp->text);
tmp = tmp->next;
}
free(tmp->text);
free(tmp);
for(pmt = otp_sub; pmt->next != NULL; free(pmt->prev)) {
pmt = pmt->next;
}
free(pmt);
}
}
/// vertical alignment
h = dys - obj->y;
if (sub_alignment == 2)
obj->y = dys * sub_pos / 100 - h;
else if (sub_alignment == 1)
obj->y = dys * sub_pos / 100 - h / 2;
else
obj->y = dys * sub_pos / 100;
if (obj->y < 0)
obj->y = 0;
if (obj->y > dys - h)
obj->y = dys - h;
obj->bbox.y2 = obj->y + h;
// calculate bbox:
if (sub_justify) xmin = 10;
obj->bbox.x1=xmin;
obj->bbox.x2=xmax;
obj->bbox.y1=obj->y;
// obj->bbox.y2=obj->y+obj->params.subtitle.lines*sub_font->height;
obj->flags|=OSDFLAG_BBOX;
osd_alloc_buf(obj);
y = obj->y;
obj->alignment = 0;
switch(vo_sub->alignment) {
case SUB_ALIGNMENT_BOTTOMLEFT:
case SUB_ALIGNMENT_MIDDLELEFT:
case SUB_ALIGNMENT_TOPLEFT:
obj->alignment |= 0x1;
break;
case SUB_ALIGNMENT_BOTTOMRIGHT:
case SUB_ALIGNMENT_MIDDLERIGHT:
case SUB_ALIGNMENT_TOPRIGHT:
obj->alignment |= 0x2;
break;
case SUB_ALIGNMENT_BOTTOMCENTER:
case SUB_ALIGNMENT_MIDDLECENTER:
case SUB_ALIGNMENT_TOPCENTER:
default:
obj->alignment |= 0x0;
}
i=j=0;
if ((l = obj->params.subtitle.lines)) {
for(counter = dxs; i < l; ++i)
if (obj->params.subtitle.xtbl[i] < counter) counter = obj->params.subtitle.xtbl[i];
for (i = 0; i < l; ++i) {
switch (obj->alignment&0x3) {
case 1:
// left
x = counter;
break;
case 2:
// right
x = 2 * obj->params.subtitle.xtbl[i] - counter - ((obj->params.subtitle.xtbl[i] == counter) ? 0 : 1);
break;
default:
//center
x = obj->params.subtitle.xtbl[i];
}
prevc = -1;
while ((c=obj->params.subtitle.utbl[j++])){
x += kerning(sub_font,prevc,c);
if ((font=sub_font->font[c])>=0)
draw_alpha_buf(obj,x,y,
sub_font->width[c],
sub_font->pic_a[font]->h+y<obj->dys ? sub_font->pic_a[font]->h : obj->dys-y,
sub_font->pic_b[font]->bmp+sub_font->start[c],
sub_font->pic_a[font]->bmp+sub_font->start[c],
sub_font->pic_a[font]->w);
x+=sub_font->width[c]+sub_font->charspace;
prevc = c;
}
y+=sub_font->height;
}
}
}
void osd_init_backend(struct osd_state *osd)
{
// check font
#ifdef CONFIG_FREETYPE
init_freetype();
#endif
#ifdef CONFIG_FONTCONFIG
if (font_fontconfig <= 0) {
#endif
#ifdef CONFIG_BITMAP_FONT
if (font_name) {
vo_font = read_font_desc(font_name, font_factor, verbose > 1);
if (!vo_font)
mp_tmsg(MSGT_CPLAYER, MSGL_ERR, "Cannot load bitmap font: %s\n",
filename_recode(font_name));
} else {
// try default:
char *mem_ptr;
vo_font = read_font_desc(mem_ptr = get_path("font/font.desc"),
font_factor, verbose > 1);
free(mem_ptr); // release the buffer created by get_path()
if (!vo_font)
vo_font = read_font_desc(MPLAYER_DATADIR "/font/font.desc",
font_factor, verbose > 1);
}
if (sub_font_name)
osd->sub_font = read_font_desc(sub_font_name, font_factor,
verbose > 1);
else
osd->sub_font = vo_font;
#endif
#ifdef CONFIG_FONTCONFIG
}
#endif
}
void osd_destroy_backend(struct osd_state *osd)
{
#ifdef CONFIG_FREETYPE
current_module = "uninit_font";
if (osd && osd->sub_font != vo_font)
free_font_desc(osd->sub_font);
free_font_desc(vo_font);
vo_font = NULL;
done_freetype();
#endif
}
void osd_font_invalidate(void)
{
#ifdef CONFIG_FREETYPE
force_load_font = 1;
#endif
}
void osd_font_load(struct osd_state *osd)
{
#ifdef CONFIG_FREETYPE
static int defer_counter = 0, prev_dxs = 0, prev_dys = 0;
#endif
#ifdef CONFIG_FREETYPE
// here is the right place to get screen dimensions
int dxs = osd->w, dys = osd->h;
if (((dxs != vo_image_width)
&& (subtitle_autoscale == 2 || subtitle_autoscale == 3))
|| ((dys != vo_image_height)
&& (subtitle_autoscale == 1 || subtitle_autoscale == 3)))
{
// screen dimensions changed
// wait a while to avoid useless reloading of the font
if (dxs == prev_dxs || dys == prev_dys) {
defer_counter++;
} else {
prev_dxs = dxs;
prev_dys = dys;
defer_counter = 0;
}
if (defer_counter >= FONT_LOAD_DEFER) force_load_font = 1;
}
if (force_load_font) {
force_load_font = 0;
load_font_ft(dxs, dys, &vo_font, font_name, osd_font_scale_factor);
if (sub_font_name)
load_font_ft(dxs, dys, &osd->sub_font, sub_font_name, text_font_scale_factor);
else
load_font_ft(dxs, dys, &osd->sub_font, font_name, text_font_scale_factor);
prev_dxs = dxs;
prev_dys = dys;
defer_counter = 0;
} else {
if (!vo_font)
load_font_ft(dxs, dys, &vo_font, font_name, osd_font_scale_factor);
if (!osd->sub_font) {
if (sub_font_name)
load_font_ft(dxs, dys, &osd->sub_font, sub_font_name, text_font_scale_factor);
else
load_font_ft(dxs, dys, &osd->sub_font, font_name, text_font_scale_factor);
}
}
#endif
}
void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function)
{
snprintf(buffer, buffer_size, "%c", osd_function);
}

375
sub/osd_libass.c Normal file
View File

@ -0,0 +1,375 @@
/*
* This file is part of mplayer2.
*
* mplayer2 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mplayer2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mplayer2. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "config.h"
#include "talloc.h"
#include "mp_msg.h"
#include "sub.h"
#include "libavutil/common.h"
#include "sub/osd_font.h"
#include "sub/ass_mp.h"
#include "mp_core.h"
int font_fontconfig = 1;
// Map OSD symbols (e.g. OSD_PLAY) to the glyphs in osd_font_pfb[].
#define OSD_CODEPOINTS 0xE000
// NOTE: \fs-5 to reduce the size of the symbols in relation to normal text.
// Done because libass doesn't center characters that are too high.
#define ASS_USE_OSD_FONT "{\\fnOSD\\fs-5}"
void osd_init_backend(struct osd_state *osd)
{
ass_add_font(osd->ass_library, "OSD", (void *)osd_font_pfb,
sizeof(osd_font_pfb));
osd->osd_render = ass_renderer_init(osd->ass_library);
mp_ass_configure_fonts(osd->osd_render);
ass_set_aspect_ratio(osd->osd_render, 1.0, 1.0);
}
void osd_destroy_backend(struct osd_state *osd)
{
if (osd && osd->osd_render) {
ass_renderer_done(osd->osd_render);
osd->osd_render = NULL;
}
}
static void eosd_draw_alpha_a8i8(unsigned char *src,
int src_w, int src_h,
int src_stride,
unsigned char *dst_a,
unsigned char *dst_i,
size_t dst_stride,
int dst_x, int dst_y,
uint32_t color)
{
const unsigned int r = (color >> 24) & 0xff;
const unsigned int g = (color >> 16) & 0xff;
const unsigned int b = (color >> 8) & 0xff;
const unsigned int a = 0xff - (color & 0xff);
int gray = (r + g + b) / 3; // not correct
dst_a += dst_y * dst_stride + dst_x;
dst_i += dst_y * dst_stride + dst_x;
int src_skip = src_stride - src_w;
int dst_skip = dst_stride - src_w;
for (int y = 0; y < src_h; y++) {
for (int x = 0; x < src_w; x++) {
unsigned char as = (*src * a) >> 8;
unsigned char bs = (gray * as) >> 8;
// to mplayer scale
as = -as;
unsigned char *a = dst_a;
unsigned char *b = dst_i;
// NOTE: many special cases, because alpha=0 means transparency,
// while alpha=1..255 is opaque..transparent
if (as) {
*b = ((*b * as) >> 8) + bs;
if (*a) {
*a = (*a * as) >> 8;
if (*a < 1)
*a = 1;
} else {
*a = as;
}
}
dst_a++;
dst_i++;
src++;
}
dst_a += dst_skip;
dst_i += dst_skip;
src += src_skip;
}
}
static void eosd_render_a8i8(unsigned char *a, unsigned char *i, size_t stride,
int x, int y, ASS_Image *imgs)
{
for (ASS_Image *p = imgs; p; p = p->next) {
eosd_draw_alpha_a8i8(p->bitmap, p->w, p->h, p->stride, a, i, stride,
x + p->dst_x, y + p->dst_y, p->color);
}
}
static bool ass_bb(ASS_Image *imgs, int *x1, int *y1, int *x2, int *y2)
{
*x1 = *y1 = INT_MAX;
*x2 = *y2 = INT_MIN;
for (ASS_Image *p = imgs; p; p = p->next) {
*x1 = FFMIN(*x1, p->dst_x);
*y1 = FFMIN(*y1, p->dst_y);
*x2 = FFMAX(*x2, p->dst_x + p->w);
*y2 = FFMAX(*y2, p->dst_y + p->h);
}
return *x1 < *x2 && *y1 < *y2;
}
static void draw_ass_osd(struct osd_state *osd, mp_osd_obj_t *obj)
{
ass_set_frame_size(osd->osd_render, osd->w, osd->h);
ASS_Image *imgs = ass_render_frame(osd->osd_render, obj->osd_track, 0,
NULL);
int x1, y1, x2, y2;
ass_bb(imgs, &x1, &y1, &x2, &y2);
obj->bbox.x1 = x1;
obj->bbox.y1 = y1;
obj->bbox.x2 = x2;
obj->bbox.y2 = y2;
obj->flags |= OSDFLAG_BBOX;
osd_alloc_buf(obj);
eosd_render_a8i8(obj->alpha_buffer, obj->bitmap_buffer, obj->stride,
-x1, -y1, imgs);
}
static void update_font_scale(ASS_Track *track, ASS_Style *style, double factor)
{
// duplicated from ass_mp.c
double fs = track->PlayResY * factor / 100.;
/* The font size is always proportional to video height only;
* real -subfont-autoscale behavior is not implemented.
* Apply a correction that corresponds to about 4:3 aspect ratio
* video to get a size somewhat closer to what non-libass rendering
* would produce with the same text_font_scale_factor
* and subtitle_autoscale.
*/
if (subtitle_autoscale == 2)
fs *= 1.3;
else if (subtitle_autoscale == 3)
fs *= 1.7;
style->FontSize = fs;
style->Outline = style->FontSize / 16;
}
static ASS_Track *create_osd_ass_track(struct osd_state *osd)
{
ASS_Track *track = mp_ass_default_track(osd->ass_library, osd->opts);
ASS_Style *style = track->styles + track->default_style;
track->PlayResX = track->PlayResY * 1.33333;
update_font_scale(track, style, text_font_scale_factor);
style->Alignment = 5;
free(style->FontName);
style->FontName = strdup(font_name ? font_name : "Sans");
return track;
}
// xxx extremely evil hack to get rid of '[ass] Event height has changed'
static void reset_ass_event(ASS_Event *event)
{
free(event->render_priv);
event->render_priv = NULL;
}
static ASS_Event *get_osd_ass_event(ASS_Track *track)
{
if (!track->n_events)
ass_alloc_event(track);
ASS_Event *event = track->events + 0;
event->Start = 0;
event->Duration = 100;
event->Style = track->default_style;
reset_ass_event(event);
return event;
}
static char *append_utf8_buffer(char *buffer, uint32_t codepoint)
{
char data[8];
uint8_t tmp;
char *output = data;
PUT_UTF8(codepoint, tmp, *output++ = tmp;);
return talloc_strndup_append_buffer(buffer, data, output - data);
}
void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function)
{
// 0xFF is never valid UTF-8, so we can use it to escape OSD symbols.
snprintf(buffer, buffer_size, "\xFF%c", osd_function);
}
static char *mangle_ass(const char *in)
{
char *res = talloc_strdup(NULL, "");
while (*in) {
if (in[0] == '\\' && strchr("nNh{}", in[1])) {
// Undo escaping, e.g. \{ -> \\{
// Note that e.g. \\j still must be emitted as \\j
// (libass only understands the escapes listed in the strchr args)
res = talloc_asprintf_append_buffer(res, "\\\\%c", in[1]);
in += 2;
continue;
}
// As used by osd_get_function_sym().
if (in[0] == '\xFF') {
res = talloc_strdup_append_buffer(res, ASS_USE_OSD_FONT);
res = append_utf8_buffer(res, OSD_CODEPOINTS + in[1]);
res = talloc_strdup_append_buffer(res, "{\\r}");
in += 2;
continue;
}
if (*in == '{')
res = talloc_strdup_append_buffer(res, "\\");
res = talloc_strndup_append_buffer(res, in, 1);
in++;
}
return res;
}
void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t* obj)
{
if (!obj->osd_track)
obj->osd_track = create_osd_ass_track(osd);
ASS_Event *event = get_osd_ass_event(obj->osd_track);
event->Text = mangle_ass(osd->osd_text);
draw_ass_osd(osd, obj);
talloc_free(event->Text);
event->Text = NULL;
}
#define OSDBAR_ELEMS 46
void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t* obj)
{
obj->flags |= OSDFLAG_CHANGED | OSDFLAG_VISIBLE;
if (vo_osd_progbar_type < 0) {
obj->flags &= ~OSDFLAG_VISIBLE;
return;
}
if (!obj->osd_track)
obj->osd_track = create_osd_ass_track(osd);
ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
style->Alignment = 10;
style->MarginL = style->MarginR = style->MarginV = 0;
// We need a fixed font size with respect to the OSD width.
// Assume the OSD bar takes 2/3 of the OSD width at PlayResY=288 and
// FontSize=22 with an OSD aspect ratio of 16:9. Rescale as needed.
// xxx can fail when unknown fonts are involved
double asp = (double)osd->w / osd->h;
double scale = (asp / 1.77777) * (obj->osd_track->PlayResY / 288.0);
style->ScaleX = style->ScaleY = scale;
style->FontSize = 22.0;
style->Outline = style->FontSize / 16 * scale;
int active = (vo_osd_progbar_value * OSDBAR_ELEMS + 255) / 256;
active = FFMIN(OSDBAR_ELEMS, FFMAX(active, 0));
char *text = talloc_strdup(NULL, "{\\q2}");
if (vo_osd_progbar_type >= 32) {
text = append_utf8_buffer(text, vo_osd_progbar_type);
} else if (vo_osd_progbar_type > 0) {
text = talloc_strdup_append_buffer(text, ASS_USE_OSD_FONT);
text = append_utf8_buffer(text, OSD_CODEPOINTS + vo_osd_progbar_type);
text = talloc_strdup_append_buffer(text, "{\\r}");
}
//xxx space in normal font, because OSD font doesn't have a space
text = talloc_strdup_append_buffer(text, "\\h");
text = talloc_strdup_append_buffer(text, ASS_USE_OSD_FONT);
text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_START);
for (int n = 0; n < active; n++)
text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_0);
for (int n = 0; n < OSDBAR_ELEMS - active; n++)
text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_1);
text = append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_END);
ASS_Event *event = get_osd_ass_event(obj->osd_track);
event->Text = text;
draw_ass_osd(osd, obj);
event->Text = NULL;
talloc_free(text);
}
void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t* obj)
{
obj->flags |= OSDFLAG_CHANGED | OSDFLAG_VISIBLE;
if (!vo_sub || !sub_visibility) {
obj->flags &= ~OSDFLAG_VISIBLE;
return;
}
if (!obj->osd_track)
obj->osd_track = mp_ass_default_track(osd->ass_library, osd->opts);
ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
style->MarginV = obj->osd_track->PlayResY * ((100 - sub_pos)/110.0);
update_font_scale(obj->osd_track, style, text_font_scale_factor);
char *text = talloc_strdup(NULL, "");
for (int n = 0; n < vo_sub->lines; n++)
text = talloc_asprintf_append_buffer(text, "%s\n", vo_sub->text[n]);
ASS_Event *event = get_osd_ass_event(obj->osd_track);
event->Text = mangle_ass(text);
draw_ass_osd(osd, obj);
talloc_free(event->Text);
event->Text = NULL;
talloc_free(text);
}
// Unimplemented.
void vo_update_text_teletext(struct osd_state *osd, mp_osd_obj_t *obj)
{
obj->flags |= OSDFLAG_CHANGED;
obj->flags &= ~OSDFLAG_VISIBLE;
if (!vo_osd_teletext_page || !vo_osd_teletext_mode)
return;
mp_msg(MSGT_OSD, MSGL_ERR, "OSD: teletext rendering not implemented\n");
}
// unneeded
void osd_font_invalidate(void) {}
void osd_font_load(struct osd_state *osd) {}

985
sub/sub.c

File diff suppressed because it is too large Load Diff

View File

@ -66,19 +66,24 @@ typedef struct mp_osd_obj_s {
int allocated;
unsigned char *alpha_buffer;
unsigned char *bitmap_buffer;
struct ass_track *osd_track;
} mp_osd_obj_t;
struct osd_state {
struct ass_library *ass_library;
// flag to signal reinitialization due to ass-related option changes
bool ass_force_reload;
int w, h;
char *osd_text;
struct ass_renderer *osd_render;
struct font_desc *sub_font;
struct ass_track *ass_track;
double pts;
double sub_offset;
bool ass_track_changed;
bool vsfilter_aspect;
struct MPOpts *opts;
};
#include "subreader.h"
@ -133,6 +138,22 @@ extern int spu_alignment;
extern int spu_aamode;
extern float spu_gaussvar;
extern char *subtitle_font_encoding;
extern float text_font_scale_factor;
extern float osd_font_scale_factor;
extern float subtitle_font_radius;
extern float subtitle_font_thickness;
extern int subtitle_autoscale;
extern char *font_name;
extern char *sub_font_name;
extern float font_factor;
extern float sub_delay;
extern float sub_fps;
extern int font_fontconfig;
extern int sub_justify;
void osd_draw_text(struct osd_state *osd, int dxs, int dys,
void (*draw_alpha)(void *ctx, int x0, int y0, int w, int h,
unsigned char* src, unsigned char *srca,
@ -149,10 +170,11 @@ void osd_draw_text_ext(struct osd_state *osd, int dxs, int dys,
void osd_remove_text(struct osd_state *osd, int dxs, int dys,
void (*remove)(int x0, int y0, int w, int h));
struct osd_state *osd_create(void);
struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib);
void osd_set_text(struct osd_state *osd, const char *text);
int osd_update(struct osd_state *osd, int dxs, int dys);
int vo_osd_changed(int new_value);
void vo_osd_resized(void);
int vo_osd_check_range_update(int,int,int,int);
void osd_free(struct osd_state *osd);
@ -170,4 +192,20 @@ void osd_set_nav_box (uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey);
#define vo_remove_text(...) osd_remove_text(global_osd, __VA_ARGS__)
#endif
// used only by osd_ft.c or osd_libass.c
void osd_alloc_buf(mp_osd_obj_t* obj);
void draw_alpha_buf(mp_osd_obj_t* obj, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride);
void vo_draw_text_from_buffer(mp_osd_obj_t* obj,void (*draw_alpha)(void *ctx, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride), void *ctx);
// defined in osd_ft.c or osd_libass.c
void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t *obj);
void vo_update_text_teletext(struct osd_state *osd, mp_osd_obj_t *obj);
void vo_update_text_progbar(struct osd_state *osd, mp_osd_obj_t *obj);
void vo_update_text_sub(struct osd_state *osd, mp_osd_obj_t *obj);
void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function);
void osd_font_invalidate(void);
void osd_font_load(struct osd_state *osd);
void osd_init_backend(struct osd_state *osd);
void osd_destroy_backend(struct osd_state *osd);
#endif /* MPLAYER_SUB_H */

View File

@ -58,6 +58,8 @@ static int initialized=0;
static int cc_mode=CC_ROLLON;
static int cc_lines=4; ///< number of visible rows in CC roll-up mode, not used in CC roll-on mode
int subcc_enabled = 0;
static void build_char_table(void)
{
int i;

View File

@ -54,6 +54,8 @@ int flip_hebrew = 1; ///flip subtitles using fribidi
int fribidi_flip_commas = 0; ///flip comma when fribidi is used
#endif
int suboverlap_enabled = 1;
// Parameter struct for the format-specific readline functions
struct readline_args {
int utf16;