1 // -*- c-basic-offset: 8; indent-tabs-mode: t -*-
2 // vim:ts=8:sw=8:noet:ai:
4 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
6 * This file is part of MPlayer.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 #include "img_format.h"
40 #include "libvo/fastmemcpy.h"
45 #include "libass/ass_mp.h"
47 #define _r(c) ((c)>>24)
48 #define _g(c) (((c)>>16)&0xFF)
49 #define _b(c) (((c)>>8)&0xFF)
50 #define _a(c) ((c)&0xFF)
51 #define rgba2y(c) ( (( 263*_r(c) + 516*_g(c) + 100*_b(c)) >> 10) + 16 )
52 #define rgba2u(c) ( ((-152*_r(c) - 298*_g(c) + 450*_b(c)) >> 10) + 128 )
53 #define rgba2v(c) ( (( 450*_r(c) - 376*_g(c) - 73*_b(c)) >> 10) + 128 )
56 static const struct vf_priv_s
{
61 // 1 = auto-added filter: insert only if chain does not support EOSD already
65 ass_renderer_t
* ass_priv
;
67 unsigned char* planes
[3];
68 unsigned char* dirty_rows
;
71 extern ass_track_t
* ass_track
;
72 extern float sub_delay
;
73 extern int sub_visibility
;
75 static int config(struct vf_instance
*vf
,
76 int width
, int height
, int d_width
, int d_height
,
77 unsigned int flags
, unsigned int outfmt
)
79 if (outfmt
== IMGFMT_IF09
) return 0;
81 vf
->priv
->outh
= height
+ ass_top_margin
+ ass_bottom_margin
;
82 vf
->priv
->outw
= width
;
84 if(!opt_screen_size_x
&& !opt_screen_size_y
){
85 d_width
= d_width
* vf
->priv
->outw
/ width
;
86 d_height
= d_height
* vf
->priv
->outh
/ height
;
89 vf
->priv
->planes
[1] = malloc(vf
->priv
->outw
* vf
->priv
->outh
);
90 vf
->priv
->planes
[2] = malloc(vf
->priv
->outw
* vf
->priv
->outh
);
91 vf
->priv
->dirty_rows
= malloc(vf
->priv
->outh
);
93 if (vf
->priv
->ass_priv
) {
94 ass_configure(vf
->priv
->ass_priv
, vf
->priv
->outw
, vf
->priv
->outh
, 0);
95 #if defined(LIBASS_VERSION) && LIBASS_VERSION >= 0x00908000
96 ass_set_aspect_ratio(vf
->priv
->ass_priv
, 1, 1);
98 ass_set_aspect_ratio(vf
->priv
->ass_priv
, 1);
102 return vf_next_config(vf
, vf
->priv
->outw
, vf
->priv
->outh
, d_width
, d_height
, flags
, outfmt
);
105 static void get_image(struct vf_instance
*vf
, mp_image_t
*mpi
)
107 if(mpi
->type
== MP_IMGTYPE_IPB
) return;
108 if(mpi
->flags
& MP_IMGFLAG_PRESERVE
) return;
109 if(mpi
->imgfmt
!= vf
->priv
->outfmt
) return; // colorspace differ
111 // width never changes, always try full DR
112 mpi
->priv
= vf
->dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
,
113 mpi
->type
, mpi
->flags
| MP_IMGFLAG_READABLE
,
117 if((vf
->dmpi
->flags
& MP_IMGFLAG_DRAW_CALLBACK
) &&
118 !(vf
->dmpi
->flags
& MP_IMGFLAG_DIRECT
)){
119 mp_msg(MSGT_ASS
, MSGL_INFO
, MSGTR_MPCODECS_FullDRNotPossible
);
123 // set up mpi as a cropped-down image of dmpi:
124 if(mpi
->flags
&MP_IMGFLAG_PLANAR
){
125 mpi
->planes
[0]=vf
->dmpi
->planes
[0] + ass_top_margin
* vf
->dmpi
->stride
[0];
126 mpi
->planes
[1]=vf
->dmpi
->planes
[1] + (ass_top_margin
>> mpi
->chroma_y_shift
) * vf
->dmpi
->stride
[1];
127 mpi
->planes
[2]=vf
->dmpi
->planes
[2] + (ass_top_margin
>> mpi
->chroma_y_shift
) * vf
->dmpi
->stride
[2];
128 mpi
->stride
[1]=vf
->dmpi
->stride
[1];
129 mpi
->stride
[2]=vf
->dmpi
->stride
[2];
131 mpi
->planes
[0]=vf
->dmpi
->planes
[0] + ass_top_margin
* vf
->dmpi
->stride
[0];
133 mpi
->stride
[0]=vf
->dmpi
->stride
[0];
134 mpi
->width
=vf
->dmpi
->width
;
135 mpi
->flags
|=MP_IMGFLAG_DIRECT
;
136 mpi
->flags
&=~MP_IMGFLAG_DRAW_CALLBACK
;
137 // vf->dmpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK;
140 static void blank(mp_image_t
*mpi
, int y1
, int y2
)
142 int color
[3] = {16, 128, 128}; // black (YUV)
145 int chroma_rows
= (y2
- y1
) >> mpi
->chroma_y_shift
;
147 dst
= mpi
->planes
[0] + y1
* mpi
->stride
[0];
148 for (y
= 0; y
< y2
- y1
; ++y
) {
149 memset(dst
, color
[0], mpi
->w
);
150 dst
+= mpi
->stride
[0];
152 dst
= mpi
->planes
[1] + (y1
>> mpi
->chroma_y_shift
) * mpi
->stride
[1];
153 for (y
= 0; y
< chroma_rows
; ++y
) {
154 memset(dst
, color
[1], mpi
->chroma_width
);
155 dst
+= mpi
->stride
[1];
157 dst
= mpi
->planes
[2] + (y1
>> mpi
->chroma_y_shift
) * mpi
->stride
[2];
158 for (y
= 0; y
< chroma_rows
; ++y
) {
159 memset(dst
, color
[2], mpi
->chroma_width
);
160 dst
+= mpi
->stride
[2];
164 static int prepare_image(struct vf_instance
*vf
, mp_image_t
*mpi
)
166 if(mpi
->flags
&MP_IMGFLAG_DIRECT
|| mpi
->flags
&MP_IMGFLAG_DRAW_CALLBACK
){
167 vf
->dmpi
= mpi
->priv
;
168 if (!vf
->dmpi
) { mp_msg(MSGT_ASS
, MSGL_WARN
, MSGTR_MPCODECS_FunWhydowegetNULL
); return 0; }
170 // we've used DR, so we're ready...
172 blank(vf
->dmpi
, 0, ass_top_margin
);
173 if (ass_bottom_margin
)
174 blank(vf
->dmpi
, vf
->priv
->outh
- ass_bottom_margin
, vf
->priv
->outh
);
175 if(!(mpi
->flags
&MP_IMGFLAG_PLANAR
))
176 vf
->dmpi
->planes
[1] = mpi
->planes
[1]; // passthrough rgb8 palette
180 // hope we'll get DR buffer:
181 vf
->dmpi
= vf_get_image(vf
->next
, vf
->priv
->outfmt
,
182 MP_IMGTYPE_TEMP
, MP_IMGFLAG_ACCEPT_STRIDE
| MP_IMGFLAG_READABLE
,
183 vf
->priv
->outw
, vf
->priv
->outh
);
186 if(mpi
->flags
&MP_IMGFLAG_PLANAR
){
187 memcpy_pic(vf
->dmpi
->planes
[0] + ass_top_margin
* vf
->dmpi
->stride
[0],
188 mpi
->planes
[0], mpi
->w
, mpi
->h
,
189 vf
->dmpi
->stride
[0], mpi
->stride
[0]);
190 memcpy_pic(vf
->dmpi
->planes
[1] + (ass_top_margin
>> mpi
->chroma_y_shift
) * vf
->dmpi
->stride
[1],
191 mpi
->planes
[1], mpi
->w
>> mpi
->chroma_x_shift
, mpi
->h
>> mpi
->chroma_y_shift
,
192 vf
->dmpi
->stride
[1], mpi
->stride
[1]);
193 memcpy_pic(vf
->dmpi
->planes
[2] + (ass_top_margin
>> mpi
->chroma_y_shift
) * vf
->dmpi
->stride
[2],
194 mpi
->planes
[2], mpi
->w
>> mpi
->chroma_x_shift
, mpi
->h
>> mpi
->chroma_y_shift
,
195 vf
->dmpi
->stride
[2], mpi
->stride
[2]);
197 memcpy_pic(vf
->dmpi
->planes
[0] + ass_top_margin
* vf
->dmpi
->stride
[0],
198 mpi
->planes
[0], mpi
->w
*(vf
->dmpi
->bpp
/8), mpi
->h
,
199 vf
->dmpi
->stride
[0], mpi
->stride
[0]);
200 vf
->dmpi
->planes
[1] = mpi
->planes
[1]; // passthrough rgb8 palette
203 blank(vf
->dmpi
, 0, ass_top_margin
);
204 if (ass_bottom_margin
)
205 blank(vf
->dmpi
, vf
->priv
->outh
- ass_bottom_margin
, vf
->priv
->outh
);
210 * \brief Copy specified rows from render_context.dmpi to render_context.planes, upsampling to 4:4:4
212 static void copy_from_image(struct vf_instance
*vf
, int first_row
, int last_row
)
219 first_row
-= (first_row
% 2);
220 last_row
+= (last_row
% 2);
221 chroma_rows
= (last_row
- first_row
) / 2;
223 assert(first_row
>= 0);
224 assert(first_row
<= last_row
);
225 assert(last_row
<= vf
->priv
->outh
);
227 for (pl
= 1; pl
< 3; ++pl
) {
228 int dst_stride
= vf
->priv
->outw
;
229 int src_stride
= vf
->dmpi
->stride
[pl
];
231 unsigned char* src
= vf
->dmpi
->planes
[pl
] + (first_row
/2) * src_stride
;
232 unsigned char* dst
= vf
->priv
->planes
[pl
] + first_row
* dst_stride
;
233 unsigned char* dst_next
= dst
+ dst_stride
;
234 for(i
= 0; i
< chroma_rows
; ++i
)
236 if ((vf
->priv
->dirty_rows
[first_row
+ i
*2] == 0) ||
237 (vf
->priv
->dirty_rows
[first_row
+ i
*2 + 1] == 0)) {
238 for (j
= 0, k
= 0; j
< vf
->dmpi
->chroma_width
; ++j
, k
+=2) {
241 *(dst
+ k
+ 1) = val
;
242 *(dst_next
+ k
) = val
;
243 *(dst_next
+ k
+ 1) = val
;
247 dst
= dst_next
+ dst_stride
;
248 dst_next
= dst
+ dst_stride
;
251 for (i
= first_row
; i
< last_row
; ++i
)
252 vf
->priv
->dirty_rows
[i
] = 1;
256 * \brief Copy all previously copied rows back to render_context.dmpi
258 static void copy_to_image(struct vf_instance
*vf
)
262 for (pl
= 1; pl
< 3; ++pl
) {
263 int dst_stride
= vf
->dmpi
->stride
[pl
];
264 int src_stride
= vf
->priv
->outw
;
266 unsigned char* dst
= vf
->dmpi
->planes
[pl
];
267 unsigned char* src
= vf
->priv
->planes
[pl
];
268 unsigned char* src_next
= vf
->priv
->planes
[pl
] + src_stride
;
269 for(i
= 0; i
< vf
->dmpi
->chroma_height
; ++i
)
271 if ((vf
->priv
->dirty_rows
[i
*2] == 1)) {
272 assert(vf
->priv
->dirty_rows
[i
*2 + 1] == 1);
273 for (j
= 0, k
= 0; j
< vf
->dmpi
->chroma_width
; ++j
, k
+=2) {
276 val
+= *(src
+ k
+ 1);
277 val
+= *(src_next
+ k
);
278 val
+= *(src_next
+ k
+ 1);
279 *(dst
+ j
) = val
>> 2;
283 src
= src_next
+ src_stride
;
284 src_next
= src
+ src_stride
;
289 static void my_draw_bitmap(struct vf_instance
*vf
, unsigned char* bitmap
, int bitmap_w
, int bitmap_h
, int stride
, int dst_x
, int dst_y
, unsigned color
)
291 unsigned char y
= rgba2y(color
);
292 unsigned char u
= rgba2u(color
);
293 unsigned char v
= rgba2v(color
);
294 unsigned char opacity
= 255 - _a(color
);
295 unsigned char *src
, *dsty
, *dstu
, *dstv
;
297 mp_image_t
* dmpi
= vf
->dmpi
;
300 dsty
= dmpi
->planes
[0] + dst_x
+ dst_y
* dmpi
->stride
[0];
301 dstu
= vf
->priv
->planes
[1] + dst_x
+ dst_y
* vf
->priv
->outw
;
302 dstv
= vf
->priv
->planes
[2] + dst_x
+ dst_y
* vf
->priv
->outw
;
303 for (i
= 0; i
< bitmap_h
; ++i
) {
304 for (j
= 0; j
< bitmap_w
; ++j
) {
305 unsigned k
= ((unsigned)src
[j
]) * opacity
/ 255;
306 dsty
[j
] = (k
*y
+ (255-k
)*dsty
[j
]) / 255;
307 dstu
[j
] = (k
*u
+ (255-k
)*dstu
[j
]) / 255;
308 dstv
[j
] = (k
*v
+ (255-k
)*dstv
[j
]) / 255;
311 dsty
+= dmpi
->stride
[0];
312 dstu
+= vf
->priv
->outw
;
313 dstv
+= vf
->priv
->outw
;
317 static int render_frame(struct vf_instance
*vf
, mp_image_t
*mpi
, const ass_image_t
* img
)
320 memset(vf
->priv
->dirty_rows
, 0, vf
->priv
->outh
); // reset dirty rows
322 copy_from_image(vf
, img
->dst_y
, img
->dst_y
+ img
->h
);
323 my_draw_bitmap(vf
, img
->bitmap
, img
->w
, img
->h
, img
->stride
,
324 img
->dst_x
, img
->dst_y
, img
->color
);
332 static int put_image(struct vf_instance
*vf
, mp_image_t
*mpi
, double pts
)
334 ass_image_t
* images
= 0;
335 if (sub_visibility
&& vf
->priv
->ass_priv
&& ass_track
&& (pts
!= MP_NOPTS_VALUE
))
336 images
= ass_mp_render_frame(vf
->priv
->ass_priv
, ass_track
, (pts
+sub_delay
) * 1000 + .5, NULL
);
338 prepare_image(vf
, mpi
);
339 if (images
) render_frame(vf
, mpi
, images
);
341 return vf_next_put_image(vf
, vf
->dmpi
, pts
);
344 static int query_format(struct vf_instance
*vf
, unsigned int fmt
)
350 return vf_next_query_format(vf
, vf
->priv
->outfmt
);
355 static int control(vf_instance_t
*vf
, int request
, void *data
)
358 case VFCTRL_INIT_EOSD
:
359 vf
->priv
->ass_priv
= ass_renderer_init((ass_library_t
*)data
);
360 if (!vf
->priv
->ass_priv
) return CONTROL_FALSE
;
361 ass_configure_fonts(vf
->priv
->ass_priv
);
363 case VFCTRL_DRAW_EOSD
:
364 if (vf
->priv
->ass_priv
) return CONTROL_TRUE
;
367 return vf_next_control(vf
, request
, data
);
370 static void uninit(struct vf_instance
*vf
)
372 if (vf
->priv
->ass_priv
)
373 ass_renderer_done(vf
->priv
->ass_priv
);
374 if (vf
->priv
->planes
[1])
375 free(vf
->priv
->planes
[1]);
376 if (vf
->priv
->planes
[2])
377 free(vf
->priv
->planes
[2]);
378 if (vf
->priv
->dirty_rows
)
379 free(vf
->priv
->dirty_rows
);
382 static const unsigned int fmt_list
[]={
389 static int vf_open(vf_instance_t
*vf
, char *args
)
392 vf
->priv
->outfmt
= vf_match_csp(&vf
->next
,fmt_list
,IMGFMT_YV12
);
393 if (vf
->priv
->outfmt
)
394 flags
= vf_next_query_format(vf
, vf
->priv
->outfmt
);
395 if (!vf
->priv
->outfmt
|| (vf
->priv
->auto_insert
&& flags
&VFCAP_EOSD
))
401 if (vf
->priv
->auto_insert
)
402 mp_msg(MSGT_ASS
, MSGL_INFO
, "[ass] auto-open\n");
405 vf
->query_format
= query_format
;
407 vf
->control
= control
;
408 vf
->get_image
= get_image
;
409 vf
->put_image
= put_image
;
410 vf
->default_caps
=VFCAP_EOSD
;
414 #define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
415 static const m_option_t vf_opts_fields
[] = {
416 {"auto", ST_OFF(auto_insert
), CONF_TYPE_FLAG
, 0 , 0, 1, NULL
},
417 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
420 static const m_struct_t vf_opts
= {
422 sizeof(struct vf_priv_s
),
427 const vf_info_t vf_info_ass
= {
428 "Render ASS/SSA subtitles",