* ./modules/*: moved plugins to the new tree. Yet untested builds include
[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.1 2002/08/04 17:23:43 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 <errno.h>                                                 /* ENOMEM */
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 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
38 #   include "i420_rgb_mmx.h"
39 #endif
40
41 static void SetOffset( int, int, int, int, vlc_bool_t *, int *, int * );
42
43 /*****************************************************************************
44  * I420_RGB15: color YUV 4:2:0 to RGB 15 bpp
45  *****************************************************************************
46  * Horizontal alignment needed:
47  *  - input: 8 pixels (8 Y bytes, 4 U/V bytes), margins not allowed
48  *  - output: 1 pixel (2 bytes), margins allowed
49  * Vertical alignment needed:
50  *  - input: 2 lines (2 Y lines, 1 U/V line)
51  *  - output: 1 line
52  *****************************************************************************/
53 void E_(I420_RGB15)( vout_thread_t *p_vout, picture_t *p_src,
54                                             picture_t *p_dest )
55 {
56     /* We got this one from the old arguments */
57     u16 *p_pic = (u16*)p_dest->p->p_pixels;
58     u8  *p_y   = p_src->Y_PIXELS;
59     u8  *p_u   = p_src->U_PIXELS;
60     u8  *p_v   = p_src->V_PIXELS;
61
62     vlc_bool_t  b_hscale;                         /* horizontal scaling type */
63     int         i_vscale;                           /* vertical scaling type */
64     int         i_x, i_y;                 /* horizontal and vertical indexes */
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     int         i_vscale;                           /* vertical scaling type */
192     int         i_x, i_y;                 /* horizontal and vertical indexes */
193     int         i_right_margin;
194     int         i_rewind;
195     int         i_scale_count;                       /* scale modulo counter */
196     int         i_chroma_width = p_vout->render.i_width / 2; /* chroma width */
197     u16 *       p_pic_start;       /* beginning of the current line for copy */
198 #if defined (MODULE_NAME_IS_i420_rgb)
199     int         i_uval, i_vval;                           /* U and V samples */
200     int         i_red, i_green, i_blue;          /* U and V modified samples */
201     u16 *       p_yuv = p_vout->chroma.p_sys->p_rgb16;
202     u16 *       p_ybase;                     /* Y dependant conversion table */
203 #endif
204
205     /* Conversion buffer pointer */
206     u16 *       p_buffer_start = (u16*)p_vout->chroma.p_sys->p_buffer;
207     u16 *       p_buffer;
208
209     /* Offset array pointer */
210     int *       p_offset_start = p_vout->chroma.p_sys->p_offset;
211     int *       p_offset;
212
213     i_right_margin = p_dest->p->i_pitch - p_dest->p->i_visible_pitch;
214
215     if( p_vout->render.i_width & 7 )
216     {
217         i_rewind = 8 - ( p_vout->render.i_width & 7 );
218     }
219     else
220     {
221         i_rewind = 0;
222     }
223
224     /* Rule: when a picture of size (x1,y1) with aspect ratio r1 is rendered
225      * on a picture of size (x2,y2) with aspect ratio r2, if x1 grows to x1'
226      * then y1 grows to y1' = x1' * y2/x2 * r2/r1 */
227     SetOffset( p_vout->render.i_width, p_vout->render.i_height,
228                p_vout->output.i_width, p_vout->output.i_height,
229                &b_hscale, &i_vscale, p_offset_start );
230
231     /*
232      * Perform conversion
233      */
234     i_scale_count = ( i_vscale == 1 ) ?
235                     p_vout->output.i_height : p_vout->render.i_height;
236     for( i_y = 0; i_y < p_vout->render.i_height; i_y++ )
237     {
238         p_pic_start = p_pic;
239         p_buffer = b_hscale ? p_buffer_start : p_pic;
240
241         for ( i_x = p_vout->render.i_width / 8; i_x--; )
242         {
243 #if defined (MODULE_NAME_IS_i420_rgb)
244             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
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 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
249             __asm__( MMX_INIT_16
250                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
251
252             __asm__( ".align 8"
253                      MMX_YUV_MUL
254                      MMX_YUV_ADD
255                      MMX_UNPACK_16
256                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
257
258             p_y += 8;
259             p_u += 4;
260             p_v += 4;
261             p_buffer += 8;
262 #endif
263         }
264
265         /* Here we do some unaligned reads and duplicate conversions, but
266          * at least we have all the pixels */
267         if( i_rewind )
268         {
269             p_y -= i_rewind;
270             p_u -= i_rewind >> 1;
271             p_v -= i_rewind >> 1;
272             p_buffer -= i_rewind;
273 #if defined (MODULE_NAME_IS_i420_rgb)
274             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
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 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
279             __asm__( MMX_INIT_16
280                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
281
282             __asm__( ".align 8"
283                      MMX_YUV_MUL
284                      MMX_YUV_ADD
285                      MMX_UNPACK_16
286                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
287
288             p_y += 8;
289             p_u += 4;
290             p_v += 4;
291             p_buffer += 8;
292 #endif
293         }
294         SCALE_WIDTH;
295         SCALE_HEIGHT( 420, 2 );
296     }
297 }
298
299 /*****************************************************************************
300  * I420_RGB32: color YUV 4:2:0 to RGB 32 bpp
301  *****************************************************************************
302  * Horizontal alignment needed:
303  *  - input: 8 pixels (8 Y bytes, 4 U/V bytes), margins not allowed
304  *  - output: 1 pixel (2 bytes), margins allowed
305  * Vertical alignment needed:
306  *  - input: 2 lines (2 Y lines, 1 U/V line)
307  *  - output: 1 line
308  *****************************************************************************/
309 void E_(I420_RGB32)( vout_thread_t *p_vout, picture_t *p_src,
310                                             picture_t *p_dest )
311 {
312     /* We got this one from the old arguments */
313     u32 *p_pic = (u32*)p_dest->p->p_pixels;
314     u8  *p_y   = p_src->Y_PIXELS;
315     u8  *p_u   = p_src->U_PIXELS;
316     u8  *p_v   = p_src->V_PIXELS;
317
318     vlc_bool_t  b_hscale;                         /* horizontal scaling type */
319     int         i_vscale;                           /* vertical scaling type */
320     int         i_x, i_y;                 /* horizontal and vertical indexes */
321     int         i_right_margin;
322     int         i_rewind;
323     int         i_scale_count;                       /* scale modulo counter */
324     int         i_chroma_width = p_vout->render.i_width / 4; /* chroma width */
325     u32 *       p_pic_start;       /* beginning of the current line for copy */
326 #if defined (MODULE_NAME_IS_i420_rgb)
327     int         i_uval, i_vval;                           /* U and V samples */
328     int         i_red, i_green, i_blue;          /* U and V modified samples */
329     u32 *       p_yuv = p_vout->chroma.p_sys->p_rgb32;
330     u32 *       p_ybase;                     /* Y dependant conversion table */
331 #endif
332
333     /* Conversion buffer pointer */
334     u32 *       p_buffer_start = (u32*)p_vout->chroma.p_sys->p_buffer;
335     u32 *       p_buffer;
336
337     /* Offset array pointer */
338     int *       p_offset_start = p_vout->chroma.p_sys->p_offset;
339     int *       p_offset;
340
341     i_right_margin = p_dest->p->i_pitch - p_dest->p->i_visible_pitch;
342
343     if( p_vout->render.i_width & 7 )
344     {
345         i_rewind = 8 - ( p_vout->render.i_width & 7 );
346     }
347     else
348     {
349         i_rewind = 0;
350     }
351
352     /* Rule: when a picture of size (x1,y1) with aspect ratio r1 is rendered
353      * on a picture of size (x2,y2) with aspect ratio r2, if x1 grows to x1'
354      * then y1 grows to y1' = x1' * y2/x2 * r2/r1 */
355     SetOffset( p_vout->render.i_width, p_vout->render.i_height,
356                p_vout->output.i_width, p_vout->output.i_height,
357                &b_hscale, &i_vscale, p_offset_start );
358
359     /*
360      * Perform conversion
361      */
362     i_scale_count = ( i_vscale == 1 ) ?
363                     p_vout->output.i_height : p_vout->render.i_height;
364     for( i_y = 0; i_y < p_vout->render.i_height; i_y++ )
365     {
366         p_pic_start = p_pic;
367         p_buffer = b_hscale ? p_buffer_start : p_pic;
368
369         for ( i_x = p_vout->render.i_width / 8; i_x--; )
370         {
371 #if defined (MODULE_NAME_IS_i420_rgb)
372             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
373             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
374             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
375             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
376 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
377             __asm__( MMX_INIT_32
378                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
379
380             __asm__( ".align 8"
381                      MMX_YUV_MUL
382                      MMX_YUV_ADD
383                      MMX_UNPACK_32
384                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
385
386             p_y += 8;
387             p_u += 4;
388             p_v += 4;
389             p_buffer += 8;
390 #endif
391         }
392
393         /* Here we do some unaligned reads and duplicate conversions, but
394          * at least we have all the pixels */
395         if( i_rewind )
396         {
397             p_y -= i_rewind;
398             p_u -= i_rewind >> 1;
399             p_v -= i_rewind >> 1;
400             p_buffer -= i_rewind;
401 #if defined (MODULE_NAME_IS_i420_rgb)
402             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
403             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
404             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
405             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
406 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
407             __asm__( MMX_INIT_32
408                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
409
410             __asm__( ".align 8"
411                      MMX_YUV_MUL
412                      MMX_YUV_ADD
413                      MMX_UNPACK_32
414                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
415
416             p_y += 8;
417             p_u += 4;
418             p_v += 4;
419             p_buffer += 8;
420 #endif
421         }
422         SCALE_WIDTH;
423         SCALE_HEIGHT( 420, 4 );
424     }
425 }
426
427 /* Following functions are local */
428
429 /*****************************************************************************
430  * SetOffset: build offset array for conversion functions
431  *****************************************************************************
432  * This function will build an offset array used in later conversion functions.
433  * It will also set horizontal and vertical scaling indicators.
434  *****************************************************************************/
435 static void SetOffset( int i_width, int i_height, int i_pic_width,
436                        int i_pic_height, vlc_bool_t *pb_hscale,
437                        int *pi_vscale, int *p_offset )
438 {
439     int i_x;                                    /* x position in destination */
440     int i_scale_count;                                     /* modulo counter */
441
442     /*
443      * Prepare horizontal offset array
444      */
445     if( i_pic_width - i_width == 0 )
446     {
447         /* No horizontal scaling: YUV conversion is done directly to picture */
448         *pb_hscale = 0;
449     }
450     else if( i_pic_width - i_width > 0 )
451     {
452         /* Prepare scaling array for horizontal extension */
453         *pb_hscale = 1;
454         i_scale_count = i_pic_width;
455         for( i_x = i_width; i_x--; )
456         {
457             while( (i_scale_count -= i_width) > 0 )
458             {
459                 *p_offset++ = 0;
460             }
461             *p_offset++ = 1;
462             i_scale_count += i_pic_width;
463         }
464     }
465     else /* if( i_pic_width - i_width < 0 ) */
466     {
467         /* Prepare scaling array for horizontal reduction */
468         *pb_hscale = 1;
469         i_scale_count = i_width;
470         for( i_x = i_pic_width; i_x--; )
471         {
472             *p_offset = 1;
473             while( (i_scale_count -= i_pic_width) > 0 )
474             {
475                 *p_offset += 1;
476             }
477             p_offset++;
478             i_scale_count += i_width;
479         }
480     }
481
482     /*
483      * Set vertical scaling indicator
484      */
485     if( i_pic_height - i_height == 0 )
486     {
487         *pi_vscale = 0;
488     }
489     else if( i_pic_height - i_height > 0 )
490     {
491         *pi_vscale = 1;
492     }
493     else /* if( i_pic_height - i_height < 0 ) */
494     {
495         *pi_vscale = -1;
496     }
497 }
498