* ./modules/video_chroma/i420_rgb16.c: fixed a segfault in the 24/32bpp
[vlc.git] / modules / video_chroma / i420_rgb16.c
1 /*****************************************************************************
2  * i420_rgb16.c : YUV to bitmap RGB conversion module for vlc
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  * $Id: i420_rgb16.c,v 1.3 2002/11/22 12:11:38 sam Exp $
6  *
7  * Authors: Samuel 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 <string.h>                                            /* strerror() */
28 #include <stdlib.h>                                      /* malloc(), free() */
29
30 #include <vlc/vlc.h>
31 #include <vlc/vout.h>
32
33 #include "i420_rgb.h"
34 #if defined (MODULE_NAME_IS_i420_rgb)
35 #   include "i420_rgb_c.h"
36 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
37 #   include "i420_rgb_mmx.h"
38 #endif
39
40 static void SetOffset( int, int, int, int, vlc_bool_t *, int *, int * );
41
42 /*****************************************************************************
43  * I420_RGB15: color YUV 4:2:0 to RGB 15 bpp
44  *****************************************************************************
45  * Horizontal alignment needed:
46  *  - input: 8 pixels (8 Y bytes, 4 U/V bytes), margins not allowed
47  *  - output: 1 pixel (2 bytes), margins allowed
48  * Vertical alignment needed:
49  *  - input: 2 lines (2 Y lines, 1 U/V line)
50  *  - output: 1 line
51  *****************************************************************************/
52 void E_(I420_RGB15)( vout_thread_t *p_vout, picture_t *p_src,
53                                             picture_t *p_dest )
54 {
55     /* We got this one from the old arguments */
56     u16 *p_pic = (u16*)p_dest->p->p_pixels;
57     u8  *p_y   = p_src->Y_PIXELS;
58     u8  *p_u   = p_src->U_PIXELS;
59     u8  *p_v   = p_src->V_PIXELS;
60
61     vlc_bool_t   b_hscale;                        /* horizontal scaling type */
62     int          i_vscale;                          /* vertical scaling type */
63     unsigned int i_x, i_y;                /* horizontal and vertical indexes */
64
65     int         i_right_margin;
66     int         i_rewind;
67     int         i_scale_count;                       /* scale modulo counter */
68     int         i_chroma_width = p_vout->render.i_width / 2; /* chroma width */
69     u16 *       p_pic_start;       /* beginning of the current line for copy */
70 #if defined (MODULE_NAME_IS_i420_rgb)
71     int         i_uval, i_vval;                           /* U and V samples */
72     int         i_red, i_green, i_blue;          /* U and V modified samples */
73     u16 *       p_yuv = p_vout->chroma.p_sys->p_rgb16;
74     u16 *       p_ybase;                     /* Y dependant conversion table */
75 #endif
76
77     /* Conversion buffer pointer */
78     u16 *       p_buffer_start = (u16*)p_vout->chroma.p_sys->p_buffer;
79     u16 *       p_buffer;
80
81     /* Offset array pointer */
82     int *       p_offset_start = p_vout->chroma.p_sys->p_offset;
83     int *       p_offset;
84
85     i_right_margin = p_dest->p->i_pitch - p_dest->p->i_visible_pitch;
86
87     if( p_vout->render.i_width & 7 )
88     {
89         i_rewind = 8 - ( p_vout->render.i_width & 7 );
90     }
91     else
92     {
93         i_rewind = 0;
94     }
95
96     /* Rule: when a picture of size (x1,y1) with aspect ratio r1 is rendered
97      * on a picture of size (x2,y2) with aspect ratio r2, if x1 grows to x1'
98      * then y1 grows to y1' = x1' * y2/x2 * r2/r1 */
99     SetOffset( p_vout->render.i_width, p_vout->render.i_height,
100                p_vout->output.i_width, p_vout->output.i_height,
101                &b_hscale, &i_vscale, p_offset_start );
102
103     /*
104      * Perform conversion
105      */
106     i_scale_count = ( i_vscale == 1 ) ?
107                     p_vout->output.i_height : p_vout->render.i_height;
108     for( i_y = 0; i_y < p_vout->render.i_height; i_y++ )
109     {
110         p_pic_start = p_pic;
111         p_buffer = b_hscale ? p_buffer_start : p_pic;
112
113         for ( i_x = p_vout->render.i_width / 8; i_x--; )
114         {
115 #if defined (MODULE_NAME_IS_i420_rgb)
116             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
117             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
118             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
119             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
120 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
121             __asm__( MMX_INIT_16
122                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
123
124             __asm__( ".align 8"
125                      MMX_YUV_MUL
126                      MMX_YUV_ADD
127                      MMX_UNPACK_15
128                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
129
130             p_y += 8;
131             p_u += 4;
132             p_v += 4;
133             p_buffer += 8;
134 #endif
135         }
136
137         /* Here we do some unaligned reads and duplicate conversions, but
138          * at least we have all the pixels */
139         if( i_rewind )
140         {
141             p_y -= i_rewind;
142             p_u -= i_rewind >> 1;
143             p_v -= i_rewind >> 1;
144             p_buffer -= i_rewind;
145 #if defined (MODULE_NAME_IS_i420_rgb)
146             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
147             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
148             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
149             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
150 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
151             __asm__( MMX_INIT_16
152                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
153
154             __asm__( ".align 8"
155                      MMX_YUV_MUL
156                      MMX_YUV_ADD
157                      MMX_UNPACK_15
158                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
159
160             p_y += 8;
161             p_u += 4;
162             p_v += 4;
163             p_buffer += 8;
164 #endif
165         }
166         SCALE_WIDTH;
167         SCALE_HEIGHT( 420, 2 );
168     }
169 }
170
171 /*****************************************************************************
172  * I420_RGB16: color YUV 4:2:0 to RGB 16 bpp
173  *****************************************************************************
174  * Horizontal alignment needed:
175  *  - input: 8 pixels (8 Y bytes, 4 U/V bytes), margins not allowed
176  *  - output: 1 pixel (2 bytes), margins allowed
177  * Vertical alignment needed:
178  *  - input: 2 lines (2 Y lines, 1 U/V line)
179  *  - output: 1 line
180  *****************************************************************************/
181 void E_(I420_RGB16)( vout_thread_t *p_vout, picture_t *p_src,
182                                             picture_t *p_dest )
183 {
184     /* We got this one from the old arguments */
185     u16 *p_pic = (u16*)p_dest->p->p_pixels;
186     u8  *p_y   = p_src->Y_PIXELS;
187     u8  *p_u   = p_src->U_PIXELS;
188     u8  *p_v   = p_src->V_PIXELS;
189
190     vlc_bool_t  b_hscale;                         /* horizontal scaling type */
191     unsigned int i_vscale;                          /* vertical scaling type */
192     unsigned int i_x, i_y;                /* horizontal and vertical indexes */
193
194     int         i_right_margin;
195     int         i_rewind;
196     int         i_scale_count;                       /* scale modulo counter */
197     int         i_chroma_width = p_vout->render.i_width / 2; /* chroma width */
198     u16 *       p_pic_start;       /* beginning of the current line for copy */
199 #if defined (MODULE_NAME_IS_i420_rgb)
200     int         i_uval, i_vval;                           /* U and V samples */
201     int         i_red, i_green, i_blue;          /* U and V modified samples */
202     u16 *       p_yuv = p_vout->chroma.p_sys->p_rgb16;
203     u16 *       p_ybase;                     /* Y dependant conversion table */
204 #endif
205
206     /* Conversion buffer pointer */
207     u16 *       p_buffer_start = (u16*)p_vout->chroma.p_sys->p_buffer;
208     u16 *       p_buffer;
209
210     /* Offset array pointer */
211     int *       p_offset_start = p_vout->chroma.p_sys->p_offset;
212     int *       p_offset;
213
214     i_right_margin = p_dest->p->i_pitch - p_dest->p->i_visible_pitch;
215
216     if( p_vout->render.i_width & 7 )
217     {
218         i_rewind = 8 - ( p_vout->render.i_width & 7 );
219     }
220     else
221     {
222         i_rewind = 0;
223     }
224
225     /* Rule: when a picture of size (x1,y1) with aspect ratio r1 is rendered
226      * on a picture of size (x2,y2) with aspect ratio r2, if x1 grows to x1'
227      * then y1 grows to y1' = x1' * y2/x2 * r2/r1 */
228     SetOffset( p_vout->render.i_width, p_vout->render.i_height,
229                p_vout->output.i_width, p_vout->output.i_height,
230                &b_hscale, &i_vscale, p_offset_start );
231
232     /*
233      * Perform conversion
234      */
235     i_scale_count = ( i_vscale == 1 ) ?
236                     p_vout->output.i_height : p_vout->render.i_height;
237     for( i_y = 0; i_y < p_vout->render.i_height; i_y++ )
238     {
239         p_pic_start = p_pic;
240         p_buffer = b_hscale ? p_buffer_start : p_pic;
241
242         for ( i_x = p_vout->render.i_width / 8; i_x--; )
243         {
244 #if defined (MODULE_NAME_IS_i420_rgb)
245             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
246             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
247             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
248             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
249 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
250             __asm__( MMX_INIT_16
251                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
252
253             __asm__( ".align 8"
254                      MMX_YUV_MUL
255                      MMX_YUV_ADD
256                      MMX_UNPACK_16
257                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
258
259             p_y += 8;
260             p_u += 4;
261             p_v += 4;
262             p_buffer += 8;
263 #endif
264         }
265
266         /* Here we do some unaligned reads and duplicate conversions, but
267          * at least we have all the pixels */
268         if( i_rewind )
269         {
270             p_y -= i_rewind;
271             p_u -= i_rewind >> 1;
272             p_v -= i_rewind >> 1;
273             p_buffer -= i_rewind;
274 #if defined (MODULE_NAME_IS_i420_rgb)
275             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
276             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
277             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
278             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
279 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
280             __asm__( MMX_INIT_16
281                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
282
283             __asm__( ".align 8"
284                      MMX_YUV_MUL
285                      MMX_YUV_ADD
286                      MMX_UNPACK_16
287                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
288
289             p_y += 8;
290             p_u += 4;
291             p_v += 4;
292             p_buffer += 8;
293 #endif
294         }
295         SCALE_WIDTH;
296         SCALE_HEIGHT( 420, 2 );
297     }
298 }
299
300 /*****************************************************************************
301  * I420_RGB32: color YUV 4:2:0 to RGB 32 bpp
302  *****************************************************************************
303  * Horizontal alignment needed:
304  *  - input: 8 pixels (8 Y bytes, 4 U/V bytes), margins not allowed
305  *  - output: 1 pixel (2 bytes), margins allowed
306  * Vertical alignment needed:
307  *  - input: 2 lines (2 Y lines, 1 U/V line)
308  *  - output: 1 line
309  *****************************************************************************/
310 void E_(I420_RGB32)( vout_thread_t *p_vout, picture_t *p_src,
311                                             picture_t *p_dest )
312 {
313     /* We got this one from the old arguments */
314     u32 *p_pic = (u32*)p_dest->p->p_pixels;
315     u8  *p_y   = p_src->Y_PIXELS;
316     u8  *p_u   = p_src->U_PIXELS;
317     u8  *p_v   = p_src->V_PIXELS;
318
319     vlc_bool_t  b_hscale;                         /* horizontal scaling type */
320     unsigned int i_vscale;                          /* vertical scaling type */
321     unsigned int i_x, i_y;                /* horizontal and vertical indexes */
322
323     int         i_right_margin;
324     int         i_rewind;
325     int         i_scale_count;                       /* scale modulo counter */
326     int         i_chroma_width = p_vout->render.i_width / 2; /* chroma width */
327     u32 *       p_pic_start;       /* beginning of the current line for copy */
328 #if defined (MODULE_NAME_IS_i420_rgb)
329     int         i_uval, i_vval;                           /* U and V samples */
330     int         i_red, i_green, i_blue;          /* U and V modified samples */
331     u32 *       p_yuv = p_vout->chroma.p_sys->p_rgb32;
332     u32 *       p_ybase;                     /* Y dependant conversion table */
333 #endif
334
335     /* Conversion buffer pointer */
336     u32 *       p_buffer_start = (u32*)p_vout->chroma.p_sys->p_buffer;
337     u32 *       p_buffer;
338
339     /* Offset array pointer */
340     int *       p_offset_start = p_vout->chroma.p_sys->p_offset;
341     int *       p_offset;
342
343     i_right_margin = p_dest->p->i_pitch - p_dest->p->i_visible_pitch;
344
345     if( p_vout->render.i_width & 7 )
346     {
347         i_rewind = 8 - ( p_vout->render.i_width & 7 );
348     }
349     else
350     {
351         i_rewind = 0;
352     }
353
354     /* Rule: when a picture of size (x1,y1) with aspect ratio r1 is rendered
355      * on a picture of size (x2,y2) with aspect ratio r2, if x1 grows to x1'
356      * then y1 grows to y1' = x1' * y2/x2 * r2/r1 */
357     SetOffset( p_vout->render.i_width, p_vout->render.i_height,
358                p_vout->output.i_width, p_vout->output.i_height,
359                &b_hscale, &i_vscale, p_offset_start );
360
361     /*
362      * Perform conversion
363      */
364     i_scale_count = ( i_vscale == 1 ) ?
365                     p_vout->output.i_height : p_vout->render.i_height;
366     for( i_y = 0; i_y < p_vout->render.i_height; i_y++ )
367     {
368         p_pic_start = p_pic;
369         p_buffer = b_hscale ? p_buffer_start : p_pic;
370
371         for ( i_x = p_vout->render.i_width / 8; i_x--; )
372         {
373 #if defined (MODULE_NAME_IS_i420_rgb)
374             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
375             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
376             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
377             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
378 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
379             __asm__( MMX_INIT_32
380                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
381
382             __asm__( ".align 8"
383                      MMX_YUV_MUL
384                      MMX_YUV_ADD
385                      MMX_UNPACK_32
386                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
387
388             p_y += 8;
389             p_u += 4;
390             p_v += 4;
391             p_buffer += 8;
392 #endif
393         }
394
395         /* Here we do some unaligned reads and duplicate conversions, but
396          * at least we have all the pixels */
397         if( i_rewind )
398         {
399             p_y -= i_rewind;
400             p_u -= i_rewind >> 1;
401             p_v -= i_rewind >> 1;
402             p_buffer -= i_rewind;
403 #if defined (MODULE_NAME_IS_i420_rgb)
404             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
405             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
406             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
407             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
408 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
409             __asm__( MMX_INIT_32
410                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
411
412             __asm__( ".align 8"
413                      MMX_YUV_MUL
414                      MMX_YUV_ADD
415                      MMX_UNPACK_32
416                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
417
418             p_y += 8;
419             p_u += 4;
420             p_v += 4;
421             p_buffer += 8;
422 #endif
423         }
424         SCALE_WIDTH;
425         SCALE_HEIGHT( 420, 4 );
426     }
427 }
428
429 /* Following functions are local */
430
431 /*****************************************************************************
432  * SetOffset: build offset array for conversion functions
433  *****************************************************************************
434  * This function will build an offset array used in later conversion functions.
435  * It will also set horizontal and vertical scaling indicators.
436  *****************************************************************************/
437 static void SetOffset( int i_width, int i_height, int i_pic_width,
438                        int i_pic_height, vlc_bool_t *pb_hscale,
439                        int *pi_vscale, int *p_offset )
440 {
441     int i_x;                                    /* x position in destination */
442     int i_scale_count;                                     /* modulo counter */
443
444     /*
445      * Prepare horizontal offset array
446      */
447     if( i_pic_width - i_width == 0 )
448     {
449         /* No horizontal scaling: YUV conversion is done directly to picture */
450         *pb_hscale = 0;
451     }
452     else if( i_pic_width - i_width > 0 )
453     {
454         /* Prepare scaling array for horizontal extension */
455         *pb_hscale = 1;
456         i_scale_count = i_pic_width;
457         for( i_x = i_width; i_x--; )
458         {
459             while( (i_scale_count -= i_width) > 0 )
460             {
461                 *p_offset++ = 0;
462             }
463             *p_offset++ = 1;
464             i_scale_count += i_pic_width;
465         }
466     }
467     else /* if( i_pic_width - i_width < 0 ) */
468     {
469         /* Prepare scaling array for horizontal reduction */
470         *pb_hscale = 1;
471         i_scale_count = i_width;
472         for( i_x = i_pic_width; i_x--; )
473         {
474             *p_offset = 1;
475             while( (i_scale_count -= i_pic_width) > 0 )
476             {
477                 *p_offset += 1;
478             }
479             p_offset++;
480             i_scale_count += i_width;
481         }
482     }
483
484     /*
485      * Set vertical scaling indicator
486      */
487     if( i_pic_height - i_height == 0 )
488     {
489         *pi_vscale = 0;
490     }
491     else if( i_pic_height - i_height > 0 )
492     {
493         *pi_vscale = 1;
494     }
495     else /* if( i_pic_height - i_height < 0 ) */
496     {
497         *pi_vscale = -1;
498     }
499 }
500