1/*
2 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "libyuv/format_conversion.h"
12
13#include "libyuv/basic_types.h"
14#include "libyuv/cpu_id.h"
15#include "libyuv/video_common.h"
16#include "libyuv/row.h"
17
18#ifdef __cplusplus
19namespace libyuv {
20extern "C" {
21#endif
22
23// Note: to do this with Neon vld4.8 would load ARGB values into 4 registers
24// and vst would select which 2 components to write. The low level would need
25// to be ARGBToBG, ARGBToGB, ARGBToRG, ARGBToGR
26
27#if !defined(YUV_DISABLE_ASM) && defined(_M_IX86)
28#define HAS_ARGBTOBAYERROW_SSSE3
29__declspec(naked) __declspec(align(16))
30static void ARGBToBayerRow_SSSE3(const uint8* src_argb,
31                                 uint8* dst_bayer, uint32 selector, int pix) {
32  __asm {
33    mov        eax, [esp + 4]    // src_argb
34    mov        edx, [esp + 8]    // dst_bayer
35    movd       xmm5, [esp + 12]  // selector
36    mov        ecx, [esp + 16]   // pix
37    pshufd     xmm5, xmm5, 0
38
39    align      16
40  wloop:
41    movdqa     xmm0, [eax]
42    lea        eax, [eax + 16]
43    pshufb     xmm0, xmm5
44    sub        ecx, 4
45    movd       [edx], xmm0
46    lea        edx, [edx + 4]
47    jg         wloop
48    ret
49  }
50}
51
52#elif !defined(YUV_DISABLE_ASM) && (defined(__x86_64__) || defined(__i386__))
53
54#define HAS_ARGBTOBAYERROW_SSSE3
55static void ARGBToBayerRow_SSSE3(const uint8* src_argb, uint8* dst_bayer,
56                                 uint32 selector, int pix) {
57  asm volatile (
58    "movd   %3,%%xmm5                          \n"
59    "pshufd $0x0,%%xmm5,%%xmm5                 \n"
60    ".p2align  4                               \n"
61"1:                                            \n"
62    "movdqa (%0),%%xmm0                        \n"
63    "lea    0x10(%0),%0                        \n"
64    "pshufb %%xmm5,%%xmm0                      \n"
65    "sub    $0x4,%2                            \n"
66    "movd   %%xmm0,(%1)                        \n"
67    "lea    0x4(%1),%1                         \n"
68    "jg     1b                                 \n"
69  : "+r"(src_argb),  // %0
70    "+r"(dst_bayer), // %1
71    "+r"(pix)        // %2
72  : "g"(selector)    // %3
73  : "memory", "cc"
74#if defined(__SSE2__)
75    , "xmm0", "xmm5"
76#endif
77
78);
79}
80#endif
81
82static void ARGBToBayerRow_C(const uint8* src_argb,
83                             uint8* dst_bayer, uint32 selector, int pix) {
84  int index0 = selector & 0xff;
85  int index1 = (selector >> 8) & 0xff;
86  // Copy a row of Bayer.
87  for (int x = 0; x < pix - 1; x += 2) {
88    dst_bayer[0] = src_argb[index0];
89    dst_bayer[1] = src_argb[index1];
90    src_argb += 8;
91    dst_bayer += 2;
92  }
93  if (pix & 1) {
94    dst_bayer[0] = src_argb[index0];
95  }
96}
97
98// generate a selector mask useful for pshufb
99static uint32 GenerateSelector(int select0, int select1) {
100  return static_cast<uint32>(select0) |
101         static_cast<uint32>((select1 + 4) << 8) |
102         static_cast<uint32>((select0 + 8) << 16) |
103         static_cast<uint32>((select1 + 12) << 24);
104}
105
106static int MakeSelectors(const int blue_index,
107                         const int green_index,
108                         const int red_index,
109                         uint32 dst_fourcc_bayer,
110                         uint32 *index_map) {
111  // Now build a lookup table containing the indices for the four pixels in each
112  // 2x2 Bayer grid.
113  switch (dst_fourcc_bayer) {
114    case FOURCC_BGGR:
115      index_map[0] = GenerateSelector(blue_index, green_index);
116      index_map[1] = GenerateSelector(green_index, red_index);
117      break;
118    case FOURCC_GBRG:
119      index_map[0] = GenerateSelector(green_index, blue_index);
120      index_map[1] = GenerateSelector(red_index, green_index);
121      break;
122    case FOURCC_RGGB:
123      index_map[0] = GenerateSelector(red_index, green_index);
124      index_map[1] = GenerateSelector(green_index, blue_index);
125      break;
126    case FOURCC_GRBG:
127      index_map[0] = GenerateSelector(green_index, red_index);
128      index_map[1] = GenerateSelector(blue_index, green_index);
129      break;
130    default:
131      return -1;  // Bad FourCC
132  }
133  return 0;
134}
135
136// Converts 32 bit ARGB to Bayer RGB formats.
137LIBYUV_API
138int ARGBToBayer(const uint8* src_argb, int src_stride_argb,
139                uint8* dst_bayer, int dst_stride_bayer,
140                int width, int height,
141                uint32 dst_fourcc_bayer) {
142  if (height < 0) {
143    height = -height;
144    src_argb = src_argb + (height - 1) * src_stride_argb;
145    src_stride_argb = -src_stride_argb;
146  }
147  void (*ARGBToBayerRow)(const uint8* src_argb, uint8* dst_bayer,
148                         uint32 selector, int pix) = ARGBToBayerRow_C;
149#if defined(HAS_ARGBTOBAYERROW_SSSE3)
150  if (TestCpuFlag(kCpuHasSSSE3) &&
151      IS_ALIGNED(width, 4) &&
152      IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
153    ARGBToBayerRow = ARGBToBayerRow_SSSE3;
154  }
155#endif
156  const int blue_index = 0;  // Offsets for ARGB format
157  const int green_index = 1;
158  const int red_index = 2;
159  uint32 index_map[2];
160  if (MakeSelectors(blue_index, green_index, red_index,
161                    dst_fourcc_bayer, index_map)) {
162    return -1;  // Bad FourCC
163  }
164
165  for (int y = 0; y < height; ++y) {
166    ARGBToBayerRow(src_argb, dst_bayer, index_map[y & 1], width);
167    src_argb += src_stride_argb;
168    dst_bayer += dst_stride_bayer;
169  }
170  return 0;
171}
172
173#define AVG(a, b) (((a) + (b)) >> 1)
174
175static void BayerRowBG(const uint8* src_bayer0, int src_stride_bayer,
176                       uint8* dst_argb, int pix) {
177  const uint8* src_bayer1 = src_bayer0 + src_stride_bayer;
178  uint8 g = src_bayer0[1];
179  uint8 r = src_bayer1[1];
180  for (int x = 0; x < pix - 2; x += 2) {
181    dst_argb[0] = src_bayer0[0];
182    dst_argb[1] = AVG(g, src_bayer0[1]);
183    dst_argb[2] = AVG(r, src_bayer1[1]);
184    dst_argb[3] = 255U;
185    dst_argb[4] = AVG(src_bayer0[0], src_bayer0[2]);
186    dst_argb[5] = src_bayer0[1];
187    dst_argb[6] = src_bayer1[1];
188    dst_argb[7] = 255U;
189    g = src_bayer0[1];
190    r = src_bayer1[1];
191    src_bayer0 += 2;
192    src_bayer1 += 2;
193    dst_argb += 8;
194  }
195  dst_argb[0] = src_bayer0[0];
196  dst_argb[1] = AVG(g, src_bayer0[1]);
197  dst_argb[2] = AVG(r, src_bayer1[1]);
198  dst_argb[3] = 255U;
199  if (!(pix & 1)) {
200    dst_argb[4] = src_bayer0[0];
201    dst_argb[5] = src_bayer0[1];
202    dst_argb[6] = src_bayer1[1];
203    dst_argb[7] = 255U;
204  }
205}
206
207static void BayerRowRG(const uint8* src_bayer0, int src_stride_bayer,
208                       uint8* dst_argb, int pix) {
209  const uint8* src_bayer1 = src_bayer0 + src_stride_bayer;
210  uint8 g = src_bayer0[1];
211  uint8 b = src_bayer1[1];
212  for (int x = 0; x < pix - 2; x += 2) {
213    dst_argb[0] = AVG(b, src_bayer1[1]);
214    dst_argb[1] = AVG(g, src_bayer0[1]);
215    dst_argb[2] = src_bayer0[0];
216    dst_argb[3] = 255U;
217    dst_argb[4] = src_bayer1[1];
218    dst_argb[5] = src_bayer0[1];
219    dst_argb[6] = AVG(src_bayer0[0], src_bayer0[2]);
220    dst_argb[7] = 255U;
221    g = src_bayer0[1];
222    b = src_bayer1[1];
223    src_bayer0 += 2;
224    src_bayer1 += 2;
225    dst_argb += 8;
226  }
227  dst_argb[0] = AVG(b, src_bayer1[1]);
228  dst_argb[1] = AVG(g, src_bayer0[1]);
229  dst_argb[2] = src_bayer0[0];
230  dst_argb[3] = 255U;
231  if (!(pix & 1)) {
232    dst_argb[4] = src_bayer1[1];
233    dst_argb[5] = src_bayer0[1];
234    dst_argb[6] = src_bayer0[0];
235    dst_argb[7] = 255U;
236  }
237}
238
239static void BayerRowGB(const uint8* src_bayer0, int src_stride_bayer,
240                       uint8* dst_argb, int pix) {
241  const uint8* src_bayer1 = src_bayer0 + src_stride_bayer;
242  uint8 b = src_bayer0[1];
243  for (int x = 0; x < pix - 2; x += 2) {
244    dst_argb[0] = AVG(b, src_bayer0[1]);
245    dst_argb[1] = src_bayer0[0];
246    dst_argb[2] = src_bayer1[0];
247    dst_argb[3] = 255U;
248    dst_argb[4] = src_bayer0[1];
249    dst_argb[5] = AVG(src_bayer0[0], src_bayer0[2]);
250    dst_argb[6] = AVG(src_bayer1[0], src_bayer1[2]);
251    dst_argb[7] = 255U;
252    b = src_bayer0[1];
253    src_bayer0 += 2;
254    src_bayer1 += 2;
255    dst_argb += 8;
256  }
257  dst_argb[0] = AVG(b, src_bayer0[1]);
258  dst_argb[1] = src_bayer0[0];
259  dst_argb[2] = src_bayer1[0];
260  dst_argb[3] = 255U;
261  if (!(pix & 1)) {
262    dst_argb[4] = src_bayer0[1];
263    dst_argb[5] = src_bayer0[0];
264    dst_argb[6] = src_bayer1[0];
265    dst_argb[7] = 255U;
266  }
267}
268
269static void BayerRowGR(const uint8* src_bayer0, int src_stride_bayer,
270                       uint8* dst_argb, int pix) {
271  const uint8* src_bayer1 = src_bayer0 + src_stride_bayer;
272  uint8 r = src_bayer0[1];
273  for (int x = 0; x < pix - 2; x += 2) {
274    dst_argb[0] = src_bayer1[0];
275    dst_argb[1] = src_bayer0[0];
276    dst_argb[2] = AVG(r, src_bayer0[1]);
277    dst_argb[3] = 255U;
278    dst_argb[4] = AVG(src_bayer1[0], src_bayer1[2]);
279    dst_argb[5] = AVG(src_bayer0[0], src_bayer0[2]);
280    dst_argb[6] = src_bayer0[1];
281    dst_argb[7] = 255U;
282    r = src_bayer0[1];
283    src_bayer0 += 2;
284    src_bayer1 += 2;
285    dst_argb += 8;
286  }
287  dst_argb[0] = src_bayer1[0];
288  dst_argb[1] = src_bayer0[0];
289  dst_argb[2] = AVG(r, src_bayer0[1]);
290  dst_argb[3] = 255U;
291  if (!(pix & 1)) {
292    dst_argb[4] = src_bayer1[0];
293    dst_argb[5] = src_bayer0[0];
294    dst_argb[6] = src_bayer0[1];
295    dst_argb[7] = 255U;
296  }
297}
298
299// Converts any Bayer RGB format to ARGB.
300LIBYUV_API
301int BayerToARGB(const uint8* src_bayer, int src_stride_bayer,
302                uint8* dst_argb, int dst_stride_argb,
303                int width, int height,
304                uint32 src_fourcc_bayer) {
305  if (height < 0) {
306    height = -height;
307    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
308    dst_stride_argb = -dst_stride_argb;
309  }
310  void (*BayerRow0)(const uint8* src_bayer, int src_stride_bayer,
311                    uint8* dst_argb, int pix);
312  void (*BayerRow1)(const uint8* src_bayer, int src_stride_bayer,
313                    uint8* dst_argb, int pix);
314  switch (src_fourcc_bayer) {
315    case FOURCC_BGGR:
316      BayerRow0 = BayerRowBG;
317      BayerRow1 = BayerRowGR;
318      break;
319    case FOURCC_GBRG:
320      BayerRow0 = BayerRowGB;
321      BayerRow1 = BayerRowRG;
322      break;
323    case FOURCC_GRBG:
324      BayerRow0 = BayerRowGR;
325      BayerRow1 = BayerRowBG;
326      break;
327    case FOURCC_RGGB:
328      BayerRow0 = BayerRowRG;
329      BayerRow1 = BayerRowGB;
330      break;
331    default:
332      return -1;    // Bad FourCC
333  }
334
335  for (int y = 0; y < height - 1; y += 2) {
336    BayerRow0(src_bayer, src_stride_bayer, dst_argb, width);
337    BayerRow1(src_bayer + src_stride_bayer, -src_stride_bayer,
338              dst_argb + dst_stride_argb, width);
339    src_bayer += src_stride_bayer * 2;
340    dst_argb += dst_stride_argb * 2;
341  }
342  if (height & 1) {
343    BayerRow0(src_bayer, -src_stride_bayer, dst_argb, width);
344  }
345  return 0;
346}
347
348// Converts any Bayer RGB format to ARGB.
349LIBYUV_API
350int BayerToI420(const uint8* src_bayer, int src_stride_bayer,
351                uint8* dst_y, int dst_stride_y,
352                uint8* dst_u, int dst_stride_u,
353                uint8* dst_v, int dst_stride_v,
354                int width, int height,
355                uint32 src_fourcc_bayer) {
356  if (width * 4 > kMaxStride) {
357    return -1;  // Size too large for row buffer
358  }
359  // Negative height means invert the image.
360  if (height < 0) {
361    height = -height;
362    int halfheight = (height + 1) >> 1;
363    dst_y = dst_y + (height - 1) * dst_stride_y;
364    dst_u = dst_u + (halfheight - 1) * dst_stride_u;
365    dst_v = dst_v + (halfheight - 1) * dst_stride_v;
366    dst_stride_y = -dst_stride_y;
367    dst_stride_u = -dst_stride_u;
368    dst_stride_v = -dst_stride_v;
369  }
370  void (*BayerRow0)(const uint8* src_bayer, int src_stride_bayer,
371                    uint8* dst_argb, int pix);
372  void (*BayerRow1)(const uint8* src_bayer, int src_stride_bayer,
373                    uint8* dst_argb, int pix);
374  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
375      ARGBToYRow_C;
376  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
377                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
378  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
379
380#if defined(HAS_ARGBTOYROW_SSSE3)
381  if (TestCpuFlag(kCpuHasSSSE3) &&
382      IS_ALIGNED(width, 16) &&
383      IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
384    ARGBToYRow = ARGBToYRow_SSSE3;
385  }
386#endif
387#if defined(HAS_ARGBTOUVROW_SSSE3)
388  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) {
389    ARGBToUVRow = ARGBToUVRow_SSSE3;
390  }
391#endif
392
393  switch (src_fourcc_bayer) {
394    case FOURCC_BGGR:
395      BayerRow0 = BayerRowBG;
396      BayerRow1 = BayerRowGR;
397      break;
398    case FOURCC_GBRG:
399      BayerRow0 = BayerRowGB;
400      BayerRow1 = BayerRowRG;
401      break;
402    case FOURCC_GRBG:
403      BayerRow0 = BayerRowGR;
404      BayerRow1 = BayerRowBG;
405      break;
406    case FOURCC_RGGB:
407      BayerRow0 = BayerRowRG;
408      BayerRow1 = BayerRowGB;
409      break;
410    default:
411      return -1;  // Bad FourCC
412  }
413
414  for (int y = 0; y < height - 1; y += 2) {
415    BayerRow0(src_bayer, src_stride_bayer, row, width);
416    BayerRow1(src_bayer + src_stride_bayer, -src_stride_bayer,
417              row + kMaxStride, width);
418    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
419    ARGBToYRow(row, dst_y, width);
420    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
421    src_bayer += src_stride_bayer * 2;
422    dst_y += dst_stride_y * 2;
423    dst_u += dst_stride_u;
424    dst_v += dst_stride_v;
425  }
426  if (height & 1) {
427    BayerRow0(src_bayer, src_stride_bayer, row, width);
428    ARGBToUVRow(row, 0, dst_u, dst_v, width);
429    ARGBToYRow(row, dst_y, width);
430  }
431  return 0;
432}
433
434// Convert I420 to Bayer.
435LIBYUV_API
436int I420ToBayer(const uint8* src_y, int src_stride_y,
437                const uint8* src_u, int src_stride_u,
438                const uint8* src_v, int src_stride_v,
439                uint8* dst_bayer, int dst_stride_bayer,
440                int width, int height,
441                uint32 dst_fourcc_bayer) {
442  // Negative height means invert the image.
443  if (height < 0) {
444    height = -height;
445    int halfheight = (height + 1) >> 1;
446    src_y = src_y + (height - 1) * src_stride_y;
447    src_u = src_u + (halfheight - 1) * src_stride_u;
448    src_v = src_v + (halfheight - 1) * src_stride_v;
449    src_stride_y = -src_stride_y;
450    src_stride_u = -src_stride_u;
451    src_stride_v = -src_stride_v;
452  }
453  void (*I422ToARGBRow)(const uint8* y_buf,
454                        const uint8* u_buf,
455                        const uint8* v_buf,
456                        uint8* rgb_buf,
457                        int width) = I422ToARGBRow_C;
458#if defined(HAS_I422TOARGBROW_NEON)
459  if (TestCpuFlag(kCpuHasNEON)) {
460    I422ToARGBRow = I422ToARGBRow_NEON;
461  }
462#elif defined(HAS_I422TOARGBROW_SSSE3)
463  if (TestCpuFlag(kCpuHasSSSE3)) {
464    I422ToARGBRow = I422ToARGBRow_SSSE3;
465  }
466#endif
467  SIMD_ALIGNED(uint8 row[kMaxStride]);
468  void (*ARGBToBayerRow)(const uint8* src_argb, uint8* dst_bayer,
469                         uint32 selector, int pix) = ARGBToBayerRow_C;
470#if defined(HAS_ARGBTOBAYERROW_SSSE3)
471  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4)) {
472    ARGBToBayerRow = ARGBToBayerRow_SSSE3;
473  }
474#endif
475  const int blue_index = 0;  // Offsets for ARGB format
476  const int green_index = 1;
477  const int red_index = 2;
478  uint32 index_map[2];
479  if (MakeSelectors(blue_index, green_index, red_index,
480                    dst_fourcc_bayer, index_map)) {
481    return -1;  // Bad FourCC
482  }
483
484  for (int y = 0; y < height; ++y) {
485    I422ToARGBRow(src_y, src_u, src_v, row, width);
486    ARGBToBayerRow(row, dst_bayer, index_map[y & 1], width);
487    dst_bayer += dst_stride_bayer;
488    src_y += src_stride_y;
489    if (y & 1) {
490      src_u += src_stride_u;
491      src_v += src_stride_v;
492    }
493  }
494  return 0;
495}
496
497#define MAKEBAYERFOURCC(BAYER)                                                 \
498LIBYUV_API                                                                     \
499int Bayer##BAYER##ToI420(const uint8* src_bayer, int src_stride_bayer,         \
500                         uint8* dst_y, int dst_stride_y,                       \
501                         uint8* dst_u, int dst_stride_u,                       \
502                         uint8* dst_v, int dst_stride_v,                       \
503                         int width, int height) {                              \
504  return BayerToI420(src_bayer, src_stride_bayer,                              \
505                     dst_y, dst_stride_y,                                      \
506                     dst_u, dst_stride_u,                                      \
507                     dst_v, dst_stride_v,                                      \
508                     width, height,                                            \
509                     FOURCC_##BAYER);                                          \
510}                                                                              \
511                                                                               \
512LIBYUV_API                                                                     \
513int I420ToBayer##BAYER(const uint8* src_y, int src_stride_y,                   \
514                       const uint8* src_u, int src_stride_u,                   \
515                       const uint8* src_v, int src_stride_v,                   \
516                       uint8* dst_bayer, int dst_stride_bayer,                 \
517                       int width, int height) {                                \
518  return I420ToBayer(src_y, src_stride_y,                                      \
519                     src_u, src_stride_u,                                      \
520                     src_v, src_stride_v,                                      \
521                     dst_bayer, dst_stride_bayer,                              \
522                     width, height,                                            \
523                     FOURCC_##BAYER);                                          \
524}                                                                              \
525                                                                               \
526LIBYUV_API                                                                     \
527int ARGBToBayer##BAYER(const uint8* src_argb, int src_stride_argb,             \
528                       uint8* dst_bayer, int dst_stride_bayer,                 \
529                       int width, int height) {                                \
530  return ARGBToBayer(src_argb, src_stride_argb,                                \
531                     dst_bayer, dst_stride_bayer,                              \
532                     width, height,                                            \
533                     FOURCC_##BAYER);                                          \
534}                                                                              \
535                                                                               \
536LIBYUV_API                                                                     \
537int Bayer##BAYER##ToARGB(const uint8* src_bayer, int src_stride_bayer,         \
538                         uint8* dst_argb, int dst_stride_argb,                 \
539                         int width, int height) {                              \
540  return BayerToARGB(src_bayer, src_stride_bayer,                              \
541                     dst_argb, dst_stride_argb,                                \
542                     width, height,                                            \
543                     FOURCC_##BAYER);                                          \
544}
545
546MAKEBAYERFOURCC(BGGR)
547MAKEBAYERFOURCC(GBRG)
548MAKEBAYERFOURCC(GRBG)
549MAKEBAYERFOURCC(RGGB)
550
551#ifdef __cplusplus
552}  // extern "C"
553}  // namespace libyuv
554#endif
555