]> git.sesse.net Git - vlc/blob - modules/video_filter/posterize.c
Inverse telecine deinterlacer
[vlc] / modules / video_filter / posterize.c
1 /*****************************************************************************
2  * posterize.c : Posterize video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2010 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Branko Kokanovic <branko.kokanovic@gmail.com>
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 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34
35 #include <assert.h>
36 #include <vlc_filter.h>
37 #include "filter_picture.h"
38
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42 static int  Create      ( vlc_object_t * );
43 static void Destroy     ( vlc_object_t * );
44
45 static picture_t *Filter( filter_t *, picture_t * );
46 static void PlanarYUVPosterize( picture_t *, picture_t *, int);
47 static void PackedYUVPosterize( picture_t *, picture_t *, int);
48 static void RVPosterize( picture_t *, picture_t *, bool, int );
49 static void YuvPosterization( uint8_t *, uint8_t *, uint8_t *, uint8_t *,
50                     uint8_t, uint8_t, uint8_t, uint8_t, int );
51
52 static const char *const ppsz_filter_options[] = {
53     "level", NULL
54 };
55
56 /*****************************************************************************
57  * Module descriptor
58  *****************************************************************************/
59 #define POSTERIZE_LEVEL_TEXT N_("Posterize level")
60 #define POSTERIZE_LEVEL_LONGTEXT N_("Posterize level "\
61                     "(number of colors is cube of this value)" )
62
63 #define CFG_PREFIX "posterize-"
64
65 vlc_module_begin ()
66     set_description( N_("Posterize video filter") )
67     set_shortname( N_("Posterize" ) )
68     set_help( N_("Posterize video by lowering the number of colors") )
69     set_category( CAT_VIDEO )
70     set_subcategory( SUBCAT_VIDEO_VFILTER )
71     set_capability( "video filter2", 0 )
72     add_integer_with_range( CFG_PREFIX "level", 6, 2, 256, NULL,
73                            POSTERIZE_LEVEL_TEXT, POSTERIZE_LEVEL_LONGTEXT,
74                            false )
75     set_callbacks( Create, Destroy )
76 vlc_module_end ()
77
78 /*****************************************************************************
79  * callback prototypes
80  *****************************************************************************/
81 static int FilterCallback( vlc_object_t *, char const *,
82                            vlc_value_t, vlc_value_t, void * );
83
84 /*****************************************************************************
85  * filter_sys_t: adjust filter method descriptor
86  *****************************************************************************/
87 struct filter_sys_t
88 {
89     int i_level;
90     vlc_mutex_t lock;
91 };
92
93 /*****************************************************************************
94  * Create: allocates Posterize video thread output method
95  *****************************************************************************
96  * This function allocates and initializes a Posterize vout method.
97  *****************************************************************************/
98 static int Create( vlc_object_t *p_this )
99 {
100     filter_t *p_filter = (filter_t *)p_this;
101     filter_sys_t *p_sys;
102
103     switch( p_filter->fmt_in.video.i_chroma )
104     {
105         CASE_PLANAR_YUV_SQUARE
106             break;
107         CASE_PACKED_YUV_422
108             break;
109         case VLC_CODEC_RGB24:
110             break;
111         case VLC_CODEC_RGB32:
112             break;
113         default:
114             msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
115                      (char*)&(p_filter->fmt_in.video.i_chroma) );
116             return VLC_EGENERIC;
117     }
118
119     if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
120     {
121         msg_Err( p_filter, "Input and output chromas don't match" );
122         return VLC_EGENERIC;
123     }
124
125     /* Allocate structure */
126     p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
127     if( p_filter->p_sys == NULL )
128         return VLC_ENOMEM;
129
130     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
131                        p_filter->p_cfg );
132     p_sys->i_level = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "level" );
133
134     vlc_mutex_init( &p_sys->lock );
135
136     var_AddCallback( p_filter, CFG_PREFIX "level", FilterCallback, NULL );
137
138     p_filter->pf_video_filter = Filter;
139
140     return VLC_SUCCESS;
141 }
142
143 /*****************************************************************************
144  * Destroy: destroy Posterize video thread output method
145  *****************************************************************************
146  * Terminate an output method created by PosterizeCreateOutputMethod
147  *****************************************************************************/
148 static void Destroy( vlc_object_t *p_this )
149 {
150     filter_t *p_filter = (filter_t *)p_this;
151
152     var_DelCallback( p_filter, CFG_PREFIX "level", FilterCallback, NULL );
153
154     vlc_mutex_destroy( &p_filter->p_sys->lock );
155     free( p_filter->p_sys );
156 }
157
158 /*****************************************************************************
159  * Render: displays previously rendered output
160  *****************************************************************************
161  * This function send the currently rendered image to Posterize image, waits
162  * until it is displayed and switch the two rendering buffers, preparing next
163  * frame.
164  *****************************************************************************/
165 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
166 {
167     picture_t *p_outpic;
168     int level;
169
170     if( !p_pic ) return NULL;
171
172     filter_sys_t *p_sys = p_filter->p_sys;
173     vlc_mutex_lock( &p_sys->lock );
174     level = p_sys->i_level;
175     vlc_mutex_unlock( &p_sys->lock );
176
177     p_outpic = filter_NewPicture( p_filter );
178     if( !p_outpic )
179     {
180         msg_Warn( p_filter, "can't get output picture" );
181         picture_Release( p_pic );
182         return NULL;
183     }
184
185     switch( p_pic->format.i_chroma )
186     {
187         case VLC_CODEC_RGB24:
188             RVPosterize( p_pic, p_outpic, false, level );
189             break;
190         case VLC_CODEC_RGB32:
191             RVPosterize( p_pic, p_outpic, true, level );
192             break;
193         CASE_PLANAR_YUV_SQUARE
194             PlanarYUVPosterize( p_pic, p_outpic, level );
195             break;
196         CASE_PACKED_YUV_422
197             PackedYUVPosterize( p_pic, p_outpic, level );
198             break;
199         default:
200             assert( false );
201     }
202
203     return CopyInfoAndRelease( p_outpic, p_pic );
204 }
205
206 /*****************************************************************************
207  * For a given level, calculates new posterized value for pixel whose value x
208  * is in range of 0-255
209  *****************************************************************************/
210 #define POSTERIZE_PIXEL(x, level) \
211        (((( x * level ) >> 8 ) * 255 ) / ( level - 1 ))
212
213 /*****************************************************************************
214  * PlanarYUVPosterize: Posterize one frame of the planar YUV video
215  *****************************************************************************
216  * This function posterizes one frame of the video by iterating through video
217  * lines. In every pass, start of Y, U and V planes is calculated and for
218  * every pixel we calculate new values of YUV values.
219  *****************************************************************************/
220 static void PlanarYUVPosterize( picture_t *p_pic, picture_t *p_outpic,
221                                int i_level )
222 {
223     uint8_t *p_in_y, *p_in_u, *p_in_v, *p_in_end_y, *p_line_end_y, *p_out_y,
224             *p_out_u, *p_out_v;
225     int i_current_line = 0;
226
227     p_in_y = p_pic->p[Y_PLANE].p_pixels;
228     p_in_end_y = p_in_y + p_pic->p[Y_PLANE].i_visible_lines
229         * p_pic->p[Y_PLANE].i_pitch;
230     p_out_y = p_outpic->p[Y_PLANE].p_pixels;
231
232     /* iterate for every visible line in the frame */
233     while( p_in_y < p_in_end_y )
234     {
235         p_line_end_y = p_in_y + p_pic->p[Y_PLANE].i_visible_pitch;
236         /* calculate start of U plane line */
237         p_in_u = p_pic->p[U_PLANE].p_pixels
238             + p_pic->p[U_PLANE].i_pitch * ( i_current_line / 2 );
239         p_out_u = p_outpic->p[U_PLANE].p_pixels
240             + p_outpic->p[U_PLANE].i_pitch * ( i_current_line / 2 );
241         /* calculate start of V plane line */
242         p_in_v = p_pic->p[V_PLANE].p_pixels
243             + p_pic->p[V_PLANE].i_pitch * ( i_current_line / 2 );
244         p_out_v = p_outpic->p[V_PLANE].p_pixels
245             + p_outpic->p[V_PLANE].i_pitch * ( i_current_line / 2 );
246         /* iterate for every two pixels in line */
247         while( p_in_y < p_line_end_y )
248         {
249             uint8_t y1, y2, u, v;
250             uint8_t posterized_y1, posterized_y2, posterized_u, posterized_v;
251             /* retrieve original YUV values */
252             y1 = *p_in_y++;
253             y2 = *p_in_y++;
254             u = *p_in_u++;
255             v = *p_in_v++;
256             /* do posterization */
257             YuvPosterization( &posterized_y1, &posterized_y2, &posterized_u,
258                              &posterized_v, y1, y2, u, v, i_level );
259             /* put posterized valued */
260             *p_out_y++ = posterized_y1;
261             *p_out_y++ = posterized_y2;
262             *p_out_u++ = posterized_u;
263             *p_out_v++ = posterized_v;
264         }
265         p_in_y += p_pic->p[Y_PLANE].i_pitch
266             - p_pic->p[Y_PLANE].i_visible_pitch;
267         p_out_y += p_outpic->p[Y_PLANE].i_pitch
268             - p_outpic->p[Y_PLANE].i_visible_pitch;
269         i_current_line++;
270     }
271 }
272
273 /*****************************************************************************
274  * PackedYUVPosterize: Posterize one frame of the packed YUV video
275  *****************************************************************************
276  * This function posterizes one frame of the video by iterating through video
277  * lines. In every pass, we calculate new values for pixels (UYVY, VYUY, YUYV
278  * and YVYU formats are supported)
279  *****************************************************************************/
280 static void PackedYUVPosterize( picture_t *p_pic, picture_t *p_outpic, int i_level )
281 {
282     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
283     uint8_t y1, y2, u, v;
284
285     p_in = p_pic->p[0].p_pixels;
286     p_in_end = p_in + p_pic->p[0].i_visible_lines
287         * p_pic->p[0].i_pitch;
288     p_out = p_outpic->p[0].p_pixels;
289
290     while( p_in < p_in_end )
291     {
292         p_line_start = p_in;
293         p_line_end = p_in + p_pic->p[0].i_visible_pitch;
294         while( p_in < p_line_end )
295         {
296             uint8_t posterized_y1, posterized_y2, posterized_u, posterized_v;
297             /* extract proper pixel values */
298             switch( p_pic->format.i_chroma )
299             {
300                 case VLC_CODEC_UYVY:
301                     u = *p_in++;
302                     y1 = *p_in++;
303                     v = *p_in++;
304                     y2 = *p_in++;
305                     break;
306                 case VLC_CODEC_VYUY:
307                     v = *p_in++;
308                     y1 = *p_in++;
309                     u = *p_in++;
310                     y2 = *p_in++;
311                     break;
312                 case VLC_CODEC_YUYV:
313                     y1 = *p_in++;
314                     u = *p_in++;
315                     y2 = *p_in++;
316                     v = *p_in++;
317                     break;
318                 case VLC_CODEC_YVYU:
319                     y1 = *p_in++;
320                     v = *p_in++;
321                     y2 = *p_in++;
322                     u = *p_in++;
323                     break;
324                 default:
325                     assert( false );
326             }
327             /* do posterization */
328             YuvPosterization( &posterized_y1, &posterized_y2, &posterized_u,
329                              &posterized_v, y1, y2, u, v, i_level );
330             /* put posterized values in proper place */
331             switch( p_pic->format.i_chroma )
332             {
333                 case VLC_CODEC_UYVY:
334                     *p_out++ = posterized_u;
335                     *p_out++ = posterized_y1;
336                     *p_out++ = posterized_v;
337                     *p_out++ = posterized_y2;
338                     break;
339                 case VLC_CODEC_VYUY:
340                     *p_out++ = posterized_v;
341                     *p_out++ = posterized_y1;
342                     *p_out++ = posterized_u;
343                     *p_out++ = posterized_y2;
344                     break;
345                 case VLC_CODEC_YUYV:
346                     *p_out++ = posterized_y1;
347                     *p_out++ = posterized_u;
348                     *p_out++ = posterized_y2;
349                     *p_out++ = posterized_v;
350                     break;
351                 case VLC_CODEC_YVYU:
352                     *p_out++ = posterized_y1;
353                     *p_out++ = posterized_v;
354                     *p_out++ = posterized_y2;
355                     *p_out++ = posterized_u;
356                     break;
357                 default:
358                     assert( false );
359             }
360         }
361         p_in += p_pic->p[0].i_pitch - p_pic->p[0].i_visible_pitch;
362         p_out += p_outpic->p[0].i_pitch
363             - p_outpic->p[0].i_visible_pitch;
364     }
365 }
366
367 /*****************************************************************************
368  * RVPosterize: Posterize one frame of the RV24/RV32 video
369  *****************************************************************************
370  * This function posterizes one frame of the video by iterating through video
371  * lines and calculating new values for every byte in chunks of 3 (RV24) or
372  * 4 (RV32) bytes
373  *****************************************************************************/
374 static void RVPosterize( picture_t *p_pic, picture_t *p_outpic,
375                                  bool rv32, int level )
376 {
377     uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out, pixel;
378
379     p_in = p_pic->p[0].p_pixels;
380     p_in_end = p_in + p_pic->p[0].i_visible_lines
381         * p_pic->p[0].i_pitch;
382     p_out = p_outpic->p[0].p_pixels;
383
384     while( p_in < p_in_end )
385     {
386         p_line_start = p_in;
387         p_line_end = p_in + p_pic->p[0].i_visible_pitch;
388         while( p_in < p_line_end )
389         {
390             pixel = *p_in++;
391             *p_out++ = POSTERIZE_PIXEL( pixel, level );
392             pixel = *p_in++;
393             *p_out++ = POSTERIZE_PIXEL( pixel, level );
394             pixel = *p_in++;
395             *p_out++ = POSTERIZE_PIXEL( pixel, level );
396             /* for rv32 we take 4 chunks at the time */
397             if ( rv32 )
398             {
399                 pixel = *p_in++;
400                 *p_out++ = POSTERIZE_PIXEL( pixel, level );
401             }
402         }
403         p_in += p_pic->p[0].i_pitch - p_pic->p[0].i_visible_pitch;
404         p_out += p_outpic->p[0].i_pitch
405             - p_outpic->p[0].i_visible_pitch;
406     }
407 }
408
409 /*****************************************************************************
410  * YuvPosterization: Lowers the color depth of YUV color space
411  *****************************************************************************
412  * This function lowers the color level of YUV color space to specified level
413  * by converting YUV color values to theirs RGB equivalents, calculates new
414  * values and then converts RGB values to YUV values again.
415  *****************************************************************************/
416 static void YuvPosterization( uint8_t* posterized_y1, uint8_t* posterized_y2,
417                              uint8_t* posterized_u, uint8_t* posterized_v,
418                              uint8_t y1, uint8_t y2, uint8_t u, uint8_t v,
419                              int i_level ) {
420     int r1, g1, b1; /* for y1 new value */
421     int r2, b2, g2; /* for y2 new value */
422     int r3, g3, b3; /* for new values of u and v */
423     /* fist convert YUV -> RGB */
424     yuv_to_rgb( &r1, &g1, &b1, y1, u, v );
425     yuv_to_rgb( &r2, &g2, &b2, y1, u, v );
426     yuv_to_rgb( &r3, &g3, &b3, ( y1 + y2 ) / 2, u, v );
427     /* round RGB values to specified posterize level */
428     r1 = POSTERIZE_PIXEL( r1, i_level );
429     g1 = POSTERIZE_PIXEL( g1, i_level );
430     b1 = POSTERIZE_PIXEL( b1, i_level );
431     r2 = POSTERIZE_PIXEL( r2, i_level );
432     g2 = POSTERIZE_PIXEL( g2, i_level );
433     b2 = POSTERIZE_PIXEL( b2, i_level );
434     r3 = POSTERIZE_PIXEL( r3, i_level );
435     g3 = POSTERIZE_PIXEL( g3, i_level );
436     b3 = POSTERIZE_PIXEL( b3, i_level );
437     /* convert from calculated RGB -> YUV */
438     *posterized_y1 = ( ( 66 * r1 + 129 * g1 +  25 * b1 + 128 ) >> 8 ) +  16;
439     *posterized_y2 = ( ( 66 * r2 + 129 * g2 +  25 * b2 + 128 ) >> 8 ) +  16;
440     *posterized_u = ( ( -38 * r3 -  74 * g3 + 112 * b3 + 128 ) >> 8 ) + 128;
441     *posterized_v = ( ( 112 * r3 -  94 * g3 -  18 * b3 + 128 ) >> 8 ) + 128;
442 }
443
444 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
445                             vlc_value_t oldval, vlc_value_t newval, void *p_data )
446 {
447     (void)oldval;    (void)p_data;
448     filter_t *p_filter = (filter_t*)p_this;
449     filter_sys_t *p_sys = p_filter->p_sys;
450
451     if( !strcmp( psz_var, CFG_PREFIX "level" ) )
452     {
453         vlc_mutex_lock( &p_sys->lock );
454         p_sys->i_level = newval.i_int;
455         vlc_mutex_unlock( &p_sys->lock );
456     }
457
458     return VLC_SUCCESS;
459 }