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 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.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)
276 #ifdef WORDS_BIGENDIAN
277 offset_r = (8 * bytes - fmt->i_lrshift) / 8;
278 offset_g = (8 * bytes - fmt->i_lgshift) / 8;
279 offset_b = (8 * bytes - fmt->i_lbshift) / 8;
281 offset_r = fmt->i_lrshift / 8;
282 offset_g = fmt->i_lgshift / 8;
283 offset_b = fmt->i_lbshift / 8;
286 data = CPicture::getLine<1>(0);
288 void get(CPixel *px, unsigned dx, bool = true) const
290 const uint8_t *src = getPointer(dx);
291 px->i = src[offset_r];
292 px->j = src[offset_g];
293 px->k = src[offset_b];
295 px->a = src[offset_a];
297 void merge(unsigned dx, const CPixel &spx, unsigned a, bool)
299 uint8_t *dst = getPointer(dx);
301 // Handle different cases of existing alpha in the
302 // destination buffer. If the existing alpha is 0,
303 // the RGB components should be copied as is and
304 // alpha set to 'a'. If the existing alpha is 255,
305 // this should behave just as the non-alpha case below.
307 // First blend the existing color based on its
308 // alpha with the incoming color.
309 ::merge(&dst[offset_r], spx.i, 255 - dst[offset_a]);
310 ::merge(&dst[offset_g], spx.j, 255 - dst[offset_a]);
311 ::merge(&dst[offset_b], spx.k, 255 - dst[offset_a]);
312 // Now blend in the new color on top with the normal formulas.
313 ::merge(&dst[offset_r], spx.i, a);
314 ::merge(&dst[offset_g], spx.j, a);
315 ::merge(&dst[offset_b], spx.k, a);
316 // Finally set dst_a = (255 * src_a + prev_a * (255 - src_a))/255.
317 ::merge(&dst[offset_a], 255, a);
319 ::merge(&dst[offset_r], spx.i, a);
320 ::merge(&dst[offset_g], spx.j, a);
321 ::merge(&dst[offset_b], spx.k, a);
327 data += picture->p[0].i_pitch;
330 uint8_t *getPointer(unsigned dx) const
332 return &data[(x + dx) * bytes];
341 class CPictureRGB16 : public CPicture {
343 CPictureRGB16(const CPicture &cfg) : CPicture(cfg)
345 data = CPicture::getLine<1>(0);
347 void get(CPixel *px, unsigned dx, bool = true) const
349 const uint16_t data = *getPointer(dx);
350 px->i = (data & fmt->i_rmask) >> fmt->i_lrshift;
351 px->j = (data & fmt->i_gmask) >> fmt->i_lgshift;
352 px->k = (data & fmt->i_bmask) >> fmt->i_lbshift;
354 void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
359 ::merge(&dpx.i, spx.i, a);
360 ::merge(&dpx.j, spx.j, a);
361 ::merge(&dpx.k, spx.k, a);
363 *getPointer(dx) = (dpx.i << fmt->i_lrshift) |
364 (dpx.j << fmt->i_lgshift) |
365 (dpx.k << fmt->i_lbshift);
370 data += picture->p[0].i_pitch;
373 uint16_t *getPointer(unsigned dx) const
375 return (uint16_t*)&data[(x + dx) * 2];
380 typedef CPictureYUVPlanar<uint8_t, 1,1, true, false> CPictureYUVA;
382 typedef CPictureYUVPlanar<uint8_t, 4,4, false, true> CPictureYV9;
383 typedef CPictureYUVPlanar<uint8_t, 4,4, false, false> CPictureI410_8;
385 typedef CPictureYUVPlanar<uint8_t, 4,1, false, false> CPictureI411_8;
387 typedef CPictureYUVSemiPlanar<false> CPictureNV12;
388 typedef CPictureYUVSemiPlanar<true> CPictureNV21;
390 typedef CPictureYUVPlanar<uint8_t, 2,2, false, true> CPictureYV12;
391 typedef CPictureYUVPlanar<uint8_t, 2,2, false, false> CPictureI420_8;
392 typedef CPictureYUVPlanar<uint16_t, 2,2, false, false> CPictureI420_16;
394 typedef CPictureYUVPlanar<uint8_t, 2,1, false, false> CPictureI422_8;
395 typedef CPictureYUVPlanar<uint16_t, 2,1, false, false> CPictureI422_16;
397 typedef CPictureYUVPlanar<uint8_t, 1,1, false, false> CPictureI444_8;
398 typedef CPictureYUVPlanar<uint16_t, 1,1, false, false> CPictureI444_16;
400 typedef CPictureYUVPacked<0, 1, 3> CPictureYUYV;
401 typedef CPictureYUVPacked<1, 0, 2> CPictureUYVY;
402 typedef CPictureYUVPacked<0, 3, 1> CPictureYVYU;
403 typedef CPictureYUVPacked<1, 2, 0> CPictureVYUY;
405 typedef CPictureRGBX<4, true> CPictureRGBA;
406 typedef CPictureRGBX<4, false> CPictureRGB32;
407 typedef CPictureRGBX<3, false> CPictureRGB24;
410 convertNone(const video_format_t *, const video_format_t *) {}
411 void operator()(CPixel &)
416 template <unsigned dst, unsigned src>
418 convertBits(const video_format_t *, const video_format_t *) {}
419 void operator()(CPixel &p)
421 p.i = p.i * ((1 << dst) - 1) / ((1 << src) - 1);
422 p.j = p.j * ((1 << dst) - 1) / ((1 << src) - 1);
423 p.k = p.k * ((1 << dst) - 1) / ((1 << src) - 1);
426 typedef convertBits< 9, 8> convert8To9Bits;
427 typedef convertBits<10, 8> convert8To10Bits;
428 typedef convertBits<16, 8> convert8To16Bits;
430 struct convertRgbToYuv8 {
431 convertRgbToYuv8(const video_format_t *, const video_format_t *) {}
432 void operator()(CPixel &p)
435 rgb_to_yuv(&y, &u, &v, p.i, p.j, p.k);
442 struct convertYuv8ToRgb {
443 convertYuv8ToRgb(const video_format_t *, const video_format_t *) {}
444 void operator()(CPixel &p)
447 yuv_to_rgb(&r, &g, &b, p.i, p.j, p.k);
454 struct convertRgbToRgbSmall {
455 convertRgbToRgbSmall(const video_format_t *dst, const video_format_t *) : fmt(*dst) {}
456 void operator()(CPixel &p)
458 p.i >>= fmt.i_rrshift;
459 p.j >>= fmt.i_rgshift;
460 p.k >>= fmt.i_rbshift;
463 const video_format_t &fmt;
466 struct convertYuvpToAny {
467 void operator()(CPixel &p)
469 unsigned index = p.i;
470 p.i = palette.palette[index][0];
471 p.j = palette.palette[index][1];
472 p.k = palette.palette[index][2];
473 p.a = palette.palette[index][3];
476 video_palette_t palette;
478 struct convertYuvpToYuva8 : public convertYuvpToAny {
479 convertYuvpToYuva8(const video_format_t *, const video_format_t *src)
481 palette = *src->p_palette;
484 struct convertYuvpToRgba : public convertYuvpToAny {
485 convertYuvpToRgba(const video_format_t *, const video_format_t *src)
487 const video_palette_t *p = src->p_palette;
488 for (int i = 0; i < p->i_entries; i++) {
490 yuv_to_rgb(&r, &g, &b,
494 palette.palette[i][0] = r;
495 palette.palette[i][1] = g;
496 palette.palette[i][2] = b;
497 palette.palette[i][3] = p->palette[i][3];
502 template <class G, class F>
504 compose(const video_format_t *dst, const video_format_t *src) : f(dst, src), g(dst, src) {}
505 void operator()(CPixel &p)
515 template <class TDst, class TSrc, class TConvert>
516 void Blend(const CPicture &dst_data, const CPicture &src_data,
517 unsigned width, unsigned height, int alpha)
521 TConvert convert(dst_data.getFormat(), src_data.getFormat());
523 for (unsigned y = 0; y < height; y++) {
524 for (unsigned x = 0; x < width; x++) {
530 unsigned a = div255(alpha * spx.a);
535 dst.merge(x, spx, a, true);
537 dst.merge(x, spx, a, false);
544 typedef void (*blend_function_t)(const CPicture &dst_data, const CPicture &src_data,
545 unsigned width, unsigned height, int alpha);
547 static const struct {
550 blend_function_t blend;
554 #define RGB(csp, picture, cvt) \
555 { csp, VLC_CODEC_YUVA, Blend<picture, CPictureYUVA, compose<cvt, convertYuv8ToRgb> > }, \
556 { csp, VLC_CODEC_RGBA, Blend<picture, CPictureRGBA, compose<cvt, convertNone> > }, \
557 { csp, VLC_CODEC_YUVP, Blend<picture, CPictureYUVP, compose<cvt, convertYuvpToRgba> > }
558 #define YUV(csp, picture, cvt) \
559 { csp, VLC_CODEC_YUVA, Blend<picture, CPictureYUVA, compose<cvt, convertNone> > }, \
560 { csp, VLC_CODEC_RGBA, Blend<picture, CPictureRGBA, compose<cvt, convertRgbToYuv8> > }, \
561 { csp, VLC_CODEC_YUVP, Blend<picture, CPictureYUVP, compose<cvt, convertYuvpToYuva8> > }
563 RGB(VLC_CODEC_RGB15, CPictureRGB16, convertRgbToRgbSmall),
564 RGB(VLC_CODEC_RGB16, CPictureRGB16, convertRgbToRgbSmall),
565 RGB(VLC_CODEC_RGB24, CPictureRGB24, convertNone),
566 RGB(VLC_CODEC_RGB32, CPictureRGB32, convertNone),
567 RGB(VLC_CODEC_RGBA, CPictureRGBA, convertNone),
569 YUV(VLC_CODEC_YV9, CPictureYV9, convertNone),
570 YUV(VLC_CODEC_I410, CPictureI410_8, convertNone),
572 YUV(VLC_CODEC_I411, CPictureI411_8, convertNone),
574 YUV(VLC_CODEC_YV12, CPictureYV12, convertNone),
575 YUV(VLC_CODEC_NV12, CPictureNV12, convertNone),
576 YUV(VLC_CODEC_NV21, CPictureNV21, convertNone),
577 YUV(VLC_CODEC_J420, CPictureI420_8, convertNone),
578 YUV(VLC_CODEC_I420, CPictureI420_8, convertNone),
579 #ifdef WORDS_BIGENDIAN
580 YUV(VLC_CODEC_I420_9B, CPictureI420_16, convert8To9Bits),
581 YUV(VLC_CODEC_I420_10B, CPictureI420_16, convert8To10Bits),
583 YUV(VLC_CODEC_I420_9L, CPictureI420_16, convert8To9Bits),
584 YUV(VLC_CODEC_I420_10L, CPictureI420_16, convert8To10Bits),
587 YUV(VLC_CODEC_J422, CPictureI422_8, convertNone),
588 YUV(VLC_CODEC_I422, CPictureI422_8, convertNone),
589 #ifdef WORDS_BIGENDIAN
590 YUV(VLC_CODEC_I422_9B, CPictureI422_16, convert8To9Bits),
591 YUV(VLC_CODEC_I422_10B, CPictureI422_16, convert8To10Bits),
593 YUV(VLC_CODEC_I422_9L, CPictureI422_16, convert8To9Bits),
594 YUV(VLC_CODEC_I422_10L, CPictureI422_16, convert8To10Bits),
597 YUV(VLC_CODEC_J444, CPictureI444_8, convertNone),
598 YUV(VLC_CODEC_I444, CPictureI444_8, convertNone),
599 #ifdef WORDS_BIGENDIAN
600 YUV(VLC_CODEC_I444_9B, CPictureI444_16, convert8To9Bits),
601 YUV(VLC_CODEC_I444_10B, CPictureI444_16, convert8To10Bits),
602 YUV(VLC_CODEC_I444_16B, CPictureI444_16, convert8To16Bits),
604 YUV(VLC_CODEC_I444_9L, CPictureI444_16, convert8To9Bits),
605 YUV(VLC_CODEC_I444_10L, CPictureI444_16, convert8To10Bits),
606 YUV(VLC_CODEC_I444_16L, CPictureI444_16, convert8To16Bits),
609 YUV(VLC_CODEC_YUYV, CPictureYUYV, convertNone),
610 YUV(VLC_CODEC_UYVY, CPictureUYVY, convertNone),
611 YUV(VLC_CODEC_YVYU, CPictureYVYU, convertNone),
612 YUV(VLC_CODEC_VYUY, CPictureVYUY, convertNone),
618 struct filter_sys_t {
619 filter_sys_t() : blend(NULL)
622 blend_function_t blend;
626 * It blends 2 picture together.
628 static void Blend(filter_t *filter,
629 picture_t *dst, const picture_t *src,
630 int x_offset, int y_offset, int alpha)
632 filter_sys_t *sys = filter->p_sys;
634 if( x_offset < 0 || y_offset < 0 )
636 msg_Err( filter, "Blend cannot process negative offsets" );
640 int width = __MIN((int)filter->fmt_out.video.i_visible_width - x_offset,
641 (int)filter->fmt_in.video.i_visible_width);
642 int height = __MIN((int)filter->fmt_out.video.i_visible_height - y_offset,
643 (int)filter->fmt_in.video.i_visible_height);
644 if (width <= 0 || height <= 0 || alpha <= 0)
647 video_format_FixRgb(&filter->fmt_out.video);
648 video_format_FixRgb(&filter->fmt_in.video);
650 sys->blend(CPicture(dst, &filter->fmt_out.video,
651 filter->fmt_out.video.i_x_offset + x_offset,
652 filter->fmt_out.video.i_y_offset + y_offset),
653 CPicture(src, &filter->fmt_in.video,
654 filter->fmt_in.video.i_x_offset,
655 filter->fmt_in.video.i_y_offset),
656 width, height, alpha);
659 static int Open(vlc_object_t *object)
661 filter_t *filter = (filter_t *)object;
662 const vlc_fourcc_t src = filter->fmt_in.video.i_chroma;
663 const vlc_fourcc_t dst = filter->fmt_out.video.i_chroma;
665 filter_sys_t *sys = new filter_sys_t();
666 for (size_t i = 0; i < sizeof(blends) / sizeof(*blends); i++) {
667 if (blends[i].src == src && blends[i].dst == dst)
668 sys->blend = blends[i].blend;
672 msg_Err(filter, "no matching alpha blending routine (chroma: %4.4s -> %4.4s)",
673 (char *)&src, (char *)&dst);
678 filter->pf_video_blend = Blend;
683 static void Close(vlc_object_t *object)
685 filter_t *filter = (filter_t *)object;
686 delete filter->p_sys;