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 *****************************************************************************/
33 #include <stdatomic.h>
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_picture.h>
39 #include "filter_picture.h"
41 /*****************************************************************************
43 *****************************************************************************/
44 static int Create ( vlc_object_t
* );
45 static void Destroy ( vlc_object_t
* );
47 static picture_t
*Filter( filter_t
*, picture_t
* );
48 static void PlanarYUVPosterize( picture_t
*, picture_t
*, int);
49 static void PackedYUVPosterize( picture_t
*, picture_t
*, int);
50 static void RVPosterize( picture_t
*, picture_t
*, bool, int );
51 static void YuvPosterization( uint8_t *, uint8_t *, uint8_t *, uint8_t *,
52 uint8_t, uint8_t, uint8_t, uint8_t, int );
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 filter", 0 )
74 add_integer_with_range( CFG_PREFIX
"level", 6, 2, 256,
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 *****************************************************************************/
94 /*****************************************************************************
95 * Create: allocates Posterize video thread output method
96 *****************************************************************************
97 * This function allocates and initializes a Posterize vout method.
98 *****************************************************************************/
99 static int Create( vlc_object_t
*p_this
)
101 filter_t
*p_filter
= (filter_t
*)p_this
;
104 switch( p_filter
->fmt_in
.video
.i_chroma
)
106 CASE_PLANAR_YUV_SQUARE
110 case VLC_CODEC_RGB24
:
112 case VLC_CODEC_RGB32
:
115 msg_Err( p_filter
, "Unsupported input chroma (%4.4s)",
116 (char*)&(p_filter
->fmt_in
.video
.i_chroma
) );
120 if( p_filter
->fmt_in
.video
.i_chroma
!= p_filter
->fmt_out
.video
.i_chroma
)
122 msg_Err( p_filter
, "Input and output chromas don't match" );
126 /* Allocate structure */
127 p_sys
= p_filter
->p_sys
= malloc( sizeof( filter_sys_t
) );
128 if( p_filter
->p_sys
== NULL
)
131 config_ChainParse( p_filter
, CFG_PREFIX
, ppsz_filter_options
,
133 atomic_init( &p_sys
->i_level
,
134 var_CreateGetIntegerCommand( p_filter
, CFG_PREFIX
"level" ) );
136 var_AddCallback( p_filter
, CFG_PREFIX
"level", FilterCallback
, p_sys
);
138 p_filter
->pf_video_filter
= Filter
;
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
)
150 filter_t
*p_filter
= (filter_t
*)p_this
;
151 filter_sys_t
*p_sys
= p_filter
->p_sys
;
153 var_DelCallback( p_filter
, CFG_PREFIX
"level", FilterCallback
, p_sys
);
157 /*****************************************************************************
158 * Render: displays previously rendered output
159 *****************************************************************************
160 * This function send the currently rendered image to Posterize image, waits
161 * until it is displayed and switch the two rendering buffers, preparing next
163 *****************************************************************************/
164 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
168 if( !p_pic
) return NULL
;
170 filter_sys_t
*p_sys
= p_filter
->p_sys
;
171 int level
= atomic_load( &p_sys
->i_level
);
173 p_outpic
= filter_NewPicture( p_filter
);
176 msg_Warn( p_filter
, "can't get output picture" );
177 picture_Release( p_pic
);
181 switch( p_pic
->format
.i_chroma
)
183 case VLC_CODEC_RGB24
:
184 RVPosterize( p_pic
, p_outpic
, false, level
);
186 case VLC_CODEC_RGB32
:
187 RVPosterize( p_pic
, p_outpic
, true, level
);
189 CASE_PLANAR_YUV_SQUARE
190 PlanarYUVPosterize( p_pic
, p_outpic
, level
);
193 PackedYUVPosterize( p_pic
, p_outpic
, level
);
196 vlc_assert_unreachable();
199 return CopyInfoAndRelease( p_outpic
, p_pic
);
202 /*****************************************************************************
203 * For a given level, calculates new posterized value for pixel whose value x
204 * is in range of 0-255
205 *****************************************************************************/
206 #define POSTERIZE_PIXEL(x, level) \
207 (((( x * level ) >> 8 ) * 255 ) / ( level - 1 ))
209 /*****************************************************************************
210 * PlanarYUVPosterize: Posterize one frame of the planar YUV video
211 *****************************************************************************
212 * This function posterizes one frame of the video by iterating through video
213 * lines. In every pass, start of Y, U and V planes is calculated and for
214 * every pixel we calculate new values of YUV values.
215 *****************************************************************************/
216 static void PlanarYUVPosterize( picture_t
*p_pic
, picture_t
*p_outpic
,
219 uint8_t *p_in_y
, *p_in_u
, *p_in_v
, *p_in_end_y
, *p_line_end_y
, *p_out_y
,
221 int i_current_line
= 0;
223 p_in_y
= p_pic
->p
[Y_PLANE
].p_pixels
;
224 p_in_end_y
= p_in_y
+ p_pic
->p
[Y_PLANE
].i_visible_lines
225 * p_pic
->p
[Y_PLANE
].i_pitch
;
226 p_out_y
= p_outpic
->p
[Y_PLANE
].p_pixels
;
228 /* iterate for every visible line in the frame */
229 while( p_in_y
< p_in_end_y
)
231 p_line_end_y
= p_in_y
+ p_pic
->p
[Y_PLANE
].i_visible_pitch
;
232 /* calculate start of U plane line */
233 p_in_u
= p_pic
->p
[U_PLANE
].p_pixels
234 + p_pic
->p
[U_PLANE
].i_pitch
* ( i_current_line
/ 2 );
235 p_out_u
= p_outpic
->p
[U_PLANE
].p_pixels
236 + p_outpic
->p
[U_PLANE
].i_pitch
* ( i_current_line
/ 2 );
237 /* calculate start of V plane line */
238 p_in_v
= p_pic
->p
[V_PLANE
].p_pixels
239 + p_pic
->p
[V_PLANE
].i_pitch
* ( i_current_line
/ 2 );
240 p_out_v
= p_outpic
->p
[V_PLANE
].p_pixels
241 + p_outpic
->p
[V_PLANE
].i_pitch
* ( i_current_line
/ 2 );
242 /* iterate for every two pixels in line */
243 while( p_in_y
< p_line_end_y
)
245 uint8_t y1
, y2
, u
, v
;
246 uint8_t posterized_y1
, posterized_y2
, posterized_u
, posterized_v
;
247 /* retrieve original YUV values */
252 /* do posterization */
253 YuvPosterization( &posterized_y1
, &posterized_y2
, &posterized_u
,
254 &posterized_v
, y1
, y2
, u
, v
, i_level
);
255 /* put posterized valued */
256 *p_out_y
++ = posterized_y1
;
257 *p_out_y
++ = posterized_y2
;
258 *p_out_u
++ = posterized_u
;
259 *p_out_v
++ = posterized_v
;
261 p_in_y
+= p_pic
->p
[Y_PLANE
].i_pitch
262 - p_pic
->p
[Y_PLANE
].i_visible_pitch
;
263 p_out_y
+= p_outpic
->p
[Y_PLANE
].i_pitch
264 - p_outpic
->p
[Y_PLANE
].i_visible_pitch
;
269 /*****************************************************************************
270 * PackedYUVPosterize: Posterize one frame of the packed YUV video
271 *****************************************************************************
272 * This function posterizes one frame of the video by iterating through video
273 * lines. In every pass, we calculate new values for pixels (UYVY, VYUY, YUYV
274 * and YVYU formats are supported)
275 *****************************************************************************/
276 static void PackedYUVPosterize( picture_t
*p_pic
, picture_t
*p_outpic
, int i_level
)
278 uint8_t *p_in
, *p_in_end
, *p_line_end
, *p_out
;
279 uint8_t y1
, y2
, u
, v
;
281 p_in
= p_pic
->p
[0].p_pixels
;
282 p_in_end
= p_in
+ p_pic
->p
[0].i_visible_lines
283 * p_pic
->p
[0].i_pitch
;
284 p_out
= p_outpic
->p
[0].p_pixels
;
286 while( p_in
< p_in_end
)
288 p_line_end
= p_in
+ p_pic
->p
[0].i_visible_pitch
;
289 while( p_in
< p_line_end
)
291 uint8_t posterized_y1
, posterized_y2
, posterized_u
, posterized_v
;
292 /* extract proper pixel values */
293 switch( p_pic
->format
.i_chroma
)
320 vlc_assert_unreachable();
322 /* do posterization */
323 YuvPosterization( &posterized_y1
, &posterized_y2
, &posterized_u
,
324 &posterized_v
, y1
, y2
, u
, v
, i_level
);
325 /* put posterized values in proper place */
326 switch( p_pic
->format
.i_chroma
)
329 *p_out
++ = posterized_u
;
330 *p_out
++ = posterized_y1
;
331 *p_out
++ = posterized_v
;
332 *p_out
++ = posterized_y2
;
335 *p_out
++ = posterized_v
;
336 *p_out
++ = posterized_y1
;
337 *p_out
++ = posterized_u
;
338 *p_out
++ = posterized_y2
;
341 *p_out
++ = posterized_y1
;
342 *p_out
++ = posterized_u
;
343 *p_out
++ = posterized_y2
;
344 *p_out
++ = posterized_v
;
347 *p_out
++ = posterized_y1
;
348 *p_out
++ = posterized_v
;
349 *p_out
++ = posterized_y2
;
350 *p_out
++ = posterized_u
;
353 vlc_assert_unreachable();
356 p_in
+= p_pic
->p
[0].i_pitch
- p_pic
->p
[0].i_visible_pitch
;
357 p_out
+= p_outpic
->p
[0].i_pitch
358 - p_outpic
->p
[0].i_visible_pitch
;
362 /*****************************************************************************
363 * RVPosterize: Posterize one frame of the RV24/RV32 video
364 *****************************************************************************
365 * This function posterizes one frame of the video by iterating through video
366 * lines and calculating new values for every byte in chunks of 3 (RV24) or
368 *****************************************************************************/
369 static void RVPosterize( picture_t
*p_pic
, picture_t
*p_outpic
,
370 bool rv32
, int level
)
372 uint8_t *p_in
, *p_in_end
, *p_line_end
, *p_out
, pixel
;
374 p_in
= p_pic
->p
[0].p_pixels
;
375 p_in_end
= p_in
+ p_pic
->p
[0].i_visible_lines
376 * p_pic
->p
[0].i_pitch
;
377 p_out
= p_outpic
->p
[0].p_pixels
;
379 while( p_in
< p_in_end
)
381 p_line_end
= p_in
+ p_pic
->p
[0].i_visible_pitch
;
382 while( p_in
< p_line_end
)
385 *p_out
++ = POSTERIZE_PIXEL( pixel
, level
);
387 *p_out
++ = POSTERIZE_PIXEL( pixel
, level
);
389 *p_out
++ = POSTERIZE_PIXEL( pixel
, level
);
390 /* for rv32 we take 4 chunks at the time */
394 *p_out
++ = POSTERIZE_PIXEL( pixel
, level
);
397 p_in
+= p_pic
->p
[0].i_pitch
- p_pic
->p
[0].i_visible_pitch
;
398 p_out
+= p_outpic
->p
[0].i_pitch
399 - p_outpic
->p
[0].i_visible_pitch
;
403 /*****************************************************************************
404 * YuvPosterization: Lowers the color depth of YUV color space
405 *****************************************************************************
406 * This function lowers the color level of YUV color space to specified level
407 * by converting YUV color values to theirs RGB equivalents, calculates new
408 * values and then converts RGB values to YUV values again.
409 *****************************************************************************/
410 static void YuvPosterization( uint8_t* posterized_y1
, uint8_t* posterized_y2
,
411 uint8_t* posterized_u
, uint8_t* posterized_v
,
412 uint8_t y1
, uint8_t y2
, uint8_t u
, uint8_t v
,
414 int r1
, g1
, b1
; /* for y1 new value */
415 int r2
, b2
, g2
; /* for y2 new value */
416 int r3
, g3
, b3
; /* for new values of u and v */
417 /* fist convert YUV -> RGB */
418 yuv_to_rgb( &r1
, &g1
, &b1
, y1
, u
, v
);
419 yuv_to_rgb( &r2
, &g2
, &b2
, y1
, u
, v
);
420 yuv_to_rgb( &r3
, &g3
, &b3
, ( y1
+ y2
) / 2, u
, v
);
421 /* round RGB values to specified posterize level */
422 r1
= POSTERIZE_PIXEL( r1
, i_level
);
423 g1
= POSTERIZE_PIXEL( g1
, i_level
);
424 b1
= POSTERIZE_PIXEL( b1
, i_level
);
425 r2
= POSTERIZE_PIXEL( r2
, i_level
);
426 g2
= POSTERIZE_PIXEL( g2
, i_level
);
427 b2
= POSTERIZE_PIXEL( b2
, i_level
);
428 r3
= POSTERIZE_PIXEL( r3
, i_level
);
429 g3
= POSTERIZE_PIXEL( g3
, i_level
);
430 b3
= POSTERIZE_PIXEL( b3
, i_level
);
431 /* convert from calculated RGB -> YUV */
432 *posterized_y1
= ( ( 66 * r1
+ 129 * g1
+ 25 * b1
+ 128 ) >> 8 ) + 16;
433 *posterized_y2
= ( ( 66 * r2
+ 129 * g2
+ 25 * b2
+ 128 ) >> 8 ) + 16;
434 *posterized_u
= ( ( -38 * r3
- 74 * g3
+ 112 * b3
+ 128 ) >> 8 ) + 128;
435 *posterized_v
= ( ( 112 * r3
- 94 * g3
- 18 * b3
+ 128 ) >> 8 ) + 128;
438 static int FilterCallback ( vlc_object_t
*p_this
, char const *psz_var
,
439 vlc_value_t oldval
, vlc_value_t newval
, void *p_data
)
441 (void)p_this
; (void)oldval
;
442 filter_sys_t
*p_sys
= p_data
;
444 if( !strcmp( psz_var
, CFG_PREFIX
"level" ) )
445 atomic_store( &p_sys
->i_level
, newval
.i_int
);