]> git.sesse.net Git - vlc/blob - modules/video_filter/blend.cpp
decoder: fix data race in input_DecoderChangePause()
[vlc] / modules / video_filter / blend.cpp
1 /*****************************************************************************
2  * blend2.cpp: Blend one picture with alpha onto another picture
3  *****************************************************************************
4  * Copyright (C) 2012 Laurent Aimar
5  * $Id$
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 <assert.h>
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.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)
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>
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:
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     }
75     CPicture(const CPicture &src) : picture(src.picture), fmt(src.fmt), x(src.x), y(src.y)
76     {
77     }
78     const video_format_t *getFormat() const
79     {
80         return fmt;
81     }
82     bool isFull(unsigned) const
83     {
84         return true;
85     }
86
87 protected:
88     template <unsigned ry>
89     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:
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     }
110     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     }
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     }
128     bool isFull(unsigned dx) const
129     {
130         return (y % ry) == 0 && ((x + dx) % rx) == 0;
131     }
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:
144     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:
157     CPictureYUVSemiPlanar(const CPicture &cfg) : CPicture(cfg)
158     {
159         data[0] = CPicture::getLine<1>(0);
160         data[1] = CPicture::getLine<2>(1);
161     }
162     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     }
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     }
178     bool isFull(unsigned dx) const
179     {
180         return (y % 2) == 0 && ((x + dx) % 2) == 0;
181     }
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:
190     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:
203     CPictureYUVPacked(const CPicture &cfg) : CPicture(cfg)
204     {
205         data = CPicture::getLine<1>(0);
206     }
207     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     }
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     }
225     bool isFull(unsigned dx) const
226     {
227         return ((x + dx) % 2) == 0;
228     }
229     void nextLine()
230     {
231         y++;
232         data += picture->p[0].i_pitch;
233     }
234 private:
235     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:
244     CPictureYUVP(const CPicture &cfg) : CPicture(cfg)
245     {
246         data = CPicture::getLine<1>(0);
247     }
248     void get(CPixel *px, unsigned dx, bool = true) const
249     {
250         px->i = *getPointer(dx);
251     }
252     void nextLine()
253     {
254         y++;
255         data += picture->p[0].i_pitch;
256     }
257 private:
258     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:
268     CPictureRGBX(const CPicture &cfg) : CPicture(cfg)
269     {
270         if (has_alpha) {
271             offset_r = 0;
272             offset_g = 1;
273             offset_b = 2;
274             offset_a = 3;
275         } else {
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;
280 #else
281             offset_r = fmt->i_lrshift / 8;
282             offset_g = fmt->i_lgshift / 8;
283             offset_b = fmt->i_lbshift / 8;
284 #endif
285         }
286         data = CPicture::getLine<1>(0);
287     }
288     void get(CPixel *px, unsigned dx, bool = true) const
289     {
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];
294         if (has_alpha)
295             px->a = src[offset_a];
296     }
297     void merge(unsigned dx, const CPixel &spx, unsigned a, bool)
298     {
299         uint8_t *dst = getPointer(dx);
300         if (has_alpha) {
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.
306
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);
318         } else {
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         }
323     }
324     void nextLine()
325     {
326         y++;
327         data += picture->p[0].i_pitch;
328     }
329 private:
330     uint8_t *getPointer(unsigned dx) const
331     {
332         return &data[(x + dx) * bytes];
333     }
334     unsigned offset_r;
335     unsigned offset_g;
336     unsigned offset_b;
337     unsigned offset_a;
338     uint8_t *data;
339 };
340
341 class CPictureRGB16 : public CPicture {
342 public:
343     CPictureRGB16(const CPicture &cfg) : CPicture(cfg)
344     {
345         data = CPicture::getLine<1>(0);
346     }
347     void get(CPixel *px, unsigned dx, bool = true) const
348     {
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;
353     }
354     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
355     {
356         CPixel dpx;
357         get(&dpx, dx, full);
358
359         ::merge(&dpx.i, spx.i, a);
360         ::merge(&dpx.j, spx.j, a);
361         ::merge(&dpx.k, spx.k, a);
362
363         *getPointer(dx) = (dpx.i << fmt->i_lrshift) |
364                           (dpx.j << fmt->i_lgshift) |
365                           (dpx.k << fmt->i_lbshift);
366     }
367     void nextLine()
368     {
369         y++;
370         data += picture->p[0].i_pitch;
371     }
372 private:
373     uint16_t *getPointer(unsigned dx) const
374     {
375         return (uint16_t*)&data[(x + dx) * 2];
376     }
377     uint8_t *data;
378 };
379
380 typedef CPictureYUVPlanar<uint8_t,  1,1, true,  false> CPictureYUVA;
381
382 typedef CPictureYUVPlanar<uint8_t,  4,4, false, true>  CPictureYV9;
383 typedef CPictureYUVPlanar<uint8_t,  4,4, false, false> CPictureI410_8;
384
385 typedef CPictureYUVPlanar<uint8_t,  4,1, false, false> CPictureI411_8;
386
387 typedef CPictureYUVSemiPlanar<false>                   CPictureNV12;
388 typedef CPictureYUVSemiPlanar<true>                    CPictureNV21;
389
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;
393
394 typedef CPictureYUVPlanar<uint8_t,  2,1, false, false> CPictureI422_8;
395 typedef CPictureYUVPlanar<uint16_t, 2,1, false, false> CPictureI422_16;
396
397 typedef CPictureYUVPlanar<uint8_t,  1,1, false, false> CPictureI444_8;
398 typedef CPictureYUVPlanar<uint16_t, 1,1, false, false> CPictureI444_16;
399
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;
404
405 typedef CPictureRGBX<4, true>  CPictureRGBA;
406 typedef CPictureRGBX<4, false> CPictureRGB32;
407 typedef CPictureRGBX<3, false> CPictureRGB24;
408
409 struct convertNone {
410     convertNone(const video_format_t *, const video_format_t *) {}
411     void operator()(CPixel &)
412     {
413     }
414 };
415
416 template <unsigned dst, unsigned src>
417 struct convertBits {
418     convertBits(const video_format_t *, const video_format_t *) {}
419     void operator()(CPixel &p)
420     {
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);
424     }
425 };
426 typedef convertBits< 9, 8> convert8To9Bits;
427 typedef convertBits<10, 8> convert8To10Bits;
428 typedef convertBits<16, 8> convert8To16Bits;
429
430 struct convertRgbToYuv8 {
431     convertRgbToYuv8(const video_format_t *, const video_format_t *) {}
432     void operator()(CPixel &p)
433     {
434         uint8_t y, u, v;
435         rgb_to_yuv(&y, &u, &v, p.i, p.j, p.k);
436         p.i = y;
437         p.j = u;
438         p.k = v;
439     }
440 };
441
442 struct convertYuv8ToRgb {
443     convertYuv8ToRgb(const video_format_t *, const video_format_t *) {}
444     void operator()(CPixel &p)
445     {
446         int r, g, b;
447         yuv_to_rgb(&r, &g, &b, p.i, p.j, p.k);
448         p.i = r;
449         p.j = g;
450         p.k = b;
451     }
452 };
453
454 struct convertRgbToRgbSmall {
455     convertRgbToRgbSmall(const video_format_t *dst, const video_format_t *) : fmt(*dst) {}
456     void operator()(CPixel &p)
457     {
458         p.i >>= fmt.i_rrshift;
459         p.j >>= fmt.i_rgshift;
460         p.k >>= fmt.i_rbshift;
461     }
462 private:
463     const video_format_t &fmt;
464 };
465
466 struct convertYuvpToAny {
467     void operator()(CPixel &p)
468     {
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];
474     }
475 protected:
476     video_palette_t palette;
477 };
478 struct convertYuvpToYuva8 : public convertYuvpToAny {
479     convertYuvpToYuva8(const video_format_t *, const video_format_t *src)
480     {
481         palette = *src->p_palette;
482     }
483 };
484 struct convertYuvpToRgba : public convertYuvpToAny {
485     convertYuvpToRgba(const video_format_t *, const video_format_t *src)
486     {
487         const video_palette_t *p = src->p_palette;
488         for (int i = 0; i < p->i_entries; i++) {
489             int r, g, b;
490             yuv_to_rgb(&r, &g, &b,
491                        p->palette[i][0],
492                        p->palette[i][1],
493                        p->palette[i][2]);
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];
498         }
499     }
500 };
501
502 template <class G, class F>
503 struct compose {
504     compose(const video_format_t *dst, const video_format_t *src) : f(dst, src), g(dst, src) {}
505     void operator()(CPixel &p)
506     {
507         f(p);
508         g(p);
509     }
510 private:
511     F f;
512     G g;
513 };
514
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)
518 {
519     TSrc src(src_data);
520     TDst dst(dst_data);
521     TConvert convert(dst_data.getFormat(), src_data.getFormat());
522
523     for (unsigned y = 0; y < height; y++) {
524         for (unsigned x = 0; x < width; x++) {
525             CPixel spx;
526
527             src.get(&spx, x);
528             convert(spx);
529
530             unsigned a = div255(alpha * spx.a);
531             if (a <= 0)
532                 continue;
533
534             if (dst.isFull(x))
535                 dst.merge(x, spx, a, true);
536             else
537                 dst.merge(x, spx, a, false);
538         }
539         src.nextLine();
540         dst.nextLine();
541     }
542 }
543
544 typedef void (*blend_function_t)(const CPicture &dst_data, const CPicture &src_data,
545                                  unsigned width, unsigned height, int alpha);
546
547 static const struct {
548     vlc_fourcc_t     dst;
549     vlc_fourcc_t     src;
550     blend_function_t blend;
551 } blends[] = {
552 #undef RGB
553 #undef YUV
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> > }
562
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),
568
569     YUV(VLC_CODEC_YV9,      CPictureYV9,      convertNone),
570     YUV(VLC_CODEC_I410,     CPictureI410_8,   convertNone),
571
572     YUV(VLC_CODEC_I411,     CPictureI411_8,   convertNone),
573
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),
582 #else
583     YUV(VLC_CODEC_I420_9L,  CPictureI420_16,  convert8To9Bits),
584     YUV(VLC_CODEC_I420_10L, CPictureI420_16,  convert8To10Bits),
585 #endif
586
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),
592 #else
593     YUV(VLC_CODEC_I422_9L,  CPictureI422_16,  convert8To9Bits),
594     YUV(VLC_CODEC_I422_10L, CPictureI422_16,  convert8To10Bits),
595 #endif
596
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),
603 #else
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),
607 #endif
608
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),
613
614 #undef RGB
615 #undef YUV
616 };
617
618 struct filter_sys_t {
619     filter_sys_t() : blend(NULL)
620     {
621     }
622     blend_function_t blend;
623 };
624
625 /**
626  * It blends 2 picture together.
627  */
628 static void Blend(filter_t *filter,
629                   picture_t *dst, const picture_t *src,
630                   int x_offset, int y_offset, int alpha)
631 {
632     filter_sys_t *sys = filter->p_sys;
633
634     if( x_offset < 0 || y_offset < 0 )
635     {
636         msg_Err( filter, "Blend cannot process negative offsets" );
637         return;
638     }
639
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)
645         return;
646
647     video_format_FixRgb(&filter->fmt_out.video);
648     video_format_FixRgb(&filter->fmt_in.video);
649
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);
657 }
658
659 static int Open(vlc_object_t *object)
660 {
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;
664
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;
669     }
670
671     if (!sys->blend) {
672        msg_Err(filter, "no matching alpha blending routine (chroma: %4.4s -> %4.4s)",
673                (char *)&src, (char *)&dst);
674         delete sys;
675         return VLC_EGENERIC;
676     }
677
678     filter->pf_video_blend = Blend;
679     filter->p_sys          = sys;
680     return VLC_SUCCESS;
681 }
682
683 static void Close(vlc_object_t *object)
684 {
685     filter_t *filter = (filter_t *)object;
686     delete filter->p_sys;
687 }
688