]> git.sesse.net Git - vlc/blob - modules/video_chroma/i420_rgb.c
swscale: use function video_format_IsSimilar.
[vlc] / modules / video_chroma / i420_rgb.c
1 /*****************************************************************************
2  * i420_rgb.c : YUV to bitmap RGB conversion module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001, 2004, 2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Sam Hocevar <sam@zoy.org>
8  *          Damien Fouilleul <damienf@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <math.h>                                            /* exp(), pow() */
34
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
37 #include <vlc_filter.h>
38 #include <vlc_cpu.h>
39
40 #include "i420_rgb.h"
41 #ifdef PLAIN
42 # include "i420_rgb_c.h"
43 static picture_t *I420_RGB8_Filter( filter_t *, picture_t * );
44 static picture_t *I420_RGB16_Filter( filter_t *, picture_t * );
45 static picture_t *I420_RGB32_Filter( filter_t *, picture_t * );
46
47 static void SetGammaTable( int *pi_table, double f_gamma );
48 static void SetYUV( filter_t * );
49 static void Set8bppPalette( filter_t *, uint8_t * );
50 #else
51 static picture_t *I420_R5G5B5_Filter( filter_t *, picture_t * );
52 static picture_t *I420_R5G6B5_Filter( filter_t *, picture_t * );
53 static picture_t *I420_A8R8G8B8_Filter( filter_t *, picture_t * );
54 static picture_t *I420_R8G8B8A8_Filter( filter_t *, picture_t * );
55 static picture_t *I420_B8G8R8A8_Filter( filter_t *, picture_t * );
56 static picture_t *I420_A8B8G8R8_Filter( filter_t *, picture_t * );
57 #endif
58
59 /*****************************************************************************
60  * RGB2PIXEL: assemble RGB components to a pixel value, returns a uint32_t
61  *****************************************************************************/
62 #define RGB2PIXEL( p_filter, i_r, i_g, i_b )                 \
63     (((((uint32_t)i_r) >> p_filter->fmt_out.video.i_rrshift) \
64                        << p_filter->fmt_out.video.i_lrshift) \
65    | ((((uint32_t)i_g) >> p_filter->fmt_out.video.i_rgshift) \
66                        << p_filter->fmt_out.video.i_lgshift) \
67    | ((((uint32_t)i_b) >> p_filter->fmt_out.video.i_rbshift) \
68                        << p_filter->fmt_out.video.i_lbshift))
69
70 /*****************************************************************************
71  * Module descriptor.
72  *****************************************************************************/
73 static int  Activate   ( vlc_object_t * );
74 static void Deactivate ( vlc_object_t * );
75
76 vlc_module_begin ()
77 #if defined (SSE2)
78     set_description( N_( "SSE2 I420,IYUV,YV12 to "
79                         "RV15,RV16,RV24,RV32 conversions") )
80     set_capability( "video filter2", 120 )
81 # define vlc_CPU_capable() vlc_CPU_SSE2()
82 #elif defined (MMX)
83     set_description( N_( "MMX I420,IYUV,YV12 to "
84                         "RV15,RV16,RV24,RV32 conversions") )
85     set_capability( "video filter2", 100 )
86 # define vlc_CPU_capable() vlc_CPU_MMX()
87 #else
88     set_description( N_("I420,IYUV,YV12 to "
89                        "RGB2,RV15,RV16,RV24,RV32 conversions") )
90     set_capability( "video filter2", 80 )
91 # define vlc_CPU_capable() (true)
92 #endif
93     set_callbacks( Activate, Deactivate )
94 vlc_module_end ()
95
96 /*****************************************************************************
97  * Activate: allocate a chroma function
98  *****************************************************************************
99  * This function allocates and initializes a chroma function
100  *****************************************************************************/
101 static int Activate( vlc_object_t *p_this )
102 {
103     filter_t *p_filter = (filter_t *)p_this;
104 #ifdef PLAIN
105     size_t i_tables_size;
106 #endif
107
108     if( !vlc_CPU_capable() )
109         return VLC_EGENERIC;
110     if( p_filter->fmt_out.video.i_width & 1
111      || p_filter->fmt_out.video.i_height & 1 )
112     {
113         return VLC_EGENERIC;
114     }
115
116     switch( p_filter->fmt_in.video.i_chroma )
117     {
118         case VLC_CODEC_YV12:
119         case VLC_CODEC_I420:
120             switch( p_filter->fmt_out.video.i_chroma )
121             {
122 #ifndef PLAIN
123                 case VLC_CODEC_RGB15:
124                 case VLC_CODEC_RGB16:
125                     /* If we don't have support for the bitmasks, bail out */
126                     if( ( p_filter->fmt_out.video.i_rmask == 0x7c00
127                        && p_filter->fmt_out.video.i_gmask == 0x03e0
128                        && p_filter->fmt_out.video.i_bmask == 0x001f ) )
129                     {
130                         /* R5G5B6 pixel format */
131                         msg_Dbg(p_this, "RGB pixel format is R5G5B5");
132                         p_filter->pf_video_filter = I420_R5G5B5_Filter;
133                     }
134                     else if( ( p_filter->fmt_out.video.i_rmask == 0xf800
135                             && p_filter->fmt_out.video.i_gmask == 0x07e0
136                             && p_filter->fmt_out.video.i_bmask == 0x001f ) )
137                     {
138                         /* R5G6B5 pixel format */
139                         msg_Dbg(p_this, "RGB pixel format is R5G6B5");
140                         p_filter->pf_video_filter = I420_R5G6B5_Filter;
141                     }
142                     else
143                         return VLC_EGENERIC;
144                     break;
145                 case VLC_CODEC_RGB32:
146                     /* If we don't have support for the bitmasks, bail out */
147                     if( p_filter->fmt_out.video.i_rmask == 0x00ff0000
148                      && p_filter->fmt_out.video.i_gmask == 0x0000ff00
149                      && p_filter->fmt_out.video.i_bmask == 0x000000ff )
150                     {
151                         /* A8R8G8B8 pixel format */
152                         msg_Dbg(p_this, "RGB pixel format is A8R8G8B8");
153                         p_filter->pf_video_filter = I420_A8R8G8B8_Filter;
154                     }
155                     else if( p_filter->fmt_out.video.i_rmask == 0xff000000
156                           && p_filter->fmt_out.video.i_gmask == 0x00ff0000
157                           && p_filter->fmt_out.video.i_bmask == 0x0000ff00 )
158                     {
159                         /* R8G8B8A8 pixel format */
160                         msg_Dbg(p_this, "RGB pixel format is R8G8B8A8");
161                         p_filter->pf_video_filter = I420_R8G8B8A8_Filter;
162                     }
163                     else if( p_filter->fmt_out.video.i_rmask == 0x0000ff00
164                           && p_filter->fmt_out.video.i_gmask == 0x00ff0000
165                           && p_filter->fmt_out.video.i_bmask == 0xff000000 )
166                     {
167                         /* B8G8R8A8 pixel format */
168                         msg_Dbg(p_this, "RGB pixel format is B8G8R8A8");
169                         p_filter->pf_video_filter = I420_B8G8R8A8_Filter;
170                     }
171                     else if( p_filter->fmt_out.video.i_rmask == 0x000000ff
172                           && p_filter->fmt_out.video.i_gmask == 0x0000ff00
173                           && p_filter->fmt_out.video.i_bmask == 0x00ff0000 )
174                     {
175                         /* A8B8G8R8 pixel format */
176                         msg_Dbg(p_this, "RGB pixel format is A8B8G8R8");
177                         p_filter->pf_video_filter = I420_A8B8G8R8_Filter;
178                     }
179                     else
180                         return VLC_EGENERIC;
181                     break;
182 #else
183                 case VLC_CODEC_RGB8:
184                     p_filter->pf_video_filter = I420_RGB8_Filter;
185                     break;
186                 case VLC_CODEC_RGB15:
187                 case VLC_CODEC_RGB16:
188                     p_filter->pf_video_filter = I420_RGB16_Filter;
189                     break;
190                 case VLC_CODEC_RGB32:
191                     p_filter->pf_video_filter = I420_RGB32_Filter;
192                     break;
193 #endif
194                 default:
195                     return VLC_EGENERIC;
196             }
197             break;
198
199         default:
200             return VLC_EGENERIC;
201     }
202
203     p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
204     if( p_filter->p_sys == NULL )
205     {
206         return VLC_EGENERIC;
207     }
208
209     switch( p_filter->fmt_out.video.i_chroma )
210     {
211 #ifdef PLAIN
212         case VLC_CODEC_RGB8:
213             p_filter->p_sys->p_buffer = malloc( VOUT_MAX_WIDTH );
214             break;
215 #endif
216         case VLC_CODEC_RGB15:
217         case VLC_CODEC_RGB16:
218             p_filter->p_sys->p_buffer = malloc( VOUT_MAX_WIDTH * 2 );
219             break;
220         case VLC_CODEC_RGB24:
221         case VLC_CODEC_RGB32:
222             p_filter->p_sys->p_buffer = malloc( VOUT_MAX_WIDTH * 4 );
223             break;
224         default:
225             p_filter->p_sys->p_buffer = NULL;
226             break;
227     }
228
229     if( p_filter->p_sys->p_buffer == NULL )
230     {
231         free( p_filter->p_sys );
232         return VLC_EGENERIC;
233     }
234
235     p_filter->p_sys->p_offset = malloc( p_filter->fmt_out.video.i_width
236                     * ( ( p_filter->fmt_out.video.i_chroma
237                            == VLC_CODEC_RGB8 ) ? 2 : 1 )
238                     * sizeof( int ) );
239     if( p_filter->p_sys->p_offset == NULL )
240     {
241         free( p_filter->p_sys->p_buffer );
242         free( p_filter->p_sys );
243         return VLC_EGENERIC;
244     }
245
246 #ifdef PLAIN
247     switch( p_filter->fmt_out.video.i_chroma )
248     {
249     case VLC_CODEC_RGB8:
250         i_tables_size = sizeof( uint8_t ) * PALETTE_TABLE_SIZE;
251         break;
252     case VLC_CODEC_RGB15:
253     case VLC_CODEC_RGB16:
254         i_tables_size = sizeof( uint16_t ) * RGB_TABLE_SIZE;
255         break;
256     default: /* RV24, RV32 */
257         i_tables_size = sizeof( uint32_t ) * RGB_TABLE_SIZE;
258         break;
259     }
260
261     p_filter->p_sys->p_base = malloc( i_tables_size );
262     if( p_filter->p_sys->p_base == NULL )
263     {
264         free( p_filter->p_sys->p_offset );
265         free( p_filter->p_sys->p_buffer );
266         free( p_filter->p_sys );
267         return -1;
268     }
269
270     SetYUV( p_filter );
271 #endif
272
273     return 0;
274 }
275
276 /*****************************************************************************
277  * Deactivate: free the chroma function
278  *****************************************************************************
279  * This function frees the previously allocated chroma function
280  *****************************************************************************/
281 static void Deactivate( vlc_object_t *p_this )
282 {
283     filter_t *p_filter = (filter_t *)p_this;
284
285 #ifdef PLAIN
286     free( p_filter->p_sys->p_base );
287 #endif
288     free( p_filter->p_sys->p_offset );
289     free( p_filter->p_sys->p_buffer );
290     free( p_filter->p_sys );
291 }
292
293 #ifndef PLAIN
294 VIDEO_FILTER_WRAPPER( I420_R5G5B5 )
295 VIDEO_FILTER_WRAPPER( I420_R5G6B5 )
296 VIDEO_FILTER_WRAPPER( I420_A8R8G8B8 )
297 VIDEO_FILTER_WRAPPER( I420_R8G8B8A8 )
298 VIDEO_FILTER_WRAPPER( I420_B8G8R8A8 )
299 VIDEO_FILTER_WRAPPER( I420_A8B8G8R8 )
300 #else
301 VIDEO_FILTER_WRAPPER( I420_RGB8 )
302 VIDEO_FILTER_WRAPPER( I420_RGB16 )
303 VIDEO_FILTER_WRAPPER( I420_RGB32 )
304
305 /*****************************************************************************
306  * SetGammaTable: return intensity table transformed by gamma curve.
307  *****************************************************************************
308  * pi_table is a table of 256 entries from 0 to 255.
309  *****************************************************************************/
310 static void SetGammaTable( int *pi_table, double f_gamma )
311 {
312     int i_y;                                               /* base intensity */
313
314     /* Use exp(gamma) instead of gamma */
315     f_gamma = exp( f_gamma );
316
317     /* Build gamma table */
318     for( i_y = 0; i_y < 256; i_y++ )
319     {
320         pi_table[ i_y ] = (int)( pow( (double)i_y / 256, f_gamma ) * 256 );
321     }
322 }
323
324 /*****************************************************************************
325  * SetYUV: compute tables and set function pointers
326  *****************************************************************************/
327 static void SetYUV( filter_t *p_filter )
328 {
329     int          pi_gamma[256];                               /* gamma table */
330     volatile int i_index;                                 /* index in tables */
331                    /* We use volatile here to work around a strange gcc-3.3.4
332                     * optimization bug */
333
334     /* Build gamma table */
335     SetGammaTable( pi_gamma, 0 ); //p_filter/*FIXME wasn't used anywhere anyway*/->f_gamma );
336
337     /*
338      * Set pointers and build YUV tables
339      */
340
341     /* Color: build red, green and blue tables */
342     switch( p_filter->fmt_out.video.i_chroma )
343     {
344     case VLC_CODEC_RGB8:
345         p_filter->p_sys->p_rgb8 = (uint8_t *)p_filter->p_sys->p_base;
346         Set8bppPalette( p_filter, p_filter->p_sys->p_rgb8 );
347         break;
348
349     case VLC_CODEC_RGB15:
350     case VLC_CODEC_RGB16:
351         p_filter->p_sys->p_rgb16 = (uint16_t *)p_filter->p_sys->p_base;
352         for( i_index = 0; i_index < RED_MARGIN; i_index++ )
353         {
354             p_filter->p_sys->p_rgb16[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_filter, pi_gamma[0], 0, 0 );
355             p_filter->p_sys->p_rgb16[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_filter, pi_gamma[255], 0, 0 );
356         }
357         for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
358         {
359             p_filter->p_sys->p_rgb16[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, pi_gamma[0], 0 );
360             p_filter->p_sys->p_rgb16[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_filter, 0, pi_gamma[255], 0 );
361         }
362         for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
363         {
364             p_filter->p_sys->p_rgb16[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, 0, pi_gamma[0] );
365             p_filter->p_sys->p_rgb16[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, 0, pi_gamma[255] );
366         }
367         for( i_index = 0; i_index < 256; i_index++ )
368         {
369             p_filter->p_sys->p_rgb16[RED_OFFSET + i_index] =   RGB2PIXEL( p_filter, pi_gamma[ i_index ], 0, 0 );
370             p_filter->p_sys->p_rgb16[GREEN_OFFSET + i_index] = RGB2PIXEL( p_filter, 0, pi_gamma[ i_index ], 0 );
371             p_filter->p_sys->p_rgb16[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_filter, 0, 0, pi_gamma[ i_index ] );
372         }
373         break;
374
375     case VLC_CODEC_RGB24:
376     case VLC_CODEC_RGB32:
377         p_filter->p_sys->p_rgb32 = (uint32_t *)p_filter->p_sys->p_base;
378         for( i_index = 0; i_index < RED_MARGIN; i_index++ )
379         {
380             p_filter->p_sys->p_rgb32[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_filter, pi_gamma[0], 0, 0 );
381             p_filter->p_sys->p_rgb32[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_filter, pi_gamma[255], 0, 0 );
382         }
383         for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
384         {
385             p_filter->p_sys->p_rgb32[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, pi_gamma[0], 0 );
386             p_filter->p_sys->p_rgb32[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_filter, 0, pi_gamma[255], 0 );
387         }
388         for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
389         {
390             p_filter->p_sys->p_rgb32[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, 0, pi_gamma[0] );
391             p_filter->p_sys->p_rgb32[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_filter, 0, 0, pi_gamma[255] );
392         }
393         for( i_index = 0; i_index < 256; i_index++ )
394         {
395             p_filter->p_sys->p_rgb32[RED_OFFSET + i_index] =   RGB2PIXEL( p_filter, pi_gamma[ i_index ], 0, 0 );
396             p_filter->p_sys->p_rgb32[GREEN_OFFSET + i_index] = RGB2PIXEL( p_filter, 0, pi_gamma[ i_index ], 0 );
397             p_filter->p_sys->p_rgb32[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_filter, 0, 0, pi_gamma[ i_index ] );
398         }
399         break;
400     }
401 }
402
403 static void Set8bppPalette( filter_t *p_filter, uint8_t *p_rgb8 )
404 {
405     #define CLIP( x ) ( ((x < 0) ? 0 : (x > 255) ? 255 : x) << 8 )
406
407     int y,u,v;
408     int r,g,b;
409     int i = 0, j = 0;
410     uint16_t *p_cmap_r = p_filter->p_sys->p_rgb_r;
411     uint16_t *p_cmap_g = p_filter->p_sys->p_rgb_g;
412     uint16_t *p_cmap_b = p_filter->p_sys->p_rgb_b;
413
414     unsigned char p_lookup[PALETTE_TABLE_SIZE];
415
416     /* This loop calculates the intersection of an YUV box and the RGB cube. */
417     for ( y = 0; y <= 256; y += 16, i += 128 - 81 )
418     {
419         for ( u = 0; u <= 256; u += 32 )
420         {
421             for ( v = 0; v <= 256; v += 32 )
422             {
423                 r = y + ( (V_RED_COEF*(v-128)) >> SHIFT );
424                 g = y + ( (U_GREEN_COEF*(u-128)
425                          + V_GREEN_COEF*(v-128)) >> SHIFT );
426                 b = y + ( (U_BLUE_COEF*(u-128)) >> SHIFT );
427
428                 if( r >= 0x00 && g >= 0x00 && b >= 0x00
429                         && r <= 0xff && g <= 0xff && b <= 0xff )
430                 {
431                     /* This one should never happen unless someone
432                      * fscked up my code */
433                     if( j == 256 )
434                     {
435                         msg_Err( p_filter, "no colors left in palette" );
436                         break;
437                     }
438
439                     /* Clip the colors */
440                     p_cmap_r[ j ] = CLIP( r );
441                     p_cmap_g[ j ] = CLIP( g );
442                     p_cmap_b[ j ] = CLIP( b );
443
444 #if 0
445             printf("+++Alloc RGB cmap %d (%d, %d, %d)\n", j,
446                p_cmap_r[ j ] >>8, p_cmap_g[ j ] >>8,
447                p_cmap_b[ j ] >>8);
448 #endif
449
450                     /* Allocate color */
451                     p_lookup[ i ] = 1;
452                     p_rgb8[ i++ ] = j;
453                     j++;
454                 }
455                 else
456                 {
457                     p_lookup[ i ] = 0;
458                     p_rgb8[ i++ ] = 0;
459                 }
460             }
461         }
462     }
463
464     /* The colors have been allocated, we can set the palette */
465     /* FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
466     p_filter->fmt_out.video.pf_setpalette( p_filter, p_cmap_r, p_cmap_g, p_cmap_b );*/
467
468 #if 0
469     /* There will eventually be a way to know which colors
470      * couldn't be allocated and try to find a replacement */
471     p_vout->i_white_pixel = 0xff;
472     p_vout->i_black_pixel = 0x00;
473     p_vout->i_gray_pixel = 0x44;
474     p_vout->i_blue_pixel = 0x3b;
475 #endif
476
477     /* This loop allocates colors that got outside the RGB cube */
478     for ( i = 0, y = 0; y <= 256; y += 16, i += 128 - 81 )
479     {
480         for ( u = 0; u <= 256; u += 32 )
481         {
482             for ( v = 0; v <= 256; v += 32, i++ )
483             {
484                 int u2, v2, dist, mindist = 100000000;
485
486                 if( p_lookup[ i ] || y == 0 )
487                 {
488                     continue;
489                 }
490
491                 /* Heavy. yeah. */
492                 for( u2 = 0; u2 <= 256; u2 += 32 )
493                 {
494                     for( v2 = 0; v2 <= 256; v2 += 32 )
495                     {
496                         j = ((y>>4)<<7) + (u2>>5)*9 + (v2>>5);
497                         dist = (u-u2)*(u-u2) + (v-v2)*(v-v2);
498
499                         /* Find the nearest color */
500                         if( p_lookup[ j ] && dist < mindist )
501                         {
502                             p_rgb8[ i ] = p_rgb8[ j ];
503                             mindist = dist;
504                         }
505
506                         j -= 128;
507
508                         /* Find the nearest color */
509                         if( p_lookup[ j ] && dist + 128 < mindist )
510                         {
511                             p_rgb8[ i ] = p_rgb8[ j ];
512                             mindist = dist + 128;
513                         }
514                     }
515                 }
516             }
517         }
518     }
519 }
520 #endif