* all: added a new access_filter module: record + one hotkey to start/stop
[vlc.git] / 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 VideoLAN
5  * $Id$
6  *
7  * Author: Sam Hocevar <sam@zoy.org>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <math.h>                                            /* exp(), pow() */
28 #include <string.h>                                            /* strerror() */
29 #include <stdlib.h>                                      /* malloc(), free() */
30
31 #include <vlc/vlc.h>
32 #include <vlc/vout.h>
33
34 #include "i420_rgb.h"
35 #if defined (MODULE_NAME_IS_i420_rgb)
36 #   include "i420_rgb_c.h"
37 #endif
38
39 /*****************************************************************************
40  * RGB2PIXEL: assemble RGB components to a pixel value, returns a uint32_t
41  *****************************************************************************/
42 #define RGB2PIXEL( p_vout, i_r, i_g, i_b )          \
43     (((((uint32_t)i_r) >> p_vout->output.i_rrshift) \
44                        << p_vout->output.i_lrshift) \
45    | ((((uint32_t)i_g) >> p_vout->output.i_rgshift) \
46                        << p_vout->output.i_lgshift) \
47    | ((((uint32_t)i_b) >> p_vout->output.i_rbshift) \
48                        << p_vout->output.i_lbshift))
49
50 /*****************************************************************************
51  * Local and extern prototypes.
52  *****************************************************************************/
53 static int  Activate   ( vlc_object_t * );
54 static void Deactivate ( vlc_object_t * );
55
56 #if defined (MODULE_NAME_IS_i420_rgb)
57 static void SetGammaTable       ( int *pi_table, double f_gamma );
58 static void SetYUV              ( vout_thread_t * );
59 static void Set8bppPalette      ( vout_thread_t *, uint8_t * );
60 #endif
61
62 /*****************************************************************************
63  * Module descriptor.
64  *****************************************************************************/
65 vlc_module_begin();
66 #if defined (MODULE_NAME_IS_i420_rgb)
67     set_description( _("I420,IYUV,YV12 to "
68                        "RGB2,RV15,RV16,RV24,RV32 conversions") );
69     set_capability( "chroma", 80 );
70 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
71     set_description( _( "MMX I420,IYUV,YV12 to "
72                         "RV15,RV16,RV24,RV32 conversions") );
73     set_capability( "chroma", 100 );
74     add_requirement( MMX );
75 #endif
76     set_callbacks( Activate, Deactivate );
77 vlc_module_end();
78
79 /*****************************************************************************
80  * Activate: allocate a chroma function
81  *****************************************************************************
82  * This function allocates and initializes a chroma function
83  *****************************************************************************/
84 static int Activate( vlc_object_t *p_this )
85 {
86     vout_thread_t *p_vout = (vout_thread_t *)p_this;
87 #if defined (MODULE_NAME_IS_i420_rgb)
88     size_t i_tables_size;
89 #endif
90
91     if( p_vout->render.i_width & 1 || p_vout->render.i_height & 1 )
92     {
93         return -1;
94     }
95
96     switch( p_vout->render.i_chroma )
97     {
98         case VLC_FOURCC('Y','V','1','2'):
99         case VLC_FOURCC('I','4','2','0'):
100         case VLC_FOURCC('I','Y','U','V'):
101             switch( p_vout->output.i_chroma )
102             {
103 #if defined (MODULE_NAME_IS_i420_rgb)
104                 case VLC_FOURCC('R','G','B','2'):
105                     p_vout->chroma.pf_convert = E_(I420_RGB8);
106                     break;
107 #endif
108                 case VLC_FOURCC('R','V','1','5'):
109                 case VLC_FOURCC('R','V','1','6'):
110 #if defined (MODULE_NAME_IS_i420_rgb_mmx)
111                     /* If we don't have support for the bitmasks, bail out */
112                     if( ( p_vout->output.i_rmask != 0x7c00
113                            || p_vout->output.i_gmask != 0x03e0
114                            || p_vout->output.i_bmask != 0x001f )
115                      && ( p_vout->output.i_rmask != 0xf800
116                            || p_vout->output.i_gmask != 0x07e0
117                            || p_vout->output.i_bmask != 0x001f ) )
118                     {
119                         return -1;
120                     }
121 #endif
122                     p_vout->chroma.pf_convert = E_(I420_RGB16);
123                     break;
124
125 #if 0
126                 /* Hmmm, is there only X11 using 32bits per pixel for RV24 ? */
127                 case VLC_FOURCC('R','V','2','4'):
128 #endif
129
130                 case VLC_FOURCC('R','V','3','2'):
131 #if defined (MODULE_NAME_IS_i420_rgb_mmx)
132                     /* If we don't have support for the bitmasks, bail out */
133                     if( p_vout->output.i_rmask != 0x00ff0000
134                          || p_vout->output.i_gmask != 0x0000ff00
135                          || p_vout->output.i_bmask != 0x000000ff )
136                     {
137                         return -1;
138                     }
139 #endif
140                     p_vout->chroma.pf_convert = E_(I420_RGB32);
141                     break;
142
143                 default:
144                     return -1;
145             }
146             break;
147
148         default:
149             return -1;
150     }
151
152     p_vout->chroma.p_sys = malloc( sizeof( chroma_sys_t ) );
153     if( p_vout->chroma.p_sys == NULL )
154     {
155         return -1;
156     }
157
158     switch( p_vout->output.i_chroma )
159     {
160 #if defined (MODULE_NAME_IS_i420_rgb)
161         case VLC_FOURCC('R','G','B','2'):
162             p_vout->chroma.p_sys->p_buffer = malloc( VOUT_MAX_WIDTH );
163             break;
164 #endif
165
166         case VLC_FOURCC('R','V','1','5'):
167         case VLC_FOURCC('R','V','1','6'):
168             p_vout->chroma.p_sys->p_buffer = malloc( VOUT_MAX_WIDTH * 2 );
169             break;
170
171         case VLC_FOURCC('R','V','2','4'):
172         case VLC_FOURCC('R','V','3','2'):
173             p_vout->chroma.p_sys->p_buffer = malloc( VOUT_MAX_WIDTH * 4 );
174             break;
175
176         default:
177             p_vout->chroma.p_sys->p_buffer = NULL;
178             break;
179     }
180
181     if( p_vout->chroma.p_sys->p_buffer == NULL )
182     {
183         free( p_vout->chroma.p_sys );
184         return -1;
185     }
186
187     p_vout->chroma.p_sys->p_offset = malloc( p_vout->output.i_width
188                     * ( ( p_vout->output.i_chroma
189                            == VLC_FOURCC('R','G','B','2') ) ? 2 : 1 )
190                     * sizeof( int ) );
191     if( p_vout->chroma.p_sys->p_offset == NULL )
192     {
193         free( p_vout->chroma.p_sys->p_buffer );
194         free( p_vout->chroma.p_sys );
195         return -1;
196     }
197
198 #if defined (MODULE_NAME_IS_i420_rgb)
199     switch( p_vout->output.i_chroma )
200     {
201     case VLC_FOURCC('R','G','B','2'):
202         i_tables_size = sizeof( uint8_t ) * PALETTE_TABLE_SIZE;
203         break;
204     case VLC_FOURCC('R','V','1','5'):
205     case VLC_FOURCC('R','V','1','6'):
206         i_tables_size = sizeof( uint16_t ) * RGB_TABLE_SIZE;
207         break;
208     default: /* RV24, RV32 */
209         i_tables_size = sizeof( uint32_t ) * RGB_TABLE_SIZE;
210         break;
211     }
212
213     p_vout->chroma.p_sys->p_base = malloc( i_tables_size );
214     if( p_vout->chroma.p_sys->p_base == NULL )
215     {
216         free( p_vout->chroma.p_sys->p_offset );
217         free( p_vout->chroma.p_sys->p_buffer );
218         free( p_vout->chroma.p_sys );
219         return -1;
220     }
221
222     SetYUV( p_vout );
223 #endif
224
225     return 0;
226 }
227
228 /*****************************************************************************
229  * Deactivate: free the chroma function
230  *****************************************************************************
231  * This function frees the previously allocated chroma function
232  *****************************************************************************/
233 static void Deactivate( vlc_object_t *p_this )
234 {
235     vout_thread_t *p_vout = (vout_thread_t *)p_this;
236
237 #if defined (MODULE_NAME_IS_i420_rgb)
238     free( p_vout->chroma.p_sys->p_base );
239 #endif
240     free( p_vout->chroma.p_sys->p_offset );
241     free( p_vout->chroma.p_sys->p_buffer );
242     free( p_vout->chroma.p_sys );
243 }
244
245 #if defined (MODULE_NAME_IS_i420_rgb)
246 /*****************************************************************************
247  * SetGammaTable: return intensity table transformed by gamma curve.
248  *****************************************************************************
249  * pi_table is a table of 256 entries from 0 to 255.
250  *****************************************************************************/
251 static void SetGammaTable( int *pi_table, double f_gamma )
252 {
253     int i_y;                                               /* base intensity */
254
255     /* Use exp(gamma) instead of gamma */
256     f_gamma = exp( f_gamma );
257
258     /* Build gamma table */
259     for( i_y = 0; i_y < 256; i_y++ )
260     {
261         pi_table[ i_y ] = (int)( pow( (double)i_y / 256, f_gamma ) * 256 );
262     }
263 }
264
265 /*****************************************************************************
266  * SetYUV: compute tables and set function pointers
267  *****************************************************************************/
268 static void SetYUV( vout_thread_t *p_vout )
269 {
270     int          pi_gamma[256];                               /* gamma table */
271     volatile int i_index;                                 /* index in tables */
272                    /* We use volatile here to work around a strange gcc-3.3.4
273                     * optimization bug */
274
275     /* Build gamma table */
276     SetGammaTable( pi_gamma, p_vout->f_gamma );
277
278     /*
279      * Set pointers and build YUV tables
280      */
281
282     /* Color: build red, green and blue tables */
283     switch( p_vout->output.i_chroma )
284     {
285     case VLC_FOURCC('R','G','B','2'):
286         p_vout->chroma.p_sys->p_rgb8 = (uint8_t *)p_vout->chroma.p_sys->p_base;
287         Set8bppPalette( p_vout, p_vout->chroma.p_sys->p_rgb8 );
288         break;
289
290     case VLC_FOURCC('R','V','1','5'):
291     case VLC_FOURCC('R','V','1','6'):
292         p_vout->chroma.p_sys->p_rgb16 = (uint16_t *)p_vout->chroma.p_sys->p_base;
293         for( i_index = 0; i_index < RED_MARGIN; i_index++ )
294         {
295             p_vout->chroma.p_sys->p_rgb16[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_vout, pi_gamma[0], 0, 0 );
296             p_vout->chroma.p_sys->p_rgb16[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_vout, pi_gamma[255], 0, 0 );
297         }
298         for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
299         {
300             p_vout->chroma.p_sys->p_rgb16[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[0], 0 );
301             p_vout->chroma.p_sys->p_rgb16[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_vout, 0, pi_gamma[255], 0 );
302         }
303         for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
304         {
305             p_vout->chroma.p_sys->p_rgb16[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[0] );
306             p_vout->chroma.p_sys->p_rgb16[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[255] );
307         }
308         for( i_index = 0; i_index < 256; i_index++ )
309         {
310             p_vout->chroma.p_sys->p_rgb16[RED_OFFSET + i_index] =   RGB2PIXEL( p_vout, pi_gamma[ i_index ], 0, 0 );
311             p_vout->chroma.p_sys->p_rgb16[GREEN_OFFSET + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[ i_index ], 0 );
312             p_vout->chroma.p_sys->p_rgb16[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_vout, 0, 0, pi_gamma[ i_index ] );
313         }
314         break;
315
316     case VLC_FOURCC('R','V','2','4'):
317     case VLC_FOURCC('R','V','3','2'):
318         p_vout->chroma.p_sys->p_rgb32 = (uint32_t *)p_vout->chroma.p_sys->p_base;
319         for( i_index = 0; i_index < RED_MARGIN; i_index++ )
320         {
321             p_vout->chroma.p_sys->p_rgb32[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_vout, pi_gamma[0], 0, 0 );
322             p_vout->chroma.p_sys->p_rgb32[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_vout, pi_gamma[255], 0, 0 );
323         }
324         for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
325         {
326             p_vout->chroma.p_sys->p_rgb32[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[0], 0 );
327             p_vout->chroma.p_sys->p_rgb32[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_vout, 0, pi_gamma[255], 0 );
328         }
329         for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
330         {
331             p_vout->chroma.p_sys->p_rgb32[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[0] );
332             p_vout->chroma.p_sys->p_rgb32[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[255] );
333         }
334         for( i_index = 0; i_index < 256; i_index++ )
335         {
336             p_vout->chroma.p_sys->p_rgb32[RED_OFFSET + i_index] =   RGB2PIXEL( p_vout, pi_gamma[ i_index ], 0, 0 );
337             p_vout->chroma.p_sys->p_rgb32[GREEN_OFFSET + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[ i_index ], 0 );
338             p_vout->chroma.p_sys->p_rgb32[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_vout, 0, 0, pi_gamma[ i_index ] );
339         }
340         break;
341     }
342 }
343
344 static void Set8bppPalette( vout_thread_t *p_vout, uint8_t *p_rgb8 )
345 {
346     #define CLIP( x ) ( ((x < 0) ? 0 : (x > 255) ? 255 : x) << 8 )
347
348     int y,u,v;
349     int r,g,b;
350     int i = 0, j = 0;
351     uint16_t *p_cmap_r=p_vout->chroma.p_sys->p_rgb_r;
352     uint16_t *p_cmap_g=p_vout->chroma.p_sys->p_rgb_g;
353     uint16_t *p_cmap_b=p_vout->chroma.p_sys->p_rgb_b;
354
355     unsigned char p_lookup[PALETTE_TABLE_SIZE];
356
357     /* This loop calculates the intersection of an YUV box and the RGB cube. */
358     for ( y = 0; y <= 256; y += 16, i += 128 - 81 )
359     {
360         for ( u = 0; u <= 256; u += 32 )
361         {
362             for ( v = 0; v <= 256; v += 32 )
363             {
364                 r = y + ( (V_RED_COEF*(v-128)) >> SHIFT );
365                 g = y + ( (U_GREEN_COEF*(u-128)
366                          + V_GREEN_COEF*(v-128)) >> SHIFT );
367                 b = y + ( (U_BLUE_COEF*(u-128)) >> SHIFT );
368
369                 if( r >= 0x00 && g >= 0x00 && b >= 0x00
370                         && r <= 0xff && g <= 0xff && b <= 0xff )
371                 {
372                     /* This one should never happen unless someone
373                      * fscked up my code */
374                     if( j == 256 )
375                     {
376                         msg_Err( p_vout, "no colors left in palette" );
377                         break;
378                     }
379
380                     /* Clip the colors */
381                     p_cmap_r[ j ] = CLIP( r );
382                     p_cmap_g[ j ] = CLIP( g );
383                     p_cmap_b[ j ] = CLIP( b );
384
385 #if 0
386                     printf("+++Alloc RGB cmap %d (%d, %d, %d)\n", j,
387                            p_cmap_r[ j ] >>8, p_cmap_g[ j ] >>8, 
388                            p_cmap_b[ j ] >>8);
389 #endif
390
391                     /* Allocate color */
392                     p_lookup[ i ] = 1;
393                     p_rgb8[ i++ ] = j;
394                     j++;
395                 }
396                 else
397                 {
398                     p_lookup[ i ] = 0;
399                     p_rgb8[ i++ ] = 0;
400                 }
401             }
402         }
403     }
404
405     /* The colors have been allocated, we can set the palette */
406     p_vout->output.pf_setpalette( p_vout, p_cmap_r, p_cmap_g, p_cmap_b );
407
408 #if 0
409     /* There will eventually be a way to know which colors
410      * couldn't be allocated and try to find a replacement */
411     p_vout->i_white_pixel = 0xff;
412     p_vout->i_black_pixel = 0x00;
413     p_vout->i_gray_pixel = 0x44;
414     p_vout->i_blue_pixel = 0x3b;
415 #endif
416
417     /* This loop allocates colors that got outside the RGB cube */
418     for ( i = 0, y = 0; y <= 256; y += 16, i += 128 - 81 )
419     {
420         for ( u = 0; u <= 256; u += 32 )
421         {
422             for ( v = 0; v <= 256; v += 32, i++ )
423             {
424                 int u2, v2, dist, mindist = 100000000;
425
426                 if( p_lookup[ i ] || y == 0 )
427                 {
428                     continue;
429                 }
430
431                 /* Heavy. yeah. */
432                 for( u2 = 0; u2 <= 256; u2 += 32 )
433                 {
434                     for( v2 = 0; v2 <= 256; v2 += 32 )
435                     {
436                         j = ((y>>4)<<7) + (u2>>5)*9 + (v2>>5);
437                         dist = (u-u2)*(u-u2) + (v-v2)*(v-v2);
438
439                         /* Find the nearest color */
440                         if( p_lookup[ j ] && dist < mindist )
441                         {
442                             p_rgb8[ i ] = p_rgb8[ j ];
443                             mindist = dist;
444                         }
445
446                         j -= 128;
447
448                         /* Find the nearest color */
449                         if( p_lookup[ j ] && dist + 128 < mindist )
450                         {
451                             p_rgb8[ i ] = p_rgb8[ j ];
452                             mindist = dist + 128;
453                         }
454                     }
455                 }
456             }
457         }
458     }
459 }
460
461 #endif
462