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