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,
31                  int y_stride,
32                  uint8* u,
33                  int u_stride,
34                  uint8* v,
35                  int v_stride,
36                  int crop_x,
37                  int crop_y,
38                  int src_width,
39                  int src_height,
40                  int crop_width,
41                  int crop_height,
42                  enum RotationMode rotation,
43                  uint32 fourcc) {
44  uint32 format = CanonicalFourCC(fourcc);
45  int aligned_src_width = (src_width + 1) & ~1;
46  const uint8* src;
47  const uint8* src_uv;
48  const int abs_src_height = (src_height < 0) ? -src_height : src_height;
49  // TODO(nisse): Why allow crop_height < 0?
50  const int abs_crop_height = (crop_height < 0) ? -crop_height : crop_height;
51  int r = 0;
52  LIBYUV_BOOL need_buf =
53      (rotation && format != FOURCC_I420 && format != FOURCC_NV12 &&
54       format != FOURCC_NV21 && format != FOURCC_YV12) ||
55      y == sample;
56  uint8* tmp_y = y;
57  uint8* tmp_u = u;
58  uint8* tmp_v = v;
59  int tmp_y_stride = y_stride;
60  int tmp_u_stride = u_stride;
61  int tmp_v_stride = v_stride;
62  uint8* rotate_buffer = NULL;
63  const int inv_crop_height =
64      (src_height < 0) ? -abs_crop_height : abs_crop_height;
65
66  if (!y || !u || !v || !sample || src_width <= 0 || crop_width <= 0 ||
67      src_height == 0 || crop_height == 0) {
68    return -1;
69  }
70
71  // One pass rotation is available for some formats. For the rest, convert
72  // to I420 (with optional vertical flipping) into a temporary I420 buffer,
73  // and then rotate the I420 to the final destination buffer.
74  // For in-place conversion, if destination y is same as source sample,
75  // also enable temporary buffer.
76  if (need_buf) {
77    int y_size = crop_width * abs_crop_height;
78    int uv_size = ((crop_width + 1) / 2) * ((abs_crop_height + 1) / 2);
79    rotate_buffer = (uint8*)malloc(y_size + uv_size * 2); /* NOLINT */
80    if (!rotate_buffer) {
81      return 1;  // Out of memory runtime error.
82    }
83    y = rotate_buffer;
84    u = y + y_size;
85    v = u + uv_size;
86    y_stride = crop_width;
87    u_stride = v_stride = ((crop_width + 1) / 2);
88  }
89
90  switch (format) {
91    // Single plane formats
92    case FOURCC_YUY2:
93      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
94      r = YUY2ToI420(src, aligned_src_width * 2, y, y_stride, u, u_stride, v,
95                     v_stride, crop_width, inv_crop_height);
96      break;
97    case FOURCC_UYVY:
98      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
99      r = UYVYToI420(src, aligned_src_width * 2, y, y_stride, u, u_stride, v,
100                     v_stride, 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, y, y_stride, u, u_stride, v,
105                       v_stride, crop_width, inv_crop_height);
106      break;
107    case FOURCC_RGBO:
108      src = sample + (src_width * crop_y + crop_x) * 2;
109      r = ARGB1555ToI420(src, src_width * 2, y, y_stride, u, u_stride, v,
110                         v_stride, crop_width, inv_crop_height);
111      break;
112    case FOURCC_R444:
113      src = sample + (src_width * crop_y + crop_x) * 2;
114      r = ARGB4444ToI420(src, src_width * 2, y, y_stride, u, u_stride, v,
115                         v_stride, crop_width, inv_crop_height);
116      break;
117    case FOURCC_24BG:
118      src = sample + (src_width * crop_y + crop_x) * 3;
119      r = RGB24ToI420(src, src_width * 3, y, y_stride, u, u_stride, v, v_stride,
120                      crop_width, inv_crop_height);
121      break;
122    case FOURCC_RAW:
123      src = sample + (src_width * crop_y + crop_x) * 3;
124      r = RAWToI420(src, src_width * 3, y, y_stride, u, u_stride, v, v_stride,
125                    crop_width, inv_crop_height);
126      break;
127    case FOURCC_ARGB:
128      src = sample + (src_width * crop_y + crop_x) * 4;
129      r = ARGBToI420(src, src_width * 4, y, y_stride, u, u_stride, v, v_stride,
130                     crop_width, inv_crop_height);
131      break;
132    case FOURCC_BGRA:
133      src = sample + (src_width * crop_y + crop_x) * 4;
134      r = BGRAToI420(src, src_width * 4, y, y_stride, u, u_stride, v, v_stride,
135                     crop_width, inv_crop_height);
136      break;
137    case FOURCC_ABGR:
138      src = sample + (src_width * crop_y + crop_x) * 4;
139      r = ABGRToI420(src, src_width * 4, y, y_stride, u, u_stride, v, v_stride,
140                     crop_width, inv_crop_height);
141      break;
142    case FOURCC_RGBA:
143      src = sample + (src_width * crop_y + crop_x) * 4;
144      r = RGBAToI420(src, src_width * 4, y, y_stride, u, u_stride, v, v_stride,
145                     crop_width, inv_crop_height);
146      break;
147    case FOURCC_I400:
148      src = sample + src_width * crop_y + crop_x;
149      r = I400ToI420(src, src_width, y, y_stride, u, u_stride, v, v_stride,
150                     crop_width, inv_crop_height);
151      break;
152    // Biplanar formats
153    case FOURCC_NV12:
154      src = sample + (src_width * crop_y + crop_x);
155      src_uv = sample + (src_width * src_height) +
156               ((crop_y / 2) * aligned_src_width) + ((crop_x / 2) * 2);
157      r = NV12ToI420Rotate(src, src_width, src_uv, aligned_src_width, y,
158                           y_stride, u, u_stride, v, v_stride, crop_width,
159                           inv_crop_height, rotation);
160      break;
161    case FOURCC_NV21:
162      src = sample + (src_width * crop_y + crop_x);
163      src_uv = sample + (src_width * src_height) +
164               ((crop_y / 2) * aligned_src_width) + ((crop_x / 2) * 2);
165      // Call NV12 but with u and v parameters swapped.
166      r = NV12ToI420Rotate(src, src_width, src_uv, aligned_src_width, y,
167                           y_stride, v, v_stride, u, u_stride, crop_width,
168                           inv_crop_height, rotation);
169      break;
170    case FOURCC_M420:
171      src = sample + (src_width * crop_y) * 12 / 8 + crop_x;
172      r = M420ToI420(src, src_width, y, y_stride, u, u_stride, v, v_stride,
173                     crop_width, inv_crop_height);
174      break;
175    // Triplanar formats
176    case FOURCC_I420:
177    case FOURCC_YV12: {
178      const uint8* src_y = sample + (src_width * crop_y + crop_x);
179      const uint8* src_u;
180      const uint8* src_v;
181      int halfwidth = (src_width + 1) / 2;
182      int halfheight = (abs_src_height + 1) / 2;
183      if (format == FOURCC_YV12) {
184        src_v = sample + src_width * abs_src_height +
185                (halfwidth * crop_y + crop_x) / 2;
186        src_u = sample + src_width * abs_src_height +
187                halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
188      } else {
189        src_u = sample + src_width * abs_src_height +
190                (halfwidth * crop_y + crop_x) / 2;
191        src_v = sample + src_width * abs_src_height +
192                halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
193      }
194      r = I420Rotate(src_y, src_width, src_u, halfwidth, src_v, halfwidth, y,
195                     y_stride, u, u_stride, v, v_stride, crop_width,
196                     inv_crop_height, rotation);
197      break;
198    }
199    case FOURCC_I422:
200    case FOURCC_YV16: {
201      const uint8* src_y = sample + src_width * crop_y + crop_x;
202      const uint8* src_u;
203      const uint8* src_v;
204      int halfwidth = (src_width + 1) / 2;
205      if (format == FOURCC_YV16) {
206        src_v = sample + src_width * abs_src_height + halfwidth * crop_y +
207                crop_x / 2;
208        src_u = sample + src_width * abs_src_height +
209                halfwidth * (abs_src_height + crop_y) + crop_x / 2;
210      } else {
211        src_u = sample + src_width * abs_src_height + halfwidth * crop_y +
212                crop_x / 2;
213        src_v = sample + src_width * abs_src_height +
214                halfwidth * (abs_src_height + crop_y) + crop_x / 2;
215      }
216      r = I422ToI420(src_y, src_width, src_u, halfwidth, src_v, halfwidth, y,
217                     y_stride, u, u_stride, v, v_stride, crop_width,
218                     inv_crop_height);
219      break;
220    }
221    case FOURCC_I444:
222    case FOURCC_YV24: {
223      const uint8* src_y = sample + src_width * crop_y + crop_x;
224      const uint8* src_u;
225      const uint8* src_v;
226      if (format == FOURCC_YV24) {
227        src_v = sample + src_width * (abs_src_height + crop_y) + crop_x;
228        src_u = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
229      } else {
230        src_u = sample + src_width * (abs_src_height + crop_y) + crop_x;
231        src_v = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
232      }
233      r = I444ToI420(src_y, src_width, src_u, src_width, src_v, src_width, y,
234                     y_stride, u, u_stride, v, v_stride, crop_width,
235                     inv_crop_height);
236      break;
237    }
238#ifdef HAVE_JPEG
239    case FOURCC_MJPG:
240      r = MJPGToI420(sample, sample_size, y, y_stride, u, u_stride, v, v_stride,
241                     src_width, abs_src_height, crop_width, inv_crop_height);
242      break;
243#endif
244    default:
245      r = -1;  // unknown fourcc - return failure code.
246  }
247
248  if (need_buf) {
249    if (!r) {
250      r = I420Rotate(y, y_stride, u, u_stride, v, v_stride, tmp_y, tmp_y_stride,
251                     tmp_u, tmp_u_stride, tmp_v, tmp_v_stride, crop_width,
252                     abs_crop_height, rotation);
253    }
254    free(rotate_buffer);
255  }
256
257  return r;
258}
259
260#ifdef __cplusplus
261}  // extern "C"
262}  // namespace libyuv
263#endif
264