2 * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
4 * This file is part of MPlayer.
6 * MPlayer is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * MPlayer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33 #include "img_format.h"
36 #include "sub/dec_sub.h"
38 #include "libvo/fastmemcpy.h"
43 #include "sub/ass_mp.h"
45 #define _r(c) ((c)>>24)
46 #define _g(c) (((c)>>16)&0xFF)
47 #define _b(c) (((c)>>8)&0xFF)
48 #define _a(c) ((c)&0xFF)
49 #define rgba2y(c) ( (( 263*_r(c) + 516*_g(c) + 100*_b(c)) >> 10) + 16 )
50 #define rgba2u(c) ( ((-152*_r(c) - 298*_g(c) + 450*_b(c)) >> 10) + 128 )
51 #define rgba2v(c) ( (( 450*_r(c) - 376*_g(c) - 73*_b(c)) >> 10) + 128 )
54 static const struct vf_priv_s
{
59 // 1 = auto-added filter: insert only if chain does not support EOSD already
63 struct osd_state
*osd
;
64 double aspect_correction
;
66 unsigned char *planes
[3];
73 static int config(struct vf_instance
*vf
,
74 int width
, int height
, int d_width
, int d_height
,
75 unsigned int flags
, unsigned int outfmt
)
77 struct MPOpts
*opts
= vf
->opts
;
78 if (outfmt
== IMGFMT_IF09
)
81 vf
->priv
->outh
= height
+ opts
->ass_top_margin
+ opts
->ass_bottom_margin
;
82 vf
->priv
->outw
= width
;
84 if (!opts
->screen_size_x
&& !opts
->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
->line_limits
= malloc((vf
->priv
->outh
+ 1) / 2 * sizeof(*vf
->priv
->line_limits
));
93 vf
->priv
->aspect_correction
= (double)width
/ height
* d_height
/ d_width
;
95 return vf_next_config(vf
, vf
->priv
->outw
, vf
->priv
->outh
, d_width
,
96 d_height
, flags
, outfmt
);
99 static void get_image(struct vf_instance
*vf
, mp_image_t
*mpi
)
101 if (mpi
->type
== MP_IMGTYPE_IPB
)
103 if (mpi
->flags
& MP_IMGFLAG_PRESERVE
)
105 if (mpi
->imgfmt
!= vf
->priv
->outfmt
)
106 return; // colorspace differ
108 // width never changes, always try full DR
109 mpi
->priv
= vf
->dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
, mpi
->type
,
110 mpi
->flags
| MP_IMGFLAG_READABLE
,
111 vf
->priv
->outw
, vf
->priv
->outh
);
113 if ((vf
->dmpi
->flags
& MP_IMGFLAG_DRAW_CALLBACK
) &&
114 !(vf
->dmpi
->flags
& MP_IMGFLAG_DIRECT
)) {
115 mp_tmsg(MSGT_ASS
, MSGL_INFO
, "Full DR not possible, trying SLICES instead!\n");
119 int tmargin
= vf
->opts
->ass_top_margin
;
120 // set up mpi as a cropped-down image of dmpi:
121 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
122 mpi
->planes
[0] = vf
->dmpi
->planes
[0] + tmargin
* vf
->dmpi
->stride
[0];
123 mpi
->planes
[1] = vf
->dmpi
->planes
[1] + (tmargin
>> mpi
->chroma_y_shift
) * vf
->dmpi
->stride
[1];
124 mpi
->planes
[2] = vf
->dmpi
->planes
[2] + (tmargin
>> mpi
->chroma_y_shift
) * vf
->dmpi
->stride
[2];
125 mpi
->stride
[1] = vf
->dmpi
->stride
[1];
126 mpi
->stride
[2] = vf
->dmpi
->stride
[2];
128 mpi
->planes
[0] = vf
->dmpi
->planes
[0] + tmargin
* vf
->dmpi
->stride
[0];
130 mpi
->stride
[0] = vf
->dmpi
->stride
[0];
131 mpi
->width
= vf
->dmpi
->width
;
132 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
133 mpi
->flags
&= ~MP_IMGFLAG_DRAW_CALLBACK
;
134 // vf->dmpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK;
137 static void blank(mp_image_t
*mpi
, int y1
, int y2
)
139 int color
[3] = {16, 128, 128}; // black (YUV)
142 int chroma_rows
= (y2
- y1
) >> mpi
->chroma_y_shift
;
144 dst
= mpi
->planes
[0] + y1
* mpi
->stride
[0];
145 for (y
= 0; y
< y2
- y1
; ++y
) {
146 memset(dst
, color
[0], mpi
->w
);
147 dst
+= mpi
->stride
[0];
149 dst
= mpi
->planes
[1] + (y1
>> mpi
->chroma_y_shift
) * mpi
->stride
[1];
150 for (y
= 0; y
< chroma_rows
; ++y
) {
151 memset(dst
, color
[1], mpi
->chroma_width
);
152 dst
+= mpi
->stride
[1];
154 dst
= mpi
->planes
[2] + (y1
>> mpi
->chroma_y_shift
) * mpi
->stride
[2];
155 for (y
= 0; y
< chroma_rows
; ++y
) {
156 memset(dst
, color
[2], mpi
->chroma_width
);
157 dst
+= mpi
->stride
[2];
161 static int prepare_image(struct vf_instance
*vf
, mp_image_t
*mpi
)
163 struct MPOpts
*opts
= vf
->opts
;
164 int tmargin
= opts
->ass_top_margin
;
165 if (mpi
->flags
& MP_IMGFLAG_DIRECT
166 || mpi
->flags
& MP_IMGFLAG_DRAW_CALLBACK
) {
167 vf
->dmpi
= mpi
->priv
;
169 mp_tmsg(MSGT_ASS
, MSGL_WARN
, "Why do we get NULL??\n");
173 // we've used DR, so we're ready...
175 blank(vf
->dmpi
, 0, tmargin
);
176 if (opts
->ass_bottom_margin
)
177 blank(vf
->dmpi
, vf
->priv
->outh
- opts
->ass_bottom_margin
,
179 if (!(mpi
->flags
& MP_IMGFLAG_PLANAR
))
180 vf
->dmpi
->planes
[1] = mpi
->planes
[1]; // passthrough rgb8 palette
184 // hope we'll get DR buffer:
185 vf
->dmpi
= vf_get_image(vf
->next
, vf
->priv
->outfmt
, MP_IMGTYPE_TEMP
,
186 MP_IMGFLAG_ACCEPT_STRIDE
| MP_IMGFLAG_READABLE
,
187 vf
->priv
->outw
, vf
->priv
->outh
);
190 if (mpi
->flags
& MP_IMGFLAG_PLANAR
) {
191 memcpy_pic(vf
->dmpi
->planes
[0] + tmargin
* vf
->dmpi
->stride
[0],
197 memcpy_pic(vf
->dmpi
->planes
[1] + (tmargin
>> mpi
->chroma_y_shift
) * vf
->dmpi
->stride
[1],
199 mpi
->w
>> mpi
->chroma_x_shift
,
200 mpi
->h
>> mpi
->chroma_y_shift
,
203 memcpy_pic(vf
->dmpi
->planes
[2] + (tmargin
>> mpi
->chroma_y_shift
) * vf
->dmpi
->stride
[2],
205 mpi
->w
>> mpi
->chroma_x_shift
,
206 mpi
->h
>> mpi
->chroma_y_shift
,
210 memcpy_pic(vf
->dmpi
->planes
[0] + tmargin
* vf
->dmpi
->stride
[0],
212 mpi
->w
* (vf
->dmpi
->bpp
/ 8),
216 vf
->dmpi
->planes
[1] = mpi
->planes
[1]; // passthrough rgb8 palette
219 blank(vf
->dmpi
, 0, tmargin
);
220 if (opts
->ass_bottom_margin
)
221 blank(vf
->dmpi
, vf
->priv
->outh
- opts
->ass_bottom_margin
,
226 static void update_limits(struct vf_instance
*vf
, int starty
, int endy
,
227 int startx
, int endx
)
230 endy
= (endy
+ 1) >> 1;
232 endx
= (endx
+ 1) >> 1;
233 for (int i
= starty
; i
< endy
; i
++) {
234 struct line_limits
*ll
= vf
->priv
->line_limits
+ i
;
235 if (startx
< ll
->start
)
243 * \brief Copy specified rows from render_context.dmpi to render_context.planes, upsampling to 4:4:4
245 static void copy_from_image(struct vf_instance
*vf
)
249 for (pl
= 1; pl
< 3; ++pl
) {
250 int dst_stride
= vf
->priv
->outw
;
251 int src_stride
= vf
->dmpi
->stride
[pl
];
253 unsigned char *src
= vf
->dmpi
->planes
[pl
];
254 unsigned char *dst
= vf
->priv
->planes
[pl
];
255 for (int i
= 0; i
< (vf
->priv
->outh
+ 1) / 2; i
++) {
256 struct line_limits
*ll
= vf
->priv
->line_limits
+ i
;
257 unsigned char *dst_next
= dst
+ dst_stride
;
258 for (int j
= ll
->start
; j
< ll
->end
; j
++) {
259 unsigned char val
= src
[j
];
261 dst
[(j
<< 1) + 1] = val
;
262 dst_next
[j
<< 1] = val
;
263 dst_next
[(j
<< 1) + 1] = val
;
266 dst
= dst_next
+ dst_stride
;
272 * \brief Copy all previously copied rows back to render_context.dmpi
274 static void copy_to_image(struct vf_instance
*vf
)
278 for (pl
= 1; pl
< 3; ++pl
) {
279 int dst_stride
= vf
->dmpi
->stride
[pl
];
280 int src_stride
= vf
->priv
->outw
;
282 unsigned char *dst
= vf
->dmpi
->planes
[pl
];
283 unsigned char *src
= vf
->priv
->planes
[pl
];
284 unsigned char *src_next
= vf
->priv
->planes
[pl
] + src_stride
;
285 for (i
= 0; i
< vf
->dmpi
->chroma_height
; ++i
) {
286 for (j
= vf
->priv
->line_limits
[i
].start
; j
< vf
->priv
->line_limits
[i
].end
; j
++) {
289 val
+= src
[(j
<< 1) + 1];
290 val
+= src_next
[j
<< 1];
291 val
+= src_next
[(j
<< 1) + 1];
295 src
= src_next
+ src_stride
;
296 src_next
= src
+ src_stride
;
301 static void my_draw_bitmap(struct vf_instance
*vf
, unsigned char *bitmap
,
302 int bitmap_w
, int bitmap_h
, int stride
,
303 int dst_x
, int dst_y
, unsigned color
)
305 unsigned char y
= rgba2y(color
);
306 unsigned char u
= rgba2u(color
);
307 unsigned char v
= rgba2v(color
);
308 unsigned char opacity
= 255 - _a(color
);
309 unsigned char *src
, *dsty
, *dstu
, *dstv
;
311 mp_image_t
*dmpi
= vf
->dmpi
;
314 dsty
= dmpi
->planes
[0] + dst_x
+ dst_y
* dmpi
->stride
[0];
315 dstu
= vf
->priv
->planes
[1] + dst_x
+ dst_y
* vf
->priv
->outw
;
316 dstv
= vf
->priv
->planes
[2] + dst_x
+ dst_y
* vf
->priv
->outw
;
317 for (i
= 0; i
< bitmap_h
; ++i
) {
318 for (j
= 0; j
< bitmap_w
; ++j
) {
319 unsigned k
= (src
[j
] * opacity
+ 255) >> 8;
320 dsty
[j
] = (k
* y
+ (255 - k
) * dsty
[j
] + 255) >> 8;
321 dstu
[j
] = (k
* u
+ (255 - k
) * dstu
[j
] + 255) >> 8;
322 dstv
[j
] = (k
* v
+ (255 - k
) * dstv
[j
] + 255) >> 8;
325 dsty
+= dmpi
->stride
[0];
326 dstu
+= vf
->priv
->outw
;
327 dstv
+= vf
->priv
->outw
;
331 static int render_frame(struct vf_instance
*vf
, mp_image_t
*mpi
,
332 const ASS_Image
*img
)
335 for (int i
= 0; i
< (vf
->priv
->outh
+ 1) / 2; i
++)
336 vf
->priv
->line_limits
[i
] = (struct line_limits
){65535, 0};
337 for (const ASS_Image
*im
= img
; im
; im
= im
->next
)
338 update_limits(vf
, im
->dst_y
, im
->dst_y
+ im
->h
,
339 im
->dst_x
, im
->dst_x
+ im
->w
);
342 my_draw_bitmap(vf
, img
->bitmap
, img
->w
, img
->h
, img
->stride
,
343 img
->dst_x
, img
->dst_y
, img
->color
);
351 static int put_image(struct vf_instance
*vf
, mp_image_t
*mpi
, double pts
)
353 struct vf_priv_s
*priv
= vf
->priv
;
354 struct MPOpts
*opts
= vf
->opts
;
355 struct osd_state
*osd
= priv
->osd
;
356 ASS_Image
*images
= 0;
357 if (pts
!= MP_NOPTS_VALUE
) {
358 osd
->dim
= (struct mp_eosd_res
){ .w
= vf
->priv
->outw
,
360 .mt
= opts
->ass_top_margin
,
361 .mb
= opts
->ass_bottom_margin
};
362 osd
->normal_scale
= vf
->priv
->aspect_correction
;
363 osd
->vsfilter_scale
= 1;
364 osd
->sub_pts
= pts
- osd
->sub_offset
;
365 osd
->support_rgba
= false;
366 struct sub_bitmaps b
;
367 sub_get_bitmaps(osd
, &b
);
371 prepare_image(vf
, mpi
);
372 render_frame(vf
, mpi
, images
);
374 return vf_next_put_image(vf
, vf
->dmpi
, pts
);
377 static int query_format(struct vf_instance
*vf
, unsigned int fmt
)
383 return vf_next_query_format(vf
, vf
->priv
->outfmt
);
388 static int control(vf_instance_t
*vf
, int request
, void *data
)
391 case VFCTRL_SET_OSD_OBJ
:
392 vf
->priv
->osd
= data
;
394 case VFCTRL_INIT_EOSD
:
396 case VFCTRL_DRAW_EOSD
:
399 return vf_next_control(vf
, request
, data
);
402 static void uninit(struct vf_instance
*vf
)
404 free(vf
->priv
->planes
[1]);
405 free(vf
->priv
->planes
[2]);
406 free(vf
->priv
->line_limits
);
410 static const unsigned int fmt_list
[] = {
417 static int vf_open(vf_instance_t
*vf
, char *args
)
420 vf
->priv
->outfmt
= vf_match_csp(&vf
->next
, fmt_list
, IMGFMT_YV12
);
421 if (vf
->priv
->outfmt
)
422 flags
= vf_next_query_format(vf
, vf
->priv
->outfmt
);
423 if (!vf
->priv
->outfmt
) {
426 } else if (vf
->priv
->auto_insert
&& flags
& VFCAP_EOSD
) {
431 if (vf
->priv
->auto_insert
)
432 mp_msg(MSGT_ASS
, MSGL_INFO
, "[ass] auto-open\n");
435 vf
->query_format
= query_format
;
437 vf
->control
= control
;
438 vf
->get_image
= get_image
;
439 vf
->put_image
= put_image
;
440 vf
->default_caps
= VFCAP_EOSD
| VFCAP_EOSD_FILTER
;
444 #define ST_OFF(f) M_ST_OFF(struct vf_priv_s, f)
445 static const m_option_t vf_opts_fields
[] = {
446 {"auto", ST_OFF(auto_insert
), CONF_TYPE_FLAG
, 0, 0, 1, NULL
},
447 {NULL
, NULL
, 0, 0, 0, 0, NULL
}
450 static const m_struct_t vf_opts
= {
452 sizeof(struct vf_priv_s
),
457 const vf_info_t vf_info_ass
= {
458 "Render ASS/SSA subtitles",