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