]> git.sesse.net Git - vlc/blob - plugins/yuv/video_yuv.c
* libdvdcss enhancements by Billy Biggs <vektor@dumbterm.net>. This breaks
[vlc] / plugins / yuv / 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  * $Id: video_yuv.c,v 1.15 2001/07/11 02:01:05 sam Exp $
9  *
10  * Authors: Vincent Seguin <seguin@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public
23  * License along with this program; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  *****************************************************************************/
27
28 #define MODULE_NAME yuv
29 #include "modules_inner.h"
30
31 /*****************************************************************************
32  * Preamble
33  *****************************************************************************/
34 #include "defs.h"
35
36 #include <math.h>                                            /* exp(), pow() */
37 #include <errno.h>                                                 /* ENOMEM */
38 #include <stdlib.h>                                                /* free() */
39 #include <string.h>                                            /* strerror() */
40
41 #include "config.h"
42 #include "common.h"
43 #include "threads.h"
44 #include "mtime.h"
45 #include "tests.h"
46
47 #include "video.h"
48 #include "video_output.h"
49
50 #include "video_common.h"
51
52 #include "intf_msg.h"
53
54 #include "modules.h"
55 #include "modules_export.h"
56
57 static int     yuv_Probe      ( probedata_t *p_data );
58 static int     yuv_Init       ( vout_thread_t *p_vout );
59 static int     yuv_Reset      ( vout_thread_t *p_vout );
60 static void    yuv_End        ( vout_thread_t *p_vout );
61
62 static void    SetGammaTable  ( int *pi_table, double f_gamma );
63 static void    SetYUV         ( vout_thread_t *p_vout );
64
65 /*****************************************************************************
66  * Functions exported as capabilities. They are declared as static so that
67  * we don't pollute the namespace too much.
68  *****************************************************************************/
69 void _M( yuv_getfunctions )( function_list_t * p_function_list )
70 {
71     p_function_list->pf_probe = yuv_Probe;
72     p_function_list->functions.yuv.pf_init = yuv_Init;
73     p_function_list->functions.yuv.pf_reset = yuv_Reset;
74     p_function_list->functions.yuv.pf_end = yuv_End;
75 }
76
77 /*****************************************************************************
78  * yuv_Probe: tests probe the audio device and return a score
79  *****************************************************************************
80  * This function tries to open the DSP and returns a score to the plugin
81  * manager so that it can choose the most appropriate one.
82  *****************************************************************************/
83 static int yuv_Probe( probedata_t *p_data )
84 {
85     if( TestMethod( YUV_METHOD_VAR, "yuv" )
86          || TestMethod( YUV_METHOD_VAR, "c" ) )
87     {
88         return( 999 );
89     }
90
91     /* This module always works */
92     return( 50 );
93 }
94
95 /*****************************************************************************
96  * yuv_Init: allocate and initialize translations tables
97  *****************************************************************************
98  * This function will allocate memory to store translation tables, depending
99  * of the screen depth.
100  *****************************************************************************/
101 static int yuv_Init( vout_thread_t *p_vout )
102 {
103     size_t      tables_size;                        /* tables size, in bytes */
104
105     /* Computes tables size - 3 Bpp use 32 bits pixel entries in tables */
106     switch( p_vout->i_bytes_per_pixel )
107     {
108     case 1:
109         tables_size = sizeof( u8 )
110                 * (p_vout->b_grayscale ? GRAY_TABLE_SIZE : PALETTE_TABLE_SIZE);
111         break;
112     case 2:
113         tables_size = sizeof( u16 )
114                 * (p_vout->b_grayscale ? GRAY_TABLE_SIZE : RGB_TABLE_SIZE);
115         break;
116     case 3:
117     case 4:
118     default:
119         tables_size = sizeof( u32 )
120                 * (p_vout->b_grayscale ? GRAY_TABLE_SIZE : RGB_TABLE_SIZE);
121         break;
122     }
123
124     /* Allocate memory */
125     p_vout->yuv.p_base = malloc( tables_size );
126     if( p_vout->yuv.p_base == NULL )
127     {
128         intf_ErrMsg("error: %s", strerror(ENOMEM));
129         return( 1 );
130     }
131
132     /* Allocate memory for conversion buffer and offset array */
133     p_vout->yuv.p_buffer = malloc( VOUT_MAX_WIDTH * p_vout->i_bytes_per_pixel );
134     if( p_vout->yuv.p_buffer == NULL )
135     {
136         intf_ErrMsg("error: %s", strerror(ENOMEM));
137         free( p_vout->yuv.p_base );
138         return( 1 );
139     }
140
141     /* In 8bpp we have a twice as big offset table because we also
142      * need the offsets for U and V (not only Y) */
143     p_vout->yuv.p_offset = malloc( p_vout->i_width * sizeof( int ) *
144                              ( ( p_vout->i_bytes_per_pixel == 1 ) ? 2 : 1 ) );
145     if( p_vout->yuv.p_offset == NULL )
146     {
147         intf_ErrMsg("error: %s", strerror(ENOMEM));
148         free( p_vout->yuv.p_base );
149         free( p_vout->yuv.p_buffer );
150         return( 1 );
151     }
152
153     /* Initialize tables */
154     SetYUV( p_vout );
155     return( 0 );
156 }
157
158 /*****************************************************************************
159  * yuv_End: destroy translations tables
160  *****************************************************************************
161  * Free memory allocated by yuv_CCreate.
162  *****************************************************************************/
163 static void yuv_End( vout_thread_t *p_vout )
164 {
165     free( p_vout->yuv.p_base );
166     free( p_vout->yuv.p_buffer );
167     free( p_vout->yuv.p_offset );
168 }
169
170 /*****************************************************************************
171  * yuv_Reset: re-initialize translations tables
172  *****************************************************************************
173  * This function will initialize the tables allocated by vout_CreateTables and
174  * set functions pointers.
175  *****************************************************************************/
176 static int yuv_Reset( vout_thread_t *p_vout )
177 {
178     yuv_End( p_vout );
179     return( yuv_Init( p_vout ) );
180 }
181
182 /*****************************************************************************
183  * SetGammaTable: return intensity table transformed by gamma curve.
184  *****************************************************************************
185  * pi_table is a table of 256 entries from 0 to 255.
186  *****************************************************************************/
187 static void SetGammaTable( int *pi_table, double f_gamma )
188 {
189     int         i_y;                                       /* base intensity */
190
191     /* Use exp(gamma) instead of gamma */
192     f_gamma = exp( f_gamma );
193
194     /* Build gamma table */
195     for( i_y = 0; i_y < 256; i_y++ )
196     {
197         pi_table[ i_y ] = pow( (double)i_y / 256, f_gamma ) * 256;
198     }
199  }
200
201 /*****************************************************************************
202  * SetYUV: compute tables and set function pointers
203  *****************************************************************************/
204 static void SetYUV( vout_thread_t *p_vout )
205 {
206     int         pi_gamma[256];                                /* gamma table */
207     int         i_index;                                  /* index in tables */
208
209     /* Build gamma table */
210     SetGammaTable( pi_gamma, p_vout->f_gamma );
211
212     /*
213      * Set pointers and build YUV tables
214      */
215     if( p_vout->b_grayscale )
216     {
217         /* Grayscale: build gray table */
218         switch( p_vout->i_bytes_per_pixel )
219         {
220         case 1:
221             {
222                 u16 bright[256], transp[256];
223
224                 p_vout->yuv.yuv.p_gray8 =  (u8 *)p_vout->yuv.p_base + GRAY_MARGIN;
225                 for( i_index = 0; i_index < GRAY_MARGIN; i_index++ )
226                 {
227                     p_vout->yuv.yuv.p_gray8[ -i_index ] =      RGB2PIXEL( p_vout, pi_gamma[0], pi_gamma[0], pi_gamma[0] );
228                     p_vout->yuv.yuv.p_gray8[ 256 + i_index ] = RGB2PIXEL( p_vout, pi_gamma[255], pi_gamma[255], pi_gamma[255] );
229                 }
230                 for( i_index = 0; i_index < 256; i_index++)
231                 {
232                     p_vout->yuv.yuv.p_gray8[ i_index ] = pi_gamma[ i_index ];
233                     bright[ i_index ] = i_index << 8;
234                     transp[ i_index ] = 0;
235                 }
236                 /* the colors have been allocated, we can set the palette */
237                 p_vout->pf_setpalette( p_vout, bright, bright, bright, transp );
238                 p_vout->i_white_pixel = 0xff;
239                 p_vout->i_black_pixel = 0x00;
240                 p_vout->i_gray_pixel = 0x44;
241                 p_vout->i_blue_pixel = 0x3b;
242
243                 break;
244             }
245         case 2:
246             p_vout->yuv.yuv.p_gray16 =  (u16 *)p_vout->yuv.p_base + GRAY_MARGIN;
247             for( i_index = 0; i_index < GRAY_MARGIN; i_index++ )
248             {
249                 p_vout->yuv.yuv.p_gray16[ -i_index ] =      RGB2PIXEL( p_vout, pi_gamma[0], pi_gamma[0], pi_gamma[0] );
250                 p_vout->yuv.yuv.p_gray16[ 256 + i_index ] = RGB2PIXEL( p_vout, pi_gamma[255], pi_gamma[255], pi_gamma[255] );
251             }
252             for( i_index = 0; i_index < 256; i_index++)
253             {
254                 p_vout->yuv.yuv.p_gray16[ i_index ] = RGB2PIXEL( p_vout, pi_gamma[i_index], pi_gamma[i_index], pi_gamma[i_index] );
255             }
256             break;
257         case 3:
258         case 4:
259             p_vout->yuv.yuv.p_gray32 =  (u32 *)p_vout->yuv.p_base + GRAY_MARGIN;
260             for( i_index = 0; i_index < GRAY_MARGIN; i_index++ )
261             {
262                 p_vout->yuv.yuv.p_gray32[ -i_index ] =      RGB2PIXEL( p_vout, pi_gamma[0], pi_gamma[0], pi_gamma[0] );
263                 p_vout->yuv.yuv.p_gray32[ 256 + i_index ] = RGB2PIXEL( p_vout, pi_gamma[255], pi_gamma[255], pi_gamma[255] );
264             }
265             for( i_index = 0; i_index < 256; i_index++)
266             {
267                 p_vout->yuv.yuv.p_gray32[ i_index ] = RGB2PIXEL( p_vout, pi_gamma[i_index], pi_gamma[i_index], pi_gamma[i_index] );
268             }
269             break;
270          }
271     }
272     else
273     {
274         /* Color: build red, green and blue tables */
275         switch( p_vout->i_bytes_per_pixel )
276         {
277         case 1:
278             {
279                 #define RGB_MIN 0
280                 #define RGB_MAX 255
281                 #define CLIP( x ) ( ((x < 0) ? 0 : (x > 255) ? 255 : x) << 8 )
282
283                 int y,u,v;
284                 int r,g,b;
285                 int uvr, uvg, uvb;
286                 int i = 0, j = 0;
287                 u16 red[256], green[256], blue[256], transp[256];
288                 unsigned char lookup[PALETTE_TABLE_SIZE];
289
290                 p_vout->yuv.yuv.p_rgb8 = (u8 *)p_vout->yuv.p_base;
291
292                 /* this loop calculates the intersection of an YUV box
293                  * and the RGB cube. */
294                 for ( y = 0; y <= 256; y += 16 )
295                 {
296                     for ( u = 0; u <= 256; u += 32 )
297                     for ( v = 0; v <= 256; v += 32 )
298                     {
299                         uvr = (V_RED_COEF*(v-128)) >> SHIFT;
300                         uvg = (U_GREEN_COEF*(u-128) + V_GREEN_COEF*(v-128)) >> SHIFT;
301                         uvb = (U_BLUE_COEF*(u-128)) >> SHIFT;
302                         r = y + uvr;
303                         g = y + uvg;
304                         b = y + uvb;
305
306                         if( r >= RGB_MIN && g >= RGB_MIN && b >= RGB_MIN
307                                 && r <= RGB_MAX && g <= RGB_MAX && b <= RGB_MAX )
308                         {
309                             /* this one should never happen unless someone fscked up my code */
310                             if(j == 256) { intf_ErrMsg( "vout error: no colors left to build palette" ); break; }
311
312                             /* clip the colors */
313                             red[j] = CLIP( r );
314                             green[j] = CLIP( g );
315                             blue[j] = CLIP( b );
316                             transp[j] = 0;
317
318                             /* allocate color */
319                             lookup[i] = 1;
320                             p_vout->yuv.yuv.p_rgb8[i++] = j;
321                             j++;
322                         }
323                         else
324                         {
325                             lookup[i] = 0;
326                             p_vout->yuv.yuv.p_rgb8[i++] = 0;
327                         }
328                     }
329                     i += 128-81;
330                 }
331
332                 /* the colors have been allocated, we can set the palette */
333                 /* there will eventually be a way to know which colors
334                  * couldn't be allocated and try to find a replacement */
335                 p_vout->pf_setpalette( p_vout, red, green, blue, transp );
336
337                 p_vout->i_white_pixel = 0xff;
338                 p_vout->i_black_pixel = 0x00;
339                 p_vout->i_gray_pixel = 0x44;
340                 p_vout->i_blue_pixel = 0x3b;
341
342                 i = 0;
343                 /* this loop allocates colors that got outside
344                  * the RGB cube */
345                 for ( y = 0; y <= 256; y += 16 )
346                 {
347                     for ( u = 0; u <= 256; u += 32 )
348                     for ( v = 0; v <= 256; v += 32 )
349                     {
350                         int u2, v2;
351                         int dist, mindist = 100000000;
352
353                         if( lookup[i] || y==0)
354                         {
355                             i++;
356                             continue;
357                         }
358
359                         /* heavy. yeah. */
360                         for( u2 = 0; u2 <= 256; u2 += 32 )
361                         for( v2 = 0; v2 <= 256; v2 += 32 )
362                         {
363                             j = ((y>>4)<<7) + (u2>>5)*9 + (v2>>5);
364                             dist = (u-u2)*(u-u2) + (v-v2)*(v-v2);
365                             if( lookup[j] )
366                             /* find the nearest color */
367                             if( dist < mindist )
368                             {
369                                 p_vout->yuv.yuv.p_rgb8[i] = p_vout->yuv.yuv.p_rgb8[j];
370                                 mindist = dist;
371                             }
372                             j -= 128;
373                             if( lookup[j] )
374                             /* find the nearest color */
375                             if( dist + 128 < mindist )
376                             {
377                                 p_vout->yuv.yuv.p_rgb8[i] = p_vout->yuv.yuv.p_rgb8[j];
378                                 mindist = dist + 128;
379                             }
380                         }
381                         i++;
382                     }
383                     i += 128-81;
384                 }
385
386                 break;
387             }
388         case 2:
389             p_vout->yuv.yuv.p_rgb16 = (u16 *)p_vout->yuv.p_base;
390             for( i_index = 0; i_index < RED_MARGIN; i_index++ )
391             {
392                 p_vout->yuv.yuv.p_rgb16[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_vout, pi_gamma[0], 0, 0 );
393                 p_vout->yuv.yuv.p_rgb16[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_vout, pi_gamma[255], 0, 0 );
394             }
395             for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
396             {
397                 p_vout->yuv.yuv.p_rgb16[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[0], 0 );
398                 p_vout->yuv.yuv.p_rgb16[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_vout, 0, pi_gamma[255], 0 );
399             }
400             for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
401             {
402                 p_vout->yuv.yuv.p_rgb16[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[0] );
403                 p_vout->yuv.yuv.p_rgb16[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[255] );
404             }
405             for( i_index = 0; i_index < 256; i_index++ )
406             {
407                 p_vout->yuv.yuv.p_rgb16[RED_OFFSET + i_index] =   RGB2PIXEL( p_vout, pi_gamma[ i_index ], 0, 0 );
408                 p_vout->yuv.yuv.p_rgb16[GREEN_OFFSET + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[ i_index ], 0 );
409                 p_vout->yuv.yuv.p_rgb16[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_vout, 0, 0, pi_gamma[ i_index ] );
410             }
411             break;
412         case 3:
413         case 4:
414             p_vout->yuv.yuv.p_rgb32 = (u32 *)p_vout->yuv.p_base;
415             for( i_index = 0; i_index < RED_MARGIN; i_index++ )
416             {
417                 p_vout->yuv.yuv.p_rgb32[RED_OFFSET - RED_MARGIN + i_index] = RGB2PIXEL( p_vout, pi_gamma[0], 0, 0 );
418                 p_vout->yuv.yuv.p_rgb32[RED_OFFSET + 256 + i_index] =        RGB2PIXEL( p_vout, pi_gamma[255], 0, 0 );
419             }
420             for( i_index = 0; i_index < GREEN_MARGIN; i_index++ )
421             {
422                 p_vout->yuv.yuv.p_rgb32[GREEN_OFFSET - GREEN_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[0], 0 );
423                 p_vout->yuv.yuv.p_rgb32[GREEN_OFFSET + 256 + i_index] =          RGB2PIXEL( p_vout, 0, pi_gamma[255], 0 );
424             }
425             for( i_index = 0; i_index < BLUE_MARGIN; i_index++ )
426             {
427                 p_vout->yuv.yuv.p_rgb32[BLUE_OFFSET - BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[0] );
428                 p_vout->yuv.yuv.p_rgb32[BLUE_OFFSET + BLUE_MARGIN + i_index] = RGB2PIXEL( p_vout, 0, 0, pi_gamma[255] );
429             }
430             for( i_index = 0; i_index < 256; i_index++ )
431             {
432                 p_vout->yuv.yuv.p_rgb32[RED_OFFSET + i_index] =   RGB2PIXEL( p_vout, pi_gamma[ i_index ], 0, 0 );
433                 p_vout->yuv.yuv.p_rgb32[GREEN_OFFSET + i_index] = RGB2PIXEL( p_vout, 0, pi_gamma[ i_index ], 0 );
434                 p_vout->yuv.yuv.p_rgb32[BLUE_OFFSET + i_index] =  RGB2PIXEL( p_vout, 0, 0, pi_gamma[ i_index ] );
435             }
436             break;
437         }
438     }
439
440     /*
441      * Set functions pointers
442      */
443     if( p_vout->b_YCbr)
444     {
445         switch( p_vout->i_bytes_per_pixel)
446         {
447 #define _X( foo ) (vout_yuv_convert_t *) _M( foo )
448         case 1:
449             p_vout->yuv.pf_yuv420 = _X( ConvertYUV420YCbr8 );
450             p_vout->yuv.pf_yuv422 = _X( ConvertYUV422YCbr8 );
451             p_vout->yuv.pf_yuv444 = _X( ConvertYUV444YCbr8 );
452             break;
453         
454         case 2:
455             p_vout->yuv.pf_yuv420 = _X( ConvertYUV420YCbr16 );
456             p_vout->yuv.pf_yuv422 = _X( ConvertYUV422YCbr16 );
457             p_vout->yuv.pf_yuv444 = _X( ConvertYUV444YCbr16 );
458            break;
459         
460         case 3:
461             p_vout->yuv.pf_yuv420 = _X( ConvertYUV420YCbr24 );
462             p_vout->yuv.pf_yuv422 = _X( ConvertYUV422YCbr24 );
463             p_vout->yuv.pf_yuv444 = _X( ConvertYUV444YCbr24 );
464              break;
465         
466         case 4:
467             p_vout->yuv.pf_yuv420 = _X( ConvertYUV420YCbr32 );
468             p_vout->yuv.pf_yuv422 = _X( ConvertYUV422YCbr32 );
469             p_vout->yuv.pf_yuv444 = _X( ConvertYUV444YCbr32 );
470             break;
471         }
472 #undef _X
473     }    
474     else if( p_vout->b_grayscale )
475     {
476         /* Grayscale */
477         switch( p_vout->i_bytes_per_pixel )
478         {
479 #define _X( foo ) (vout_yuv_convert_t *) _M( foo )
480         case 1:
481             p_vout->yuv.pf_yuv420 = _X( ConvertY4Gray8 );
482             p_vout->yuv.pf_yuv422 = _X( ConvertY4Gray8 );
483             p_vout->yuv.pf_yuv444 = _X( ConvertY4Gray8 );
484             break;
485         case 2:
486             p_vout->yuv.pf_yuv420 = _X( ConvertY4Gray16 );
487             p_vout->yuv.pf_yuv422 = _X( ConvertY4Gray16 );
488             p_vout->yuv.pf_yuv444 = _X( ConvertY4Gray16 );
489             break;
490         case 3:
491             p_vout->yuv.pf_yuv420 = _X( ConvertY4Gray24 );
492             p_vout->yuv.pf_yuv422 = _X( ConvertY4Gray24 );
493             p_vout->yuv.pf_yuv444 = _X( ConvertY4Gray24 );
494             break;
495         case 4:
496             p_vout->yuv.pf_yuv420 = _X( ConvertY4Gray32 );
497             p_vout->yuv.pf_yuv422 = _X( ConvertY4Gray32 );
498             p_vout->yuv.pf_yuv444 = _X( ConvertY4Gray32 );
499             break;
500 #undef _X
501         }
502     }
503     else
504     {
505         /* Color */
506         switch( p_vout->i_bytes_per_pixel )
507         {
508 #define _X( foo ) (vout_yuv_convert_t *) _M( foo )
509         case 1:
510             p_vout->yuv.pf_yuv420 = _X( ConvertYUV420RGB8 );
511             p_vout->yuv.pf_yuv422 = _X( ConvertYUV422RGB8 );
512             p_vout->yuv.pf_yuv444 = _X( ConvertYUV444RGB8 );
513             break;
514         case 2:
515             p_vout->yuv.pf_yuv420 = _X( ConvertYUV420RGB16 );
516             p_vout->yuv.pf_yuv422 = _X( ConvertYUV422RGB16 );
517             p_vout->yuv.pf_yuv444 = _X( ConvertYUV444RGB16 );
518             break;
519         case 3:
520             p_vout->yuv.pf_yuv420 = _X( ConvertYUV420RGB24 );
521             p_vout->yuv.pf_yuv422 = _X( ConvertYUV422RGB24 );
522             p_vout->yuv.pf_yuv444 = _X( ConvertYUV444RGB24 );
523             break;
524         case 4:
525             p_vout->yuv.pf_yuv420 = _X( ConvertYUV420RGB32 );
526             p_vout->yuv.pf_yuv422 = _X( ConvertYUV422RGB32 );
527             p_vout->yuv.pf_yuv444 = _X( ConvertYUV444RGB32 );
528             break;
529 #undef _X
530       }
531  
532     }
533 }
534
535 /*****************************************************************************
536  * SetOffset: build offset array for conversion functions
537  *****************************************************************************
538  * This function will build an offset array used in later conversion functions.
539  * It will also set horizontal and vertical scaling indicators. If b_double
540  * is set, the p_offset structure has interleaved Y and U/V offsets.
541  *****************************************************************************/
542 void _M( SetOffset )( int i_width, int i_height, int i_pic_width,
543                       int i_pic_height, boolean_t *pb_h_scaling,
544                       int *pi_v_scaling, int *p_offset, boolean_t b_double )
545 {
546     int i_x;                                    /* x position in destination */
547     int i_scale_count;                                     /* modulo counter */
548
549     /*
550      * Prepare horizontal offset array
551      */
552     if( i_pic_width - i_width == 0 )
553     {
554         /* No horizontal scaling: YUV conversion is done directly to picture */
555         *pb_h_scaling = 0;
556     }
557     else if( i_pic_width - i_width > 0 )
558     {
559         /* Prepare scaling array for horizontal extension */
560         *pb_h_scaling = 1;
561         i_scale_count = i_pic_width;
562         if( !b_double )
563         {
564             for( i_x = i_width; i_x--; )
565             {
566                 while( (i_scale_count -= i_width) > 0 )
567                 {
568                     *p_offset++ = 0;
569                 }
570                 *p_offset++ = 1;
571                 i_scale_count += i_pic_width;
572             }
573         }
574         else
575         {
576             int i_dummy = 0;
577             for( i_x = i_width; i_x--; )
578             {
579                 while( (i_scale_count -= i_width) > 0 )
580                 {
581                     *p_offset++ = 0;
582                     *p_offset++ = 0;
583                 }
584                 *p_offset++ = 1;
585                 *p_offset++ = i_dummy;
586                 i_dummy = 1 - i_dummy;
587                 i_scale_count += i_pic_width;
588             }
589         }
590     }
591     else /* if( i_pic_width - i_width < 0 ) */
592     {
593         /* Prepare scaling array for horizontal reduction */
594         *pb_h_scaling = 1;
595         i_scale_count = i_width;
596         if( !b_double )
597         {
598            for( i_x = i_pic_width; i_x--; )
599             {
600                 *p_offset = 1;
601                 while( (i_scale_count -= i_pic_width) > 0 )
602                 {
603                     *p_offset += 1;
604                 }
605                 p_offset++;
606                 i_scale_count += i_width;
607             }
608         }
609         else
610         {
611             int i_remainder = 0;
612             int i_jump;
613             for( i_x = i_pic_width; i_x--; )
614             {
615                 i_jump = 1;
616                 while( (i_scale_count -= i_pic_width) > 0 )
617                 {
618                     i_jump += 1;
619                 }
620                 *p_offset++ = i_jump;
621                 *p_offset++ = ( i_jump += i_remainder ) >> 1;
622                 i_remainder = i_jump & 1;
623                 i_scale_count += i_width;
624             }
625         }
626      }
627
628     /*
629      * Set vertical scaling indicator
630      */
631     if( i_pic_height - i_height == 0 )
632     {
633         *pi_v_scaling = 0;
634     }
635     else if( i_pic_height - i_height > 0 )
636     {
637         *pi_v_scaling = 1;
638     }
639     else /* if( i_pic_height - i_height < 0 ) */
640     {
641         *pi_v_scaling = -1;
642     }
643 }
644