1 /*****************************************************************************
2 * posterize.c : Posterize video plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2010 VLC authors and VideoLAN
7 * Authors: Branko Kokanovic <branko.kokanovic@gmail.com>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_atomic.h>
37 #include <vlc_filter.h>
38 #include "filter_picture.h"
40 /*****************************************************************************
42 *****************************************************************************/
43 static int Create ( vlc_object_t * );
44 static void Destroy ( vlc_object_t * );
46 static picture_t *Filter( filter_t *, picture_t * );
47 static void PlanarYUVPosterize( picture_t *, picture_t *, int);
48 static void PackedYUVPosterize( picture_t *, picture_t *, int);
49 static void RVPosterize( picture_t *, picture_t *, bool, int );
50 static void YuvPosterization( uint8_t *, uint8_t *, uint8_t *, uint8_t *,
51 uint8_t, uint8_t, uint8_t, uint8_t, int );
53 static const char *const ppsz_filter_options[] = {
57 /*****************************************************************************
59 *****************************************************************************/
60 #define POSTERIZE_LEVEL_TEXT N_("Posterize level")
61 #define POSTERIZE_LEVEL_LONGTEXT N_("Posterize level "\
62 "(number of colors is cube of this value)" )
64 #define CFG_PREFIX "posterize-"
67 set_description( N_("Posterize video filter") )
68 set_shortname( N_("Posterize" ) )
69 set_help( N_("Posterize video by lowering the number of colors") )
70 set_category( CAT_VIDEO )
71 set_subcategory( SUBCAT_VIDEO_VFILTER )
72 set_capability( "video filter2", 0 )
73 add_integer_with_range( CFG_PREFIX "level", 6, 2, 256,
74 POSTERIZE_LEVEL_TEXT, POSTERIZE_LEVEL_LONGTEXT,
76 set_callbacks( Create, Destroy )
79 /*****************************************************************************
81 *****************************************************************************/
82 static int FilterCallback( vlc_object_t *, char const *,
83 vlc_value_t, vlc_value_t, void * );
85 /*****************************************************************************
86 * filter_sys_t: adjust filter method descriptor
87 *****************************************************************************/
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 )
100 filter_t *p_filter = (filter_t *)p_this;
103 switch( p_filter->fmt_in.video.i_chroma )
105 CASE_PLANAR_YUV_SQUARE
109 case VLC_CODEC_RGB24:
111 case VLC_CODEC_RGB32:
114 msg_Err( p_filter, "Unsupported input chroma (%4.4s)",
115 (char*)&(p_filter->fmt_in.video.i_chroma) );
119 if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma )
121 msg_Err( p_filter, "Input and output chromas don't match" );
125 /* Allocate structure */
126 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
127 if( p_filter->p_sys == NULL )
130 config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
132 atomic_init( &p_sys->i_level,
133 var_CreateGetIntegerCommand( p_filter, CFG_PREFIX "level" ) );
135 var_AddCallback( p_filter, CFG_PREFIX "level", FilterCallback, p_sys );
137 p_filter->pf_video_filter = Filter;
142 /*****************************************************************************
143 * Destroy: destroy Posterize video thread output method
144 *****************************************************************************
145 * Terminate an output method created by PosterizeCreateOutputMethod
146 *****************************************************************************/
147 static void Destroy( vlc_object_t *p_this )
149 filter_t *p_filter = (filter_t *)p_this;
150 filter_sys_t *p_sys = p_filter->p_sys;
152 var_DelCallback( p_filter, CFG_PREFIX "level", FilterCallback, p_sys );
156 /*****************************************************************************
157 * Render: displays previously rendered output
158 *****************************************************************************
159 * This function send the currently rendered image to Posterize image, waits
160 * until it is displayed and switch the two rendering buffers, preparing next
162 *****************************************************************************/
163 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic )
167 if( !p_pic ) return NULL;
169 filter_sys_t *p_sys = p_filter->p_sys;
170 int level = atomic_load( &p_sys->i_level );
172 p_outpic = filter_NewPicture( p_filter );
175 msg_Warn( p_filter, "can't get output picture" );
176 picture_Release( p_pic );
180 switch( p_pic->format.i_chroma )
182 case VLC_CODEC_RGB24:
183 RVPosterize( p_pic, p_outpic, false, level );
185 case VLC_CODEC_RGB32:
186 RVPosterize( p_pic, p_outpic, true, level );
188 CASE_PLANAR_YUV_SQUARE
189 PlanarYUVPosterize( p_pic, p_outpic, level );
192 PackedYUVPosterize( p_pic, p_outpic, level );
198 return CopyInfoAndRelease( p_outpic, p_pic );
201 /*****************************************************************************
202 * For a given level, calculates new posterized value for pixel whose value x
203 * is in range of 0-255
204 *****************************************************************************/
205 #define POSTERIZE_PIXEL(x, level) \
206 (((( x * level ) >> 8 ) * 255 ) / ( level - 1 ))
208 /*****************************************************************************
209 * PlanarYUVPosterize: Posterize one frame of the planar YUV video
210 *****************************************************************************
211 * This function posterizes one frame of the video by iterating through video
212 * lines. In every pass, start of Y, U and V planes is calculated and for
213 * every pixel we calculate new values of YUV values.
214 *****************************************************************************/
215 static void PlanarYUVPosterize( picture_t *p_pic, picture_t *p_outpic,
218 uint8_t *p_in_y, *p_in_u, *p_in_v, *p_in_end_y, *p_line_end_y, *p_out_y,
220 int i_current_line = 0;
222 p_in_y = p_pic->p[Y_PLANE].p_pixels;
223 p_in_end_y = p_in_y + p_pic->p[Y_PLANE].i_visible_lines
224 * p_pic->p[Y_PLANE].i_pitch;
225 p_out_y = p_outpic->p[Y_PLANE].p_pixels;
227 /* iterate for every visible line in the frame */
228 while( p_in_y < p_in_end_y )
230 p_line_end_y = p_in_y + p_pic->p[Y_PLANE].i_visible_pitch;
231 /* calculate start of U plane line */
232 p_in_u = p_pic->p[U_PLANE].p_pixels
233 + p_pic->p[U_PLANE].i_pitch * ( i_current_line / 2 );
234 p_out_u = p_outpic->p[U_PLANE].p_pixels
235 + p_outpic->p[U_PLANE].i_pitch * ( i_current_line / 2 );
236 /* calculate start of V plane line */
237 p_in_v = p_pic->p[V_PLANE].p_pixels
238 + p_pic->p[V_PLANE].i_pitch * ( i_current_line / 2 );
239 p_out_v = p_outpic->p[V_PLANE].p_pixels
240 + p_outpic->p[V_PLANE].i_pitch * ( i_current_line / 2 );
241 /* iterate for every two pixels in line */
242 while( p_in_y < p_line_end_y )
244 uint8_t y1, y2, u, v;
245 uint8_t posterized_y1, posterized_y2, posterized_u, posterized_v;
246 /* retrieve original YUV values */
251 /* do posterization */
252 YuvPosterization( &posterized_y1, &posterized_y2, &posterized_u,
253 &posterized_v, y1, y2, u, v, i_level );
254 /* put posterized valued */
255 *p_out_y++ = posterized_y1;
256 *p_out_y++ = posterized_y2;
257 *p_out_u++ = posterized_u;
258 *p_out_v++ = posterized_v;
260 p_in_y += p_pic->p[Y_PLANE].i_pitch
261 - p_pic->p[Y_PLANE].i_visible_pitch;
262 p_out_y += p_outpic->p[Y_PLANE].i_pitch
263 - p_outpic->p[Y_PLANE].i_visible_pitch;
268 /*****************************************************************************
269 * PackedYUVPosterize: Posterize one frame of the packed YUV video
270 *****************************************************************************
271 * This function posterizes one frame of the video by iterating through video
272 * lines. In every pass, we calculate new values for pixels (UYVY, VYUY, YUYV
273 * and YVYU formats are supported)
274 *****************************************************************************/
275 static void PackedYUVPosterize( picture_t *p_pic, picture_t *p_outpic, int i_level )
277 uint8_t *p_in, *p_in_end, *p_line_end, *p_out;
278 uint8_t y1, y2, u, v;
280 p_in = p_pic->p[0].p_pixels;
281 p_in_end = p_in + p_pic->p[0].i_visible_lines
282 * p_pic->p[0].i_pitch;
283 p_out = p_outpic->p[0].p_pixels;
285 while( p_in < p_in_end )
287 p_line_end = p_in + p_pic->p[0].i_visible_pitch;
288 while( p_in < p_line_end )
290 uint8_t posterized_y1, posterized_y2, posterized_u, posterized_v;
291 /* extract proper pixel values */
292 switch( p_pic->format.i_chroma )
321 /* do posterization */
322 YuvPosterization( &posterized_y1, &posterized_y2, &posterized_u,
323 &posterized_v, y1, y2, u, v, i_level );
324 /* put posterized values in proper place */
325 switch( p_pic->format.i_chroma )
328 *p_out++ = posterized_u;
329 *p_out++ = posterized_y1;
330 *p_out++ = posterized_v;
331 *p_out++ = posterized_y2;
334 *p_out++ = posterized_v;
335 *p_out++ = posterized_y1;
336 *p_out++ = posterized_u;
337 *p_out++ = posterized_y2;
340 *p_out++ = posterized_y1;
341 *p_out++ = posterized_u;
342 *p_out++ = posterized_y2;
343 *p_out++ = posterized_v;
346 *p_out++ = posterized_y1;
347 *p_out++ = posterized_v;
348 *p_out++ = posterized_y2;
349 *p_out++ = posterized_u;
355 p_in += p_pic->p[0].i_pitch - p_pic->p[0].i_visible_pitch;
356 p_out += p_outpic->p[0].i_pitch
357 - p_outpic->p[0].i_visible_pitch;
361 /*****************************************************************************
362 * RVPosterize: Posterize one frame of the RV24/RV32 video
363 *****************************************************************************
364 * This function posterizes one frame of the video by iterating through video
365 * lines and calculating new values for every byte in chunks of 3 (RV24) or
367 *****************************************************************************/
368 static void RVPosterize( picture_t *p_pic, picture_t *p_outpic,
369 bool rv32, int level )
371 uint8_t *p_in, *p_in_end, *p_line_end, *p_out, pixel;
373 p_in = p_pic->p[0].p_pixels;
374 p_in_end = p_in + p_pic->p[0].i_visible_lines
375 * p_pic->p[0].i_pitch;
376 p_out = p_outpic->p[0].p_pixels;
378 while( p_in < p_in_end )
380 p_line_end = p_in + p_pic->p[0].i_visible_pitch;
381 while( p_in < p_line_end )
384 *p_out++ = POSTERIZE_PIXEL( pixel, level );
386 *p_out++ = POSTERIZE_PIXEL( pixel, level );
388 *p_out++ = POSTERIZE_PIXEL( pixel, level );
389 /* for rv32 we take 4 chunks at the time */
393 *p_out++ = POSTERIZE_PIXEL( pixel, level );
396 p_in += p_pic->p[0].i_pitch - p_pic->p[0].i_visible_pitch;
397 p_out += p_outpic->p[0].i_pitch
398 - p_outpic->p[0].i_visible_pitch;
402 /*****************************************************************************
403 * YuvPosterization: Lowers the color depth of YUV color space
404 *****************************************************************************
405 * This function lowers the color level of YUV color space to specified level
406 * by converting YUV color values to theirs RGB equivalents, calculates new
407 * values and then converts RGB values to YUV values again.
408 *****************************************************************************/
409 static void YuvPosterization( uint8_t* posterized_y1, uint8_t* posterized_y2,
410 uint8_t* posterized_u, uint8_t* posterized_v,
411 uint8_t y1, uint8_t y2, uint8_t u, uint8_t v,
413 int r1, g1, b1; /* for y1 new value */
414 int r2, b2, g2; /* for y2 new value */
415 int r3, g3, b3; /* for new values of u and v */
416 /* fist convert YUV -> RGB */
417 yuv_to_rgb( &r1, &g1, &b1, y1, u, v );
418 yuv_to_rgb( &r2, &g2, &b2, y1, u, v );
419 yuv_to_rgb( &r3, &g3, &b3, ( y1 + y2 ) / 2, u, v );
420 /* round RGB values to specified posterize level */
421 r1 = POSTERIZE_PIXEL( r1, i_level );
422 g1 = POSTERIZE_PIXEL( g1, i_level );
423 b1 = POSTERIZE_PIXEL( b1, i_level );
424 r2 = POSTERIZE_PIXEL( r2, i_level );
425 g2 = POSTERIZE_PIXEL( g2, i_level );
426 b2 = POSTERIZE_PIXEL( b2, i_level );
427 r3 = POSTERIZE_PIXEL( r3, i_level );
428 g3 = POSTERIZE_PIXEL( g3, i_level );
429 b3 = POSTERIZE_PIXEL( b3, i_level );
430 /* convert from calculated RGB -> YUV */
431 *posterized_y1 = ( ( 66 * r1 + 129 * g1 + 25 * b1 + 128 ) >> 8 ) + 16;
432 *posterized_y2 = ( ( 66 * r2 + 129 * g2 + 25 * b2 + 128 ) >> 8 ) + 16;
433 *posterized_u = ( ( -38 * r3 - 74 * g3 + 112 * b3 + 128 ) >> 8 ) + 128;
434 *posterized_v = ( ( 112 * r3 - 94 * g3 - 18 * b3 + 128 ) >> 8 ) + 128;
437 static int FilterCallback ( vlc_object_t *p_this, char const *psz_var,
438 vlc_value_t oldval, vlc_value_t newval, void *p_data )
440 (void)p_this; (void)oldval;
441 filter_sys_t *p_sys = p_data;
443 if( !strcmp( psz_var, CFG_PREFIX "level" ) )
444 atomic_store( &p_sys->i_level, newval.i_int );