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