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 I420 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* tmp_argb = crop_argb;
55  int tmp_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 * abs_crop_height * 4;
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;
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_YU12:
180    case FOURCC_YV12: {
181      const uint8* src_y = sample + (src_width * crop_y + crop_x);
182      const uint8* src_u;
183      const uint8* src_v;
184      int halfwidth = (src_width + 1) / 2;
185      int halfheight = (abs_src_height + 1) / 2;
186      if (format == FOURCC_YV12) {
187        src_v = sample + src_width * abs_src_height +
188            (halfwidth * crop_y + crop_x) / 2;
189        src_u = sample + src_width * abs_src_height +
190            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
191      } else {
192        src_u = sample + src_width * abs_src_height +
193            (halfwidth * crop_y + crop_x) / 2;
194        src_v = sample + src_width * abs_src_height +
195            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
196      }
197      r = I420ToARGB(src_y, src_width,
198                     src_u, halfwidth,
199                     src_v, halfwidth,
200                     crop_argb, argb_stride,
201                     crop_width, inv_crop_height);
202      break;
203    }
204
205    case FOURCC_J420: {
206      const uint8* src_y = sample + (src_width * crop_y + crop_x);
207      const uint8* src_u;
208      const uint8* src_v;
209      int halfwidth = (src_width + 1) / 2;
210      int halfheight = (abs_src_height + 1) / 2;
211      src_u = sample + src_width * abs_src_height +
212          (halfwidth * crop_y + crop_x) / 2;
213      src_v = sample + src_width * abs_src_height +
214          halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
215      r = J420ToARGB(src_y, src_width,
216                     src_u, halfwidth,
217                     src_v, halfwidth,
218                     crop_argb, argb_stride,
219                     crop_width, inv_crop_height);
220      break;
221    }
222
223    case FOURCC_I422:
224    case FOURCC_YV16: {
225      const uint8* src_y = sample + src_width * crop_y + crop_x;
226      const uint8* src_u;
227      const uint8* src_v;
228      int halfwidth = (src_width + 1) / 2;
229      if (format == FOURCC_YV16) {
230        src_v = sample + src_width * abs_src_height +
231            halfwidth * crop_y + crop_x / 2;
232        src_u = sample + src_width * abs_src_height +
233            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
234      } else {
235        src_u = sample + src_width * abs_src_height +
236            halfwidth * crop_y + crop_x / 2;
237        src_v = sample + src_width * abs_src_height +
238            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
239      }
240      r = I422ToARGB(src_y, src_width,
241                     src_u, halfwidth,
242                     src_v, halfwidth,
243                     crop_argb, argb_stride,
244                     crop_width, inv_crop_height);
245      break;
246    }
247    case FOURCC_I444:
248    case FOURCC_YV24: {
249      const uint8* src_y = sample + src_width * crop_y + crop_x;
250      const uint8* src_u;
251      const uint8* src_v;
252      if (format == FOURCC_YV24) {
253        src_v = sample + src_width * (abs_src_height + crop_y) + crop_x;
254        src_u = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
255      } else {
256        src_u = sample + src_width * (abs_src_height + crop_y) + crop_x;
257        src_v = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
258      }
259      r = I444ToARGB(src_y, src_width,
260                     src_u, src_width,
261                     src_v, src_width,
262                     crop_argb, argb_stride,
263                     crop_width, inv_crop_height);
264      break;
265    }
266    case FOURCC_I411: {
267      int quarterwidth = (src_width + 3) / 4;
268      const uint8* src_y = sample + src_width * crop_y + crop_x;
269      const uint8* src_u = sample + src_width * abs_src_height +
270          quarterwidth * crop_y + crop_x / 4;
271      const uint8* src_v = sample + src_width * abs_src_height +
272          quarterwidth * (abs_src_height + crop_y) + crop_x / 4;
273      r = I411ToARGB(src_y, src_width,
274                     src_u, quarterwidth,
275                     src_v, quarterwidth,
276                     crop_argb, argb_stride,
277                     crop_width, inv_crop_height);
278      break;
279    }
280#ifdef HAVE_JPEG
281    case FOURCC_MJPG:
282      r = MJPGToARGB(sample, sample_size,
283                     crop_argb, argb_stride,
284                     src_width, abs_src_height, crop_width, inv_crop_height);
285      break;
286#endif
287    default:
288      r = -1;  // unknown fourcc - return failure code.
289  }
290
291  if (need_buf) {
292    if (!r) {
293      r = ARGBRotate(crop_argb, argb_stride,
294                     tmp_argb, tmp_argb_stride,
295                     crop_width, abs_crop_height, rotation);
296    }
297    free(rotate_buffer);
298  }
299
300  return r;
301}
302
303#ifdef __cplusplus
304}  // extern "C"
305}  // namespace libyuv
306#endif
307