f15054f8e427a4d306561bf1ed0e7a07cbd39aa6
[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$
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 #if defined (MODULE_NAME_IS_i420_rgb)
43 /*****************************************************************************
44  * I420_RGB16: color YUV 4:2:0 to RGB 16 bpp with dithering
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_RGB16_dithering)( 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     uint16_t *p_pic = (uint16_t*)p_dest->p->p_pixels;
58     uint8_t  *p_y   = p_src->Y_PIXELS;
59     uint8_t  *p_u   = p_src->U_PIXELS;
60     uint8_t  *p_v   = p_src->V_PIXELS;
61
62     vlc_bool_t   b_hscale;                        /* horizontal scaling type */
63     int          i_vscale;                          /* vertical scaling type */
64     unsigned int i_x, i_y;                /* horizontal and vertical indexes */
65     unsigned int i_real_y;                                          /* y % 4 */
66
67     int         i_right_margin;
68     int         i_rewind;
69     int         i_scale_count;                       /* scale modulo counter */
70     int         i_chroma_width = p_vout->render.i_width / 2; /* chroma width */
71     uint16_t *  p_pic_start;       /* beginning of the current line for copy */
72     int         i_uval, i_vval;                           /* U and V samples */
73     int         i_red, i_green, i_blue;          /* U and V modified samples */
74     uint16_t *  p_yuv = p_vout->chroma.p_sys->p_rgb16;
75     uint16_t *  p_ybase;                     /* Y dependant conversion table */
76
77     /* Conversion buffer pointer */
78     uint16_t *  p_buffer_start = (uint16_t*)p_vout->chroma.p_sys->p_buffer;
79     uint16_t *  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     const int i_source_margin = p_src->p[0].i_pitch
86                                  - p_src->p[0].i_visible_pitch;
87     const int i_source_margin_c = p_src->p[1].i_pitch
88                                  - p_src->p[1].i_visible_pitch;
89
90     /* The dithering matrices */
91     int dither10[4] = {  0x0,  0x8,  0x2,  0xa };
92     int dither11[4] = {  0xc,  0x4,  0xe,  0x6 };
93     int dither12[4] = {  0x3,  0xb,  0x1,  0x9 };
94     int dither13[4] = {  0xf,  0x7,  0xd,  0x5 };
95
96     for(i_x = 0; i_x < 4; i_x++)
97     {
98         dither10[i_x] = dither10[i_x] << (SHIFT - 4 + p_vout->output.i_rrshift);
99         dither11[i_x] = dither11[i_x] << (SHIFT - 4 + p_vout->output.i_rrshift);
100         dither12[i_x] = dither12[i_x] << (SHIFT - 4 + p_vout->output.i_rrshift);
101         dither13[i_x] = dither13[i_x] << (SHIFT - 4 + p_vout->output.i_rrshift);
102     }
103
104     i_right_margin = p_dest->p->i_pitch - p_dest->p->i_visible_pitch;
105
106     if( p_vout->render.i_width & 7 )
107     {
108         i_rewind = 8 - ( p_vout->render.i_width & 7 );
109     }
110     else
111     {
112         i_rewind = 0;
113     }
114
115     /* Rule: when a picture of size (x1,y1) with aspect ratio r1 is rendered
116      * on a picture of size (x2,y2) with aspect ratio r2, if x1 grows to x1'
117      * then y1 grows to y1' = x1' * y2/x2 * r2/r1 */
118     SetOffset( p_vout->render.i_width, p_vout->render.i_height,
119                p_vout->output.i_width, p_vout->output.i_height,
120                &b_hscale, &i_vscale, p_offset_start );
121
122     /*
123      * Perform conversion
124      */
125     i_scale_count = ( i_vscale == 1 ) ?
126                     p_vout->output.i_height : p_vout->render.i_height;
127     for( i_y = 0; i_y < p_vout->render.i_height; i_y++ )
128     {
129         i_real_y = i_y & 0x3;
130         p_pic_start = p_pic;
131         p_buffer = b_hscale ? p_buffer_start : p_pic;
132
133         for ( i_x = p_vout->render.i_width / 8; i_x--; )
134         {
135             int *p_dither = dither10;
136             CONVERT_YUV_PIXEL_DITHER(2);
137             p_dither = dither11;
138             CONVERT_Y_PIXEL_DITHER(2);
139             p_dither = dither12;
140             CONVERT_YUV_PIXEL_DITHER(2);
141             p_dither = dither13;
142             CONVERT_Y_PIXEL_DITHER(2);
143             p_dither = dither10;
144             CONVERT_YUV_PIXEL_DITHER(2);
145             p_dither = dither11;
146             CONVERT_Y_PIXEL_DITHER(2);
147             p_dither = dither12;
148             CONVERT_YUV_PIXEL_DITHER(2);
149             p_dither = dither13;
150             CONVERT_Y_PIXEL_DITHER(2);
151         }
152
153         /* Here we do some unaligned reads and duplicate conversions, but
154          * at least we have all the pixels */
155         if( i_rewind )
156         {
157             int *p_dither = dither10;
158             p_y -= i_rewind;
159             p_u -= i_rewind >> 1;
160             p_v -= i_rewind >> 1;
161             p_buffer -= i_rewind;
162             CONVERT_YUV_PIXEL_DITHER(2);
163             p_dither = dither11;
164             CONVERT_Y_PIXEL_DITHER(2);
165             p_dither = dither12;
166             CONVERT_YUV_PIXEL_DITHER(2);
167             p_dither = dither13;
168             CONVERT_Y_PIXEL_DITHER(2);
169             p_dither = dither10;
170             CONVERT_YUV_PIXEL_DITHER(2);
171             p_dither = dither11;
172             CONVERT_Y_PIXEL_DITHER(2);
173             p_dither = dither12;
174             CONVERT_YUV_PIXEL_DITHER(2);
175             p_dither = dither13;
176             CONVERT_Y_PIXEL_DITHER(2);
177         }
178         SCALE_WIDTH;
179         SCALE_HEIGHT( 420, 2 );
180
181         p_y += i_source_margin;
182         if( i_y % 2 )
183         {
184             p_u += i_source_margin_c;
185             p_v += i_source_margin_c;
186         }
187     }
188 }
189 #endif
190
191 /*****************************************************************************
192  * I420_RGB16: color YUV 4:2:0 to RGB 16 bpp
193  *****************************************************************************
194  * Horizontal alignment needed:
195  *  - input: 8 pixels (8 Y bytes, 4 U/V bytes), margins not allowed
196  *  - output: 1 pixel (2 bytes), margins allowed
197  * Vertical alignment needed:
198  *  - input: 2 lines (2 Y lines, 1 U/V line)
199  *  - output: 1 line
200  *****************************************************************************/
201 void E_(I420_RGB16)( vout_thread_t *p_vout, picture_t *p_src,
202                                             picture_t *p_dest )
203 {
204     /* We got this one from the old arguments */
205     uint16_t *p_pic = (uint16_t*)p_dest->p->p_pixels;
206     uint8_t  *p_y   = p_src->Y_PIXELS;
207     uint8_t  *p_u   = p_src->U_PIXELS;
208     uint8_t  *p_v   = p_src->V_PIXELS;
209
210     vlc_bool_t  b_hscale;                         /* horizontal scaling type */
211     unsigned int i_vscale;                          /* vertical scaling type */
212     unsigned int i_x, i_y;                /* horizontal and vertical indexes */
213
214     int         i_right_margin;
215     int         i_rewind;
216     int         i_scale_count;                       /* scale modulo counter */
217     int         i_chroma_width = p_vout->render.i_width / 2; /* chroma width */
218     uint16_t *  p_pic_start;       /* beginning of the current line for copy */
219 #if defined (MODULE_NAME_IS_i420_rgb)
220     int         i_uval, i_vval;                           /* U and V samples */
221     int         i_red, i_green, i_blue;          /* U and V modified samples */
222     uint16_t *  p_yuv = p_vout->chroma.p_sys->p_rgb16;
223     uint16_t *  p_ybase;                     /* Y dependant conversion table */
224 #endif
225
226     /* Conversion buffer pointer */
227     uint16_t *  p_buffer_start = (uint16_t*)p_vout->chroma.p_sys->p_buffer;
228     uint16_t *  p_buffer;
229
230     /* Offset array pointer */
231     int *       p_offset_start = p_vout->chroma.p_sys->p_offset;
232     int *       p_offset;
233
234     const int i_source_margin = p_src->p[0].i_pitch
235                                  - p_src->p[0].i_visible_pitch;
236     const int i_source_margin_c = p_src->p[1].i_pitch
237                                  - p_src->p[1].i_visible_pitch;
238
239     i_right_margin = p_dest->p->i_pitch - p_dest->p->i_visible_pitch;
240
241     if( p_vout->render.i_width & 7 )
242     {
243         i_rewind = 8 - ( p_vout->render.i_width & 7 );
244     }
245     else
246     {
247         i_rewind = 0;
248     }
249
250     /* Rule: when a picture of size (x1,y1) with aspect ratio r1 is rendered
251      * on a picture of size (x2,y2) with aspect ratio r2, if x1 grows to x1'
252      * then y1 grows to y1' = x1' * y2/x2 * r2/r1 */
253     SetOffset( p_vout->render.i_width, p_vout->render.i_height,
254                p_vout->output.i_width, p_vout->output.i_height,
255                &b_hscale, &i_vscale, p_offset_start );
256
257     /*
258      * Perform conversion
259      */
260     i_scale_count = ( i_vscale == 1 ) ?
261                     p_vout->output.i_height : p_vout->render.i_height;
262     for( i_y = 0; i_y < p_vout->render.i_height; i_y++ )
263     {
264         p_pic_start = p_pic;
265         p_buffer = b_hscale ? p_buffer_start : p_pic;
266
267 #if defined (MODULE_NAME_IS_i420_rgb)
268         for ( i_x = p_vout->render.i_width / 8; i_x--; )
269         {
270             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
271             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
272             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
273             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
274         }
275 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
276         if( p_vout->output.i_rmask == 0x7c00 )
277         {
278             /* 15bpp 5/5/5 */
279             for ( i_x = p_vout->render.i_width / 8; i_x--; )
280             {
281                 __asm__( MMX_INIT_16
282                          : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
283
284                 __asm__( ".align 8"
285                          MMX_YUV_MUL
286                          MMX_YUV_ADD
287                          MMX_UNPACK_15
288                          : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
289
290                 p_y += 8;
291                 p_u += 4;
292                 p_v += 4;
293                 p_buffer += 8;
294             }
295         }
296         else
297         {
298             /* 16bpp 5/6/5 */
299             for ( i_x = p_vout->render.i_width / 8; i_x--; )
300             {
301                 __asm__( MMX_INIT_16
302                          : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
303
304                 __asm__( ".align 8"
305                          MMX_YUV_MUL
306                          MMX_YUV_ADD
307                          MMX_UNPACK_16
308                          : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
309
310                 p_y += 8;
311                 p_u += 4;
312                 p_v += 4;
313                 p_buffer += 8;
314             }
315         }
316 #endif
317
318         /* Here we do some unaligned reads and duplicate conversions, but
319          * at least we have all the pixels */
320         if( i_rewind )
321         {
322             p_y -= i_rewind;
323             p_u -= i_rewind >> 1;
324             p_v -= i_rewind >> 1;
325             p_buffer -= i_rewind;
326 #if defined (MODULE_NAME_IS_i420_rgb)
327             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
328             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
329             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
330             CONVERT_YUV_PIXEL(2);  CONVERT_Y_PIXEL(2);
331 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
332             __asm__( MMX_INIT_16
333                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
334
335             if( p_vout->output.i_rmask == 0x7c00 )
336             {
337                 /* 15bpp 5/5/5 */
338                 __asm__( ".align 8"
339                          MMX_YUV_MUL
340                          MMX_YUV_ADD
341                          MMX_UNPACK_15
342                          : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
343             }
344             else
345             {
346                 /* 16bpp 5/6/5 */
347                 __asm__( ".align 8"
348                          MMX_YUV_MUL
349                          MMX_YUV_ADD
350                          MMX_UNPACK_16
351                          : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
352             }
353
354             p_y += 8;
355             p_u += 4;
356             p_v += 4;
357             p_buffer += 8;
358 #endif
359         }
360         SCALE_WIDTH;
361         SCALE_HEIGHT( 420, 2 );
362
363         p_y += i_source_margin;
364         if( i_y % 2 )
365         {
366             p_u += i_source_margin_c;
367             p_v += i_source_margin_c;
368         }
369     }
370 }
371
372 /*****************************************************************************
373  * I420_RGB32: color YUV 4:2:0 to RGB 32 bpp
374  *****************************************************************************
375  * Horizontal alignment needed:
376  *  - input: 8 pixels (8 Y bytes, 4 U/V bytes), margins not allowed
377  *  - output: 1 pixel (2 bytes), margins allowed
378  * Vertical alignment needed:
379  *  - input: 2 lines (2 Y lines, 1 U/V line)
380  *  - output: 1 line
381  *****************************************************************************/
382 void E_(I420_RGB32)( vout_thread_t *p_vout, picture_t *p_src,
383                                             picture_t *p_dest )
384 {
385     /* We got this one from the old arguments */
386     uint32_t *p_pic = (uint32_t*)p_dest->p->p_pixels;
387     uint8_t  *p_y   = p_src->Y_PIXELS;
388     uint8_t  *p_u   = p_src->U_PIXELS;
389     uint8_t  *p_v   = p_src->V_PIXELS;
390
391     vlc_bool_t  b_hscale;                         /* horizontal scaling type */
392     unsigned int i_vscale;                          /* vertical scaling type */
393     unsigned int i_x, i_y;                /* horizontal and vertical indexes */
394
395     int         i_right_margin;
396     int         i_rewind;
397     int         i_scale_count;                       /* scale modulo counter */
398     int         i_chroma_width = p_vout->render.i_width / 2; /* chroma width */
399     uint32_t *  p_pic_start;       /* beginning of the current line for copy */
400 #if defined (MODULE_NAME_IS_i420_rgb)
401     int         i_uval, i_vval;                           /* U and V samples */
402     int         i_red, i_green, i_blue;          /* U and V modified samples */
403     uint32_t *  p_yuv = p_vout->chroma.p_sys->p_rgb32;
404     uint32_t *  p_ybase;                     /* Y dependant conversion table */
405 #endif
406
407     /* Conversion buffer pointer */
408     uint32_t *  p_buffer_start = (uint32_t*)p_vout->chroma.p_sys->p_buffer;
409     uint32_t *  p_buffer;
410
411     /* Offset array pointer */
412     int *       p_offset_start = p_vout->chroma.p_sys->p_offset;
413     int *       p_offset;
414
415     const int i_source_margin = p_src->p[0].i_pitch
416                                  - p_src->p[0].i_visible_pitch;
417     const int i_source_margin_c = p_src->p[1].i_pitch
418                                  - p_src->p[1].i_visible_pitch;
419
420     i_right_margin = p_dest->p->i_pitch - p_dest->p->i_visible_pitch;
421
422     if( p_vout->render.i_width & 7 )
423     {
424         i_rewind = 8 - ( p_vout->render.i_width & 7 );
425     }
426     else
427     {
428         i_rewind = 0;
429     }
430
431     /* Rule: when a picture of size (x1,y1) with aspect ratio r1 is rendered
432      * on a picture of size (x2,y2) with aspect ratio r2, if x1 grows to x1'
433      * then y1 grows to y1' = x1' * y2/x2 * r2/r1 */
434     SetOffset( p_vout->render.i_width, p_vout->render.i_height,
435                p_vout->output.i_width, p_vout->output.i_height,
436                &b_hscale, &i_vscale, p_offset_start );
437
438     /*
439      * Perform conversion
440      */
441     i_scale_count = ( i_vscale == 1 ) ?
442                     p_vout->output.i_height : p_vout->render.i_height;
443     for( i_y = 0; i_y < p_vout->render.i_height; i_y++ )
444     {
445         p_pic_start = p_pic;
446         p_buffer = b_hscale ? p_buffer_start : p_pic;
447
448         for ( i_x = p_vout->render.i_width / 8; i_x--; )
449         {
450 #if defined (MODULE_NAME_IS_i420_rgb)
451             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
452             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
453             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
454             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
455 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
456             __asm__( MMX_INIT_32
457                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
458
459             __asm__( ".align 8"
460                      MMX_YUV_MUL
461                      MMX_YUV_ADD
462                      MMX_UNPACK_32
463                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
464
465             p_y += 8;
466             p_u += 4;
467             p_v += 4;
468             p_buffer += 8;
469 #endif
470         }
471
472         /* Here we do some unaligned reads and duplicate conversions, but
473          * at least we have all the pixels */
474         if( i_rewind )
475         {
476             p_y -= i_rewind;
477             p_u -= i_rewind >> 1;
478             p_v -= i_rewind >> 1;
479             p_buffer -= i_rewind;
480 #if defined (MODULE_NAME_IS_i420_rgb)
481             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
482             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
483             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
484             CONVERT_YUV_PIXEL(4);  CONVERT_Y_PIXEL(4);
485 #elif defined (MODULE_NAME_IS_i420_rgb_mmx)
486             __asm__( MMX_INIT_32
487                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
488
489             __asm__( ".align 8"
490                      MMX_YUV_MUL
491                      MMX_YUV_ADD
492                      MMX_UNPACK_32
493                      : : "r" (p_y), "r" (p_u), "r" (p_v), "r" (p_buffer) );
494
495             p_y += 8;
496             p_u += 4;
497             p_v += 4;
498             p_buffer += 8;
499 #endif
500         }
501         SCALE_WIDTH;
502         SCALE_HEIGHT( 420, 4 );
503
504         p_y += i_source_margin;
505         if( i_y % 2 )
506         {
507             p_u += i_source_margin_c;
508             p_v += i_source_margin_c;
509         }
510     }
511 }
512
513 /* Following functions are local */
514
515 /*****************************************************************************
516  * SetOffset: build offset array for conversion functions
517  *****************************************************************************
518  * This function will build an offset array used in later conversion functions.
519  * It will also set horizontal and vertical scaling indicators.
520  *****************************************************************************/
521 static void SetOffset( int i_width, int i_height, int i_pic_width,
522                        int i_pic_height, vlc_bool_t *pb_hscale,
523                        int *pi_vscale, int *p_offset )
524 {
525     int i_x;                                    /* x position in destination */
526     int i_scale_count;                                     /* modulo counter */
527
528     /*
529      * Prepare horizontal offset array
530      */
531     if( i_pic_width - i_width == 0 )
532     {
533         /* No horizontal scaling: YUV conversion is done directly to picture */
534         *pb_hscale = 0;
535     }
536     else if( i_pic_width - i_width > 0 )
537     {
538         /* Prepare scaling array for horizontal extension */
539         *pb_hscale = 1;
540         i_scale_count = i_pic_width;
541         for( i_x = i_width; i_x--; )
542         {
543             while( (i_scale_count -= i_width) > 0 )
544             {
545                 *p_offset++ = 0;
546             }
547             *p_offset++ = 1;
548             i_scale_count += i_pic_width;
549         }
550     }
551     else /* if( i_pic_width - i_width < 0 ) */
552     {
553         /* Prepare scaling array for horizontal reduction */
554         *pb_hscale = 1;
555         i_scale_count = i_width;
556         for( i_x = i_pic_width; i_x--; )
557         {
558             *p_offset = 1;
559             while( (i_scale_count -= i_pic_width) > 0 )
560             {
561                 *p_offset += 1;
562             }
563             p_offset++;
564             i_scale_count += i_width;
565         }
566     }
567
568     /*
569      * Set vertical scaling indicator
570      */
571     if( i_pic_height - i_height == 0 )
572     {
573         *pi_vscale = 0;
574     }
575     else if( i_pic_height - i_height > 0 )
576     {
577         *pi_vscale = 1;
578     }
579     else /* if( i_pic_height - i_height < 0 ) */
580     {
581         *pi_vscale = -1;
582     }
583 }
584