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.h"
12#include "libyuv/convert_argb.h"
13
14#ifdef HAVE_JPEG
15#include "libyuv/mjpeg_decoder.h"
16#endif
17
18#ifdef __cplusplus
19namespace libyuv {
20extern "C" {
21#endif
22
23#ifdef HAVE_JPEG
24struct I420Buffers {
25  uint8* y;
26  int y_stride;
27  uint8* u;
28  int u_stride;
29  uint8* v;
30  int v_stride;
31  int w;
32  int h;
33};
34
35static void JpegCopyI420(void* opaque,
36                         const uint8* const* data,
37                         const int* strides,
38                         int rows) {
39  I420Buffers* dest = (I420Buffers*)(opaque);
40  I420Copy(data[0], strides[0], data[1], strides[1], data[2], strides[2],
41           dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
42           dest->v_stride, dest->w, rows);
43  dest->y += rows * dest->y_stride;
44  dest->u += ((rows + 1) >> 1) * dest->u_stride;
45  dest->v += ((rows + 1) >> 1) * dest->v_stride;
46  dest->h -= rows;
47}
48
49static void JpegI422ToI420(void* opaque,
50                           const uint8* const* data,
51                           const int* strides,
52                           int rows) {
53  I420Buffers* dest = (I420Buffers*)(opaque);
54  I422ToI420(data[0], strides[0], data[1], strides[1], data[2], strides[2],
55             dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
56             dest->v_stride, dest->w, rows);
57  dest->y += rows * dest->y_stride;
58  dest->u += ((rows + 1) >> 1) * dest->u_stride;
59  dest->v += ((rows + 1) >> 1) * dest->v_stride;
60  dest->h -= rows;
61}
62
63static void JpegI444ToI420(void* opaque,
64                           const uint8* const* data,
65                           const int* strides,
66                           int rows) {
67  I420Buffers* dest = (I420Buffers*)(opaque);
68  I444ToI420(data[0], strides[0], data[1], strides[1], data[2], strides[2],
69             dest->y, dest->y_stride, dest->u, dest->u_stride, dest->v,
70             dest->v_stride, dest->w, rows);
71  dest->y += rows * dest->y_stride;
72  dest->u += ((rows + 1) >> 1) * dest->u_stride;
73  dest->v += ((rows + 1) >> 1) * dest->v_stride;
74  dest->h -= rows;
75}
76
77static void JpegI400ToI420(void* opaque,
78                           const uint8* const* data,
79                           const int* strides,
80                           int rows) {
81  I420Buffers* dest = (I420Buffers*)(opaque);
82  I400ToI420(data[0], strides[0], dest->y, dest->y_stride, dest->u,
83             dest->u_stride, dest->v, dest->v_stride, dest->w, rows);
84  dest->y += rows * dest->y_stride;
85  dest->u += ((rows + 1) >> 1) * dest->u_stride;
86  dest->v += ((rows + 1) >> 1) * dest->v_stride;
87  dest->h -= rows;
88}
89
90// Query size of MJPG in pixels.
91LIBYUV_API
92int MJPGSize(const uint8* sample, size_t sample_size, int* width, int* height) {
93  MJpegDecoder mjpeg_decoder;
94  LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
95  if (ret) {
96    *width = mjpeg_decoder.GetWidth();
97    *height = mjpeg_decoder.GetHeight();
98  }
99  mjpeg_decoder.UnloadFrame();
100  return ret ? 0 : -1;  // -1 for runtime failure.
101}
102
103// MJPG (Motion JPeg) to I420
104// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
105LIBYUV_API
106int MJPGToI420(const uint8* sample,
107               size_t sample_size,
108               uint8* y,
109               int y_stride,
110               uint8* u,
111               int u_stride,
112               uint8* v,
113               int v_stride,
114               int w,
115               int h,
116               int dw,
117               int dh) {
118  if (sample_size == kUnknownDataSize) {
119    // ERROR: MJPEG frame size unknown
120    return -1;
121  }
122
123  // TODO(fbarchard): Port MJpeg to C.
124  MJpegDecoder mjpeg_decoder;
125  LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
126  if (ret &&
127      (mjpeg_decoder.GetWidth() != w || mjpeg_decoder.GetHeight() != h)) {
128    // ERROR: MJPEG frame has unexpected dimensions
129    mjpeg_decoder.UnloadFrame();
130    return 1;  // runtime failure
131  }
132  if (ret) {
133    I420Buffers bufs = {y, y_stride, u, u_stride, v, v_stride, dw, dh};
134    // YUV420
135    if (mjpeg_decoder.GetColorSpace() == MJpegDecoder::kColorSpaceYCbCr &&
136        mjpeg_decoder.GetNumComponents() == 3 &&
137        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
138        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
139        mjpeg_decoder.GetVertSampFactor(1) == 1 &&
140        mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
141        mjpeg_decoder.GetVertSampFactor(2) == 1 &&
142        mjpeg_decoder.GetHorizSampFactor(2) == 1) {
143      ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dw, dh);
144      // YUV422
145    } else if (mjpeg_decoder.GetColorSpace() ==
146                   MJpegDecoder::kColorSpaceYCbCr &&
147               mjpeg_decoder.GetNumComponents() == 3 &&
148               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
149               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
150               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
151               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
152               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
153               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
154      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh);
155      // YUV444
156    } else if (mjpeg_decoder.GetColorSpace() ==
157                   MJpegDecoder::kColorSpaceYCbCr &&
158               mjpeg_decoder.GetNumComponents() == 3 &&
159               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
160               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
161               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
162               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
163               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
164               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
165      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh);
166      // YUV400
167    } else if (mjpeg_decoder.GetColorSpace() ==
168                   MJpegDecoder::kColorSpaceGrayscale &&
169               mjpeg_decoder.GetNumComponents() == 1 &&
170               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
171               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
172      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh);
173    } else {
174      // TODO(fbarchard): Implement conversion for any other colorspace/sample
175      // factors that occur in practice.
176      // ERROR: Unable to convert MJPEG frame because format is not supported
177      mjpeg_decoder.UnloadFrame();
178      return 1;
179    }
180  }
181  return ret ? 0 : 1;
182}
183
184#ifdef HAVE_JPEG
185struct ARGBBuffers {
186  uint8* argb;
187  int argb_stride;
188  int w;
189  int h;
190};
191
192static void JpegI420ToARGB(void* opaque,
193                           const uint8* const* data,
194                           const int* strides,
195                           int rows) {
196  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
197  I420ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
198             dest->argb, dest->argb_stride, dest->w, rows);
199  dest->argb += rows * dest->argb_stride;
200  dest->h -= rows;
201}
202
203static void JpegI422ToARGB(void* opaque,
204                           const uint8* const* data,
205                           const int* strides,
206                           int rows) {
207  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
208  I422ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
209             dest->argb, dest->argb_stride, dest->w, rows);
210  dest->argb += rows * dest->argb_stride;
211  dest->h -= rows;
212}
213
214static void JpegI444ToARGB(void* opaque,
215                           const uint8* const* data,
216                           const int* strides,
217                           int rows) {
218  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
219  I444ToARGB(data[0], strides[0], data[1], strides[1], data[2], strides[2],
220             dest->argb, dest->argb_stride, dest->w, rows);
221  dest->argb += rows * dest->argb_stride;
222  dest->h -= rows;
223}
224
225static void JpegI400ToARGB(void* opaque,
226                           const uint8* const* data,
227                           const int* strides,
228                           int rows) {
229  ARGBBuffers* dest = (ARGBBuffers*)(opaque);
230  I400ToARGB(data[0], strides[0], dest->argb, dest->argb_stride, dest->w, rows);
231  dest->argb += rows * dest->argb_stride;
232  dest->h -= rows;
233}
234
235// MJPG (Motion JPeg) to ARGB
236// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
237LIBYUV_API
238int MJPGToARGB(const uint8* sample,
239               size_t sample_size,
240               uint8* argb,
241               int argb_stride,
242               int w,
243               int h,
244               int dw,
245               int dh) {
246  if (sample_size == kUnknownDataSize) {
247    // ERROR: MJPEG frame size unknown
248    return -1;
249  }
250
251  // TODO(fbarchard): Port MJpeg to C.
252  MJpegDecoder mjpeg_decoder;
253  LIBYUV_BOOL ret = mjpeg_decoder.LoadFrame(sample, sample_size);
254  if (ret &&
255      (mjpeg_decoder.GetWidth() != w || mjpeg_decoder.GetHeight() != h)) {
256    // ERROR: MJPEG frame has unexpected dimensions
257    mjpeg_decoder.UnloadFrame();
258    return 1;  // runtime failure
259  }
260  if (ret) {
261    ARGBBuffers bufs = {argb, argb_stride, dw, dh};
262    // YUV420
263    if (mjpeg_decoder.GetColorSpace() == MJpegDecoder::kColorSpaceYCbCr &&
264        mjpeg_decoder.GetNumComponents() == 3 &&
265        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
266        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
267        mjpeg_decoder.GetVertSampFactor(1) == 1 &&
268        mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
269        mjpeg_decoder.GetVertSampFactor(2) == 1 &&
270        mjpeg_decoder.GetHorizSampFactor(2) == 1) {
271      ret = mjpeg_decoder.DecodeToCallback(&JpegI420ToARGB, &bufs, dw, dh);
272      // YUV422
273    } else if (mjpeg_decoder.GetColorSpace() ==
274                   MJpegDecoder::kColorSpaceYCbCr &&
275               mjpeg_decoder.GetNumComponents() == 3 &&
276               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
277               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
278               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
279               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
280               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
281               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
282      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToARGB, &bufs, dw, dh);
283      // YUV444
284    } else if (mjpeg_decoder.GetColorSpace() ==
285                   MJpegDecoder::kColorSpaceYCbCr &&
286               mjpeg_decoder.GetNumComponents() == 3 &&
287               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
288               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
289               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
290               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
291               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
292               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
293      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToARGB, &bufs, dw, dh);
294      // YUV400
295    } else if (mjpeg_decoder.GetColorSpace() ==
296                   MJpegDecoder::kColorSpaceGrayscale &&
297               mjpeg_decoder.GetNumComponents() == 1 &&
298               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
299               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
300      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToARGB, &bufs, dw, dh);
301    } else {
302      // TODO(fbarchard): Implement conversion for any other colorspace/sample
303      // factors that occur in practice.
304      // ERROR: Unable to convert MJPEG frame because format is not supported
305      mjpeg_decoder.UnloadFrame();
306      return 1;
307    }
308  }
309  return ret ? 0 : 1;
310}
311#endif
312
313#endif
314
315#ifdef __cplusplus
316}  // extern "C"
317}  // namespace libyuv
318#endif
319