2 * This file is part of mplayer2.
4 * mplayer2 is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * mplayer2 is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with mplayer2. If not, see <http://www.gnu.org/licenses/>.
28 #include "libavutil/common.h"
30 static const char osd_font_pfb
[] =
31 #include "sub/osd_font.h"
34 #include "sub/ass_mp.h"
38 // Map OSD symbols (e.g. OSD_PLAY) to the glyphs in osd_font_pfb[].
39 #define OSD_CODEPOINTS 0xE000
41 void osd_init_backend(struct osd_state
*osd
)
43 /* The player adds file-specific embedded fonts with ass_add_font()
44 * and clears them later with ass_clear_fonts(). Since these
45 * operations affect the whole library, this would lose the OSD font.
46 * So we use a separate ass_library instance for OSD rendering.
48 osd
->osd_ass_library
= mp_ass_init(osd
->opts
);
49 ass_add_font(osd
->osd_ass_library
, "OSD", (void *)osd_font_pfb
,
50 sizeof(osd_font_pfb
) - 1);
52 osd
->osd_render
= ass_renderer_init(osd
->osd_ass_library
);
53 mp_ass_configure_fonts(osd
->osd_render
);
54 ass_set_aspect_ratio(osd
->osd_render
, 1.0, 1.0);
57 void osd_destroy_backend(struct osd_state
*osd
)
61 ass_renderer_done(osd
->osd_render
);
62 osd
->osd_render
= NULL
;
63 ass_library_done(osd
->osd_ass_library
);
64 osd
->osd_ass_library
= NULL
;
68 static void eosd_draw_alpha_a8i8(unsigned char *src
,
77 const unsigned int r
= (color
>> 24) & 0xff;
78 const unsigned int g
= (color
>> 16) & 0xff;
79 const unsigned int b
= (color
>> 8) & 0xff;
80 const unsigned int a
= 0xff - (color
& 0xff);
82 int gray
= (r
+ g
+ b
) / 3; // not correct
84 dst_a
+= dst_y
* dst_stride
+ dst_x
;
85 dst_i
+= dst_y
* dst_stride
+ dst_x
;
87 for (int y
= 0; y
< src_h
; y
++) {
88 for (int x
= 0; x
< src_w
; x
++) {
89 unsigned char as
= ((*src
) * a
+ 255) >> 8;
90 unsigned char bs
= (gray
* as
+ 255) >> 8;
91 /* In the old mplayer OSD format, alpha=0 means transparency,
92 * but alpha=1..255 is opaque..transparent. */
93 *dst_i
= (*dst_i
* (255-as
) >> 8) + bs
;
94 unsigned char old_transparency
= *dst_a
- 1;
95 *dst_a
= (old_transparency
* (255-as
) + 255 >> 8) + 1;
101 dst_a
+= dst_stride
- src_w
;
102 dst_i
+= dst_stride
- src_w
;
103 src
+= src_stride
- src_w
;
107 static void eosd_render_a8i8(unsigned char *a
, unsigned char *i
, size_t stride
,
108 int x
, int y
, ASS_Image
*imgs
)
110 for (ASS_Image
*p
= imgs
; p
; p
= p
->next
) {
111 eosd_draw_alpha_a8i8(p
->bitmap
, p
->w
, p
->h
, p
->stride
, a
, i
, stride
,
112 x
+ p
->dst_x
, y
+ p
->dst_y
, p
->color
);
116 static bool ass_bb(ASS_Image
*imgs
, int *x1
, int *y1
, int *x2
, int *y2
)
120 for (ASS_Image
*p
= imgs
; p
; p
= p
->next
) {
121 *x1
= FFMIN(*x1
, p
->dst_x
);
122 *y1
= FFMIN(*y1
, p
->dst_y
);
123 *x2
= FFMAX(*x2
, p
->dst_x
+ p
->w
);
124 *y2
= FFMAX(*y2
, p
->dst_y
+ p
->h
);
126 return *x1
< *x2
&& *y1
< *y2
;
129 static void draw_ass_osd(struct osd_state
*osd
, mp_osd_obj_t
*obj
)
131 ass_set_frame_size(osd
->osd_render
, osd
->w
, osd
->h
);
133 ASS_Image
*imgs
= ass_render_frame(osd
->osd_render
, obj
->osd_track
, 0,
137 if (!ass_bb(imgs
, &x1
, &y1
, &x2
, &y2
)) {
138 obj
->flags
&= ~OSDFLAG_VISIBLE
;
146 obj
->flags
|= OSDFLAG_BBOX
;
149 eosd_render_a8i8(obj
->alpha_buffer
, obj
->bitmap_buffer
, obj
->stride
,
154 static void update_font_scale(ASS_Track
*track
, ASS_Style
*style
, double factor
)
156 // duplicated from ass_mp.c
157 double fs
= track
->PlayResY
* factor
/ 100.;
158 /* The font size is always proportional to video height only;
159 * real -subfont-autoscale behavior is not implemented.
160 * Apply a correction that corresponds to about 4:3 aspect ratio
161 * video to get a size somewhat closer to what non-libass rendering
162 * would produce with the same text_font_scale_factor
163 * and subtitle_autoscale.
165 if (subtitle_autoscale
== 2)
167 else if (subtitle_autoscale
== 3)
169 style
->FontSize
= fs
;
170 style
->Outline
= style
->FontSize
/ 16;
174 static ASS_Track
*create_osd_ass_track(struct osd_state
*osd
)
176 ASS_Track
*track
= mp_ass_default_track(osd
->osd_ass_library
, osd
->opts
);
177 ASS_Style
*style
= track
->styles
+ track
->default_style
;
179 track
->PlayResX
= track
->PlayResY
* 1.33333;
181 update_font_scale(track
, style
, osd_font_scale_factor
);
183 style
->Alignment
= 5;
185 free(style
->FontName
);
186 style
->FontName
= strdup(font_name
? font_name
: "Sans");
191 static ASS_Event
*get_osd_ass_event(ASS_Track
*track
)
193 ass_flush_events(track
);
194 ass_alloc_event(track
);
195 ASS_Event
*event
= track
->events
+ 0;
197 event
->Duration
= 100;
198 event
->Style
= track
->default_style
;
202 static char *append_utf8_buffer(char *buffer
, uint32_t codepoint
)
207 PUT_UTF8(codepoint
, tmp
, *output
++ = tmp
;);
208 return talloc_strndup_append_buffer(buffer
, data
, output
- data
);
211 void osd_get_function_sym(char *buffer
, size_t buffer_size
, int osd_function
)
213 // 0xFF is never valid UTF-8, so we can use it to escape OSD symbols.
214 snprintf(buffer
, buffer_size
, "\xFF%c", osd_function
);
217 static char *mangle_ass(const char *in
)
219 char *res
= talloc_strdup(NULL
, "");
221 // As used by osd_get_function_sym().
222 if (in
[0] == '\xFF' && in
[1]) {
223 /* The special play/pause etc symbols are taller than normal
224 * letters, and do not look very good when libass renders them
225 * with the base of the letters at the same level. Scale
226 * the symbols down to 70% size so they're closer to the
227 * letter sizes in usual fonts. */
228 res
= talloc_strdup_append_buffer(res
, "{\\fnOSD\\fscx70\\fscy70}");
229 res
= append_utf8_buffer(res
, OSD_CODEPOINTS
+ in
[1]);
230 res
= talloc_strdup_append_buffer(res
, "{\\r}");
235 res
= talloc_strdup_append_buffer(res
, "\\");
236 res
= talloc_strndup_append_buffer(res
, in
, 1);
237 /* Libass has a couple of escapes like "\n" and "\{", but only
238 * those specific cases are recognized, and there is no escape
239 * for "\" itself. This makes it impossible to quote strings
240 * like "\n"; for example, "\\n" would be interpreted as a
241 * literal "\" followed by "\n" escape for linefeed. Work around
242 * this by adding the ZERO WIDTH NO-BREAK SPACE character after
243 * each "\". This ensures it will not be interpreted as an escape
244 * and should not change visible output.
247 res
= append_utf8_buffer(res
, 0xfeff);
253 void vo_update_text_osd(struct osd_state
*osd
, mp_osd_obj_t
* obj
)
256 obj
->osd_track
= create_osd_ass_track(osd
);
257 ASS_Event
*event
= get_osd_ass_event(obj
->osd_track
);
258 event
->Text
= mangle_ass(osd
->osd_text
);
259 draw_ass_osd(osd
, obj
);
260 talloc_free(event
->Text
);
264 #define OSDBAR_ELEMS 46
266 void vo_update_text_progbar(struct osd_state
*osd
, mp_osd_obj_t
* obj
)
268 obj
->flags
|= OSDFLAG_CHANGED
| OSDFLAG_VISIBLE
;
270 if (vo_osd_progbar_type
< 0) {
271 obj
->flags
&= ~OSDFLAG_VISIBLE
;
276 obj
->osd_track
= create_osd_ass_track(osd
);
278 ASS_Style
*style
= obj
->osd_track
->styles
+ obj
->osd_track
->default_style
;
280 style
->Alignment
= 10;
281 style
->MarginL
= style
->MarginR
= style
->MarginV
= 0;
283 // We need a fixed font size with respect to the OSD width.
284 // Assume the OSD bar takes 2/3 of the OSD width at PlayResY=288 and
285 // FontSize=17 with an OSD aspect ratio of 16:9. Rescale as needed.
286 // xxx can fail when unknown fonts are involved
287 double asp
= (double)osd
->w
/ osd
->h
;
288 double scale
= (asp
/ 1.77777) * (obj
->osd_track
->PlayResY
/ 288.0);
289 style
->ScaleX
= style
->ScaleY
= scale
;
290 style
->FontSize
= 17.0;
291 style
->Outline
= style
->FontSize
/ 16 * scale
;
293 int active
= (vo_osd_progbar_value
* OSDBAR_ELEMS
+ 255) / 256;
294 active
= FFMIN(OSDBAR_ELEMS
, FFMAX(active
, 0));
296 char *text
= talloc_strdup(NULL
, "{\\q2}");
298 if (vo_osd_progbar_type
>= 32) {
299 text
= append_utf8_buffer(text
, vo_osd_progbar_type
);
300 } else if (vo_osd_progbar_type
> 0) {
301 text
= talloc_strdup_append_buffer(text
, "{\\fnOSD}");
302 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ vo_osd_progbar_type
);
303 text
= talloc_strdup_append_buffer(text
, "{\\r}");
306 //xxx space in normal font, because OSD font doesn't have a space
307 text
= talloc_strdup_append_buffer(text
, "\\h");
308 text
= talloc_strdup_append_buffer(text
, "{\\fnOSD}");
310 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ OSD_PB_START
);
311 for (int n
= 0; n
< active
; n
++)
312 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ OSD_PB_0
);
313 for (int n
= 0; n
< OSDBAR_ELEMS
- active
; n
++)
314 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ OSD_PB_1
);
315 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ OSD_PB_END
);
317 ASS_Event
*event
= get_osd_ass_event(obj
->osd_track
);
319 draw_ass_osd(osd
, obj
);
325 void vo_update_text_sub(struct osd_state
*osd
, mp_osd_obj_t
* obj
)
327 struct MPOpts
*opts
= osd
->opts
;
329 obj
->flags
|= OSDFLAG_CHANGED
| OSDFLAG_VISIBLE
;
331 if (!vo_sub
|| !opts
->sub_visibility
) {
332 obj
->flags
&= ~OSDFLAG_VISIBLE
;
337 obj
->osd_track
= mp_ass_default_track(osd
->osd_ass_library
, osd
->opts
);
339 ASS_Style
*style
= obj
->osd_track
->styles
+ obj
->osd_track
->default_style
;
341 style
->MarginV
= obj
->osd_track
->PlayResY
* ((100 - sub_pos
)/110.0);
342 update_font_scale(obj
->osd_track
, style
, text_font_scale_factor
);
344 char *text
= talloc_strdup(NULL
, "");
346 for (int n
= 0; n
< vo_sub
->lines
; n
++)
347 text
= talloc_asprintf_append_buffer(text
, "%s\n", vo_sub
->text
[n
]);
349 ASS_Event
*event
= get_osd_ass_event(obj
->osd_track
);
350 event
->Text
= mangle_ass(text
);
351 draw_ass_osd(osd
, obj
);
352 talloc_free(event
->Text
);
359 void vo_update_text_teletext(struct osd_state
*osd
, mp_osd_obj_t
*obj
)
361 obj
->flags
|= OSDFLAG_CHANGED
;
362 obj
->flags
&= ~OSDFLAG_VISIBLE
;
363 if (!vo_osd_teletext_page
|| !vo_osd_teletext_mode
)
365 mp_msg(MSGT_OSD
, MSGL_ERR
, "OSD: teletext rendering not implemented\n");