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 #include "sub/osd_font.h"
32 #include "sub/ass_mp.h"
36 // Map OSD symbols (e.g. OSD_PLAY) to the glyphs in osd_font_pfb[].
37 #define OSD_CODEPOINTS 0xE000
39 void osd_init_backend(struct osd_state
*osd
)
41 /* The player adds file-specific embedded fonts with ass_add_font()
42 * and clears them later with ass_clear_fonts(). Since these
43 * operations affect the whole library, this would lose the OSD font.
44 * So we use a separate ass_library instance for OSD rendering.
46 osd
->osd_ass_library
= mp_ass_init(osd
->opts
);
47 ass_add_font(osd
->osd_ass_library
, "OSD", (void *)osd_font_pfb
,
48 sizeof(osd_font_pfb
));
50 osd
->osd_render
= ass_renderer_init(osd
->osd_ass_library
);
51 mp_ass_configure_fonts(osd
->osd_render
);
52 ass_set_aspect_ratio(osd
->osd_render
, 1.0, 1.0);
55 void osd_destroy_backend(struct osd_state
*osd
)
59 ass_renderer_done(osd
->osd_render
);
60 osd
->osd_render
= NULL
;
61 ass_library_done(osd
->osd_ass_library
);
62 osd
->osd_ass_library
= NULL
;
66 static void eosd_draw_alpha_a8i8(unsigned char *src
,
75 const unsigned int r
= (color
>> 24) & 0xff;
76 const unsigned int g
= (color
>> 16) & 0xff;
77 const unsigned int b
= (color
>> 8) & 0xff;
78 const unsigned int a
= 0xff - (color
& 0xff);
80 int gray
= (r
+ g
+ b
) / 3; // not correct
82 dst_a
+= dst_y
* dst_stride
+ dst_x
;
83 dst_i
+= dst_y
* dst_stride
+ dst_x
;
85 for (int y
= 0; y
< src_h
; y
++) {
86 for (int x
= 0; x
< src_w
; x
++) {
87 unsigned char as
= ((*src
) * a
+ 255) >> 8;
88 unsigned char bs
= (gray
* as
+ 255) >> 8;
89 /* In the old mplayer OSD format, alpha=0 means transparency,
90 * but alpha=1..255 is opaque..transparent. */
91 *dst_i
= (*dst_i
* (255-as
) >> 8) + bs
;
92 unsigned char old_transparency
= *dst_a
- 1;
93 *dst_a
= (old_transparency
* (255-as
) + 255 >> 8) + 1;
99 dst_a
+= dst_stride
- src_w
;
100 dst_i
+= dst_stride
- src_w
;
101 src
+= src_stride
- src_w
;
105 static void eosd_render_a8i8(unsigned char *a
, unsigned char *i
, size_t stride
,
106 int x
, int y
, ASS_Image
*imgs
)
108 for (ASS_Image
*p
= imgs
; p
; p
= p
->next
) {
109 eosd_draw_alpha_a8i8(p
->bitmap
, p
->w
, p
->h
, p
->stride
, a
, i
, stride
,
110 x
+ p
->dst_x
, y
+ p
->dst_y
, p
->color
);
114 static bool ass_bb(ASS_Image
*imgs
, int *x1
, int *y1
, int *x2
, int *y2
)
118 for (ASS_Image
*p
= imgs
; p
; p
= p
->next
) {
119 *x1
= FFMIN(*x1
, p
->dst_x
);
120 *y1
= FFMIN(*y1
, p
->dst_y
);
121 *x2
= FFMAX(*x2
, p
->dst_x
+ p
->w
);
122 *y2
= FFMAX(*y2
, p
->dst_y
+ p
->h
);
124 return *x1
< *x2
&& *y1
< *y2
;
127 static void draw_ass_osd(struct osd_state
*osd
, mp_osd_obj_t
*obj
)
129 ass_set_frame_size(osd
->osd_render
, osd
->w
, osd
->h
);
131 ASS_Image
*imgs
= ass_render_frame(osd
->osd_render
, obj
->osd_track
, 0,
135 if (!ass_bb(imgs
, &x1
, &y1
, &x2
, &y2
)) {
136 obj
->flags
&= ~OSDFLAG_VISIBLE
;
144 obj
->flags
|= OSDFLAG_BBOX
;
147 eosd_render_a8i8(obj
->alpha_buffer
, obj
->bitmap_buffer
, obj
->stride
,
152 static void update_font_scale(ASS_Track
*track
, ASS_Style
*style
, double factor
)
154 // duplicated from ass_mp.c
155 double fs
= track
->PlayResY
* factor
/ 100.;
156 /* The font size is always proportional to video height only;
157 * real -subfont-autoscale behavior is not implemented.
158 * Apply a correction that corresponds to about 4:3 aspect ratio
159 * video to get a size somewhat closer to what non-libass rendering
160 * would produce with the same text_font_scale_factor
161 * and subtitle_autoscale.
163 if (subtitle_autoscale
== 2)
165 else if (subtitle_autoscale
== 3)
167 style
->FontSize
= fs
;
168 style
->Outline
= style
->FontSize
/ 16;
172 static ASS_Track
*create_osd_ass_track(struct osd_state
*osd
)
174 ASS_Track
*track
= mp_ass_default_track(osd
->osd_ass_library
, osd
->opts
);
175 ASS_Style
*style
= track
->styles
+ track
->default_style
;
177 track
->PlayResX
= track
->PlayResY
* 1.33333;
179 update_font_scale(track
, style
, osd_font_scale_factor
);
181 style
->Alignment
= 5;
183 free(style
->FontName
);
184 style
->FontName
= strdup(font_name
? font_name
: "Sans");
189 static ASS_Event
*get_osd_ass_event(ASS_Track
*track
)
191 ass_flush_events(track
);
192 ass_alloc_event(track
);
193 ASS_Event
*event
= track
->events
+ 0;
195 event
->Duration
= 100;
196 event
->Style
= track
->default_style
;
200 static char *append_utf8_buffer(char *buffer
, uint32_t codepoint
)
205 PUT_UTF8(codepoint
, tmp
, *output
++ = tmp
;);
206 return talloc_strndup_append_buffer(buffer
, data
, output
- data
);
209 void osd_get_function_sym(char *buffer
, size_t buffer_size
, int osd_function
)
211 // 0xFF is never valid UTF-8, so we can use it to escape OSD symbols.
212 snprintf(buffer
, buffer_size
, "\xFF%c", osd_function
);
215 static char *mangle_ass(const char *in
)
217 char *res
= talloc_strdup(NULL
, "");
219 // As used by osd_get_function_sym().
220 if (in
[0] == '\xFF' && in
[1]) {
221 /* The special play/pause etc symbols are taller than normal
222 * letters, and do not look very good when libass renders them
223 * with the base of the letters at the same level. Scale
224 * the symbols down to 70% size so they're closer to the
225 * letter sizes in usual fonts. */
226 res
= talloc_strdup_append_buffer(res
, "{\\fnOSD\\fscx70\\fscy70}");
227 res
= append_utf8_buffer(res
, OSD_CODEPOINTS
+ in
[1]);
228 res
= talloc_strdup_append_buffer(res
, "{\\r}");
233 res
= talloc_strdup_append_buffer(res
, "\\");
234 res
= talloc_strndup_append_buffer(res
, in
, 1);
235 /* Libass has a couple of escapes like "\n" and "\{", but only
236 * those specific cases are recognized, and there is no escape
237 * for "\" itself. This makes it impossible to quote strings
238 * like "\n"; for example, "\\n" would be interpreted as a
239 * literal "\" followed by "\n" escape for linefeed. Work around
240 * this by adding the ZERO WIDTH NO-BREAK SPACE character after
241 * each "\". This ensures it will not be interpreted as an escape
242 * and should not change visible output.
245 res
= append_utf8_buffer(res
, 0xfeff);
251 void vo_update_text_osd(struct osd_state
*osd
, mp_osd_obj_t
* obj
)
254 obj
->osd_track
= create_osd_ass_track(osd
);
255 ASS_Event
*event
= get_osd_ass_event(obj
->osd_track
);
256 event
->Text
= mangle_ass(osd
->osd_text
);
257 draw_ass_osd(osd
, obj
);
258 talloc_free(event
->Text
);
262 #define OSDBAR_ELEMS 46
264 void vo_update_text_progbar(struct osd_state
*osd
, mp_osd_obj_t
* obj
)
266 obj
->flags
|= OSDFLAG_CHANGED
| OSDFLAG_VISIBLE
;
268 if (vo_osd_progbar_type
< 0) {
269 obj
->flags
&= ~OSDFLAG_VISIBLE
;
274 obj
->osd_track
= create_osd_ass_track(osd
);
276 ASS_Style
*style
= obj
->osd_track
->styles
+ obj
->osd_track
->default_style
;
278 style
->Alignment
= 10;
279 style
->MarginL
= style
->MarginR
= style
->MarginV
= 0;
281 // We need a fixed font size with respect to the OSD width.
282 // Assume the OSD bar takes 2/3 of the OSD width at PlayResY=288 and
283 // FontSize=17 with an OSD aspect ratio of 16:9. Rescale as needed.
284 // xxx can fail when unknown fonts are involved
285 double asp
= (double)osd
->w
/ osd
->h
;
286 double scale
= (asp
/ 1.77777) * (obj
->osd_track
->PlayResY
/ 288.0);
287 style
->ScaleX
= style
->ScaleY
= scale
;
288 style
->FontSize
= 17.0;
289 style
->Outline
= style
->FontSize
/ 16 * scale
;
291 int active
= (vo_osd_progbar_value
* OSDBAR_ELEMS
+ 255) / 256;
292 active
= FFMIN(OSDBAR_ELEMS
, FFMAX(active
, 0));
294 char *text
= talloc_strdup(NULL
, "{\\q2}");
296 if (vo_osd_progbar_type
>= 32) {
297 text
= append_utf8_buffer(text
, vo_osd_progbar_type
);
298 } else if (vo_osd_progbar_type
> 0) {
299 text
= talloc_strdup_append_buffer(text
, "{\\fnOSD}");
300 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ vo_osd_progbar_type
);
301 text
= talloc_strdup_append_buffer(text
, "{\\r}");
304 //xxx space in normal font, because OSD font doesn't have a space
305 text
= talloc_strdup_append_buffer(text
, "\\h");
306 text
= talloc_strdup_append_buffer(text
, "{\\fnOSD}");
308 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ OSD_PB_START
);
309 for (int n
= 0; n
< active
; n
++)
310 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ OSD_PB_0
);
311 for (int n
= 0; n
< OSDBAR_ELEMS
- active
; n
++)
312 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ OSD_PB_1
);
313 text
= append_utf8_buffer(text
, OSD_CODEPOINTS
+ OSD_PB_END
);
315 ASS_Event
*event
= get_osd_ass_event(obj
->osd_track
);
317 draw_ass_osd(osd
, obj
);
323 void vo_update_text_sub(struct osd_state
*osd
, mp_osd_obj_t
* obj
)
325 obj
->flags
|= OSDFLAG_CHANGED
| OSDFLAG_VISIBLE
;
327 if (!vo_sub
|| !sub_visibility
) {
328 obj
->flags
&= ~OSDFLAG_VISIBLE
;
333 obj
->osd_track
= mp_ass_default_track(osd
->osd_ass_library
, osd
->opts
);
335 ASS_Style
*style
= obj
->osd_track
->styles
+ obj
->osd_track
->default_style
;
337 style
->MarginV
= obj
->osd_track
->PlayResY
* ((100 - sub_pos
)/110.0);
338 update_font_scale(obj
->osd_track
, style
, text_font_scale_factor
);
340 char *text
= talloc_strdup(NULL
, "");
342 for (int n
= 0; n
< vo_sub
->lines
; n
++)
343 text
= talloc_asprintf_append_buffer(text
, "%s\n", vo_sub
->text
[n
]);
345 ASS_Event
*event
= get_osd_ass_event(obj
->osd_track
);
346 event
->Text
= mangle_ass(text
);
347 draw_ass_osd(osd
, obj
);
348 talloc_free(event
->Text
);
355 void vo_update_text_teletext(struct osd_state
*osd
, mp_osd_obj_t
*obj
)
357 obj
->flags
|= OSDFLAG_CHANGED
;
358 obj
->flags
&= ~OSDFLAG_VISIBLE
;
359 if (!vo_osd_teletext_page
|| !vo_osd_teletext_mode
)
361 mp_msg(MSGT_OSD
, MSGL_ERR
, "OSD: teletext rendering not implemented\n");