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