|
1 /* |
|
2 * Copyright 2012 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/mjpeg_decoder.h" |
|
12 |
|
13 #ifdef HAVE_JPEG |
|
14 #include <assert.h> |
|
15 |
|
16 #if !defined(__pnacl__) && !defined(__CLR_VER) && !defined(COVERAGE_ENABLED) &&\ |
|
17 !defined(TARGET_IPHONE_SIMULATOR) |
|
18 // Must be included before jpeglib. |
|
19 #include <setjmp.h> |
|
20 #define HAVE_SETJMP |
|
21 #endif |
|
22 struct FILE; // For jpeglib.h. |
|
23 |
|
24 // C++ build requires extern C for jpeg internals. |
|
25 #ifdef __cplusplus |
|
26 extern "C" { |
|
27 #endif |
|
28 |
|
29 #include <jpeglib.h> |
|
30 |
|
31 #ifdef __cplusplus |
|
32 } // extern "C" |
|
33 #endif |
|
34 |
|
35 #include "libyuv/planar_functions.h" // For CopyPlane(). |
|
36 |
|
37 namespace libyuv { |
|
38 |
|
39 #ifdef HAVE_SETJMP |
|
40 struct SetJmpErrorMgr { |
|
41 jpeg_error_mgr base; // Must be at the top |
|
42 jmp_buf setjmp_buffer; |
|
43 }; |
|
44 #endif |
|
45 |
|
46 const int MJpegDecoder::kColorSpaceUnknown = JCS_UNKNOWN; |
|
47 const int MJpegDecoder::kColorSpaceGrayscale = JCS_GRAYSCALE; |
|
48 const int MJpegDecoder::kColorSpaceRgb = JCS_RGB; |
|
49 const int MJpegDecoder::kColorSpaceYCbCr = JCS_YCbCr; |
|
50 const int MJpegDecoder::kColorSpaceCMYK = JCS_CMYK; |
|
51 const int MJpegDecoder::kColorSpaceYCCK = JCS_YCCK; |
|
52 |
|
53 MJpegDecoder::MJpegDecoder() |
|
54 : has_scanline_padding_(LIBYUV_FALSE), |
|
55 num_outbufs_(0), |
|
56 scanlines_(NULL), |
|
57 scanlines_sizes_(NULL), |
|
58 databuf_(NULL), |
|
59 databuf_strides_(NULL) { |
|
60 decompress_struct_ = new jpeg_decompress_struct; |
|
61 source_mgr_ = new jpeg_source_mgr; |
|
62 #ifdef HAVE_SETJMP |
|
63 error_mgr_ = new SetJmpErrorMgr; |
|
64 decompress_struct_->err = jpeg_std_error(&error_mgr_->base); |
|
65 // Override standard exit()-based error handler. |
|
66 error_mgr_->base.error_exit = &ErrorHandler; |
|
67 #endif |
|
68 decompress_struct_->client_data = NULL; |
|
69 source_mgr_->init_source = &init_source; |
|
70 source_mgr_->fill_input_buffer = &fill_input_buffer; |
|
71 source_mgr_->skip_input_data = &skip_input_data; |
|
72 source_mgr_->resync_to_restart = &jpeg_resync_to_restart; |
|
73 source_mgr_->term_source = &term_source; |
|
74 jpeg_create_decompress(decompress_struct_); |
|
75 decompress_struct_->src = source_mgr_; |
|
76 buf_vec_.buffers = &buf_; |
|
77 buf_vec_.len = 1; |
|
78 } |
|
79 |
|
80 MJpegDecoder::~MJpegDecoder() { |
|
81 jpeg_destroy_decompress(decompress_struct_); |
|
82 delete decompress_struct_; |
|
83 delete source_mgr_; |
|
84 #ifdef HAVE_SETJMP |
|
85 delete error_mgr_; |
|
86 #endif |
|
87 DestroyOutputBuffers(); |
|
88 } |
|
89 |
|
90 LIBYUV_BOOL MJpegDecoder::LoadFrame(const uint8* src, size_t src_len) { |
|
91 if (!ValidateJpeg(src, src_len)) { |
|
92 return LIBYUV_FALSE; |
|
93 } |
|
94 |
|
95 buf_.data = src; |
|
96 buf_.len = (int)(src_len); |
|
97 buf_vec_.pos = 0; |
|
98 decompress_struct_->client_data = &buf_vec_; |
|
99 #ifdef HAVE_SETJMP |
|
100 if (setjmp(error_mgr_->setjmp_buffer)) { |
|
101 // We called jpeg_read_header, it experienced an error, and we called |
|
102 // longjmp() and rewound the stack to here. Return error. |
|
103 return LIBYUV_FALSE; |
|
104 } |
|
105 #endif |
|
106 if (jpeg_read_header(decompress_struct_, TRUE) != JPEG_HEADER_OK) { |
|
107 // ERROR: Bad MJPEG header |
|
108 return LIBYUV_FALSE; |
|
109 } |
|
110 AllocOutputBuffers(GetNumComponents()); |
|
111 for (int i = 0; i < num_outbufs_; ++i) { |
|
112 int scanlines_size = GetComponentScanlinesPerImcuRow(i); |
|
113 if (scanlines_sizes_[i] != scanlines_size) { |
|
114 if (scanlines_[i]) { |
|
115 delete scanlines_[i]; |
|
116 } |
|
117 scanlines_[i] = new uint8* [scanlines_size]; |
|
118 scanlines_sizes_[i] = scanlines_size; |
|
119 } |
|
120 |
|
121 // We allocate padding for the final scanline to pad it up to DCTSIZE bytes |
|
122 // to avoid memory errors, since jpeglib only reads full MCUs blocks. For |
|
123 // the preceding scanlines, the padding is not needed/wanted because the |
|
124 // following addresses will already be valid (they are the initial bytes of |
|
125 // the next scanline) and will be overwritten when jpeglib writes out that |
|
126 // next scanline. |
|
127 int databuf_stride = GetComponentStride(i); |
|
128 int databuf_size = scanlines_size * databuf_stride; |
|
129 if (databuf_strides_[i] != databuf_stride) { |
|
130 if (databuf_[i]) { |
|
131 delete databuf_[i]; |
|
132 } |
|
133 databuf_[i] = new uint8[databuf_size]; |
|
134 databuf_strides_[i] = databuf_stride; |
|
135 } |
|
136 |
|
137 if (GetComponentStride(i) != GetComponentWidth(i)) { |
|
138 has_scanline_padding_ = LIBYUV_TRUE; |
|
139 } |
|
140 } |
|
141 return LIBYUV_TRUE; |
|
142 } |
|
143 |
|
144 static int DivideAndRoundUp(int numerator, int denominator) { |
|
145 return (numerator + denominator - 1) / denominator; |
|
146 } |
|
147 |
|
148 static int DivideAndRoundDown(int numerator, int denominator) { |
|
149 return numerator / denominator; |
|
150 } |
|
151 |
|
152 // Returns width of the last loaded frame. |
|
153 int MJpegDecoder::GetWidth() { |
|
154 return decompress_struct_->image_width; |
|
155 } |
|
156 |
|
157 // Returns height of the last loaded frame. |
|
158 int MJpegDecoder::GetHeight() { |
|
159 return decompress_struct_->image_height; |
|
160 } |
|
161 |
|
162 // Returns format of the last loaded frame. The return value is one of the |
|
163 // kColorSpace* constants. |
|
164 int MJpegDecoder::GetColorSpace() { |
|
165 return decompress_struct_->jpeg_color_space; |
|
166 } |
|
167 |
|
168 // Number of color components in the color space. |
|
169 int MJpegDecoder::GetNumComponents() { |
|
170 return decompress_struct_->num_components; |
|
171 } |
|
172 |
|
173 // Sample factors of the n-th component. |
|
174 int MJpegDecoder::GetHorizSampFactor(int component) { |
|
175 return decompress_struct_->comp_info[component].h_samp_factor; |
|
176 } |
|
177 |
|
178 int MJpegDecoder::GetVertSampFactor(int component) { |
|
179 return decompress_struct_->comp_info[component].v_samp_factor; |
|
180 } |
|
181 |
|
182 int MJpegDecoder::GetHorizSubSampFactor(int component) { |
|
183 return decompress_struct_->max_h_samp_factor / |
|
184 GetHorizSampFactor(component); |
|
185 } |
|
186 |
|
187 int MJpegDecoder::GetVertSubSampFactor(int component) { |
|
188 return decompress_struct_->max_v_samp_factor / |
|
189 GetVertSampFactor(component); |
|
190 } |
|
191 |
|
192 int MJpegDecoder::GetImageScanlinesPerImcuRow() { |
|
193 return decompress_struct_->max_v_samp_factor * DCTSIZE; |
|
194 } |
|
195 |
|
196 int MJpegDecoder::GetComponentScanlinesPerImcuRow(int component) { |
|
197 int vs = GetVertSubSampFactor(component); |
|
198 return DivideAndRoundUp(GetImageScanlinesPerImcuRow(), vs); |
|
199 } |
|
200 |
|
201 int MJpegDecoder::GetComponentWidth(int component) { |
|
202 int hs = GetHorizSubSampFactor(component); |
|
203 return DivideAndRoundUp(GetWidth(), hs); |
|
204 } |
|
205 |
|
206 int MJpegDecoder::GetComponentHeight(int component) { |
|
207 int vs = GetVertSubSampFactor(component); |
|
208 return DivideAndRoundUp(GetHeight(), vs); |
|
209 } |
|
210 |
|
211 // Get width in bytes padded out to a multiple of DCTSIZE |
|
212 int MJpegDecoder::GetComponentStride(int component) { |
|
213 return (GetComponentWidth(component) + DCTSIZE - 1) & ~(DCTSIZE - 1); |
|
214 } |
|
215 |
|
216 int MJpegDecoder::GetComponentSize(int component) { |
|
217 return GetComponentWidth(component) * GetComponentHeight(component); |
|
218 } |
|
219 |
|
220 LIBYUV_BOOL MJpegDecoder::UnloadFrame() { |
|
221 #ifdef HAVE_SETJMP |
|
222 if (setjmp(error_mgr_->setjmp_buffer)) { |
|
223 // We called jpeg_abort_decompress, it experienced an error, and we called |
|
224 // longjmp() and rewound the stack to here. Return error. |
|
225 return LIBYUV_FALSE; |
|
226 } |
|
227 #endif |
|
228 jpeg_abort_decompress(decompress_struct_); |
|
229 return LIBYUV_TRUE; |
|
230 } |
|
231 |
|
232 // TODO(fbarchard): Allow rectangle to be specified: x, y, width, height. |
|
233 LIBYUV_BOOL MJpegDecoder::DecodeToBuffers( |
|
234 uint8** planes, int dst_width, int dst_height) { |
|
235 if (dst_width != GetWidth() || |
|
236 dst_height > GetHeight()) { |
|
237 // ERROR: Bad dimensions |
|
238 return LIBYUV_FALSE; |
|
239 } |
|
240 #ifdef HAVE_SETJMP |
|
241 if (setjmp(error_mgr_->setjmp_buffer)) { |
|
242 // We called into jpeglib, it experienced an error sometime during this |
|
243 // function call, and we called longjmp() and rewound the stack to here. |
|
244 // Return error. |
|
245 return LIBYUV_FALSE; |
|
246 } |
|
247 #endif |
|
248 if (!StartDecode()) { |
|
249 return LIBYUV_FALSE; |
|
250 } |
|
251 SetScanlinePointers(databuf_); |
|
252 int lines_left = dst_height; |
|
253 // Compute amount of lines to skip to implement vertical crop. |
|
254 // TODO(fbarchard): Ensure skip is a multiple of maximum component |
|
255 // subsample. ie 2 |
|
256 int skip = (GetHeight() - dst_height) / 2; |
|
257 if (skip > 0) { |
|
258 // There is no API to skip lines in the output data, so we read them |
|
259 // into the temp buffer. |
|
260 while (skip >= GetImageScanlinesPerImcuRow()) { |
|
261 if (!DecodeImcuRow()) { |
|
262 FinishDecode(); |
|
263 return LIBYUV_FALSE; |
|
264 } |
|
265 skip -= GetImageScanlinesPerImcuRow(); |
|
266 } |
|
267 if (skip > 0) { |
|
268 // Have a partial iMCU row left over to skip. Must read it and then |
|
269 // copy the parts we want into the destination. |
|
270 if (!DecodeImcuRow()) { |
|
271 FinishDecode(); |
|
272 return LIBYUV_FALSE; |
|
273 } |
|
274 for (int i = 0; i < num_outbufs_; ++i) { |
|
275 // TODO(fbarchard): Compute skip to avoid this |
|
276 assert(skip % GetVertSubSampFactor(i) == 0); |
|
277 int rows_to_skip = |
|
278 DivideAndRoundDown(skip, GetVertSubSampFactor(i)); |
|
279 int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i) - |
|
280 rows_to_skip; |
|
281 int data_to_skip = rows_to_skip * GetComponentStride(i); |
|
282 CopyPlane(databuf_[i] + data_to_skip, GetComponentStride(i), |
|
283 planes[i], GetComponentWidth(i), |
|
284 GetComponentWidth(i), scanlines_to_copy); |
|
285 planes[i] += scanlines_to_copy * GetComponentWidth(i); |
|
286 } |
|
287 lines_left -= (GetImageScanlinesPerImcuRow() - skip); |
|
288 } |
|
289 } |
|
290 |
|
291 // Read full MCUs but cropped horizontally |
|
292 for (; lines_left > GetImageScanlinesPerImcuRow(); |
|
293 lines_left -= GetImageScanlinesPerImcuRow()) { |
|
294 if (!DecodeImcuRow()) { |
|
295 FinishDecode(); |
|
296 return LIBYUV_FALSE; |
|
297 } |
|
298 for (int i = 0; i < num_outbufs_; ++i) { |
|
299 int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i); |
|
300 CopyPlane(databuf_[i], GetComponentStride(i), |
|
301 planes[i], GetComponentWidth(i), |
|
302 GetComponentWidth(i), scanlines_to_copy); |
|
303 planes[i] += scanlines_to_copy * GetComponentWidth(i); |
|
304 } |
|
305 } |
|
306 |
|
307 if (lines_left > 0) { |
|
308 // Have a partial iMCU row left over to decode. |
|
309 if (!DecodeImcuRow()) { |
|
310 FinishDecode(); |
|
311 return LIBYUV_FALSE; |
|
312 } |
|
313 for (int i = 0; i < num_outbufs_; ++i) { |
|
314 int scanlines_to_copy = |
|
315 DivideAndRoundUp(lines_left, GetVertSubSampFactor(i)); |
|
316 CopyPlane(databuf_[i], GetComponentStride(i), |
|
317 planes[i], GetComponentWidth(i), |
|
318 GetComponentWidth(i), scanlines_to_copy); |
|
319 planes[i] += scanlines_to_copy * GetComponentWidth(i); |
|
320 } |
|
321 } |
|
322 return FinishDecode(); |
|
323 } |
|
324 |
|
325 LIBYUV_BOOL MJpegDecoder::DecodeToCallback(CallbackFunction fn, void* opaque, |
|
326 int dst_width, int dst_height) { |
|
327 if (dst_width != GetWidth() || |
|
328 dst_height > GetHeight()) { |
|
329 // ERROR: Bad dimensions |
|
330 return LIBYUV_FALSE; |
|
331 } |
|
332 #ifdef HAVE_SETJMP |
|
333 if (setjmp(error_mgr_->setjmp_buffer)) { |
|
334 // We called into jpeglib, it experienced an error sometime during this |
|
335 // function call, and we called longjmp() and rewound the stack to here. |
|
336 // Return error. |
|
337 return LIBYUV_FALSE; |
|
338 } |
|
339 #endif |
|
340 if (!StartDecode()) { |
|
341 return LIBYUV_FALSE; |
|
342 } |
|
343 SetScanlinePointers(databuf_); |
|
344 int lines_left = dst_height; |
|
345 // TODO(fbarchard): Compute amount of lines to skip to implement vertical crop |
|
346 int skip = (GetHeight() - dst_height) / 2; |
|
347 if (skip > 0) { |
|
348 while (skip >= GetImageScanlinesPerImcuRow()) { |
|
349 if (!DecodeImcuRow()) { |
|
350 FinishDecode(); |
|
351 return LIBYUV_FALSE; |
|
352 } |
|
353 skip -= GetImageScanlinesPerImcuRow(); |
|
354 } |
|
355 if (skip > 0) { |
|
356 // Have a partial iMCU row left over to skip. |
|
357 if (!DecodeImcuRow()) { |
|
358 FinishDecode(); |
|
359 return LIBYUV_FALSE; |
|
360 } |
|
361 for (int i = 0; i < num_outbufs_; ++i) { |
|
362 // TODO(fbarchard): Compute skip to avoid this |
|
363 assert(skip % GetVertSubSampFactor(i) == 0); |
|
364 int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i)); |
|
365 int data_to_skip = rows_to_skip * GetComponentStride(i); |
|
366 // Change our own data buffer pointers so we can pass them to the |
|
367 // callback. |
|
368 databuf_[i] += data_to_skip; |
|
369 } |
|
370 int scanlines_to_copy = GetImageScanlinesPerImcuRow() - skip; |
|
371 (*fn)(opaque, databuf_, databuf_strides_, scanlines_to_copy); |
|
372 // Now change them back. |
|
373 for (int i = 0; i < num_outbufs_; ++i) { |
|
374 int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i)); |
|
375 int data_to_skip = rows_to_skip * GetComponentStride(i); |
|
376 databuf_[i] -= data_to_skip; |
|
377 } |
|
378 lines_left -= scanlines_to_copy; |
|
379 } |
|
380 } |
|
381 // Read full MCUs until we get to the crop point. |
|
382 for (; lines_left >= GetImageScanlinesPerImcuRow(); |
|
383 lines_left -= GetImageScanlinesPerImcuRow()) { |
|
384 if (!DecodeImcuRow()) { |
|
385 FinishDecode(); |
|
386 return LIBYUV_FALSE; |
|
387 } |
|
388 (*fn)(opaque, databuf_, databuf_strides_, GetImageScanlinesPerImcuRow()); |
|
389 } |
|
390 if (lines_left > 0) { |
|
391 // Have a partial iMCU row left over to decode. |
|
392 if (!DecodeImcuRow()) { |
|
393 FinishDecode(); |
|
394 return LIBYUV_FALSE; |
|
395 } |
|
396 (*fn)(opaque, databuf_, databuf_strides_, lines_left); |
|
397 } |
|
398 return FinishDecode(); |
|
399 } |
|
400 |
|
401 void MJpegDecoder::init_source(j_decompress_ptr cinfo) { |
|
402 fill_input_buffer(cinfo); |
|
403 } |
|
404 |
|
405 boolean MJpegDecoder::fill_input_buffer(j_decompress_ptr cinfo) { |
|
406 BufferVector* buf_vec = (BufferVector*)(cinfo->client_data); |
|
407 if (buf_vec->pos >= buf_vec->len) { |
|
408 assert(0 && "No more data"); |
|
409 // ERROR: No more data |
|
410 return FALSE; |
|
411 } |
|
412 cinfo->src->next_input_byte = buf_vec->buffers[buf_vec->pos].data; |
|
413 cinfo->src->bytes_in_buffer = buf_vec->buffers[buf_vec->pos].len; |
|
414 ++buf_vec->pos; |
|
415 return TRUE; |
|
416 } |
|
417 |
|
418 void MJpegDecoder::skip_input_data(j_decompress_ptr cinfo, |
|
419 long num_bytes) { // NOLINT |
|
420 cinfo->src->next_input_byte += num_bytes; |
|
421 } |
|
422 |
|
423 void MJpegDecoder::term_source(j_decompress_ptr cinfo) { |
|
424 // Nothing to do. |
|
425 } |
|
426 |
|
427 #ifdef HAVE_SETJMP |
|
428 void MJpegDecoder::ErrorHandler(j_common_ptr cinfo) { |
|
429 // This is called when a jpeglib command experiences an error. Unfortunately |
|
430 // jpeglib's error handling model is not very flexible, because it expects the |
|
431 // error handler to not return--i.e., it wants the program to terminate. To |
|
432 // recover from errors we use setjmp() as shown in their example. setjmp() is |
|
433 // C's implementation for the "call with current continuation" functionality |
|
434 // seen in some functional programming languages. |
|
435 // A formatted message can be output, but is unsafe for release. |
|
436 #ifdef DEBUG |
|
437 char buf[JMSG_LENGTH_MAX]; |
|
438 (*cinfo->err->format_message)(cinfo, buf); |
|
439 // ERROR: Error in jpeglib: buf |
|
440 #endif |
|
441 |
|
442 SetJmpErrorMgr* mgr = (SetJmpErrorMgr*)(cinfo->err); |
|
443 // This rewinds the call stack to the point of the corresponding setjmp() |
|
444 // and causes it to return (for a second time) with value 1. |
|
445 longjmp(mgr->setjmp_buffer, 1); |
|
446 } |
|
447 #endif |
|
448 |
|
449 void MJpegDecoder::AllocOutputBuffers(int num_outbufs) { |
|
450 if (num_outbufs != num_outbufs_) { |
|
451 // We could perhaps optimize this case to resize the output buffers without |
|
452 // necessarily having to delete and recreate each one, but it's not worth |
|
453 // it. |
|
454 DestroyOutputBuffers(); |
|
455 |
|
456 scanlines_ = new uint8** [num_outbufs]; |
|
457 scanlines_sizes_ = new int[num_outbufs]; |
|
458 databuf_ = new uint8* [num_outbufs]; |
|
459 databuf_strides_ = new int[num_outbufs]; |
|
460 |
|
461 for (int i = 0; i < num_outbufs; ++i) { |
|
462 scanlines_[i] = NULL; |
|
463 scanlines_sizes_[i] = 0; |
|
464 databuf_[i] = NULL; |
|
465 databuf_strides_[i] = 0; |
|
466 } |
|
467 |
|
468 num_outbufs_ = num_outbufs; |
|
469 } |
|
470 } |
|
471 |
|
472 void MJpegDecoder::DestroyOutputBuffers() { |
|
473 for (int i = 0; i < num_outbufs_; ++i) { |
|
474 delete [] scanlines_[i]; |
|
475 delete [] databuf_[i]; |
|
476 } |
|
477 delete [] scanlines_; |
|
478 delete [] databuf_; |
|
479 delete [] scanlines_sizes_; |
|
480 delete [] databuf_strides_; |
|
481 scanlines_ = NULL; |
|
482 databuf_ = NULL; |
|
483 scanlines_sizes_ = NULL; |
|
484 databuf_strides_ = NULL; |
|
485 num_outbufs_ = 0; |
|
486 } |
|
487 |
|
488 // JDCT_IFAST and do_block_smoothing improve performance substantially. |
|
489 LIBYUV_BOOL MJpegDecoder::StartDecode() { |
|
490 decompress_struct_->raw_data_out = TRUE; |
|
491 decompress_struct_->dct_method = JDCT_IFAST; // JDCT_ISLOW is default |
|
492 decompress_struct_->dither_mode = JDITHER_NONE; |
|
493 // Not applicable to 'raw': |
|
494 decompress_struct_->do_fancy_upsampling = LIBYUV_FALSE; |
|
495 // Only for buffered mode: |
|
496 decompress_struct_->enable_2pass_quant = LIBYUV_FALSE; |
|
497 // Blocky but fast: |
|
498 decompress_struct_->do_block_smoothing = LIBYUV_FALSE; |
|
499 |
|
500 if (!jpeg_start_decompress(decompress_struct_)) { |
|
501 // ERROR: Couldn't start JPEG decompressor"; |
|
502 return LIBYUV_FALSE; |
|
503 } |
|
504 return LIBYUV_TRUE; |
|
505 } |
|
506 |
|
507 LIBYUV_BOOL MJpegDecoder::FinishDecode() { |
|
508 // jpeglib considers it an error if we finish without decoding the whole |
|
509 // image, so we call "abort" rather than "finish". |
|
510 jpeg_abort_decompress(decompress_struct_); |
|
511 return LIBYUV_TRUE; |
|
512 } |
|
513 |
|
514 void MJpegDecoder::SetScanlinePointers(uint8** data) { |
|
515 for (int i = 0; i < num_outbufs_; ++i) { |
|
516 uint8* data_i = data[i]; |
|
517 for (int j = 0; j < scanlines_sizes_[i]; ++j) { |
|
518 scanlines_[i][j] = data_i; |
|
519 data_i += GetComponentStride(i); |
|
520 } |
|
521 } |
|
522 } |
|
523 |
|
524 inline LIBYUV_BOOL MJpegDecoder::DecodeImcuRow() { |
|
525 return (unsigned int)(GetImageScanlinesPerImcuRow()) == |
|
526 jpeg_read_raw_data(decompress_struct_, |
|
527 scanlines_, |
|
528 GetImageScanlinesPerImcuRow()); |
|
529 } |
|
530 |
|
531 // The helper function which recognizes the jpeg sub-sampling type. |
|
532 JpegSubsamplingType MJpegDecoder::JpegSubsamplingTypeHelper( |
|
533 int* subsample_x, int* subsample_y, int number_of_components) { |
|
534 if (number_of_components == 3) { // Color images. |
|
535 if (subsample_x[0] == 1 && subsample_y[0] == 1 && |
|
536 subsample_x[1] == 2 && subsample_y[1] == 2 && |
|
537 subsample_x[2] == 2 && subsample_y[2] == 2) { |
|
538 return kJpegYuv420; |
|
539 } else if (subsample_x[0] == 1 && subsample_y[0] == 1 && |
|
540 subsample_x[1] == 2 && subsample_y[1] == 1 && |
|
541 subsample_x[2] == 2 && subsample_y[2] == 1) { |
|
542 return kJpegYuv422; |
|
543 } else if (subsample_x[0] == 1 && subsample_y[0] == 1 && |
|
544 subsample_x[1] == 1 && subsample_y[1] == 1 && |
|
545 subsample_x[2] == 1 && subsample_y[2] == 1) { |
|
546 return kJpegYuv444; |
|
547 } |
|
548 } else if (number_of_components == 1) { // Grey-scale images. |
|
549 if (subsample_x[0] == 1 && subsample_y[0] == 1) { |
|
550 return kJpegYuv400; |
|
551 } |
|
552 } |
|
553 return kJpegUnknown; |
|
554 } |
|
555 |
|
556 } // namespace libyuv |
|
557 #endif // HAVE_JPEG |
|
558 |