From 74e7a1e937c10d9f4d8ce9b0ba4edee52044a757 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 22 Mar 2012 06:26:37 +0100 Subject: 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. --- sub/ass_mp.c | 15 +- sub/font_load.h | 7 - sub/font_load_ft.c | 13 +- sub/osd_font.h | 2 +- sub/osd_ft.c | 969 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sub/osd_libass.c | 375 ++++++++++++++++++++ sub/sub.c | 985 +++-------------------------------------------------- sub/sub.h | 40 ++- sub/sub_cc.c | 2 + sub/subreader.c | 2 + 10 files changed, 1440 insertions(+), 970 deletions(-) create mode 100644 sub/osd_ft.c create mode 100644 sub/osd_libass.c (limited to 'sub') diff --git a/sub/ass_mp.c b/sub/ass_mp.c index 88c55862f2..3dd743776d 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -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 diff --git a/sub/font_load.h b/sub/font_load.h index 7efe067aaf..933f84804f 100644 --- a/sub/font_load.h +++ b/sub/font_load.h @@ -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; diff --git a/sub/font_load_ft.c b/sub/font_load_ft.c index eb6d70b657..9eb0ab50e8 100644 --- a/sub/font_load_ft.c +++ b/sub/font_load_ft.c @@ -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; diff --git a/sub/osd_font.h b/sub/osd_font.h index 6be45bc1fa..616c27d155 100644 --- a/sub/osd_font.h +++ b/sub/osd_font.h @@ -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, diff --git a/sub/osd_ft.c b/sub/osd_ft.c new file mode 100644 index 0000000000..f44100a3b9 --- /dev/null +++ b/sub/osd_ft.c @@ -0,0 +1,969 @@ +#include +#include +#include + +#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(hpic_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;iwidth[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>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;ifont[tc.unicode])>=0 && y+hmwidth[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+ydys ? 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); +} diff --git a/sub/osd_libass.c b/sub/osd_libass.c new file mode 100644 index 0000000000..4f4d588a3d --- /dev/null +++ b/sub/osd_libass.c @@ -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 . + */ + +#include +#include +#include +#include + +#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) {} diff --git a/sub/sub.c b/sub/sub.c index 744642dfdd..485550a470 100644 --- a/sub/sub.c +++ b/sub/sub.c @@ -36,33 +36,10 @@ #include "mplayer.h" #include "mp_msg.h" #include "libvo/video_out.h" -#include "font_load.h" #include "sub.h" #include "spudec.h" #include "libavutil/common.h" -#define NEW_SPLITTING - - -// 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; -}; -//^ char * const sub_osd_names[]={ _("Seekbar"), @@ -81,14 +58,10 @@ char * const sub_osd_names[]={ }; char * const sub_osd_names_short[] ={ "", "|>", "||", "[]", "<<" , ">>", "", "", "", "", "", "", "" }; -//static int vo_font_loaded=-1; -font_desc_t* vo_font=NULL; - void* vo_osd_teletext_page=NULL; int vo_osd_teletext_half = 0; int vo_osd_teletext_mode=0; int vo_osd_teletext_format=0; -int vo_osd_teletext_scale=0; int sub_unicode=0; int sub_utf8=0; int sub_pos=100; @@ -102,16 +75,28 @@ int sub_justify=0; static nav_highlight_t nav_hl; #endif -// 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(hpic_a[font]->h) h=vo_font->pic_a[font]->h; - return h; -} +int vo_osd_progbar_type=-1; +int vo_osd_progbar_value=100; // 0..256 +subtitle* vo_sub=NULL; +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; + +char *font_name = NULL; +char *sub_font_name = NULL; +float font_factor = 0.75; +float sub_delay = 0; +float sub_fps = 0; // renders char to a big per-object buffer where alpha and bitmap are separated -static 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 draw_alpha_buf(mp_osd_obj_t* obj, int x0,int y0, int w,int h, unsigned char* src, unsigned char *srca, int stride) { int dststride = obj->stride; int dstskip = obj->stride-w; @@ -144,7 +129,7 @@ static void draw_alpha_buf(mp_osd_obj_t* obj, int x0,int y0, int w,int h, unsign } // allocates/enlarges the alpha/bitmap buffer -static void alloc_buf(mp_osd_obj_t* obj) +void osd_alloc_buf(mp_osd_obj_t* obj) { int len; if (obj->bbox.x2 < obj->bbox.x1) obj->bbox.x2 = obj->bbox.x1; @@ -163,7 +148,7 @@ static void alloc_buf(mp_osd_obj_t* obj) } // renders the buffer -inline static 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) +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) { if (obj->allocated > 0) { draw_alpha(ctx, @@ -190,81 +175,6 @@ no_utf8: return c; } -//if obj is NULL, don't render, and just return the metrics -//sw, sh: screen width/height -//out_w/out_h: return width/height of the text box (write-only) -static void vo_update_text_osd_render(const char *cp, mp_osd_obj_t *obj, - int sw, int sh, int *out_w, int *out_h) -{ - int x = 0; - int y = 0; - int line_height = get_height('t', 0); - int w = 0; - int h = line_height; - int font; - - while (*cp && h < sh && line_height > 0) { - const char* prev = cp; - uint16_t c = utf8_get_char(&cp); - int lf = (c == '\n'); - int newx; - render_one_glyph(vo_font, c); - newx = x + vo_font->width[c]; - if (lf || (newx > sw)) { - if (!lf) - cp = prev; - //can't put at least one char per line? - //rare but nasty corner case... simply exit and avoid endless loop - if (!lf && x == 0) - break; - y += h; - h = line_height; - w = FFMAX(x, w); - x = 0; - continue; - } - if (obj && (font = vo_font->font[c]) >= 0) - draw_alpha_buf(obj, obj->x + x, obj->y + 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 = newx + vo_font->charspace; - h = get_height(c, h); - } - - y += h; - w = FFMAX(x, w) - vo_font->charspace; - - *out_w = FFMAX(w, 0); - *out_h = y; -} - -inline static void vo_update_text_osd(struct osd_state *osd, mp_osd_obj_t* obj, - int dxs, int dys) -{ - const char *cp = osd->osd_text; - int w, h, sw, sh; - - obj->bbox.x1 = obj->x = 20; - obj->bbox.y1 = obj->y = 10; - sw = dxs - obj->x; - sh = dys - obj->y; - - //first pass: calculate obj bounding box - vo_update_text_osd_render(cp, NULL, sw, sh, &w, &h); - - obj->bbox.x2 = obj->bbox.x1 + w; - obj->bbox.y2 = obj->bbox.y1 + h; - obj->flags |= OSDFLAG_BBOX; - - alloc_buf(obj); - - //second pass: actually draw the text - vo_update_text_osd_render(cp, obj, sw, sh, &w, &h); -} - #ifdef CONFIG_DVDNAV void osd_set_nav_box (uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey) { nav_hl.sx = sx; @@ -300,7 +210,7 @@ inline static void vo_update_nav (mp_osd_obj_t *obj, int dxs, int dys, int left_ obj->bbox.x2 = ex; obj->bbox.y2 = ey; - alloc_buf (obj); + osd_alloc_buf (obj); len = obj->stride * (obj->bbox.y2 - obj->bbox.y1); memset (obj->bitmap_buffer, OSD_NAV_BOX_ALPHA, len); memset (obj->alpha_buffer, OSD_NAV_BOX_ALPHA, len); @@ -310,776 +220,11 @@ inline static void vo_update_nav (mp_osd_obj_t *obj, int dxs, int dys, int left_ } #endif -// 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; - } -} -inline static void vo_update_text_teletext(mp_osd_obj_t *obj, int dxs, int dys) -{ - 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]; - - 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;iwidth[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>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; - 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;ifont[tc.unicode])>=0 && y+hmwidth[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]); -} - -int vo_osd_progbar_type=-1; -int vo_osd_progbar_value=100; // 0..256 - -// 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 - -inline static void vo_update_text_progbar(mp_osd_obj_t* obj,int dxs,int dys){ - - 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=(dys-vo_