upnp: remove m_server_list_lock, p_server_list is already protected by s_lock
[vlc.git] / 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 <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include "filter_picture.h"
35
36 /*****************************************************************************
37  * Module descriptor
38  *****************************************************************************/
39 static int  Open (vlc_object_t *);
40 static void Close(vlc_object_t *);
41
42 vlc_module_begin()
43     set_description(N_("Video pictures blending"))
44     set_capability("video blending", 100)
45     set_callbacks(Open, Close)
46 vlc_module_end()
47
48 static inline unsigned div255(unsigned v)
49 {
50     /* It is exact for 8 bits, and has a max error of 1 for 9 and 10 bits
51      * while respecting full opacity/transparency */
52     return ((v >> 8) + v + 1) >> 8;
53     //return v / 255;
54 }
55
56 template <typename T>
57 void merge(T *dst, unsigned src, unsigned f)
58 {
59     *dst = div255((255 - f) * (*dst) + src * f);
60 }
61
62 struct CPixel {
63     unsigned i, j, k;
64     unsigned a;
65 };
66
67 class CPicture {
68 public:
69     CPicture(const picture_t *picture,
70              const video_format_t *fmt,
71              unsigned x, unsigned y) : picture(picture), fmt(fmt), x(x), y(y)
72     {
73     }
74     CPicture(const CPicture &src) : picture(src.picture), fmt(src.fmt), x(src.x), y(src.y)
75     {
76     }
77     const video_format_t *getFormat() const
78     {
79         return fmt;
80     }
81     bool isFull(unsigned) const
82     {
83         return true;
84     }
85
86 protected:
87     template <unsigned ry>
88     uint8_t *getLine(unsigned plane = 0) const
89     {
90         return &picture->p[plane].p_pixels[(y / ry) * picture->p[plane].i_pitch];
91     }
92     const picture_t *picture;
93     const video_format_t *fmt;
94     unsigned x;
95     unsigned y;
96 };
97
98 template <typename pixel, unsigned rx, unsigned ry, bool has_alpha, bool swap_uv>
99 class CPictureYUVPlanar : public CPicture {
100 public:
101     CPictureYUVPlanar(const CPicture &cfg) : CPicture(cfg)
102     {
103         data[0] = CPicture::getLine< 1>(0);
104         data[1] = CPicture::getLine<ry>(swap_uv ? 2 : 1);
105         data[2] = CPicture::getLine<ry>(swap_uv ? 1 : 2);
106         if (has_alpha)
107             data[3] = CPicture::getLine<1>(3);
108     }
109     void get(CPixel *px, unsigned dx, bool full = true) const
110     {
111         px->i = *getPointer(0, dx);
112         if (full) {
113             px->j = *getPointer(1, dx);
114             px->k = *getPointer(2, dx);
115         }
116         if (has_alpha)
117             px->a = *getPointer(3, dx);
118     }
119     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
120     {
121         ::merge(getPointer(0, dx), spx.i, a);
122         if (full) {
123             ::merge(getPointer(1, dx), spx.j, a);
124             ::merge(getPointer(2, dx), spx.k, a);
125         }
126     }
127     bool isFull(unsigned dx) const
128     {
129         return (y % ry) == 0 && ((x + dx) % rx) == 0;
130     }
131     void nextLine()
132     {
133         y++;
134         data[0] += picture->p[0].i_pitch;
135         if ((y % ry) == 0) {
136             data[1] += picture->p[swap_uv ? 2 : 1].i_pitch;
137             data[2] += picture->p[swap_uv ? 1 : 2].i_pitch;
138         }
139         if (has_alpha)
140             data[3] += picture->p[3].i_pitch;
141     }
142 private:
143     pixel *getPointer(unsigned plane, unsigned dx) const
144     {
145         if (plane == 1 || plane == 2)
146             return (pixel*)&data[plane][(x + dx) / rx * sizeof(pixel)];
147         else
148             return (pixel*)&data[plane][(x + dx) /  1 * sizeof(pixel)];
149     }
150     uint8_t *data[4];
151 };
152
153 template <bool swap_uv>
154 class CPictureYUVSemiPlanar : public CPicture {
155 public:
156     CPictureYUVSemiPlanar(const CPicture &cfg) : CPicture(cfg)
157     {
158         data[0] = CPicture::getLine<1>(0);
159         data[1] = CPicture::getLine<2>(1);
160     }
161     void get(CPixel *px, unsigned dx, bool full = true) const
162     {
163         px->i = *getPointer(0, dx);
164         if (full) {
165             px->j = getPointer(1, dx)[swap_uv];
166             px->k = getPointer(1, dx)[!swap_uv];
167         }
168     }
169     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
170     {
171         ::merge(getPointer(0, dx), spx.i, a);
172         if (full) {
173             ::merge(&getPointer(1, dx)[ swap_uv], spx.j, a);
174             ::merge(&getPointer(1, dx)[!swap_uv], spx.k, a);
175         }
176     }
177     bool isFull(unsigned dx) const
178     {
179         return (y % 2) == 0 && ((x + dx) % 2) == 0;
180     }
181     void nextLine()
182     {
183         y++;
184         data[0] += picture->p[0].i_pitch;
185         if ((y % 2) == 0)
186             data[1] += picture->p[1].i_pitch;
187     }
188 private:
189     uint8_t *getPointer(unsigned plane, unsigned dx) const
190     {
191         if (plane == 0)
192             return &data[plane][x + dx];
193         else
194             return &data[plane][(x + dx) / 2 * 2];
195     }
196     uint8_t *data[2];
197 };
198
199 template <unsigned offset_y, unsigned offset_u, unsigned offset_v>
200 class CPictureYUVPacked : public CPicture {
201 public:
202     CPictureYUVPacked(const CPicture &cfg) : CPicture(cfg)
203     {
204         data = CPicture::getLine<1>(0);
205     }
206     void get(CPixel *px, unsigned dx, bool full = true) const
207     {
208         uint8_t *data = getPointer(dx);
209         px->i = data[offset_y];
210         if (full) {
211             px->j = data[offset_u];
212             px->k = data[offset_v];
213         }
214     }
215     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
216     {
217         uint8_t *data = getPointer(dx);
218         ::merge(&data[offset_y], spx.i, a);
219         if (full) {
220             ::merge(&data[offset_u], spx.j, a);
221             ::merge(&data[offset_v], spx.k, a);
222         }
223     }
224     bool isFull(unsigned dx) const
225     {
226         return ((x + dx) % 2) == 0;
227     }
228     void nextLine()
229     {
230         y++;
231         data += picture->p[0].i_pitch;
232     }
233 private:
234     uint8_t *getPointer(unsigned dx) const
235     {
236         return &data[(x + dx) * 2];
237     }
238     uint8_t *data;
239 };
240
241 class CPictureYUVP : public CPicture {
242 public:
243     CPictureYUVP(const CPicture &cfg) : CPicture(cfg)
244     {
245         data = CPicture::getLine<1>(0);
246     }
247     void get(CPixel *px, unsigned dx, bool = true) const
248     {
249         px->i = *getPointer(dx);
250     }
251     void nextLine()
252     {
253         y++;
254         data += picture->p[0].i_pitch;
255     }
256 private:
257     uint8_t *getPointer(unsigned dx) const
258     {
259         return &data[x + dx];
260     }
261     uint8_t *data;
262 };
263
264 template <unsigned bytes, bool has_alpha>
265 class CPictureRGBX : public CPicture {
266 public:
267     CPictureRGBX(const CPicture &cfg) : CPicture(cfg)
268     {
269         if (has_alpha) {
270             if (fmt->i_chroma == VLC_CODEC_BGRA) {
271                 offset_r = 2;
272                 offset_g = 1;
273                 offset_b = 0;
274             } else {
275                 offset_r = 0;
276                 offset_g = 1;
277                 offset_b = 2;
278             }
279             offset_a = 3;
280         } else {
281 #ifdef WORDS_BIGENDIAN
282             offset_r = (8 * bytes - fmt->i_lrshift) / 8;
283             offset_g = (8 * bytes - fmt->i_lgshift) / 8;
284             offset_b = (8 * bytes - fmt->i_lbshift) / 8;
285 #else
286             offset_r = fmt->i_lrshift / 8;
287             offset_g = fmt->i_lgshift / 8;
288             offset_b = fmt->i_lbshift / 8;
289 #endif
290         }
291         data = CPicture::getLine<1>(0);
292     }
293     void get(CPixel *px, unsigned dx, bool = true) const
294     {
295         const uint8_t *src = getPointer(dx);
296         px->i = src[offset_r];
297         px->j = src[offset_g];
298         px->k = src[offset_b];
299         if (has_alpha)
300             px->a = src[offset_a];
301     }
302     void merge(unsigned dx, const CPixel &spx, unsigned a, bool)
303     {
304         uint8_t *dst = getPointer(dx);
305         if (has_alpha) {
306             // Handle different cases of existing alpha in the
307             // destination buffer. If the existing alpha is 0,
308             // the RGB components should be copied as is and
309             // alpha set to 'a'. If the existing alpha is 255,
310             // this should behave just as the non-alpha case below.
311
312             // First blend the existing color based on its
313             // alpha with the incoming color.
314             ::merge(&dst[offset_r], spx.i, 255 - dst[offset_a]);
315             ::merge(&dst[offset_g], spx.j, 255 - dst[offset_a]);
316             ::merge(&dst[offset_b], spx.k, 255 - dst[offset_a]);
317             // Now blend in the new color on top with the normal formulas.
318             ::merge(&dst[offset_r], spx.i, a);
319             ::merge(&dst[offset_g], spx.j, a);
320             ::merge(&dst[offset_b], spx.k, a);
321             // Finally set dst_a = (255 * src_a + prev_a * (255 - src_a))/255.
322             ::merge(&dst[offset_a], 255, a);
323         } else {
324             ::merge(&dst[offset_r], spx.i, a);
325             ::merge(&dst[offset_g], spx.j, a);
326             ::merge(&dst[offset_b], spx.k, a);
327         }
328     }
329     void nextLine()
330     {
331         y++;
332         data += picture->p[0].i_pitch;
333     }
334 private:
335     uint8_t *getPointer(unsigned dx) const
336     {
337         return &data[(x + dx) * bytes];
338     }
339     unsigned offset_r;
340     unsigned offset_g;
341     unsigned offset_b;
342     unsigned offset_a;
343     uint8_t *data;
344 };
345
346 class CPictureRGB16 : public CPicture {
347 public:
348     CPictureRGB16(const CPicture &cfg) : CPicture(cfg)
349     {
350         data = CPicture::getLine<1>(0);
351     }
352     void get(CPixel *px, unsigned dx, bool = true) const
353     {
354         const uint16_t data = *getPointer(dx);
355         px->i = (data & fmt->i_rmask) >> fmt->i_lrshift;
356         px->j = (data & fmt->i_gmask) >> fmt->i_lgshift;
357         px->k = (data & fmt->i_bmask) >> fmt->i_lbshift;
358     }
359     void merge(unsigned dx, const CPixel &spx, unsigned a, bool full)
360     {
361         CPixel dpx;
362         get(&dpx, dx, full);
363
364         ::merge(&dpx.i, spx.i, a);
365         ::merge(&dpx.j, spx.j, a);
366         ::merge(&dpx.k, spx.k, a);
367
368         *getPointer(dx) = (dpx.i << fmt->i_lrshift) |
369                           (dpx.j << fmt->i_lgshift) |
370                           (dpx.k << fmt->i_lbshift);
371     }
372     void nextLine()
373     {
374         y++;
375         data += picture->p[0].i_pitch;
376     }
377 private:
378     uint16_t *getPointer(unsigned dx) const
379     {
380         return (uint16_t*)&data[(x + dx) * 2];
381     }
382     uint8_t *data;
383 };
384
385 typedef CPictureYUVPlanar<uint8_t,  1,1, true,  false> CPictureYUVA;
386
387 typedef CPictureYUVPlanar<uint8_t,  4,4, false, true>  CPictureYV9;
388 typedef CPictureYUVPlanar<uint8_t,  4,4, false, false> CPictureI410_8;
389
390 typedef CPictureYUVPlanar<uint8_t,  4,1, false, false> CPictureI411_8;
391
392 typedef CPictureYUVSemiPlanar<false>                   CPictureNV12;
393 typedef CPictureYUVSemiPlanar<true>                    CPictureNV21;
394
395 typedef CPictureYUVPlanar<uint8_t,  2,2, false, true>  CPictureYV12;
396 typedef CPictureYUVPlanar<uint8_t,  2,2, false, false> CPictureI420_8;
397 typedef CPictureYUVPlanar<uint16_t, 2,2, false, false> CPictureI420_16;
398
399 typedef CPictureYUVPlanar<uint8_t,  2,1, false, false> CPictureI422_8;
400 typedef CPictureYUVPlanar<uint16_t, 2,1, false, false> CPictureI422_16;
401
402 typedef CPictureYUVPlanar<uint8_t,  1,1, false, false> CPictureI444_8;
403 typedef CPictureYUVPlanar<uint16_t, 1,1, false, false> CPictureI444_16;
404
405 typedef CPictureYUVPacked<0, 1, 3> CPictureYUYV;
406 typedef CPictureYUVPacked<1, 0, 2> CPictureUYVY;
407 typedef CPictureYUVPacked<0, 3, 1> CPictureYVYU;
408 typedef CPictureYUVPacked<1, 2, 0> CPictureVYUY;
409
410 typedef CPictureRGBX<4, true>  CPictureRGBA;
411 typedef CPictureRGBX<4, true>  CPictureBGRA;
412 typedef CPictureRGBX<4, false> CPictureRGB32;
413 typedef CPictureRGBX<3, false> CPictureRGB24;
414
415 struct convertNone {
416     convertNone(const video_format_t *, const video_format_t *) {}
417     void operator()(CPixel &)
418     {
419     }
420 };
421
422 template <unsigned dst, unsigned src>
423 struct convertBits {
424     convertBits(const video_format_t *, const video_format_t *) {}
425     void operator()(CPixel &p)
426     {
427         p.i = p.i * ((1 << dst) - 1) /  ((1 << src) - 1);
428         p.j = p.j * ((1 << dst) - 1) /  ((1 << src) - 1);
429         p.k = p.k * ((1 << dst) - 1) /  ((1 << src) - 1);
430     }
431 };
432 typedef convertBits< 9, 8> convert8To9Bits;
433 typedef convertBits<10, 8> convert8To10Bits;
434 typedef convertBits<16, 8> convert8To16Bits;
435
436 struct convertRgbToYuv8 {
437     convertRgbToYuv8(const video_format_t *, const video_format_t *) {}
438     void operator()(CPixel &p)
439     {
440         uint8_t y, u, v;
441         rgb_to_yuv(&y, &u, &v, p.i, p.j, p.k);
442         p.i = y;
443         p.j = u;
444         p.k = v;
445     }
446 };
447
448 struct convertYuv8ToRgb {
449     convertYuv8ToRgb(const video_format_t *, const video_format_t *) {}
450     void operator()(CPixel &p)
451     {
452         int r, g, b;
453         yuv_to_rgb(&r, &g, &b, p.i, p.j, p.k);
454         p.i = r;
455         p.j = g;
456         p.k = b;
457     }
458 };
459
460 struct convertRgbToRgbSmall {
461     convertRgbToRgbSmall(const video_format_t *dst, const video_format_t *) : fmt(*dst) {}
462     void operator()(CPixel &p)
463     {
464         p.i >>= fmt.i_rrshift;
465         p.j >>= fmt.i_rgshift;
466         p.k >>= fmt.i_rbshift;
467     }
468 private:
469     const video_format_t &fmt;
470 };
471
472 struct convertYuvpToAny {
473     void operator()(CPixel &p)
474     {
475         unsigned index = p.i;
476         p.i = palette.palette[index][0];
477         p.j = palette.palette[index][1];
478         p.k = palette.palette[index][2];
479         p.a = palette.palette[index][3];
480     }
481 protected:
482     video_palette_t palette;
483 };
484 struct convertYuvpToYuva8 : public convertYuvpToAny {
485     convertYuvpToYuva8(const video_format_t *, const video_format_t *src)
486     {
487         palette = *src->p_palette;
488     }
489 };
490 struct convertYuvpToRgba : public convertYuvpToAny {
491     convertYuvpToRgba(const video_format_t *, const video_format_t *src)
492     {
493         const video_palette_t *p = src->p_palette;
494         for (int i = 0; i < p->i_entries; i++) {
495             int r, g, b;
496             yuv_to_rgb(&r, &g, &b,
497                        p->palette[i][0],
498                        p->palette[i][1],
499                        p->palette[i][2]);
500             palette.palette[i][0] = r;
501             palette.palette[i][1] = g;
502             palette.palette[i][2] = b;
503             palette.palette[i][3] = p->palette[i][3];
504         }
505     }
506 };
507
508 template <class G, class F>
509 struct compose {
510     compose(const video_format_t *dst, const video_format_t *src) : f(dst, src), g(dst, src) {}
511     void operator()(CPixel &p)
512     {
513         f(p);
514         g(p);
515     }
516 private:
517     F f;
518     G g;
519 };
520
521 template <class TDst, class TSrc, class TConvert>
522 void Blend(const CPicture &dst_data, const CPicture &src_data,
523            unsigned width, unsigned height, int alpha)
524 {
525     TSrc src(src_data);
526     TDst dst(dst_data);
527     TConvert convert(dst_data.getFormat(), src_data.getFormat());
528
529     for (unsigned y = 0; y < height; y++) {
530         for (unsigned x = 0; x < width; x++) {
531             CPixel spx;
532
533             src.get(&spx, x);
534             convert(spx);
535
536             unsigned a = div255(alpha * spx.a);
537             if (a <= 0)
538                 continue;
539
540             if (dst.isFull(x))
541                 dst.merge(x, spx, a, true);
542             else
543                 dst.merge(x, spx, a, false);
544         }
545         src.nextLine();
546         dst.nextLine();
547     }
548 }
549
550 typedef void (*blend_function_t)(const CPicture &dst_data, const CPicture &src_data,
551                                  unsigned width, unsigned height, int alpha);
552
553 static const struct {
554     vlc_fourcc_t     dst;
555     vlc_fourcc_t     src;
556     blend_function_t blend;
557 } blends[] = {
558 #undef RGB
559 #undef YUV
560 #define RGB(csp, picture, cvt) \
561     { csp, VLC_CODEC_YUVA, Blend<picture, CPictureYUVA, compose<cvt, convertYuv8ToRgb> > }, \
562     { csp, VLC_CODEC_RGBA, Blend<picture, CPictureRGBA, compose<cvt, convertNone> > }, \
563     { csp, VLC_CODEC_YUVP, Blend<picture, CPictureYUVP, compose<cvt, convertYuvpToRgba> > }
564 #define YUV(csp, picture, cvt) \
565     { csp, VLC_CODEC_YUVA, Blend<picture, CPictureYUVA, compose<cvt, convertNone> > }, \
566     { csp, VLC_CODEC_RGBA, Blend<picture, CPictureRGBA, compose<cvt, convertRgbToYuv8> > }, \
567     { csp, VLC_CODEC_YUVP, Blend<picture, CPictureYUVP, compose<cvt, convertYuvpToYuva8> > }
568
569     RGB(VLC_CODEC_RGB15,    CPictureRGB16,    convertRgbToRgbSmall),
570     RGB(VLC_CODEC_RGB16,    CPictureRGB16,    convertRgbToRgbSmall),
571     RGB(VLC_CODEC_RGB24,    CPictureRGB24,    convertNone),
572     RGB(VLC_CODEC_RGB32,    CPictureRGB32,    convertNone),
573     RGB(VLC_CODEC_RGBA,     CPictureRGBA,     convertNone),
574     RGB(VLC_CODEC_BGRA,     CPictureBGRA,     convertNone),
575
576     YUV(VLC_CODEC_YV9,      CPictureYV9,      convertNone),
577     YUV(VLC_CODEC_I410,     CPictureI410_8,   convertNone),
578
579     YUV(VLC_CODEC_I411,     CPictureI411_8,   convertNone),
580
581     YUV(VLC_CODEC_YV12,     CPictureYV12,     convertNone),
582     YUV(VLC_CODEC_NV12,     CPictureNV12,     convertNone),
583     YUV(VLC_CODEC_NV21,     CPictureNV21,     convertNone),
584     YUV(VLC_CODEC_J420,     CPictureI420_8,   convertNone),
585     YUV(VLC_CODEC_I420,     CPictureI420_8,   convertNone),
586 #ifdef WORDS_BIGENDIAN
587     YUV(VLC_CODEC_I420_9B,  CPictureI420_16,  convert8To9Bits),
588     YUV(VLC_CODEC_I420_10B, CPictureI420_16,  convert8To10Bits),
589 #else
590     YUV(VLC_CODEC_I420_9L,  CPictureI420_16,  convert8To9Bits),
591     YUV(VLC_CODEC_I420_10L, CPictureI420_16,  convert8To10Bits),
592 #endif
593
594     YUV(VLC_CODEC_J422,     CPictureI422_8,   convertNone),
595     YUV(VLC_CODEC_I422,     CPictureI422_8,   convertNone),
596 #ifdef WORDS_BIGENDIAN
597     YUV(VLC_CODEC_I422_9B,  CPictureI422_16,  convert8To9Bits),
598     YUV(VLC_CODEC_I422_10B, CPictureI422_16,  convert8To10Bits),
599 #else
600     YUV(VLC_CODEC_I422_9L,  CPictureI422_16,  convert8To9Bits),
601     YUV(VLC_CODEC_I422_10L, CPictureI422_16,  convert8To10Bits),
602 #endif
603
604     YUV(VLC_CODEC_J444,     CPictureI444_8,   convertNone),
605     YUV(VLC_CODEC_I444,     CPictureI444_8,   convertNone),
606 #ifdef WORDS_BIGENDIAN
607     YUV(VLC_CODEC_I444_9B,  CPictureI444_16,  convert8To9Bits),
608     YUV(VLC_CODEC_I444_10B, CPictureI444_16,  convert8To10Bits),
609     YUV(VLC_CODEC_I444_16B, CPictureI444_16,  convert8To16Bits),
610 #else
611     YUV(VLC_CODEC_I444_9L,  CPictureI444_16,  convert8To9Bits),
612     YUV(VLC_CODEC_I444_10L, CPictureI444_16,  convert8To10Bits),
613     YUV(VLC_CODEC_I444_16L, CPictureI444_16,  convert8To16Bits),
614 #endif
615
616     YUV(VLC_CODEC_YUYV,     CPictureYUYV,     convertNone),
617     YUV(VLC_CODEC_UYVY,     CPictureUYVY,     convertNone),
618     YUV(VLC_CODEC_YVYU,     CPictureYVYU,     convertNone),
619     YUV(VLC_CODEC_VYUY,     CPictureVYUY,     convertNone),
620
621 #undef RGB
622 #undef YUV
623 };
624
625 struct filter_sys_t {
626     filter_sys_t() : blend(NULL)
627     {
628     }
629     blend_function_t blend;
630 };
631
632 /**
633  * It blends 2 picture together.
634  */
635 static void Blend(filter_t *filter,
636                   picture_t *dst, const picture_t *src,
637                   int x_offset, int y_offset, int alpha)
638 {
639     filter_sys_t *sys = filter->p_sys;
640
641     if( x_offset < 0 || y_offset < 0 )
642     {
643         msg_Err( filter, "Blend cannot process negative offsets" );
644         return;
645     }
646
647     int width  = __MIN((int)filter->fmt_out.video.i_visible_width - x_offset,
648                        (int)filter->fmt_in.video.i_visible_width);
649     int height = __MIN((int)filter->fmt_out.video.i_visible_height - y_offset,
650                        (int)filter->fmt_in.video.i_visible_height);
651     if (width <= 0 || height <= 0 || alpha <= 0)
652         return;
653
654     video_format_FixRgb(&filter->fmt_out.video);
655     video_format_FixRgb(&filter->fmt_in.video);
656
657     sys->blend(CPicture(dst, &filter->fmt_out.video,
658                         filter->fmt_out.video.i_x_offset + x_offset,
659                         filter->fmt_out.video.i_y_offset + y_offset),
660                CPicture(src, &filter->fmt_in.video,
661                         filter->fmt_in.video.i_x_offset,
662                         filter->fmt_in.video.i_y_offset),
663                width, height, alpha);
664 }
665
666 static int Open(vlc_object_t *object)
667 {
668     filter_t *filter = (filter_t *)object;
669     const vlc_fourcc_t src = filter->fmt_in.video.i_chroma;
670     const vlc_fourcc_t dst = filter->fmt_out.video.i_chroma;
671
672     filter_sys_t *sys = new filter_sys_t();
673     for (size_t i = 0; i < sizeof(blends) / sizeof(*blends); i++) {
674         if (blends[i].src == src && blends[i].dst == dst)
675             sys->blend = blends[i].blend;
676     }
677
678     if (!sys->blend) {
679        msg_Err(filter, "no matching alpha blending routine (chroma: %4.4s -> %4.4s)",
680                (char *)&src, (char *)&dst);
681         delete sys;
682         return VLC_EGENERIC;
683     }
684
685     filter->pf_video_blend = Blend;
686     filter->p_sys          = sys;
687     return VLC_SUCCESS;
688 }
689
690 static void Close(vlc_object_t *object)
691 {
692     filter_t *filter = (filter_t *)object;
693     delete filter->p_sys;
694 }
695