1 /*****************************************************************************
2 * posterize.c : Posterize video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2010 the VideoLAN team
7 * Authors: Branko Kokanovic <branko.kokanovic@gmail.com>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37 #include "filter_picture.h"
39 /*****************************************************************************
41 *****************************************************************************/
42 static int Create ( vlc_object_t * );
43 static void Destroy ( vlc_object_t * );
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);
54 static const char *const ppsz_filter_options[] = {
58 /*****************************************************************************
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)" )
65 #define CFG_PREFIX "posterize-"
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,
77 set_callbacks( Create, Destroy )
80 /*****************************************************************************
82 *****************************************************************************/
83 static int FilterCallback( vlc_object_t *, char const *,
84 vlc_value_t, vlc_value_t, void * );
86 /*****************************************************************************
87 * filter_sys_t: adjust filter method descriptor
88 *****************************************************************************/
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 )
102 filter_t *p_filter = (filter_t *)p_this;
105 switch( p_filter->fmt_in.video.i_chroma )
107 CASE_PLANAR_YUV_SQUARE
111 case VLC_CODEC_RGB24:
113 case VLC_CODEC_RGB32:
116 msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
117 (char*)&(p_filter->fmt_in.video.i_chroma) );
121 if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
123 msg_Err( p_filter, "Input and output chromas don't match" );
127 /* Allocate structure */
128 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
129 if( p_filter->p_sys == NULL )
132 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
134 p_sys->i_level = var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "level" );
136 vlc_mutex_init( &p_sys->lock );
138 var_AddCallback( p_filter, CFG_PREFIX "level", FilterCallback, NULL );
140 p_filter->pf_video_filter = Filter;
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 )
152 filter_t *p_filter = (filter_t *)p_this;
154 var_DelCallback( p_filter, CFG_PREFIX "level", FilterCallback, NULL );
156 vlc_mutex_destroy( &p_filter->p_sys->lock );
157 free( p_filter->p_sys );
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
166 *****************************************************************************/
167 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
172 if( !p_pic ) return NULL;
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 );
179 p_outpic = filter_NewPicture( p_filter );
182 msg_Warn( p_filter, "can't get output picture" );
183 picture_Release( p_pic );
187 switch( p_pic->format.i_chroma )
189 case VLC_CODEC_RGB24:
190 RVPosterize( p_pic, p_outpic, false, level );
192 case VLC_CODEC_RGB32:
193 RVPosterize( p_pic, p_outpic, true, level );
195 CASE_PLANAR_YUV_SQUARE
196 PlanarYUVPosterize( p_pic, p_outpic, level );
199 PackedYUVPosterize( p_pic, p_outpic, level );
205 return CopyInfoAndRelease( p_outpic, p_pic );
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 ))
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,
225 uint8_t *p_in_y, *p_in_u, *p_in_v, *p_in_end_y, *p_line_end_y, *p_out_y,
227 int i_current_line = 0;
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;
234 /* iterate for every visible line in the frame */
235 while( p_in_y < p_in_end_y )
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 )
251 uint8_t y1, y2, u, v;
252 uint8_t posterized_y1, posterized_y2, posterized_u, posterized_v;
253 /* retrieve original YUV values */
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;
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;
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 )
284 uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out;
285 uint8_t y1, y2, u, v;
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;
292 while( p_in < p_in_end )
295 p_line_end = p_in + p_pic->p[0].i_visible_pitch;
296 while( p_in < p_line_end )
298 uint8_t posterized_y1, posterized_y2, posterized_u, posterized_v;
299 /* extract proper pixel values */
300 switch( p_pic->format.i_chroma )
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 )
336 *p_out++ = posterized_u;
337 *p_out++ = posterized_y1;
338 *p_out++ = posterized_v;
339 *p_out++ = posterized_y2;
342 *p_out++ = posterized_v;
343 *p_out++ = posterized_y1;
344 *p_out++ = posterized_u;
345 *p_out++ = posterized_y2;
348 *p_out++ = posterized_y1;
349 *p_out++ = posterized_u;
350 *p_out++ = posterized_y2;
351 *p_out++ = posterized_v;
354 *p_out++ = posterized_y1;
355 *p_out++ = posterized_v;
356 *p_out++ = posterized_y2;
357 *p_out++ = posterized_u;
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;
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
375 *****************************************************************************/
376 static void RVPosterize( picture_t *p_pic, picture_t *p_outpic,
377 bool rv32, int level )
379 uint8_t *p_in, *p_in_end, *p_line_start, *p_line_end, *p_out, pixel;
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;
386 while( p_in < p_in_end )
389 p_line_end = p_in + p_pic->p[0].i_visible_pitch;
390 while( p_in < p_line_end )
393 *p_out++ = POSTERIZE_PIXEL( pixel, level );
395 *p_out++ = POSTERIZE_PIXEL( pixel, level );
397 *p_out++ = POSTERIZE_PIXEL( pixel, level );
398 /* for rv32 we take 4 chunks at the time */
402 *p_out++ = POSTERIZE_PIXEL( pixel, level );
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;
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,
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;
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 )
458 int16_t noclipped_r = ( 298 * c + 409 * e + 128 ) >> 8;
459 if ( noclipped_r < 0 )
463 else if ( noclipped_r > 255 )
471 int16_t noclipped_g = ( 298 * c - 100 * d - 208 * e + 128 ) >> 8;
472 if ( noclipped_g < 0 )
476 else if ( noclipped_g > 255 )
484 int16_t noclipped_b = ( 298 * c + 516 * d + 128 ) >> 8;
485 if ( noclipped_b < 0 )
489 else if ( noclipped_b > 255 )
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 )
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;
506 if( !strcmp( psz_var, CFG_PREFIX "level" ) )
508 vlc_mutex_lock( &p_sys->lock );
509 p_sys->i_level = newval.i_int;
510 vlc_mutex_unlock( &p_sys->lock );