Add video filter to convert side by side 3D streams to anaglyph images.
[vlc.git] / modules / video_filter / anaglyph.c
1 /*****************************************************************************
2  * anaglyph.c : Create an image compatible with anaglyph glasses from a 3D video
3  *****************************************************************************
4  * Copyright (C) 2000-2012 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea .t videolan d@t org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #   include "config.h"
26 #endif
27
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30
31 #include <vlc_filter.h>
32 #include "filter_picture.h"
33
34 static int Create(vlc_object_t *);
35 static void Destroy(vlc_object_t *);
36 static picture_t *Filter(filter_t *, picture_t *);
37 static void make_projection_matrix(filter_t *, int, int *);
38 static void combine_side_by_side_yuv420(picture_t *, picture_t *, int, int);
39
40 #define SCHEME_TEXT N_("Color scheme")
41 #define SCHEME_LONGTEXT N_("Define the glasse's color scheme")
42
43 #define FILTER_PREFIX "anaglyph-"
44
45 /* See http://en.wikipedia.org/wiki/Anaglyph_image for a list of known
46  * color schemes */
47 enum scheme_e
48 {
49     unknown = 0,
50     red_green,
51     red_blue,
52     red_cyan,
53     trioscopic,
54     magenta_cyan,
55 };
56
57 static const char *const ppsz_scheme_values[] = {
58     "red-green",
59     "red-blue",
60     "red-cyan",
61     "trioscopic",
62     "magenta-cyan",
63     };
64 static const char *const ppsz_scheme_descriptions[] = {
65     "pure red (left)  pure green (right)",
66     "pure red (left)  pure blue (right)",
67     "pure red (left)  pure cyan (right)",
68     "pure green (left)  pure magenta (right)",
69     "magenta (left)  cyan (right)",
70     };
71
72 vlc_module_begin()
73     set_description(N_("Convert 3D picture to anaglyph image video filter"));
74     set_shortname(N_("Anaglyph"))
75     set_category(CAT_VIDEO)
76     set_subcategory(SUBCAT_VIDEO_VFILTER)
77     set_capability("video filter2", 0)
78     add_string(FILTER_PREFIX "scheme", "red-cyan", SCHEME_TEXT, SCHEME_LONGTEXT, false)
79         change_string_list(ppsz_scheme_values, ppsz_scheme_descriptions, 0)
80     set_callbacks(Create, Destroy)
81 vlc_module_end()
82
83 static const char *const ppsz_filter_options[] = {
84     "scheme", NULL
85 };
86
87 struct filter_sys_t
88 {
89     int left, right;
90 };
91
92
93 static int Create(vlc_object_t *p_this)
94 {
95     printf("pouet\n");
96     filter_t *p_filter = (filter_t *)p_this;
97
98     switch (p_filter->fmt_in.video.i_chroma)
99     {
100         case VLC_CODEC_I420:
101         case VLC_CODEC_J420:
102         case VLC_CODEC_YV12:
103             break;
104
105         default:
106             msg_Err(p_filter, "Unsupported input chroma (%4.4s)",
107                     (char*)&(p_filter->fmt_in.video.i_chroma));
108             return VLC_EGENERIC;
109     }
110
111     p_filter->p_sys = malloc(sizeof(filter_sys_t));
112     if (!p_filter->p_sys)
113         return VLC_ENOMEM;
114     filter_sys_t *p_sys = p_filter->p_sys;
115
116     config_ChainParse(p_filter, FILTER_PREFIX, ppsz_filter_options,
117                       p_filter->p_cfg);
118
119     char *psz_scheme = var_CreateGetStringCommand(p_filter,
120                                                   FILTER_PREFIX "scheme");
121     enum scheme_e scheme = red_cyan;
122     if (psz_scheme)
123     {
124         if (!strcmp(psz_scheme, "red-green"))
125             scheme = red_green;
126         else if (!strcmp(psz_scheme, "red-blue"))
127             scheme = red_blue;
128         else if (!strcmp(psz_scheme, "red-cyan"))
129             scheme = red_cyan;
130         else if (!strcmp(psz_scheme, "trioscopic"))
131             scheme = trioscopic;
132         else if (!strcmp(psz_scheme, "magenta-cyan"))
133             scheme = magenta_cyan;
134         else
135             msg_Err(p_filter, "Unknown anaglyph color scheme '%s'", psz_scheme);
136     }
137     free(psz_scheme);
138
139     switch (scheme)
140     {
141         case red_green:
142             p_sys->left = 0xff0000;
143             p_sys->right = 0x00ff00;
144             break;
145         case red_blue:
146             p_sys->left = 0xff0000;
147             p_sys->right = 0x0000ff;
148             break;
149         case red_cyan:
150             p_sys->left = 0xff0000;
151             p_sys->right = 0x00ffff;
152             break;
153         case trioscopic:
154             p_sys->left = 0x00ff00;
155             p_sys->right = 0xff00ff;
156             break;
157         case magenta_cyan:
158             p_sys->left = 0xff00ff;
159             p_sys->right = 0x00ffff;
160             break;
161         case unknown:
162             msg_Err(p_filter, "Oops");
163             break;
164     }
165
166     p_filter->pf_video_filter = Filter;
167     return VLC_SUCCESS;
168 }
169
170 static void Destroy(vlc_object_t *p_this)
171 {
172     filter_t *p_filter = (filter_t *)p_this;
173     filter_sys_t *p_sys = p_filter->p_sys;
174     free(p_sys);
175 }
176
177 static picture_t *Filter(filter_t *p_filter, picture_t *p_pic)
178 {
179     filter_sys_t *p_sys = p_filter->p_sys;
180
181     if (!p_pic)
182         return NULL;
183
184     picture_t *p_outpic = filter_NewPicture(p_filter);
185     if (!p_outpic)
186     {
187         picture_Release(p_pic);
188         return NULL;
189     }
190
191     switch (p_pic->format.i_chroma)
192     {
193         case VLC_CODEC_I420:
194         case VLC_CODEC_J420:
195         case VLC_CODEC_YV12:
196             combine_side_by_side_yuv420(p_pic, p_outpic,
197                                         p_sys->left, p_sys->right);
198             break;
199
200         default:
201             msg_Warn(p_filter, "Unsupported input chroma (%4.4s)",
202                      (char*)&(p_pic->format.i_chroma));
203             picture_Release(p_pic);
204             return NULL;
205     }
206
207     return CopyInfoAndRelease(p_outpic, p_pic);
208 }
209
210
211 static void combine_side_by_side_yuv420(picture_t *p_inpic, picture_t *p_outpic,
212                                         int left, int right)
213 {
214     uint8_t *y1inl = p_inpic->p[Y_PLANE].p_pixels;
215     uint8_t *y2inl;
216     uint8_t *uinl = p_inpic->p[U_PLANE].p_pixels;
217     uint8_t *vinl = p_inpic->p[V_PLANE].p_pixels;
218
219     uint8_t *y1out = p_outpic->p[Y_PLANE].p_pixels;
220     uint8_t *y2out;
221     uint8_t *uout = p_outpic->p[U_PLANE].p_pixels;
222     uint8_t *vout = p_outpic->p[V_PLANE].p_pixels;
223
224     const int in_pitch = p_inpic->p[Y_PLANE].i_pitch;
225     const int out_pitch = p_outpic->p[Y_PLANE].i_pitch;
226
227     const int visible_pitch = p_inpic->p[Y_PLANE].i_visible_pitch;
228     const int visible_lines = p_inpic->p[Y_PLANE].i_visible_lines;
229     const int uv_visible_pitch = p_inpic->p[U_PLANE].i_visible_pitch;
230
231     const uint8_t *yend = y1inl + visible_lines * in_pitch;
232
233     while (y1inl < yend)
234     {
235         uint8_t *y1inr = y1inl + visible_pitch/2;
236         uint8_t *y2inr;
237         uint8_t *uinr = uinl + uv_visible_pitch/2;
238         uint8_t *vinr = vinl + uv_visible_pitch/2;
239
240         const uint8_t *y1end = y1inr;
241         y2inl = y1inl + in_pitch;
242         y2inr = y1inr + in_pitch;
243         y2out = y1out + out_pitch;
244
245         while (y1inl < y1end)
246         {
247             int rl, gl, bl, rr, gr, br, r, g, b;
248
249             int rshift = !!((0xff0000&left) && (0xff0000&right));
250             int gshift = !!((0x00ff00&left) && (0x00ff00&right));
251             int bshift = !!((0x0000ff&left) && (0x0000ff&right));
252
253             yuv_to_rgb(&rl, &gl, &bl, *y1inl, *uinl, *vinl);
254             yuv_to_rgb(&rr, &gr, &br, *y1inr, *uinr, *vinr);
255             r = ((!!(0xff0000&left))*rl + (!!(0xff0000&right))*rr)>>rshift;
256             g = ((!!(0x00ff00&left))*gl + (!!(0x00ff00&right))*gr)>>gshift;
257             b = ((!!(0x0000ff&left))*bl + (!!(0x0000ff&right))*br)>>bshift;
258             rgb_to_yuv(y1out, uout++, vout++, r, g, b);
259             y1out[1] = *y1out;
260             y1out+=2;
261             y1inl++;
262             y1inr++;
263
264             yuv_to_rgb(&rl, &gl, &bl, *y1inl, *uinl, *vinl);
265             yuv_to_rgb(&rr, &gr, &br, *y1inr, *uinr, *vinr);
266             r = ((!!(0xff0000&left))*rl + (!!(0xff0000&right))*rr)>>rshift;
267             g = ((!!(0x00ff00&left))*gl + (!!(0x00ff00&right))*gr)>>gshift;
268             b = ((!!(0x0000ff&left))*bl + (!!(0x0000ff&right))*br)>>bshift;
269             rgb_to_yuv(y1out, uout++, vout++, r, g, b);
270             y1out[1] = *y1out;
271             y1out+=2;
272             y1inl++;
273             y1inr++;
274
275             yuv_to_rgb(&rl, &gl, &bl, *y2inl, *uinl, *vinl);
276             yuv_to_rgb(&rr, &gr, &br, *y2inr, *uinr, *vinr);
277             r = ((!!(0xff0000&left))*rl + (!!(0xff0000&right))*rr)>>rshift;
278             g = ((!!(0x00ff00&left))*gl + (!!(0x00ff00&right))*gr)>>gshift;
279             b = ((!!(0x0000ff&left))*bl + (!!(0x0000ff&right))*br)>>bshift;
280             rgb_to_yuv(y2out, uout/*will be overwritten later, as will vout*/, vout, r, g, b);
281             y2out[1] = *y2out;
282             y2out+=2;
283             y2inl++;
284             y2inr++;
285
286             yuv_to_rgb(&rl, &gl, &bl, *y2inl, *uinl, *vinl);
287             yuv_to_rgb(&rr, &gr, &br, *y2inr, *uinr, *vinr);
288             r = ((!!(0xff0000&left))*rl + (!!(0xff0000&right))*rr)>>rshift;
289             g = ((!!(0x00ff00&left))*gl + (!!(0x00ff00&right))*gr)>>gshift;
290             b = ((!!(0x0000ff&left))*bl + (!!(0x0000ff&right))*br)>>bshift;
291             rgb_to_yuv(y2out, uout/*will be overwritten later, as will vout*/, vout, r, g, b);
292             y2out[1] = *y2out;
293             y2out+=2;
294             y2inl++;
295             y2inr++;
296
297             uinl++;
298             vinl++;
299             uinr++;
300             vinr++;
301         }
302
303         y1inl = y1inr + 2*in_pitch - visible_pitch;
304         y1out += 2*out_pitch - visible_pitch;
305         uinl = uinr + p_inpic->p[U_PLANE].i_pitch - uv_visible_pitch;
306         vinl = vinr + p_inpic->p[V_PLANE].i_pitch - uv_visible_pitch;
307         uout += p_outpic->p[U_PLANE].i_pitch - uv_visible_pitch;
308         vout += p_outpic->p[V_PLANE].i_pitch - uv_visible_pitch;
309     }
310 }
311