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