1 /*****************************************************************************
2 * blend2.cpp: Blend one picture with alpha onto another picture
3 *****************************************************************************
4 * Copyright (C) 2012 Laurent Aimar
7 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include <vlc_picture.h>
35 #include "filter_picture.h"
37 /*****************************************************************************
39 *****************************************************************************/
40 static int Open (vlc_object_t
*);
41 static void Close(vlc_object_t
*);
44 set_description(N_("Video pictures blending"))
45 set_capability("video blending", 100)
46 set_callbacks(Open
, Close
)
49 static inline unsigned div255(unsigned v
)
51 /* It is exact for 8 bits, and has a max error of 1 for 9 and 10 bits
52 * while respecting full opacity/transparency */
53 return ((v
>> 8) + v
+ 1) >> 8;
58 void merge(T
*dst
, unsigned src
, unsigned f
)
60 *dst
= div255((255 - f
) * (*dst
) + src
* f
);
70 CPicture(const picture_t
*picture
,
71 const video_format_t
*fmt
,
72 unsigned x
, unsigned y
) : picture(picture
), fmt(fmt
), x(x
), y(y
)
75 CPicture(const CPicture
&src
) : picture(src
.picture
), fmt(src
.fmt
), x(src
.x
), y(src
.y
)
78 const video_format_t
*getFormat() const
82 bool isFull(unsigned) const
88 template <unsigned ry
>
89 uint8_t *getLine(unsigned plane
= 0) const
91 return &picture
->p
[plane
].p_pixels
[(y
/ ry
) * picture
->p
[plane
].i_pitch
];
93 const picture_t
*picture
;
94 const video_format_t
*fmt
;
99 template <typename pixel
, unsigned rx
, unsigned ry
, bool has_alpha
, bool swap_uv
>
100 class CPictureYUVPlanar
: public CPicture
{
102 CPictureYUVPlanar(const CPicture
&cfg
) : CPicture(cfg
)
104 data
[0] = CPicture::getLine
< 1>(0);
105 data
[1] = CPicture::getLine
<ry
>(swap_uv
? 2 : 1);
106 data
[2] = CPicture::getLine
<ry
>(swap_uv
? 1 : 2);
108 data
[3] = CPicture::getLine
<1>(3);
110 void get(CPixel
*px
, unsigned dx
, bool full
= true) const
112 px
->i
= *getPointer(0, dx
);
114 px
->j
= *getPointer(1, dx
);
115 px
->k
= *getPointer(2, dx
);
118 px
->a
= *getPointer(3, dx
);
120 void merge(unsigned dx
, const CPixel
&spx
, unsigned a
, bool full
)
122 ::merge(getPointer(0, dx
), spx
.i
, a
);
124 ::merge(getPointer(1, dx
), spx
.j
, a
);
125 ::merge(getPointer(2, dx
), spx
.k
, a
);
128 bool isFull(unsigned dx
) const
130 return (y
% ry
) == 0 && ((x
+ dx
) % rx
) == 0;
135 data
[0] += picture
->p
[0].i_pitch
;
137 data
[1] += picture
->p
[swap_uv
? 2 : 1].i_pitch
;
138 data
[2] += picture
->p
[swap_uv
? 1 : 2].i_pitch
;
141 data
[3] += picture
->p
[3].i_pitch
;
144 pixel
*getPointer(unsigned plane
, unsigned dx
) const
146 if (plane
== 1 || plane
== 2)
147 return (pixel
*)&data
[plane
][(x
+ dx
) / rx
* sizeof(pixel
)];
149 return (pixel
*)&data
[plane
][(x
+ dx
) / 1 * sizeof(pixel
)];
154 template <bool swap_uv
>
155 class CPictureYUVSemiPlanar
: public CPicture
{
157 CPictureYUVSemiPlanar(const CPicture
&cfg
) : CPicture(cfg
)
159 data
[0] = CPicture::getLine
<1>(0);
160 data
[1] = CPicture::getLine
<2>(1);
162 void get(CPixel
*px
, unsigned dx
, bool full
= true) const
164 px
->i
= *getPointer(0, dx
);
166 px
->j
= getPointer(1, dx
)[swap_uv
];
167 px
->k
= getPointer(1, dx
)[!swap_uv
];
170 void merge(unsigned dx
, const CPixel
&spx
, unsigned a
, bool full
)
172 ::merge(getPointer(0, dx
), spx
.i
, a
);
174 ::merge(&getPointer(1, dx
)[ swap_uv
], spx
.j
, a
);
175 ::merge(&getPointer(1, dx
)[!swap_uv
], spx
.k
, a
);
178 bool isFull(unsigned dx
) const
180 return (y
% 2) == 0 && ((x
+ dx
) % 2) == 0;
185 data
[0] += picture
->p
[0].i_pitch
;
187 data
[1] += picture
->p
[1].i_pitch
;
190 uint8_t *getPointer(unsigned plane
, unsigned dx
) const
193 return &data
[plane
][x
+ dx
];
195 return &data
[plane
][(x
+ dx
) / 2 * 2];
200 template <unsigned offset_y
, unsigned offset_u
, unsigned offset_v
>
201 class CPictureYUVPacked
: public CPicture
{
203 CPictureYUVPacked(const CPicture
&cfg
) : CPicture(cfg
)
205 data
= CPicture::getLine
<1>(0);
207 void get(CPixel
*px
, unsigned dx
, bool full
= true) const
209 uint8_t *data
= getPointer(dx
);
210 px
->i
= data
[offset_y
];
212 px
->j
= data
[offset_u
];
213 px
->k
= data
[offset_v
];
216 void merge(unsigned dx
, const CPixel
&spx
, unsigned a
, bool full
)
218 uint8_t *data
= getPointer(dx
);
219 ::merge(&data
[offset_y
], spx
.i
, a
);
221 ::merge(&data
[offset_u
], spx
.j
, a
);
222 ::merge(&data
[offset_v
], spx
.k
, a
);
225 bool isFull(unsigned dx
) const
227 return ((x
+ dx
) % 2) == 0;
232 data
+= picture
->p
[0].i_pitch
;
235 uint8_t *getPointer(unsigned dx
) const
237 return &data
[(x
+ dx
) * 2];
242 class CPictureYUVP
: public CPicture
{
244 CPictureYUVP(const CPicture
&cfg
) : CPicture(cfg
)
246 data
= CPicture::getLine
<1>(0);
248 void get(CPixel
*px
, unsigned dx
, bool = true) const
250 px
->i
= *getPointer(dx
);
255 data
+= picture
->p
[0].i_pitch
;
258 uint8_t *getPointer(unsigned dx
) const
260 return &data
[x
+ dx
];
265 template <unsigned bytes
, bool has_alpha
>
266 class CPictureRGBX
: public CPicture
{
268 CPictureRGBX(const CPicture
&cfg
) : CPicture(cfg
)
271 if (fmt
->i_chroma
== VLC_CODEC_BGRA
) {
282 #ifdef WORDS_BIGENDIAN
283 offset_r
= (8 * bytes
- fmt
->i_lrshift
) / 8;
284 offset_g
= (8 * bytes
- fmt
->i_lgshift
) / 8;
285 offset_b
= (8 * bytes
- fmt
->i_lbshift
) / 8;
287 offset_r
= fmt
->i_lrshift
/ 8;
288 offset_g
= fmt
->i_lgshift
/ 8;
289 offset_b
= fmt
->i_lbshift
/ 8;
292 data
= CPicture::getLine
<1>(0);
294 void get(CPixel
*px
, unsigned dx
, bool = true) const
296 const uint8_t *src
= getPointer(dx
);
297 px
->i
= src
[offset_r
];
298 px
->j
= src
[offset_g
];
299 px
->k
= src
[offset_b
];
301 px
->a
= src
[offset_a
];
303 void merge(unsigned dx
, const CPixel
&spx
, unsigned a
, bool)
305 uint8_t *dst
= getPointer(dx
);
307 // Handle different cases of existing alpha in the
308 // destination buffer. If the existing alpha is 0,
309 // the RGB components should be copied as is and
310 // alpha set to 'a'. If the existing alpha is 255,
311 // this should behave just as the non-alpha case below.
313 // First blend the existing color based on its
314 // alpha with the incoming color.
315 ::merge(&dst
[offset_r
], spx
.i
, 255 - dst
[offset_a
]);
316 ::merge(&dst
[offset_g
], spx
.j
, 255 - dst
[offset_a
]);
317 ::merge(&dst
[offset_b
], spx
.k
, 255 - dst
[offset_a
]);
318 // Now blend in the new color on top with the normal formulas.
319 ::merge(&dst
[offset_r
], spx
.i
, a
);
320 ::merge(&dst
[offset_g
], spx
.j
, a
);
321 ::merge(&dst
[offset_b
], spx
.k
, a
);
322 // Finally set dst_a = (255 * src_a + prev_a * (255 - src_a))/255.
323 ::merge(&dst
[offset_a
], 255, a
);
325 ::merge(&dst
[offset_r
], spx
.i
, a
);
326 ::merge(&dst
[offset_g
], spx
.j
, a
);
327 ::merge(&dst
[offset_b
], spx
.k
, a
);
333 data
+= picture
->p
[0].i_pitch
;
336 uint8_t *getPointer(unsigned dx
) const
338 return &data
[(x
+ dx
) * bytes
];
347 class CPictureRGB16
: public CPicture
{
349 CPictureRGB16(const CPicture
&cfg
) : CPicture(cfg
)
351 data
= CPicture::getLine
<1>(0);
353 void get(CPixel
*px
, unsigned dx
, bool = true) const
355 const uint16_t data
= *getPointer(dx
);
356 px
->i
= (data
& fmt
->i_rmask
) >> fmt
->i_lrshift
;
357 px
->j
= (data
& fmt
->i_gmask
) >> fmt
->i_lgshift
;
358 px
->k
= (data
& fmt
->i_bmask
) >> fmt
->i_lbshift
;
360 void merge(unsigned dx
, const CPixel
&spx
, unsigned a
, bool full
)
365 ::merge(&dpx
.i
, spx
.i
, a
);
366 ::merge(&dpx
.j
, spx
.j
, a
);
367 ::merge(&dpx
.k
, spx
.k
, a
);
369 *getPointer(dx
) = (dpx
.i
<< fmt
->i_lrshift
) |
370 (dpx
.j
<< fmt
->i_lgshift
) |
371 (dpx
.k
<< fmt
->i_lbshift
);
376 data
+= picture
->p
[0].i_pitch
;
379 uint16_t *getPointer(unsigned dx
) const
381 return (uint16_t*)&data
[(x
+ dx
) * 2];
386 typedef CPictureYUVPlanar
<uint8_t, 1,1, true, false> CPictureYUVA
;
388 typedef CPictureYUVPlanar
<uint8_t, 4,4, false, true> CPictureYV9
;
389 typedef CPictureYUVPlanar
<uint8_t, 4,4, false, false> CPictureI410_8
;
391 typedef CPictureYUVPlanar
<uint8_t, 4,1, false, false> CPictureI411_8
;
393 typedef CPictureYUVSemiPlanar
<false> CPictureNV12
;
394 typedef CPictureYUVSemiPlanar
<true> CPictureNV21
;
396 typedef CPictureYUVPlanar
<uint8_t, 2,2, false, true> CPictureYV12
;
397 typedef CPictureYUVPlanar
<uint8_t, 2,2, false, false> CPictureI420_8
;
398 typedef CPictureYUVPlanar
<uint16_t, 2,2, false, false> CPictureI420_16
;
400 typedef CPictureYUVPlanar
<uint8_t, 2,1, false, false> CPictureI422_8
;
401 typedef CPictureYUVPlanar
<uint16_t, 2,1, false, false> CPictureI422_16
;
403 typedef CPictureYUVPlanar
<uint8_t, 1,1, false, false> CPictureI444_8
;
404 typedef CPictureYUVPlanar
<uint16_t, 1,1, false, false> CPictureI444_16
;
406 typedef CPictureYUVPacked
<0, 1, 3> CPictureYUYV
;
407 typedef CPictureYUVPacked
<1, 0, 2> CPictureUYVY
;
408 typedef CPictureYUVPacked
<0, 3, 1> CPictureYVYU
;
409 typedef CPictureYUVPacked
<1, 2, 0> CPictureVYUY
;
411 typedef CPictureRGBX
<4, true> CPictureRGBA
;
412 typedef CPictureRGBX
<4, true> CPictureBGRA
;
413 typedef CPictureRGBX
<4, false> CPictureRGB32
;
414 typedef CPictureRGBX
<3, false> CPictureRGB24
;
417 convertNone(const video_format_t
*, const video_format_t
*) {}
418 void operator()(CPixel
&)
423 template <unsigned dst
, unsigned src
>
425 convertBits(const video_format_t
*, const video_format_t
*) {}
426 void operator()(CPixel
&p
)
428 p
.i
= p
.i
* ((1 << dst
) - 1) / ((1 << src
) - 1);
429 p
.j
= p
.j
* ((1 << dst
) - 1) / ((1 << src
) - 1);
430 p
.k
= p
.k
* ((1 << dst
) - 1) / ((1 << src
) - 1);
433 typedef convertBits
< 9, 8> convert8To9Bits
;
434 typedef convertBits
<10, 8> convert8To10Bits
;
435 typedef convertBits
<16, 8> convert8To16Bits
;
437 struct convertRgbToYuv8
{
438 convertRgbToYuv8(const video_format_t
*, const video_format_t
*) {}
439 void operator()(CPixel
&p
)
442 rgb_to_yuv(&y
, &u
, &v
, p
.i
, p
.j
, p
.k
);
449 struct convertYuv8ToRgb
{
450 convertYuv8ToRgb(const video_format_t
*, const video_format_t
*) {}
451 void operator()(CPixel
&p
)
454 yuv_to_rgb(&r
, &g
, &b
, p
.i
, p
.j
, p
.k
);
461 struct convertRgbToRgbSmall
{
462 convertRgbToRgbSmall(const video_format_t
*dst
, const video_format_t
*) : fmt(*dst
) {}
463 void operator()(CPixel
&p
)
465 p
.i
>>= fmt
.i_rrshift
;
466 p
.j
>>= fmt
.i_rgshift
;
467 p
.k
>>= fmt
.i_rbshift
;
470 const video_format_t
&fmt
;
473 struct convertYuvpToAny
{
474 void operator()(CPixel
&p
)
476 unsigned index
= p
.i
;
477 p
.i
= palette
.palette
[index
][0];
478 p
.j
= palette
.palette
[index
][1];
479 p
.k
= palette
.palette
[index
][2];
480 p
.a
= palette
.palette
[index
][3];
483 video_palette_t palette
;
485 struct convertYuvpToYuva8
: public convertYuvpToAny
{
486 convertYuvpToYuva8(const video_format_t
*, const video_format_t
*src
)
488 palette
= *src
->p_palette
;
491 struct convertYuvpToRgba
: public convertYuvpToAny
{
492 convertYuvpToRgba(const video_format_t
*, const video_format_t
*src
)
494 const video_palette_t
*p
= src
->p_palette
;
495 for (int i
= 0; i
< p
->i_entries
; i
++) {
497 yuv_to_rgb(&r
, &g
, &b
,
501 palette
.palette
[i
][0] = r
;
502 palette
.palette
[i
][1] = g
;
503 palette
.palette
[i
][2] = b
;
504 palette
.palette
[i
][3] = p
->palette
[i
][3];
509 template <class G
, class F
>
511 compose(const video_format_t
*dst
, const video_format_t
*src
) : f(dst
, src
), g(dst
, src
) {}
512 void operator()(CPixel
&p
)
522 template <class TDst
, class TSrc
, class TConvert
>
523 void Blend(const CPicture
&dst_data
, const CPicture
&src_data
,
524 unsigned width
, unsigned height
, int alpha
)
528 TConvert
convert(dst_data
.getFormat(), src_data
.getFormat());
530 for (unsigned y
= 0; y
< height
; y
++) {
531 for (unsigned x
= 0; x
< width
; x
++) {
537 unsigned a
= div255(alpha
* spx
.a
);
542 dst
.merge(x
, spx
, a
, true);
544 dst
.merge(x
, spx
, a
, false);
551 typedef void (*blend_function_t
)(const CPicture
&dst_data
, const CPicture
&src_data
,
552 unsigned width
, unsigned height
, int alpha
);
554 static const struct {
557 blend_function_t blend
;
561 #define RGB(csp, picture, cvt) \
562 { csp, VLC_CODEC_YUVA, Blend<picture, CPictureYUVA, compose<cvt, convertYuv8ToRgb> > }, \
563 { csp, VLC_CODEC_RGBA, Blend<picture, CPictureRGBA, compose<cvt, convertNone> > }, \
564 { csp, VLC_CODEC_YUVP, Blend<picture, CPictureYUVP, compose<cvt, convertYuvpToRgba> > }
565 #define YUV(csp, picture, cvt) \
566 { csp, VLC_CODEC_YUVA, Blend<picture, CPictureYUVA, compose<cvt, convertNone> > }, \
567 { csp, VLC_CODEC_RGBA, Blend<picture, CPictureRGBA, compose<cvt, convertRgbToYuv8> > }, \
568 { csp, VLC_CODEC_YUVP, Blend<picture, CPictureYUVP, compose<cvt, convertYuvpToYuva8> > }
570 RGB(VLC_CODEC_RGB15
, CPictureRGB16
, convertRgbToRgbSmall
),
571 RGB(VLC_CODEC_RGB16
, CPictureRGB16
, convertRgbToRgbSmall
),
572 RGB(VLC_CODEC_RGB24
, CPictureRGB24
, convertNone
),
573 RGB(VLC_CODEC_RGB32
, CPictureRGB32
, convertNone
),
574 RGB(VLC_CODEC_RGBA
, CPictureRGBA
, convertNone
),
575 RGB(VLC_CODEC_BGRA
, CPictureBGRA
, convertNone
),
577 YUV(VLC_CODEC_YV9
, CPictureYV9
, convertNone
),
578 YUV(VLC_CODEC_I410
, CPictureI410_8
, convertNone
),
580 YUV(VLC_CODEC_I411
, CPictureI411_8
, convertNone
),
582 YUV(VLC_CODEC_YV12
, CPictureYV12
, convertNone
),
583 YUV(VLC_CODEC_NV12
, CPictureNV12
, convertNone
),
584 YUV(VLC_CODEC_NV21
, CPictureNV21
, convertNone
),
585 YUV(VLC_CODEC_J420
, CPictureI420_8
, convertNone
),
586 YUV(VLC_CODEC_I420
, CPictureI420_8
, convertNone
),
587 #ifdef WORDS_BIGENDIAN
588 YUV(VLC_CODEC_I420_9B
, CPictureI420_16
, convert8To9Bits
),
589 YUV(VLC_CODEC_I420_10B
, CPictureI420_16
, convert8To10Bits
),
591 YUV(VLC_CODEC_I420_9L
, CPictureI420_16
, convert8To9Bits
),
592 YUV(VLC_CODEC_I420_10L
, CPictureI420_16
, convert8To10Bits
),
595 YUV(VLC_CODEC_J422
, CPictureI422_8
, convertNone
),
596 YUV(VLC_CODEC_I422
, CPictureI422_8
, convertNone
),
597 #ifdef WORDS_BIGENDIAN
598 YUV(VLC_CODEC_I422_9B
, CPictureI422_16
, convert8To9Bits
),
599 YUV(VLC_CODEC_I422_10B
, CPictureI422_16
, convert8To10Bits
),
601 YUV(VLC_CODEC_I422_9L
, CPictureI422_16
, convert8To9Bits
),
602 YUV(VLC_CODEC_I422_10L
, CPictureI422_16
, convert8To10Bits
),
605 YUV(VLC_CODEC_J444
, CPictureI444_8
, convertNone
),
606 YUV(VLC_CODEC_I444
, CPictureI444_8
, convertNone
),
607 #ifdef WORDS_BIGENDIAN
608 YUV(VLC_CODEC_I444_9B
, CPictureI444_16
, convert8To9Bits
),
609 YUV(VLC_CODEC_I444_10B
, CPictureI444_16
, convert8To10Bits
),
610 YUV(VLC_CODEC_I444_16B
, CPictureI444_16
, convert8To16Bits
),
612 YUV(VLC_CODEC_I444_9L
, CPictureI444_16
, convert8To9Bits
),
613 YUV(VLC_CODEC_I444_10L
, CPictureI444_16
, convert8To10Bits
),
614 YUV(VLC_CODEC_I444_16L
, CPictureI444_16
, convert8To16Bits
),
617 YUV(VLC_CODEC_YUYV
, CPictureYUYV
, convertNone
),
618 YUV(VLC_CODEC_UYVY
, CPictureUYVY
, convertNone
),
619 YUV(VLC_CODEC_YVYU
, CPictureYVYU
, convertNone
),
620 YUV(VLC_CODEC_VYUY
, CPictureVYUY
, convertNone
),
626 struct filter_sys_t
{
627 filter_sys_t() : blend(NULL
)
630 blend_function_t blend
;
634 * It blends 2 picture together.
636 static void Blend(filter_t
*filter
,
637 picture_t
*dst
, const picture_t
*src
,
638 int x_offset
, int y_offset
, int alpha
)
640 filter_sys_t
*sys
= filter
->p_sys
;
642 if( x_offset
< 0 || y_offset
< 0 )
644 msg_Err( filter
, "Blend cannot process negative offsets" );
648 int width
= __MIN((int)filter
->fmt_out
.video
.i_visible_width
- x_offset
,
649 (int)filter
->fmt_in
.video
.i_visible_width
);
650 int height
= __MIN((int)filter
->fmt_out
.video
.i_visible_height
- y_offset
,
651 (int)filter
->fmt_in
.video
.i_visible_height
);
652 if (width
<= 0 || height
<= 0 || alpha
<= 0)
655 video_format_FixRgb(&filter
->fmt_out
.video
);
656 video_format_FixRgb(&filter
->fmt_in
.video
);
658 sys
->blend(CPicture(dst
, &filter
->fmt_out
.video
,
659 filter
->fmt_out
.video
.i_x_offset
+ x_offset
,
660 filter
->fmt_out
.video
.i_y_offset
+ y_offset
),
661 CPicture(src
, &filter
->fmt_in
.video
,
662 filter
->fmt_in
.video
.i_x_offset
,
663 filter
->fmt_in
.video
.i_y_offset
),
664 width
, height
, alpha
);
667 static int Open(vlc_object_t
*object
)
669 filter_t
*filter
= (filter_t
*)object
;
670 const vlc_fourcc_t src
= filter
->fmt_in
.video
.i_chroma
;
671 const vlc_fourcc_t dst
= filter
->fmt_out
.video
.i_chroma
;
673 filter_sys_t
*sys
= new filter_sys_t();
674 for (size_t i
= 0; i
< sizeof(blends
) / sizeof(*blends
); i
++) {
675 if (blends
[i
].src
== src
&& blends
[i
].dst
== dst
)
676 sys
->blend
= blends
[i
].blend
;
680 msg_Err(filter
, "no matching alpha blending routine (chroma: %4.4s -> %4.4s)",
681 (char *)&src
, (char *)&dst
);
686 filter
->pf_video_blend
= Blend
;
691 static void Close(vlc_object_t
*object
)
693 filter_t
*filter
= (filter_t
*)object
;
694 delete filter
->p_sys
;