]> git.sesse.net Git - vlc/blob - plugins/yuvmmx/video_yuv.c
Bon, puisque �a semble commiter sous BeOS, je commite.
[vlc] / plugins / yuvmmx / video_yuv.c
1 /*****************************************************************************
2  * video_yuv.c: YUV transformation functions
3  * Provides functions to perform the YUV conversion. The functions provided here
4  * are a complete and portable C implementation, and may be replaced in certain
5  * case by optimized functions.
6  *****************************************************************************
7  * Copyright (C) 1999, 2000 VideoLAN
8  *
9  * Authors:
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this program; if not, write to the
23  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include "defs.h"
31
32 #include <math.h>                                            /* exp(), pow() */
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <stdlib.h>                                                /* free() */
35 #include <string.h>                                            /* strerror() */
36
37 #include "config.h"
38 #include "common.h"
39 #include "threads.h"
40 #include "mtime.h"
41 #include "plugins.h"
42 #include "video.h"
43 #include "video_output.h"
44 #include "video_yuv.h"
45
46 #include "intf_msg.h"
47
48 /*****************************************************************************
49  * Constants
50  *****************************************************************************/
51
52 int     yuv_SysInit     ( vout_thread_t *p_vout );
53 int     yuv_SysReset    ( vout_thread_t *p_vout );
54 void    yuv_SysEnd      ( vout_thread_t *p_vout );
55
56 /*****************************************************************************
57  * vout_InitYUV: allocate and initialize translations tables
58  *****************************************************************************
59  * This function will allocate memory to store translation tables, depending
60  * of the screen depth.
61  *****************************************************************************/
62 int yuv_SysInit( vout_thread_t *p_vout )
63 {
64     size_t      tables_size;                        /* tables size, in bytes */
65
66     /* Computes tables size - 3 Bpp use 32 bits pixel entries in tables */
67     switch( p_vout->i_bytes_per_pixel )
68     {
69     case 1:
70         tables_size = sizeof( u8 )
71                 * (p_vout->b_grayscale ? GRAY_TABLE_SIZE : PALETTE_TABLE_SIZE);
72         break;
73     case 2:
74         tables_size = sizeof( u16 )
75                 * (p_vout->b_grayscale ? GRAY_TABLE_SIZE : RGB_TABLE_SIZE);
76         break;
77     case 3:
78     case 4:
79     default:
80         tables_size = sizeof( u32 )
81                 * (p_vout->b_grayscale ? GRAY_TABLE_SIZE : RGB_TABLE_SIZE);
82         break;
83     }
84
85     /* Allocate memory */
86     p_vout->yuv.p_base = malloc( tables_size );
87     if( p_vout->yuv.p_base == NULL )
88     {
89         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
90         return( 1 );
91     }
92
93     /* Allocate memory for conversion buffer and offset array */
94     p_vout->yuv.p_buffer = malloc( VOUT_MAX_WIDTH * p_vout->i_bytes_per_pixel );
95     if( p_vout->yuv.p_buffer == NULL )
96     {
97         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
98         free( p_vout->yuv.p_base );
99         return( 1 );
100     }
101     p_vout->yuv.p_offset = malloc( p_vout->i_width * sizeof( int ) );
102     if( p_vout->yuv.p_offset == NULL )
103     {
104         intf_ErrMsg("error: %s\n", strerror(ENOMEM));
105         free( p_vout->yuv.p_base );
106         free( p_vout->yuv.p_buffer );
107         return( 1 );
108     }
109
110     /* Initialize tables */
111     SetYUV( p_vout );
112     return( 0 );
113 }
114
115 /*****************************************************************************
116  * yuv_SysReset: re-initialize translations tables
117  *****************************************************************************
118  * This function will initialize the tables allocated by vout_CreateTables and
119  * set functions pointers.
120  *****************************************************************************/
121 int yuv_SysReset( vout_thread_t *p_vout )
122 {
123     yuv_SysEnd( p_vout );
124     return( yuv_SysInit( p_vout ) );
125 }
126
127 /*****************************************************************************
128  * yuv_SysEnd: destroy translations tables
129  *****************************************************************************
130  * Free memory allocated by yuv_SysCreate.
131  *****************************************************************************/
132 void yuv_SysEnd( vout_thread_t *p_vout )
133 {
134     free( p_vout->yuv.p_base );
135     free( p_vout->yuv.p_buffer );
136     free( p_vout->yuv.p_offset );
137 }
138
139 /* following functions are local */
140
141 /*****************************************************************************
142  * SetGammaTable: return intensity table transformed by gamma curve.
143  *****************************************************************************
144  * pi_table is a table of 256 entries from 0 to 255.
145  *****************************************************************************/
146 void SetGammaTable( int *pi_table, double f_gamma )
147 {
148     int         i_y;                                       /* base intensity */
149
150     /* Use exp(gamma) instead of gamma */
151     f_gamma = exp( f_gamma );
152
153     /* Build gamma table */
154     for( i_y = 0; i_y < 256; i_y++ )
155     {
156         pi_table[ i_y ] = pow( (double)i_y / 256, f_gamma ) * 256;
157     }
158  }
159
160 /*****************************************************************************
161  * SetYUV: compute tables and set function pointers
162 + *****************************************************************************/
163 void SetYUV( vout_thread_t *p_vout )
164 {
165     int         pi_gamma[256];                                /* gamma table */
166     int         i_index;                                  /* index in tables */
167
168     /* Build gamma table */
169     SetGammaTable( pi_gamma, p_vout->f_gamma );
170
171     /*
172      * Set pointers and build YUV tables
173      */
174     if( p_vout->b_grayscale )
175     {
176         /* Grayscale: build gray table */
177         switch( p_vout->i_bytes_per_pixel )
178         {
179         case 1:
180             {
181                 u16 bright[256], transp[256];
182
183                 p_vout->yuv.yuv.p_gray8 =  (u8 *)p_vout->yuv.p_base + GRAY_MARGIN;
184                 for( i_index = 0; i_index < GRAY_MARGIN; i_index++ )
185                 {
186                     p_vout->yuv.yuv.p_gray8[ -i_index ] =      RGB2PIXEL( p_vout, pi_gamma[0], pi_gamma[0], pi_gamma[0] );
187                     p_vout->yuv.yuv.p_gray8[ 256 + i_index ] = RGB2PIXEL( p_vout, pi_gamma[255], pi_gamma[255], pi_gamma[255] );
188                 }
189                 for( i_index = 0; i_index < 256; i_index++)
190                 {
191                     p_vout->yuv.yuv.p_gray8[ i_index ] = pi_gamma[ i_index ];
192                     bright[ i_index ] = i_index << 8;
193                     transp[ i_index ] = 0;
194                 }
195                 /* the colors have been allocated, we can set the palette */
196                 p_vout->p_set_palette( p_vout, bright, bright, bright, transp );
197                 p_vout->i_white_pixel = 0xff;
198                 p_vout->i_black_pixel = 0x00;
199                 p_vout->i_gray_pixel = 0x44;
200                 p_vout->i_blue_pixel = 0x3b;
201
202                 break;
203             }
204         case 2:
205             p_vout->yuv.yuv.p_gray16 =  (u16 *)p_vout->yuv.p_base + GRAY_MARGIN;
206             for( i_index = 0; i_index < GRAY_MARGIN; i_index++ )
207             {
208                 p_vout->yuv.yuv.p_gray16[ -i_index ] =      RGB2PIXEL( p_vout, pi_gamma[0], pi_gamma[0], pi_gamma[0] );
209                 p_vout->yuv.yuv.p_gray16[ 256 + i_index ] = RGB2PIXEL( p_vout, pi_gamma[255], pi_gamma[255], pi_gamma[255] );
210             }
211             for( i_index = 0; i_index < 256; i_index++)
212             {
213                 p_vout->yuv.yuv.p_gray16[ i_index ] = RGB2PIXEL( p_vout, pi_gamma[i_index], pi_gamma[i_index], pi_gamma[i_index] );
214             }
215             break;
216         case 3:
217         case 4:
218             p_vout->yuv.yuv.p_gray32 =  (u32 *)p_vout->yuv.p_base + GRAY_MARGIN;
219             for( i_index = 0; i_index < GRAY_MARGIN; i_index++ )
220             {
221                 p_vout->yuv.yuv.p_gray32[ -i_index ] =      RGB2PIXEL( p_vout, pi_gamma[0], pi_gamma[0], pi_gamma[0] );
222                 p_vout->yuv.yuv.p_gray32[ 256 + i_index ] = RGB2PIXEL( p_vout, pi_gamma[255], pi_gamma[255], pi_gamma[255] );
223             }
224             for( i_index = 0; i_index < 256; i_index++)
225             {
226                 p_vout->yuv.yuv.p_gray32[ i_index ] = RGB2PIXEL( p_vout, pi_gamma[i_index], pi_gamma[i_index], pi_gamma[i_index] );
227             }
228             break;
229          }
230     }
231     else
232     {
233         /* Color: build red, green and blue tables */
234         switch( p_vout->i_bytes_per_pixel )
235         {
236         case 1:
237             {
238                 #define RGB_MIN 0
239                 #define RGB_MAX 255
240                 #define CLIP( x ) ( ((x < 0) ? 0 : (x > 255) ? 255 : x) << 8 )
241
242                 int y,u,v;
243                 int r,g,b;
244                 int uvr, uvg, uvb;
245                 int i = 0, j = 0;
246                 u16 red[256], green[256], blue[256], transp[256];
247                 unsigned char lookup[PALETTE_TABLE_SIZE];
248
249                 p_vout->yuv.yuv.p_rgb8 = (u8 *)p_vout->yuv.p_base;
250
251                 /* this loop calculates the intersection of an YUV box
252                  * and the RGB cube. */
253                 for ( y = 0; y <= 256; y += 16 )
254                 {
255                     for ( u = 0; u <= 256; u += 32 )
256                     for ( v = 0; v <= 256; v += 32 )
257                     {
258                         uvr = (V_RED_COEF*(v-128)) >> SHIFT;
259                         uvg = (U_GREEN_COEF*(u-128) + V_GREEN_COEF*(v-128)) >> SHIFT;
260                         uvb = (U_BLUE_COEF*(u-128)) >> SHIFT;
261                         r = y + uvr;
262                         g = y + uvg;
263                         b = y + uvb;
264
265                         if( r >= RGB_MIN && g >= RGB_MIN && b >= RGB_MIN
266                                 && r <= RGB_MAX && g <= RGB_MAX && b <= RGB_MAX )
267                         {
268                             /* this one should never happen unless someone fscked up my code */
269                             if(j == 256) { intf_ErrMsg( "vout error: no colors left to build palette\n" ); break; }
270
271                             /* clip the colors */
272                             red[j] = CLIP( r );
273                             green[j] = CLIP( g );
274                             blue[j] = CLIP( b );
275                             transp[j] = 0;
276
277                             /* allocate color */
278                             lookup[i] = 1;
279                             p_vout->yuv.yuv.p_rgb8[i++] = j;
280                             j++;
281                         }
282                         else
283                         {
284                             lookup[i] = 0;
285                             p_vout->yuv.yuv.p_rgb8[i++] = 0;
286                         }
287                     }
288                     i += 128-81;
289                 }
290
291                 /* the colors have been allocated, we can set the palette */
292                 /* there will eventually be a way to know which colors
293                  * couldn't be allocated and try to find a replacement */
294                 p_vout->p_set_palette( p_vout, red, green, blue, transp );
295
296                 p_vout->i_white_pixel = 0xff;
297                 p_vout->i_black_pixel = 0x00;
298                 p_vout->i_gray_pixel = 0x44;
299                 p_vout->i_blue_pixel = 0x3b;
300
301                 i = 0;
302                 /* this loop allocates colors that got outside
303                  * the RGB cube */
304                 for ( y = 0; y <= 256; y += 16 )
305                 {
306                     for ( u = 0; u <= 256; u += 32 )
307                     for ( v = 0; v <= 256; v += 32 )
308                     {
309                         int u2, v2;
310                         int dist, mindist = 100000000;
311
312                         if( lookup[i] || y==0)
313                         {
314                             i++;
315                             continue;
316                         }
317
318                         /* heavy. yeah. */
319                         for( u2 = 0; u2 <= 256; u2 += 32 )
320                         for( v2 = 0; v2 <= 256; v2 += 32 )
321                         {
322                             j = ((y>>4)<<7) + (u2>>5)*9 + (v2>>5);
323                             dist = (u-u2)*(u-u2) + (v-v2)*(v-v2);
324                             if( lookup[j] )
325                             /* find the nearest color */
326                             if( dist < mindist )
327                             {
328                                 p_vout->yuv.yuv.p_rgb8[i] = p_vout->yuv.yuv.p_rgb8[j];
329                                 mindist = dist;
330                             }
331                             j -= 128;
332                             if( lookup[j] )
333                             /* find the nearest color */
334                             if( dist + 128 < mindist )
335                             {
336                                 p_vout->yuv.yuv.p_rgb8[i] = p_vout->yuv.yuv.p_rgb8[j];
337                                 mindist = dist + 128;
338                             }
339                         }
340                         i++;
341                     }
342                     i += 128-81;
343                 }
344
345                 break;
346             }
347         case 2:
348             p_vout->yuv.yuv.p_rgb16 = (u16 *)p_vout->yuv.p_base;
349             for( i_index = 0; i_index < RED_MARGIN; i_index++ )
350             {
351                 p_vout->yuv.yuv.p_rgb16[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_vout, pi_gamma[0], 0, 0 );
352                 p_vout->yuv.yuv.p_rgb16[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_vout, pi_gamma[255], 0, 0 );
353             }
354             for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
355             {
356                 p_vout->yuv.yuv.p_rgb16[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[0], 0 );
357                 p_vout->yuv.yuv.p_rgb16[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_vout, 0, pi_gamma[255], 0 );
358             }
359             for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
360             {
361                 p_vout->yuv.yuv.p_rgb16[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[0] );
362                 p_vout->yuv.yuv.p_rgb16[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[255] );
363             }
364             for( i_index = 0; i_index < 256; i_index++ )
365             {
366                 p_vout->yuv.yuv.p_rgb16[RED_OFFSET + i_index] =   RGB2PIXEL( p_vout, pi_gamma[ i_index ], 0, 0 );
367                 p_vout->yuv.yuv.p_rgb16[GREEN_OFFSET + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[ i_index ], 0 );
368                 p_vout->yuv.yuv.p_rgb16[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_vout, 0, 0, pi_gamma[ i_index ] );
369             }
370             break;
371         case 3:
372         case 4:
373             p_vout->yuv.yuv.p_rgb32 = (u32 *)p_vout->yuv.p_base;
374             for( i_index = 0; i_index < RED_MARGIN; i_index++ )
375             {
376                 p_vout->yuv.yuv.p_rgb32[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_vout, pi_gamma[0], 0, 0 );
377                 p_vout->yuv.yuv.p_rgb32[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_vout, pi_gamma[255], 0, 0 );
378             }
379             for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
380             {
381                 p_vout->yuv.yuv.p_rgb32[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[0], 0 );
382                 p_vout->yuv.yuv.p_rgb32[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_vout, 0, pi_gamma[255], 0 );
383             }
384             for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
385             {
386                 p_vout->yuv.yuv.p_rgb32[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[0] );
387                 p_vout->yuv.yuv.p_rgb32[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[255] );
388             }
389             for( i_index = 0; i_index < 256; i_index++ )
390             {
391                 p_vout->yuv.yuv.p_rgb32[RED_OFFSET + i_index] =   RGB2PIXEL( p_vout, pi_gamma[ i_index ], 0, 0 );
392                 p_vout->yuv.yuv.p_rgb32[GREEN_OFFSET + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[ i_index ], 0 );
393                 p_vout->yuv.yuv.p_rgb32[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_vout, 0, 0, pi_gamma[ i_index ] );
394             }
395             break;
396         }
397     }
398
399     /*
400      * Set functions pointers
401      */
402     if( p_vout->b_grayscale )
403     {
404         /* Grayscale */
405         switch( p_vout->i_bytes_per_pixel )
406         {
407         case 1:
408             p_vout->yuv.p_Convert420 = (vout_yuv_convert_t *) ConvertY4Gray8;
409             p_vout->yuv.p_Convert422 = (vout_yuv_convert_t *) ConvertY4Gray8;
410             p_vout->yuv.p_Convert444 = (vout_yuv_convert_t *) ConvertY4Gray8;
411             break;
412         case 2:
413             p_vout->yuv.p_Convert420 = (vout_yuv_convert_t *) ConvertY4Gray16;
414             p_vout->yuv.p_Convert422 = (vout_yuv_convert_t *) ConvertY4Gray16;
415             p_vout->yuv.p_Convert444 = (vout_yuv_convert_t *) ConvertY4Gray16;
416             break;
417         case 3:
418             p_vout->yuv.p_Convert420 = (vout_yuv_convert_t *) ConvertY4Gray24;
419             p_vout->yuv.p_Convert422 = (vout_yuv_convert_t *) ConvertY4Gray24;
420             p_vout->yuv.p_Convert444 = (vout_yuv_convert_t *) ConvertY4Gray24;
421             break;
422         case 4:
423             p_vout->yuv.p_Convert420 = (vout_yuv_convert_t *) ConvertY4Gray32;
424             p_vout->yuv.p_Convert422 = (vout_yuv_convert_t *) ConvertY4Gray32;
425             p_vout->yuv.p_Convert444 = (vout_yuv_convert_t *) ConvertY4Gray32;
426             break;
427         }
428     }
429     else
430     {
431         /* Color */
432         switch( p_vout->i_bytes_per_pixel )
433         {
434         case 1:
435             p_vout->yuv.p_Convert420 = (vout_yuv_convert_t *) ConvertYUV420RGB8;
436             p_vout->yuv.p_Convert422 = (vout_yuv_convert_t *) ConvertYUV422RGB8;
437             p_vout->yuv.p_Convert444 = (vout_yuv_convert_t *) ConvertYUV444RGB8;
438             break;
439         case 2:
440             p_vout->yuv.p_Convert420 =   (vout_yuv_convert_t *) ConvertYUV420RGB16;
441             p_vout->yuv.p_Convert422 =   (vout_yuv_convert_t *) ConvertYUV422RGB16;
442             p_vout->yuv.p_Convert444 =   (vout_yuv_convert_t *) ConvertYUV444RGB16;
443             break;
444         case 3:
445             p_vout->yuv.p_Convert420 =   (vout_yuv_convert_t *) ConvertYUV420RGB24;
446             p_vout->yuv.p_Convert422 =   (vout_yuv_convert_t *) ConvertYUV422RGB24;
447             p_vout->yuv.p_Convert444 =   (vout_yuv_convert_t *) ConvertYUV444RGB24;
448             break;
449         case 4:
450             p_vout->yuv.p_Convert420 =   (vout_yuv_convert_t *) ConvertYUV420RGB32;
451             p_vout->yuv.p_Convert422 =   (vout_yuv_convert_t *) ConvertYUV422RGB32;
452             p_vout->yuv.p_Convert444 =   (vout_yuv_convert_t *) ConvertYUV444RGB32;
453             break;
454         }
455     }
456 }
457
458 /*****************************************************************************
459  * SetOffset: build offset array for conversion functions
460  *****************************************************************************
461  * This function will build an offset array used in later conversion functions.
462  * It will also set horizontal and vertical scaling indicators.
463  *****************************************************************************/
464 void SetOffset( int i_width, int i_height, int i_pic_width, int i_pic_height,
465                 boolean_t *pb_h_scaling, int *pi_v_scaling, int *p_offset )
466 {
467     int i_x;                                    /* x position in destination */
468     int i_scale_count;                                     /* modulo counter */
469
470     /*
471      * Prepare horizontal offset array
472      */
473     if( i_pic_width - i_width > 0 )
474     {
475         /* Prepare scaling array for horizontal extension */
476         *pb_h_scaling =  1;
477         i_scale_count =         i_pic_width;
478         for( i_x = i_width; i_x--; )
479         {
480             while( (i_scale_count -= i_width) > 0 )
481             {
482                 *p_offset++ = 0;
483             }
484             *p_offset++ = 1;
485             i_scale_count += i_pic_width;
486         }
487     }
488     else if( i_pic_width - i_width < 0 )
489     {
490         /* Prepare scaling array for horizontal reduction */
491         *pb_h_scaling =  1;
492         i_scale_count =         i_pic_width;
493         for( i_x = i_pic_width; i_x--; )
494         {
495             *p_offset = 1;
496             while( (i_scale_count -= i_pic_width) >= 0 )
497             {
498                 *p_offset += 1;
499             }
500             p_offset++;
501             i_scale_count += i_width;
502         }
503     }
504     else
505     {
506         /* No horizontal scaling: YUV conversion is done directly to picture */
507         *pb_h_scaling = 0;
508     }
509
510     /*
511      * Set vertical scaling indicator
512      */
513     if( i_pic_height - i_height > 0 )
514     {
515         *pi_v_scaling = 1;
516     }
517     else if( i_pic_height - i_height < 0 )
518     {
519         *pi_v_scaling = -1;
520     }
521     else
522     {
523         *pi_v_scaling = 0;
524     }
525 }
526