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/convert_argb.h"
12
13#include "libyuv/cpu_id.h"
14#include "libyuv/format_conversion.h"
15#ifdef HAVE_JPEG
16#include "libyuv/mjpeg_decoder.h"
17#endif
18#include "libyuv/rotate_argb.h"
19#include "libyuv/row.h"
20#include "libyuv/video_common.h"
21
22#ifdef __cplusplus
23namespace libyuv {
24extern "C" {
25#endif
26
27// Convert camera sample to I420 with cropping, rotation and vertical flip.
28// src_width is used for source stride computation
29// src_height is used to compute location of planes, and indicate inversion
30// sample_size is measured in bytes and is the size of the frame.
31//   With MJPEG it is the compressed size of the frame.
32LIBYUV_API
33int ConvertToARGB(const uint8* sample, size_t sample_size,
34                  uint8* crop_argb, int argb_stride,
35                  int crop_x, int crop_y,
36                  int src_width, int src_height,
37                  int crop_width, int crop_height,
38                  enum RotationMode rotation,
39                  uint32 fourcc) {
40  uint32 format = CanonicalFourCC(fourcc);
41  int aligned_src_width = (src_width + 1) & ~1;
42  const uint8* src;
43  const uint8* src_uv;
44  int abs_src_height = (src_height < 0) ? -src_height : src_height;
45  int inv_crop_height = (crop_height < 0) ? -crop_height : crop_height;
46  int r = 0;
47
48  // One pass rotation is available for some formats. For the rest, convert
49  // to I420 (with optional vertical flipping) into a temporary I420 buffer,
50  // and then rotate the I420 to the final destination buffer.
51  // For in-place conversion, if destination crop_argb is same as source sample,
52  // also enable temporary buffer.
53  LIBYUV_BOOL need_buf = (rotation && format != FOURCC_ARGB) ||
54      crop_argb == sample;
55  uint8* tmp_argb = crop_argb;
56  int tmp_argb_stride = argb_stride;
57  uint8* rotate_buffer = NULL;
58  int abs_crop_height = (crop_height < 0) ? -crop_height : crop_height;
59
60  if (crop_argb == NULL || sample == NULL ||
61      src_width <= 0 || crop_width <= 0 ||
62      src_height == 0 || crop_height == 0) {
63    return -1;
64  }
65  if (src_height < 0) {
66    inv_crop_height = -inv_crop_height;
67  }
68
69  if (need_buf) {
70    int argb_size = crop_width * abs_crop_height * 4;
71    rotate_buffer = (uint8*)malloc(argb_size);
72    if (!rotate_buffer) {
73      return 1;  // Out of memory runtime error.
74    }
75    crop_argb = rotate_buffer;
76    argb_stride = crop_width;
77  }
78
79  switch (format) {
80    // Single plane formats
81    case FOURCC_YUY2:
82      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
83      r = YUY2ToARGB(src, aligned_src_width * 2,
84                     crop_argb, argb_stride,
85                     crop_width, inv_crop_height);
86      break;
87    case FOURCC_UYVY:
88      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
89      r = UYVYToARGB(src, aligned_src_width * 2,
90                     crop_argb, argb_stride,
91                     crop_width, inv_crop_height);
92      break;
93    case FOURCC_24BG:
94      src = sample + (src_width * crop_y + crop_x) * 3;
95      r = RGB24ToARGB(src, src_width * 3,
96                      crop_argb, argb_stride,
97                      crop_width, inv_crop_height);
98      break;
99    case FOURCC_RAW:
100      src = sample + (src_width * crop_y + crop_x) * 3;
101      r = RAWToARGB(src, src_width * 3,
102                    crop_argb, argb_stride,
103                    crop_width, inv_crop_height);
104      break;
105    case FOURCC_ARGB:
106      src = sample + (src_width * crop_y + crop_x) * 4;
107      r = ARGBToARGB(src, src_width * 4,
108                     crop_argb, argb_stride,
109                     crop_width, inv_crop_height);
110      break;
111    case FOURCC_BGRA:
112      src = sample + (src_width * crop_y + crop_x) * 4;
113      r = BGRAToARGB(src, src_width * 4,
114                     crop_argb, argb_stride,
115                     crop_width, inv_crop_height);
116      break;
117    case FOURCC_ABGR:
118      src = sample + (src_width * crop_y + crop_x) * 4;
119      r = ABGRToARGB(src, src_width * 4,
120                     crop_argb, argb_stride,
121                     crop_width, inv_crop_height);
122      break;
123    case FOURCC_RGBA:
124      src = sample + (src_width * crop_y + crop_x) * 4;
125      r = RGBAToARGB(src, src_width * 4,
126                     crop_argb, argb_stride,
127                     crop_width, inv_crop_height);
128      break;
129    case FOURCC_RGBP:
130      src = sample + (src_width * crop_y + crop_x) * 2;
131      r = RGB565ToARGB(src, src_width * 2,
132                       crop_argb, argb_stride,
133                       crop_width, inv_crop_height);
134      break;
135    case FOURCC_RGBO:
136      src = sample + (src_width * crop_y + crop_x) * 2;
137      r = ARGB1555ToARGB(src, src_width * 2,
138                         crop_argb, argb_stride,
139                         crop_width, inv_crop_height);
140      break;
141    case FOURCC_R444:
142      src = sample + (src_width * crop_y + crop_x) * 2;
143      r = ARGB4444ToARGB(src, src_width * 2,
144                         crop_argb, argb_stride,
145                         crop_width, inv_crop_height);
146      break;
147    // TODO(fbarchard): Support cropping Bayer by odd numbers
148    // by adjusting fourcc.
149    case FOURCC_BGGR:
150      src = sample + (src_width * crop_y + crop_x);
151      r = BayerBGGRToARGB(src, src_width,
152                          crop_argb, argb_stride,
153                          crop_width, inv_crop_height);
154      break;
155
156    case FOURCC_GBRG:
157      src = sample + (src_width * crop_y + crop_x);
158      r = BayerGBRGToARGB(src, src_width,
159                          crop_argb, argb_stride,
160                          crop_width, inv_crop_height);
161      break;
162
163    case FOURCC_GRBG:
164      src = sample + (src_width * crop_y + crop_x);
165      r = BayerGRBGToARGB(src, src_width,
166                          crop_argb, argb_stride,
167                          crop_width, inv_crop_height);
168      break;
169
170    case FOURCC_RGGB:
171      src = sample + (src_width * crop_y + crop_x);
172      r = BayerRGGBToARGB(src, src_width,
173                          crop_argb, argb_stride,
174                          crop_width, inv_crop_height);
175      break;
176
177    case FOURCC_I400:
178      src = sample + src_width * crop_y + crop_x;
179      r = I400ToARGB(src, src_width,
180                     crop_argb, argb_stride,
181                     crop_width, inv_crop_height);
182      break;
183
184    // Biplanar formats
185    case FOURCC_NV12:
186      src = sample + (src_width * crop_y + crop_x);
187      src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x;
188      r = NV12ToARGB(src, src_width,
189                     src_uv, aligned_src_width,
190                     crop_argb, argb_stride,
191                     crop_width, inv_crop_height);
192      break;
193    case FOURCC_NV21:
194      src = sample + (src_width * crop_y + crop_x);
195      src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x;
196      // Call NV12 but with u and v parameters swapped.
197      r = NV21ToARGB(src, src_width,
198                     src_uv, aligned_src_width,
199                     crop_argb, argb_stride,
200                     crop_width, inv_crop_height);
201      break;
202    case FOURCC_M420:
203      src = sample + (src_width * crop_y) * 12 / 8 + crop_x;
204      r = M420ToARGB(src, src_width,
205                     crop_argb, argb_stride,
206                     crop_width, inv_crop_height);
207      break;
208//    case FOURCC_Q420:
209//      src = sample + (src_width + aligned_src_width * 2) * crop_y + crop_x;
210//      src_uv = sample + (src_width + aligned_src_width * 2) * crop_y +
211//               src_width + crop_x * 2;
212//      r = Q420ToARGB(src, src_width * 3,
213//                    src_uv, src_width * 3,
214//                    crop_argb, argb_stride,
215//                    crop_width, inv_crop_height);
216//      break;
217    // Triplanar formats
218    case FOURCC_I420:
219    case FOURCC_YU12:
220    case FOURCC_YV12: {
221      const uint8* src_y = sample + (src_width * crop_y + crop_x);
222      const uint8* src_u;
223      const uint8* src_v;
224      int halfwidth = (src_width + 1) / 2;
225      int halfheight = (abs_src_height + 1) / 2;
226      if (format == FOURCC_YV12) {
227        src_v = sample + src_width * abs_src_height +
228            (halfwidth * crop_y + crop_x) / 2;
229        src_u = sample + src_width * abs_src_height +
230            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
231      } else {
232        src_u = sample + src_width * abs_src_height +
233            (halfwidth * crop_y + crop_x) / 2;
234        src_v = sample + src_width * abs_src_height +
235            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
236      }
237      r = I420ToARGB(src_y, src_width,
238                     src_u, halfwidth,
239                     src_v, halfwidth,
240                     crop_argb, argb_stride,
241                     crop_width, inv_crop_height);
242      break;
243    }
244    case FOURCC_I422:
245    case FOURCC_YV16: {
246      const uint8* src_y = sample + src_width * crop_y + crop_x;
247      const uint8* src_u;
248      const uint8* src_v;
249      int halfwidth = (src_width + 1) / 2;
250      if (format == FOURCC_YV16) {
251        src_v = sample + src_width * abs_src_height +
252            halfwidth * crop_y + crop_x / 2;
253        src_u = sample + src_width * abs_src_height +
254            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
255      } else {
256        src_u = sample + src_width * abs_src_height +
257            halfwidth * crop_y + crop_x / 2;
258        src_v = sample + src_width * abs_src_height +
259            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
260      }
261      r = I422ToARGB(src_y, src_width,
262                     src_u, halfwidth,
263                     src_v, halfwidth,
264                     crop_argb, argb_stride,
265                     crop_width, inv_crop_height);
266      break;
267    }
268    case FOURCC_I444:
269    case FOURCC_YV24: {
270      const uint8* src_y = sample + src_width * crop_y + crop_x;
271      const uint8* src_u;
272      const uint8* src_v;
273      if (format == FOURCC_YV24) {
274        src_v = sample + src_width * (abs_src_height + crop_y) + crop_x;
275        src_u = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
276      } else {
277        src_u = sample + src_width * (abs_src_height + crop_y) + crop_x;
278        src_v = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
279      }
280      r = I444ToARGB(src_y, src_width,
281                     src_u, src_width,
282                     src_v, src_width,
283                     crop_argb, argb_stride,
284                     crop_width, inv_crop_height);
285      break;
286    }
287    case FOURCC_I411: {
288      int quarterwidth = (src_width + 3) / 4;
289      const uint8* src_y = sample + src_width * crop_y + crop_x;
290      const uint8* src_u = sample + src_width * abs_src_height +
291          quarterwidth * crop_y + crop_x / 4;
292      const uint8* src_v = sample + src_width * abs_src_height +
293          quarterwidth * (abs_src_height + crop_y) + crop_x / 4;
294      r = I411ToARGB(src_y, src_width,
295                     src_u, quarterwidth,
296                     src_v, quarterwidth,
297                     crop_argb, argb_stride,
298                     crop_width, inv_crop_height);
299      break;
300    }
301#ifdef HAVE_JPEG
302    case FOURCC_MJPG:
303      r = MJPGToARGB(sample, sample_size,
304                     crop_argb, argb_stride,
305                     src_width, abs_src_height, crop_width, inv_crop_height);
306      break;
307#endif
308    default:
309      r = -1;  // unknown fourcc - return failure code.
310  }
311
312  if (need_buf) {
313    if (!r) {
314      r = ARGBRotate(crop_argb, argb_stride,
315                     tmp_argb, tmp_argb_stride,
316                     crop_width, abs_crop_height, rotation);
317    }
318    free(rotate_buffer);
319  }
320
321  return r;
322}
323
324#ifdef __cplusplus
325}  // extern "C"
326}  // namespace libyuv
327#endif
328