1 /*****************************************************************************
2  * blend2.cpp: Blend one picture with alpha onto another picture
3  *****************************************************************************
4  * Copyright (C) 2012 Laurent Aimar
5  * $Id: cea8cd6a923953be91441fcba127ba139b4105f0 $
6  *
7  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23 
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 
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"
36 
37 /*****************************************************************************
38  * Module descriptor
39  *****************************************************************************/
40 static int  Open (vlc_object_t *);
41 static void Close(vlc_object_t *);
42 
43 vlc_module_begin()
44     set_description(N_("Video pictures blending"))
45     set_capability("video blending", 100)
set_callbacks(Open,Close)46     set_callbacks(Open, Close)
47 vlc_module_end()
48 
49 static inline unsigned div255(unsigned v)
50 {
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;
54     //return v / 255;
55 }
56 
57 template <typename T>
merge(T * dst,unsigned src,unsigned f)58 void merge(T *dst, unsigned src, unsigned f)
59 {
60     *dst = div255((255 - f) * (*dst) + src * f);
61 }
62 
63 struct CPixel {
64     unsigned i, j, k;
65     unsigned a;
66 };
67 
68 class CPicture {
69 public:
CPicture(const picture_t * picture,const video_format_t * fmt,unsigned x,unsigned y)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)
73     {
74     }
CPicture(const CPicture & src)75     CPicture(const CPicture &src) : picture(src.picture), fmt(src.fmt), x(src.x), y(src.y)
76     {
77     }
getFormat() const78     const video_format_t *getFormat() const
79     {
80         return fmt;
81     }
isFull(unsigned) const82     bool isFull(unsigned) const
83     {
84         return true;
85     }
86 
87 protected:
88     template <unsigned ry>
getLine(unsigned plane=0) const89     uint8_t *getLine(unsigned plane = 0) const
90     {
91         return &picture->p[plane].p_pixels[(y / ry) * picture->p[plane].i_pitch];
92     }
93     const picture_t *picture;
94     const video_format_t *fmt;
95     unsigned x;
96     unsigned y;
97 };
98 
99 template <typename pixel, unsigned rx, unsigned ry, bool has_alpha, bool swap_uv>
100 class CPictureYUVPlanar : public CPicture {
101 public:
CPictureYUVPlanar(const CPicture & cfg)102     CPictureYUVPlanar(const CPicture &cfg) : CPicture(cfg)
103     {
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);
107         if (has_alpha)
108             data[3] = CPicture::getLine<1>(3);
109     }
get(CPixel * px,unsigned dx,bool full=true) const110     void get(CPixel *px, unsigned dx, bool full = true) const
111     {
112         px->i = *getPointer(0, dx);
113         if (full) {
114             px->j = *getPointer(1, dx);
115             px->k = *getPointer(2, dx);
116         }
117         if (has_alpha)
118             px->a = *getPointer(3, dx);
119     }
merge(unsigned dx,const CPixel & spx,unsigned a,bool full)120     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
121     {
122         ::merge(getPointer(0, dx), spx.i, a);
123         if (full) {
124             ::merge(getPointer(1, dx), spx.j, a);
125             ::merge(getPointer(2, dx), spx.k, a);
126         }
127     }
isFull(unsigned dx) const128     bool isFull(unsigned dx) const
129     {
130         return (y % ry) == 0 && ((x + dx) % rx) == 0;
131     }
nextLine()132     void nextLine()
133     {
134         y++;
135         data[0] += picture->p[0].i_pitch;
136         if ((y % ry) == 0) {
137             data[1] += picture->p[swap_uv ? 2 : 1].i_pitch;
138             data[2] += picture->p[swap_uv ? 1 : 2].i_pitch;
139         }
140         if (has_alpha)
141             data[3] += picture->p[3].i_pitch;
142     }
143 private:
getPointer(unsigned plane,unsigned dx) const144     pixel *getPointer(unsigned plane, unsigned dx) const
145     {
146         if (plane == 1 || plane == 2)
147             return (pixel*)&data[plane][(x + dx) / rx * sizeof(pixel)];
148         else
149             return (pixel*)&data[plane][(x + dx) /  1 * sizeof(pixel)];
150     }
151     uint8_t *data[4];
152 };
153 
154 template <bool swap_uv>
155 class CPictureYUVSemiPlanar : public CPicture {
156 public:
CPictureYUVSemiPlanar(const CPicture & cfg)157     CPictureYUVSemiPlanar(const CPicture &cfg) : CPicture(cfg)
158     {
159         data[0] = CPicture::getLine<1>(0);
160         data[1] = CPicture::getLine<2>(1);
161     }
get(CPixel * px,unsigned dx,bool full=true) const162     void get(CPixel *px, unsigned dx, bool full = true) const
163     {
164         px->i = *getPointer(0, dx);
165         if (full) {
166             px->j = getPointer(1, dx)[swap_uv];
167             px->k = getPointer(1, dx)[!swap_uv];
168         }
169     }
merge(unsigned dx,const CPixel & spx,unsigned a,bool full)170     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
171     {
172         ::merge(getPointer(0, dx), spx.i, a);
173         if (full) {
174             ::merge(&getPointer(1, dx)[ swap_uv], spx.j, a);
175             ::merge(&getPointer(1, dx)[!swap_uv], spx.k, a);
176         }
177     }
isFull(unsigned dx) const178     bool isFull(unsigned dx) const
179     {
180         return (y % 2) == 0 && ((x + dx) % 2) == 0;
181     }
nextLine()182     void nextLine()
183     {
184         y++;
185         data[0] += picture->p[0].i_pitch;
186         if ((y % 2) == 0)
187             data[1] += picture->p[1].i_pitch;
188     }
189 private:
getPointer(unsigned plane,unsigned dx) const190     uint8_t *getPointer(unsigned plane, unsigned dx) const
191     {
192         if (plane == 0)
193             return &data[plane][x + dx];
194         else
195             return &data[plane][(x + dx) / 2 * 2];
196     }
197     uint8_t *data[2];
198 };
199 
200 template <unsigned offset_y, unsigned offset_u, unsigned offset_v>
201 class CPictureYUVPacked : public CPicture {
202 public:
CPictureYUVPacked(const CPicture & cfg)203     CPictureYUVPacked(const CPicture &cfg) : CPicture(cfg)
204     {
205         data = CPicture::getLine<1>(0);
206     }
get(CPixel * px,unsigned dx,bool full=true) const207     void get(CPixel *px, unsigned dx, bool full = true) const
208     {
209         uint8_t *data = getPointer(dx);
210         px->i = data[offset_y];
211         if (full) {
212             px->j = data[offset_u];
213             px->k = data[offset_v];
214         }
215     }
merge(unsigned dx,const CPixel & spx,unsigned a,bool full)216     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
217     {
218         uint8_t *data = getPointer(dx);
219         ::merge(&data[offset_y], spx.i, a);
220         if (full) {
221             ::merge(&data[offset_u], spx.j, a);
222             ::merge(&data[offset_v], spx.k, a);
223         }
224     }
isFull(unsigned dx) const225     bool isFull(unsigned dx) const
226     {
227         return ((x + dx) % 2) == 0;
228     }
nextLine()229     void nextLine()
230     {
231         y++;
232         data += picture->p[0].i_pitch;
233     }
234 private:
getPointer(unsigned dx) const235     uint8_t *getPointer(unsigned dx) const
236     {
237         return &data[(x + dx) * 2];
238     }
239     uint8_t *data;
240 };
241 
242 class CPictureYUVP : public CPicture {
243 public:
CPictureYUVP(const CPicture & cfg)244     CPictureYUVP(const CPicture &cfg) : CPicture(cfg)
245     {
246         data = CPicture::getLine<1>(0);
247     }
get(CPixel * px,unsigned dx,bool=true) const248     void get(CPixel *px, unsigned dx, bool = true) const
249     {
250         px->i = *getPointer(dx);
251     }
nextLine()252     void nextLine()
253     {
254         y++;
255         data += picture->p[0].i_pitch;
256     }
257 private:
getPointer(unsigned dx) const258     uint8_t *getPointer(unsigned dx) const
259     {
260         return &data[x + dx];
261     }
262     uint8_t *data;
263 };
264 
265 template <unsigned bytes, bool has_alpha>
266 class CPictureRGBX : public CPicture {
267 public:
CPictureRGBX(const CPicture & cfg)268     CPictureRGBX(const CPicture &cfg) : CPicture(cfg)
269     {
270         if (has_alpha) {
271             if (fmt->i_chroma == VLC_CODEC_BGRA) {
272                 offset_r = 2;
273                 offset_g = 1;
274                 offset_b = 0;
275             } else {
276                 offset_r = 0;
277                 offset_g = 1;
278                 offset_b = 2;
279             }
280             offset_a = 3;
281         } else {
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;
286 #else
287             offset_r = fmt->i_lrshift / 8;
288             offset_g = fmt->i_lgshift / 8;
289             offset_b = fmt->i_lbshift / 8;
290 #endif
291         }
292         data = CPicture::getLine<1>(0);
293     }
get(CPixel * px,unsigned dx,bool=true) const294     void get(CPixel *px, unsigned dx, bool = true) const
295     {
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];
300         if (has_alpha)
301             px->a = src[offset_a];
302     }
merge(unsigned dx,const CPixel & spx,unsigned a,bool)303     void merge(unsigned dx, const CPixel &spx, unsigned a, bool)
304     {
305         uint8_t *dst = getPointer(dx);
306         if (has_alpha) {
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.
312 
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);
324         } else {
325             ::merge(&dst[offset_r], spx.i, a);
326             ::merge(&dst[offset_g], spx.j, a);
327             ::merge(&dst[offset_b], spx.k, a);
328         }
329     }
nextLine()330     void nextLine()
331     {
332         y++;
333         data += picture->p[0].i_pitch;
334     }
335 private:
getPointer(unsigned dx) const336     uint8_t *getPointer(unsigned dx) const
337     {
338         return &data[(x + dx) * bytes];
339     }
340     unsigned offset_r;
341     unsigned offset_g;
342     unsigned offset_b;
343     unsigned offset_a;
344     uint8_t *data;
345 };
346 
347 class CPictureRGB16 : public CPicture {
348 public:
CPictureRGB16(const CPicture & cfg)349     CPictureRGB16(const CPicture &cfg) : CPicture(cfg)
350     {
351         data = CPicture::getLine<1>(0);
352     }
get(CPixel * px,unsigned dx,bool=true) const353     void get(CPixel *px, unsigned dx, bool = true) const
354     {
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;
359     }
merge(unsigned dx,const CPixel & spx,unsigned a,bool full)360     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
361     {
362         CPixel dpx;
363         get(&dpx, dx, full);
364 
365         ::merge(&dpx.i, spx.i, a);
366         ::merge(&dpx.j, spx.j, a);
367         ::merge(&dpx.k, spx.k, a);
368 
369         *getPointer(dx) = (dpx.i << fmt->i_lrshift) |
370                           (dpx.j << fmt->i_lgshift) |
371                           (dpx.k << fmt->i_lbshift);
372     }
nextLine()373     void nextLine()
374     {
375         y++;
376         data += picture->p[0].i_pitch;
377     }
378 private:
getPointer(unsigned dx) const379     uint16_t *getPointer(unsigned dx) const
380     {
381         return (uint16_t*)&data[(x + dx) * 2];
382     }
383     uint8_t *data;
384 };
385 
386 typedef CPictureYUVPlanar<uint8_t,  1,1, true,  false> CPictureYUVA;
387 
388 typedef CPictureYUVPlanar<uint8_t,  4,4, false, true>  CPictureYV9;
389 typedef CPictureYUVPlanar<uint8_t,  4,4, false, false> CPictureI410_8;
390 
391 typedef CPictureYUVPlanar<uint8_t,  4,1, false, false> CPictureI411_8;
392 
393 typedef CPictureYUVSemiPlanar<false>                   CPictureNV12;
394 typedef CPictureYUVSemiPlanar<true>                    CPictureNV21;
395 
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;
399 
400 typedef CPictureYUVPlanar<uint8_t,  2,1, false, false> CPictureI422_8;
401 typedef CPictureYUVPlanar<uint16_t, 2,1, false, false> CPictureI422_16;
402 
403 typedef CPictureYUVPlanar<uint8_t,  1,1, false, false> CPictureI444_8;
404 typedef CPictureYUVPlanar<uint16_t, 1,1, false, false> CPictureI444_16;
405 
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;
410 
411 typedef CPictureRGBX<4, true>  CPictureRGBA;
412 typedef CPictureRGBX<4, true>  CPictureBGRA;
413 typedef CPictureRGBX<4, false> CPictureRGB32;
414 typedef CPictureRGBX<3, false> CPictureRGB24;
415 
416 struct convertNone {
convertNoneconvertNone417     convertNone(const video_format_t *, const video_format_t *) {}
operator ()convertNone418     void operator()(CPixel &)
419     {
420     }
421 };
422 
423 template <unsigned dst, unsigned src>
424 struct convertBits {
convertBitsconvertBits425     convertBits(const video_format_t *, const video_format_t *) {}
operator ()convertBits426     void operator()(CPixel &p)
427     {
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);
431     }
432 };
433 typedef convertBits< 9, 8> convert8To9Bits;
434 typedef convertBits<10, 8> convert8To10Bits;
435 typedef convertBits<16, 8> convert8To16Bits;
436 
437 struct convertRgbToYuv8 {
convertRgbToYuv8convertRgbToYuv8438     convertRgbToYuv8(const video_format_t *, const video_format_t *) {}
operator ()convertRgbToYuv8439     void operator()(CPixel &p)
440     {
441         uint8_t y, u, v;
442         rgb_to_yuv(&y, &u, &v, p.i, p.j, p.k);
443         p.i = y;
444         p.j = u;
445         p.k = v;
446     }
447 };
448 
449 struct convertYuv8ToRgb {
convertYuv8ToRgbconvertYuv8ToRgb450     convertYuv8ToRgb(const video_format_t *, const video_format_t *) {}
operator ()convertYuv8ToRgb451     void operator()(CPixel &p)
452     {
453         int r, g, b;
454         yuv_to_rgb(&r, &g, &b, p.i, p.j, p.k);
455         p.i = r;
456         p.j = g;
457         p.k = b;
458     }
459 };
460 
461 struct convertRgbToRgbSmall {
convertRgbToRgbSmallconvertRgbToRgbSmall462     convertRgbToRgbSmall(const video_format_t *dst, const video_format_t *) : fmt(*dst) {}
operator ()convertRgbToRgbSmall463     void operator()(CPixel &p)
464     {
465         p.i >>= fmt.i_rrshift;
466         p.j >>= fmt.i_rgshift;
467         p.k >>= fmt.i_rbshift;
468     }
469 private:
470     const video_format_t &fmt;
471 };
472 
473 struct convertYuvpToAny {
operator ()convertYuvpToAny474     void operator()(CPixel &p)
475     {
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];
481     }
482 protected:
483     video_palette_t palette;
484 };
485 struct convertYuvpToYuva8 : public convertYuvpToAny {
convertYuvpToYuva8convertYuvpToYuva8486     convertYuvpToYuva8(const video_format_t *, const video_format_t *src)
487     {
488         palette = *src->p_palette;
489     }
490 };
491 struct convertYuvpToRgba : public convertYuvpToAny {
convertYuvpToRgbaconvertYuvpToRgba492     convertYuvpToRgba(const video_format_t *, const video_format_t *src)
493     {
494         const video_palette_t *p = src->p_palette;
495         for (int i = 0; i < p->i_entries; i++) {
496             int r, g, b;
497             yuv_to_rgb(&r, &g, &b,
498                        p->palette[i][0],
499                        p->palette[i][1],
500                        p->palette[i][2]);
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];
505         }
506     }
507 };
508 
509 template <class G, class F>
510 struct compose {
composecompose511     compose(const video_format_t *dst, const video_format_t *src) : f(dst, src), g(dst, src) {}
operator ()compose512     void operator()(CPixel &p)
513     {
514         f(p);
515         g(p);
516     }
517 private:
518     F f;
519     G g;
520 };
521 
522 template <class TDst, class TSrc, class TConvert>
Blend(const CPicture & dst_data,const CPicture & src_data,unsigned width,unsigned height,int alpha)523 void Blend(const CPicture &dst_data, const CPicture &src_data,
524            unsigned width, unsigned height, int alpha)
525 {
526     TSrc src(src_data);
527     TDst dst(dst_data);
528     TConvert convert(dst_data.getFormat(), src_data.getFormat());
529 
530     for (unsigned y = 0; y < height; y++) {
531         for (unsigned x = 0; x < width; x++) {
532             CPixel spx;
533 
534             src.get(&spx, x);
535             convert(spx);
536 
537             unsigned a = div255(alpha * spx.a);
538             if (a <= 0)
539                 continue;
540 
541             if (dst.isFull(x))
542                 dst.merge(x, spx, a, true);
543             else
544                 dst.merge(x, spx, a, false);
545         }
546         src.nextLine();
547         dst.nextLine();
548     }
549 }
550 
551 typedef void (*blend_function_t)(const CPicture &dst_data, const CPicture &src_data,
552                                  unsigned width, unsigned height, int alpha);
553 
554 static const struct {
555     vlc_fourcc_t     dst;
556     vlc_fourcc_t     src;
557     blend_function_t blend;
558 } blends[] = {
559 #undef RGB
560 #undef YUV
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> > }
569 
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),
576 
577     YUV(VLC_CODEC_YV9,      CPictureYV9,      convertNone),
578     YUV(VLC_CODEC_I410,     CPictureI410_8,   convertNone),
579 
580     YUV(VLC_CODEC_I411,     CPictureI411_8,   convertNone),
581 
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),
590 #else
591     YUV(VLC_CODEC_I420_9L,  CPictureI420_16,  convert8To9Bits),
592     YUV(VLC_CODEC_I420_10L, CPictureI420_16,  convert8To10Bits),
593 #endif
594 
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),
600 #else
601     YUV(VLC_CODEC_I422_9L,  CPictureI422_16,  convert8To9Bits),
602     YUV(VLC_CODEC_I422_10L, CPictureI422_16,  convert8To10Bits),
603 #endif
604 
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),
611 #else
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),
615 #endif
616 
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),
621 
622 #undef RGB
623 #undef YUV
624 };
625 
626 struct filter_sys_t {
filter_sys_tfilter_sys_t627     filter_sys_t() : blend(NULL)
628     {
629     }
630     blend_function_t blend;
631 };
632 
633 /**
634  * It blends 2 picture together.
635  */
Blend(filter_t * filter,picture_t * dst,const picture_t * src,int x_offset,int y_offset,int alpha)636 static void Blend(filter_t *filter,
637                   picture_t *dst, const picture_t *src,
638                   int x_offset, int y_offset, int alpha)
639 {
640     filter_sys_t *sys = filter->p_sys;
641 
642     if( x_offset < 0 || y_offset < 0 )
643     {
644         msg_Err( filter, "Blend cannot process negative offsets" );
645         return;
646     }
647 
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)
653         return;
654 
655     video_format_FixRgb(&filter->fmt_out.video);
656     video_format_FixRgb(&filter->fmt_in.video);
657 
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);
665 }
666 
Open(vlc_object_t * object)667 static int Open(vlc_object_t *object)
668 {
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;
672 
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;
677     }
678 
679     if (!sys->blend) {
680        msg_Err(filter, "no matching alpha blending routine (chroma: %4.4s -> %4.4s)",
681                (char *)&src, (char *)&dst);
682         delete sys;
683         return VLC_EGENERIC;
684     }
685 
686     filter->pf_video_blend = Blend;
687     filter->p_sys          = sys;
688     return VLC_SUCCESS;
689 }
690 
Close(vlc_object_t * object)691 static void Close(vlc_object_t *object)
692 {
693     filter_t *filter = (filter_t *)object;
694     delete filter->p_sys;
695 }
696 
697