root/branches/perian-1.1/ColorConversions.c

Revision 887, 14.6 kB (checked in by astrange, 5 months ago)

Merge trunk to 1.1 branch.

Line 
1 /*
2  *  ColorConversions.h
3  *  Perian
4  *
5  *  Created by Alexander Strange on 1/10/07.
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2.1 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA */
19
20 #include "ColorConversions.h"
21 #include <QuickTime/QuickTime.h>
22 #include <Accelerate/Accelerate.h>
23 #include <sys/types.h>
24 #include <sys/sysctl.h>
25
26 #ifndef GOOD_COMPILER
27 #define noinline __attribute__((noinline))
28 #else
29 #define noinline
30 #endif
31
32 #define unlikely(x) __builtin_expect(x, 0)
33
34 //-----------------------------------------------------------------
35 // FastY420
36 //-----------------------------------------------------------------
37 // Returns y420 data directly to QuickTime which then converts
38 // in RGB for display
39 //-----------------------------------------------------------------
40
41 void FastY420(UInt8 *baseAddr, AVFrame *picture)
42 {
43     PlanarPixmapInfoYUV420 *planar;
44        
45         /*From Docs: PixMap baseAddr points to a big-endian PlanarPixmapInfoYUV420 struct; see ImageCodec.i. */
46     planar = (PlanarPixmapInfoYUV420 *) baseAddr;
47    
48     // if ya can't set da poiners, set da offsets
49     planar->componentInfoY.offset = EndianU32_NtoB(picture->data[0] - baseAddr);
50     planar->componentInfoCb.offset =  EndianU32_NtoB(picture->data[1] - baseAddr);
51     planar->componentInfoCr.offset =  EndianU32_NtoB(picture->data[2] - baseAddr);
52    
53     // for the 16/32 add look at EDGE in mpegvideo.c
54     planar->componentInfoY.rowBytes = EndianU32_NtoB(picture->linesize[0]);
55     planar->componentInfoCb.rowBytes = EndianU32_NtoB(picture->linesize[1]);
56     planar->componentInfoCr.rowBytes = EndianU32_NtoB(picture->linesize[2]);
57 }
58
59 //-----------------------------------------------------------------
60 // FFusionSlowDecompress
61 //-----------------------------------------------------------------
62 // We have to return 2yuv values because
63 // QT version has no built-in y420 component.
64 // Since we do the conversion ourselves it is not really optimized....
65 // The function should never be called since many people now
66 // have a decent OS/QT version.
67 //-----------------------------------------------------------------
68
69 static void noinline Y420toY422_lastrow(UInt8 *o, const UInt8 *yc, const UInt8 *uc, const UInt8 *vc, unsigned halfWidth)
70 {
71         unsigned x;
72         for(x=0; x < halfWidth; x++)
73         {
74                 unsigned x4 = x*4, x2 = x*2;
75                 o[x4] = uc[x];
76                 o[x4+1] = yc[x2];
77                 o[x4+2] = vc[x];
78                 o[x4+3] = yc[x2+1];
79         }
80 }
81
82 #define HandleLastRow(o, yc, uc, vc, halfWidth, height) if (unlikely(height & 1)) Y420toY422_lastrow(o, yc, uc, vc, halfWidth)
83
84 #ifdef __BIG_ENDIAN__
85 //hand-unrolled code is a bad idea on modern CPUs. luckily, this does not run on modern CPUs, only G3s.
86 //also, big-endian only
87
88 static void Y420toY422_ppc_scalar(UInt8* baseAddr, unsigned outRB, unsigned width, unsigned height, AVFrame * picture)
89 {
90          unsigned             y = height / 2;
91          unsigned             halfWidth = width / 2, halfHalfWidth = width / 4;
92          UInt8          *inY = picture->data[0], *inU = picture->data[1], *inV = picture->data[2];
93          int             rB = picture->linesize[0], rbU = picture->linesize[1], rbV = picture->linesize[2];
94          
95          while (y--) {
96                  UInt32         *ldst = (UInt32 *) baseAddr, *ldstr2 = (UInt32 *) (baseAddr + outRB);
97                  UInt32         *lsrc = (UInt32 *) inY, *lsrcr2 = (UInt32 *) (inY + rB);
98                  UInt16         *sU = (UInt16 *) inU, *sV = (UInt16 *) inV;
99                  ptrdiff_t              off;
100                  
101                  for (off = 0; off < halfHalfWidth; off++) {
102                          UInt16          chrU = sU[off], chrV = sV[off];
103                          UInt32          row1luma = lsrc[off], row2luma = lsrcr2[off];
104                          UInt32          chromas1 = (chrU & 0xff00) << 16 | (chrV & 0xff00), chromas2 = (chrU & 0xff) << 24 | (chrV & 0xff) << 8;
105                          int             off2 = off * 2;
106                          
107                          ldst[off2] = chromas1 | (row1luma & 0xff000000) >> 8 | (row1luma & 0xff0000) >> 16;
108                          ldstr2[off2] = chromas1 | (row2luma & 0xff000000) >> 8 | (row2luma & 0xff0000) >> 16;
109                          off2++;
110                          ldst[off2] = chromas2 | (row1luma & 0xff00) << 8 | row1luma & 0xff;
111                          ldstr2[off2] = chromas2 | (row2luma & 0xff00) << 8 | row2luma & 0xff;
112                  }
113                  
114                  if (halfWidth % 4) {
115                          UInt16         *ssrc = (UInt16 *) inY, *ssrcr2 = (UInt16 *) (inY + rB);
116                          
117                          ptrdiff_t       off = halfWidth - 2;
118                          UInt32          chromas = inV[off] << 8 | (inU[off] << 24);
119                          UInt16          row1luma = ssrc[off], row2luma = ssrcr2[off];
120                          
121                          ldst[off] = chromas | row1luma & 0xff | (row1luma & 0xff00) << 8;
122                          ldstr2[off] = chromas | row2luma & 0xff | (row2luma & 0xff00) << 8;
123                  }
124                  inY += rB * 2;
125                  inU += rbU;
126                  inV += rbV;
127                  baseAddr += outRB * 2;
128          }
129        
130         HandleLastRow(baseAddr, inY, inU, inV, halfWidth, height);
131  }
132
133 static void Y420toY422_ppc_altivec(UInt8 * o, unsigned outRB, unsigned width, unsigned height, AVFrame * picture)
134 {
135         UInt8                   *yc = picture->data[0], *uc = picture->data[1], *vc = picture->data[2];
136         unsigned                rY = picture->linesize[0], rU = picture->linesize[1], rV = picture->linesize[2];
137         unsigned                y,x,x2,x4, vWidth = width / 32, halfheight = height / 2;
138        
139         for (y = 0; y < halfheight; y ++) {
140                 vUInt8 *ov = (vUInt8 *)o, *ov2 = (vUInt8 *)(o + outRB), *yv2 = (vUInt8 *)(yc + rY);
141                 vUInt8 *uv  = (vUInt8 *)uc, *vv = (vUInt8 *)vc, *yv = (vUInt8 *)yc;
142                
143                 for (x = 0; x < vWidth; x++) {
144                         x2 = x*2; x4 = x*4;
145                         // ldl/stl = mark data as least recently used in cache so they will be flushed out
146                         __builtin_prefetch(&yv[x+1], 0, 0); __builtin_prefetch(&yv2[x+1], 0, 0);
147                         __builtin_prefetch(&uv[x+1], 0, 0); __builtin_prefetch(&vv[x+1], 0, 0);
148                         vUInt8 tmp_u = vec_ldl(0, &uv[x]), tmp_v = vec_ldl(0, &vv[x]), chroma = vec_mergeh(tmp_u, tmp_v),
149                                         tmp_y = vec_ldl(0, &yv[x2]), tmp_y2 = vec_ldl(0, &yv2[x2]),
150                                         tmp_y3 = vec_ldl(16, &yv[x2]), tmp_y4 = vec_ldl(16, &yv2[x2]), chromal = vec_mergel(tmp_u, tmp_v);
151                        
152                         vec_stl(vec_mergeh(chroma, tmp_y), 0, &ov[x4]);
153                         vec_stl(vec_mergel(chroma, tmp_y), 16, &ov[x4]);
154                         vec_stl(vec_mergeh(chromal, tmp_y3), 32, &ov[x4]);
155                         vec_stl(vec_mergel(chromal, tmp_y3), 48, &ov[x4]);
156                        
157                         vec_stl(vec_mergeh(chroma, tmp_y2), 0, &ov2[x4]);
158                         vec_stl(vec_mergel(chroma, tmp_y2), 16, &ov2[x4]);
159                         vec_stl(vec_mergeh(chromal, tmp_y4), 32, &ov2[x4]);
160                         vec_stl(vec_mergel(chromal, tmp_y4), 48, &ov2[x4]);
161                 }
162                
163                 if (width % 32) { //spill to scalar for the end if the row isn't a multiple of 32
164                         UInt8 *o2 = o + outRB, *yc2 = yc + rY;
165                         for (x = vWidth * 32, x2 = x*2; x < width; x += 2, x2 += 4) {
166                                 unsigned             hx = x / 2;
167                                 o2[x2] = o[x2] = uc[hx];
168                                 o[x2 + 1] = yc[x];
169                                 o2[x2 + 1] = yc2[x];
170                                 o2[x2 + 2] = o[x2 + 2] = vc[hx];
171                                 o[x2 + 3] = yc[x + 1];
172                                 o2[x2 + 3] = yc2[x + 1];
173                         }                       
174                 }
175                
176                 o += outRB; o += outRB;
177                 yc += rY; yc += rY;
178                 uc += rU;
179                 vc += rV;
180         }
181        
182         HandleLastRow(o, yc, uc, vc, width / 2, height);
183 }
184
185 void Y420toY422(UInt8 * o, unsigned outRB, unsigned width, unsigned height, AVFrame * picture)
186 {
187         static void (*y420_function)(UInt8* baseAddr, unsigned outRB, unsigned width, unsigned height, AVFrame * picture) = NULL;
188         if (!y420_function) {
189                 int sels[2] = { CTL_HW, HW_VECTORUNIT }; // from http://developer.apple.com/hardwaredrivers/ve/g3_compatibility.html
190                 int vType = 0; //0 == scalar only
191                 size_t length = sizeof(vType);
192                 int error = sysctl(sels, 2, &vType, &length, NULL, 0);
193                 if( 0 == error && vType ) y420_function = Y420toY422_ppc_altivec;
194                 else
195                 y420_function = Y420toY422_ppc_scalar;
196         }
197        
198         y420_function(o, outRB, width, height, picture);
199 }
200 #else
201 #include <emmintrin.h>
202
203 static void Y420toY422_sse2(UInt8 *  o, unsigned outRB, unsigned width, unsigned height, AVFrame * picture)
204 {
205         UInt8   *yc = picture->data[0], *uc = picture->data[1], *vc = picture->data[2];
206         unsigned        rY = picture->linesize[0], rU = picture->linesize[1], rV = picture->linesize[2];
207         unsigned        y,x, vWidth = width / 32, halfheight = height / 2;
208         unsigned        halfwidth = width / 2;
209        
210         for (y = 0; y < halfheight; y++) {
211                 UInt8   * o2 = o + outRB,   * yc2 = yc + rY;
212                 __m128i * ov = (__m128i*)o, * ov2 = (__m128i*)o2, * yv = (__m128i*)yc, * yv2 = (__m128i*)yc2;
213                 __m128i * uv = (__m128i*)uc,* vv  = (__m128i*)vc;
214                
215 #if 0
216                 asm volatile(
217                         "\n0:                   \n\t"
218                         "movdqa         (%2),   %%xmm0  \n\t"
219                         "movdqa         16(%2), %%xmm2  \n\t"
220                         "movdqa         (%3),           %%xmm1  \n\t"
221                         "movdqa         16(%3), %%xmm3  \n\t"
222                         "movdqu         (%4),   %%xmm4  \n\t"
223                         "movdqu         (%5),   %%xmm5  \n\t"
224                         "addl           $32,    %2              \n\t"
225                         "addl           $32,    %3              \n\t"
226                         "addl           $16,    %4              \n\t"
227                         "addl           $16,    %5              \n\t"
228                         "movdqa         %%xmm4, %%xmm6  \n\t"
229                         "punpcklbw      %%xmm5, %%xmm4  \n\t" /*chroma_l*/
230                         "punpckhbw      %%xmm5, %%xmm6  \n\t" /*chroma_h*/
231                         "movdqa         %%xmm4, %%xmm5  \n\t"
232                         "punpcklbw      %%xmm0, %%xmm5  \n\t"
233                         "movntdq        %%xmm5, (%0)    \n\t" /*ov[x4]*/
234                         "movdqa         %%xmm4, %%xmm5  \n\t"
235                         "punpckhbw      %%xmm0, %%xmm5  \n\t"
236                         "movntdq        %%xmm5, 16(%0)  \n\t" /*ov[x4+1]*/
237                         "movdqa         %%xmm6, %%xmm5  \n\t"
238                         "punpcklbw      %%xmm2, %%xmm5  \n\t"
239                         "movntdq        %%xmm5, 32(%0)  \n\t" /*ov[x4+2]*/
240                         "movdqa         %%xmm6, %%xmm5  \n\t"
241                         "punpckhbw      %%xmm2, %%xmm5  \n\t"
242                         "movntdq        %%xmm5, 48(%0)  \n\t" /*ov[x4+3]*/
243                         "addl           $64,    %0              \n\t"
244                         "movdqa         %%xmm4, %%xmm5  \n\t"
245                         "punpcklbw      %%xmm1, %%xmm5  \n\t"
246                         "movntdq        %%xmm5, (%1)    \n\t" /*ov2[x4]*/
247                         "punpckhbw      %%xmm1, %%xmm4  \n\t"
248                         "movntdq        %%xmm4, 16(%1)  \n\t" /*ov2[x4+1]*/
249                         "movdqa         %%xmm6, %%xmm5  \n\t"
250                         "punpcklbw      %%xmm3, %%xmm5  \n\t"
251                         "movntdq        %%xmm5, 32(%1)  \n\t" /*ov2[x4+2]*/
252                         "punpckhbw      %%xmm3, %%xmm6  \n\t"
253                         "movntdq        %%xmm6, 48(%1)  \n\t" /*ov2[x4+3]*/
254                         "addl           $64,    %1              \n\t"
255                         "dec            %6                              \n\t"
256                         "jnz            0b                              \n\t"
257                         : "+r" (ov), "+r" (ov2),
258                         "+r" (yv), "+r" (yv2), "+r" (uv), "+r" (vv)
259                         : "r" (vWidth)
260                         );
261 #else
262                 for (x = 0; x < vWidth; x++) {
263                         unsigned x2 = x*2, x4 = x*4;
264
265                         __m128i tmp_y = yv[x2], tmp_y3 = yv[x2+1],
266                                         tmp_y2 = yv2[x2], tmp_y4 = yv2[x2+1],
267                                         tmp_u = _mm_loadu_si128(&uv[x]), tmp_v = _mm_loadu_si128(&vv[x]),
268                                         chroma_l = _mm_unpacklo_epi8(tmp_u, tmp_v),
269                                         chroma_h = _mm_unpackhi_epi8(tmp_u, tmp_v);
270                        
271                         _mm_stream_si128(&ov[x4],   _mm_unpacklo_epi8(chroma_l, tmp_y));
272                         _mm_stream_si128(&ov[x4+1], _mm_unpackhi_epi8(chroma_l, tmp_y));
273                         _mm_stream_si128(&ov[x4+2], _mm_unpacklo_epi8(chroma_h, tmp_y3));
274                         _mm_stream_si128(&ov[x4+3], _mm_unpackhi_epi8(chroma_h, tmp_y3));
275                        
276                         _mm_stream_si128(&ov2[x4],  _mm_unpacklo_epi8(chroma_l, tmp_y2));
277                         _mm_stream_si128(&ov2[x4+1],_mm_unpackhi_epi8(chroma_l, tmp_y2));
278                         _mm_stream_si128(&ov2[x4+2],_mm_unpacklo_epi8(chroma_h, tmp_y4));
279                         _mm_stream_si128(&ov2[x4+3],_mm_unpackhi_epi8(chroma_h, tmp_y4));
280                 }
281 #endif
282
283                 for (x=vWidth * 16; x < halfwidth; x++) {
284                         unsigned x4 = x*4, x2 = x*2;
285                         o2[x4] = o[x4] = uc[x];
286                         o[x4 + 1] = yc[x2];
287                         o2[x4 + 1] = yc2[x2];
288                         o2[x4 + 2] = o[x4 + 2] = vc[x];
289                         o[x4 + 3] = yc[x2 + 1];
290                         o2[x4 + 3] = yc2[x2 + 1];
291                 }                       
292                
293                 o += outRB*2;
294                 yc += rY*2;
295                 uc += rU;
296                 vc += rV;
297         }
298        
299         _mm_sfence();
300        
301         HandleLastRow(o, yc, uc, vc, halfwidth, height);
302 }
303
304
305 static void noinline Y420toY422_x86_scalar(UInt8 * o, unsigned outRB, unsigned width, unsigned height, AVFrame * picture)
306 {
307         UInt8           *yc = picture->data[0], *u = picture->data[1], *v = picture->data[2];
308         unsigned        rY = picture->linesize[0], rU = picture->linesize[1], rV = picture->linesize[2], halfheight = height / 2, halfwidth = width / 2;
309         unsigned        y, x;
310        
311         for (y = 0; y < halfheight; y ++) {
312                 UInt8 *o2 = o + outRB, *yc2 = yc + rY;
313                
314                 for (x = 0; x < halfwidth; x++) {
315                         unsigned x4 = x*4, x2 = x*2;
316                         o2[x4] = o[x4] = u[x];
317                         o[x4 + 1] = yc[x2];
318                         o2[x4 + 1] = yc2[x2];
319                         o2[x4 + 2] = o[x4 + 2] = v[x];
320                         o[x4 + 3] = yc[x2 + 1];
321                         o2[x4 + 3] = yc2[x2 + 1];
322                 }
323                
324                 o += outRB*2;
325                 yc += rY*2;
326                 u += rU;
327                 v += rV;
328         }
329
330         HandleLastRow(o, yc, u, v, halfwidth, height);
331 }
332
333 void Y420toY422(UInt8 * o, unsigned outRB, unsigned width, unsigned height, AVFrame * picture)
334 {
335         uintptr_t yc = (uintptr_t)picture->data[0];
336
337         //make sure the ffmpeg picture buffers are aligned enough, they're only guaranteed to be 8-byte for some reason...
338         if (!unlikely((yc | picture->linesize[0]) % 16)) Y420toY422_sse2(o, outRB, width, height, picture);
339         else Y420toY422_x86_scalar(o, outRB, width, height, picture);
340 }
341 #endif
342
343 void YA420toV408(UInt8* o, unsigned outRB, unsigned width, unsigned height, AVFrame * picture)
344 {
345         UInt8          *yc = picture->data[0], *u = picture->data[1], *v = picture->data[2], *a = picture->data[3];
346         unsigned       rY = picture->linesize[0], rU = picture->linesize[1], rV = picture->linesize[2], rA = picture->linesize[3], y, x;
347        
348         for (y = 0; y < height; y++) {
349                 for (x = 0; x < width; x++) {
350                         o[x*4] = u[x/2];
351                         o[x*4+1] = yc[x];
352                         o[x*4+2] = v[x/2];
353                         o[x*4+3] = a[x];
354                 }
355                
356                 o += outRB;
357                 yc += rY;
358                 a += rA;
359                 if (y & 1) {
360                         u += rU;
361                         v += rV;
362                 }
363         }
364 }
365
366 void BGR24toRGB24(UInt8 *baseAddr, unsigned rowBytes, unsigned width, unsigned height, AVFrame *picture)
367 {
368         unsigned i, j;
369         UInt8 *srcPtr = picture->data[0];
370        
371         for (i = 0; i < height; ++i)
372         {
373                 for (j = 0; j < width; j ++)
374                 {
375                         unsigned j3 = j * 3;
376                         baseAddr[j3] = srcPtr[j3+2];
377                         baseAddr[j3+1] = srcPtr[j3+1];
378                         baseAddr[j3+2] = srcPtr[j3];
379                 }
380                 baseAddr += rowBytes;
381                 srcPtr += picture->linesize[0];
382         }
383 }
384
385 void RGB32toRGB32(UInt8 *baseAddr, unsigned rowBytes, unsigned width, unsigned height, AVFrame *picture)
386 {
387         unsigned y;
388         UInt8 *srcPtr = picture->data[0];
389        
390         for (y = 0; y < height; y++) {
391 #ifdef __BIG_ENDIAN__
392                 memcpy(baseAddr, srcPtr, width * 4);
393 #else
394                 unsigned x;
395                 UInt32 *oRow = (UInt32 *)baseAddr, *iRow = (UInt32 *)srcPtr;
396                 for (x = 0; x < width; x++) {oRow[x] = EndianU32_BtoN(iRow[x]);}
397 #endif
398                
399                 baseAddr += rowBytes;
400                 srcPtr += picture->linesize[0];
401         }
402 }
403
404 void RGB24toRGB24(UInt8 *baseAddr, unsigned rowBytes, unsigned width, unsigned height, AVFrame *picture)
405 {
406         unsigned y;
407         UInt8 *srcPtr = picture->data[0];
408        
409         for (y = 0; y < height; y++) {
410                 memcpy(baseAddr, srcPtr, width * 3);
411                
412                 baseAddr += rowBytes;
413                 srcPtr += picture->linesize[0];
414         }
415 }
416
417 void Y422toY422(UInt8* o, unsigned outRB, unsigned width, unsigned height, AVFrame * picture)
418 {
419         UInt8          *yc = picture->data[0], *u = picture->data[1], *v = picture->data[2];
420         unsigned       rY = picture->linesize[0], rU = picture->linesize[1], rV = picture->linesize[2], y, x, halfwidth = width / 2;
421        
422         for (y = 0; y < height; y++) {
423                 for (x = 0; x < halfwidth; x++) {
424                         unsigned x2 = x * 2, x4 = x * 4;
425                         o[x4] = u[x];
426                         o[x4 + 1] = yc[x2];
427                         o[x4 + 2] = v[x];
428                         o[x4 + 3] = yc[x2 + 1];
429                 }
430                
431                 o += outRB;
432                 yc += rY;
433                 u += rU;
434                 v += rV;
435         }
436 }
Note: See TracBrowser for help on using the browser.