1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/libyuv/source/mjpeg_decoder.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,558 @@ 1.4 +/* 1.5 + * Copyright 2012 The LibYuv Project Authors. All rights reserved. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license 1.8 + * that can be found in the LICENSE file in the root of the source 1.9 + * tree. An additional intellectual property rights grant can be found 1.10 + * in the file PATENTS. All contributing project authors may 1.11 + * be found in the AUTHORS file in the root of the source tree. 1.12 + */ 1.13 + 1.14 +#include "libyuv/mjpeg_decoder.h" 1.15 + 1.16 +#ifdef HAVE_JPEG 1.17 +#include <assert.h> 1.18 + 1.19 +#if !defined(__pnacl__) && !defined(__CLR_VER) && !defined(COVERAGE_ENABLED) &&\ 1.20 + !defined(TARGET_IPHONE_SIMULATOR) 1.21 +// Must be included before jpeglib. 1.22 +#include <setjmp.h> 1.23 +#define HAVE_SETJMP 1.24 +#endif 1.25 +struct FILE; // For jpeglib.h. 1.26 + 1.27 +// C++ build requires extern C for jpeg internals. 1.28 +#ifdef __cplusplus 1.29 +extern "C" { 1.30 +#endif 1.31 + 1.32 +#include <jpeglib.h> 1.33 + 1.34 +#ifdef __cplusplus 1.35 +} // extern "C" 1.36 +#endif 1.37 + 1.38 +#include "libyuv/planar_functions.h" // For CopyPlane(). 1.39 + 1.40 +namespace libyuv { 1.41 + 1.42 +#ifdef HAVE_SETJMP 1.43 +struct SetJmpErrorMgr { 1.44 + jpeg_error_mgr base; // Must be at the top 1.45 + jmp_buf setjmp_buffer; 1.46 +}; 1.47 +#endif 1.48 + 1.49 +const int MJpegDecoder::kColorSpaceUnknown = JCS_UNKNOWN; 1.50 +const int MJpegDecoder::kColorSpaceGrayscale = JCS_GRAYSCALE; 1.51 +const int MJpegDecoder::kColorSpaceRgb = JCS_RGB; 1.52 +const int MJpegDecoder::kColorSpaceYCbCr = JCS_YCbCr; 1.53 +const int MJpegDecoder::kColorSpaceCMYK = JCS_CMYK; 1.54 +const int MJpegDecoder::kColorSpaceYCCK = JCS_YCCK; 1.55 + 1.56 +MJpegDecoder::MJpegDecoder() 1.57 + : has_scanline_padding_(LIBYUV_FALSE), 1.58 + num_outbufs_(0), 1.59 + scanlines_(NULL), 1.60 + scanlines_sizes_(NULL), 1.61 + databuf_(NULL), 1.62 + databuf_strides_(NULL) { 1.63 + decompress_struct_ = new jpeg_decompress_struct; 1.64 + source_mgr_ = new jpeg_source_mgr; 1.65 +#ifdef HAVE_SETJMP 1.66 + error_mgr_ = new SetJmpErrorMgr; 1.67 + decompress_struct_->err = jpeg_std_error(&error_mgr_->base); 1.68 + // Override standard exit()-based error handler. 1.69 + error_mgr_->base.error_exit = &ErrorHandler; 1.70 +#endif 1.71 + decompress_struct_->client_data = NULL; 1.72 + source_mgr_->init_source = &init_source; 1.73 + source_mgr_->fill_input_buffer = &fill_input_buffer; 1.74 + source_mgr_->skip_input_data = &skip_input_data; 1.75 + source_mgr_->resync_to_restart = &jpeg_resync_to_restart; 1.76 + source_mgr_->term_source = &term_source; 1.77 + jpeg_create_decompress(decompress_struct_); 1.78 + decompress_struct_->src = source_mgr_; 1.79 + buf_vec_.buffers = &buf_; 1.80 + buf_vec_.len = 1; 1.81 +} 1.82 + 1.83 +MJpegDecoder::~MJpegDecoder() { 1.84 + jpeg_destroy_decompress(decompress_struct_); 1.85 + delete decompress_struct_; 1.86 + delete source_mgr_; 1.87 +#ifdef HAVE_SETJMP 1.88 + delete error_mgr_; 1.89 +#endif 1.90 + DestroyOutputBuffers(); 1.91 +} 1.92 + 1.93 +LIBYUV_BOOL MJpegDecoder::LoadFrame(const uint8* src, size_t src_len) { 1.94 + if (!ValidateJpeg(src, src_len)) { 1.95 + return LIBYUV_FALSE; 1.96 + } 1.97 + 1.98 + buf_.data = src; 1.99 + buf_.len = (int)(src_len); 1.100 + buf_vec_.pos = 0; 1.101 + decompress_struct_->client_data = &buf_vec_; 1.102 +#ifdef HAVE_SETJMP 1.103 + if (setjmp(error_mgr_->setjmp_buffer)) { 1.104 + // We called jpeg_read_header, it experienced an error, and we called 1.105 + // longjmp() and rewound the stack to here. Return error. 1.106 + return LIBYUV_FALSE; 1.107 + } 1.108 +#endif 1.109 + if (jpeg_read_header(decompress_struct_, TRUE) != JPEG_HEADER_OK) { 1.110 + // ERROR: Bad MJPEG header 1.111 + return LIBYUV_FALSE; 1.112 + } 1.113 + AllocOutputBuffers(GetNumComponents()); 1.114 + for (int i = 0; i < num_outbufs_; ++i) { 1.115 + int scanlines_size = GetComponentScanlinesPerImcuRow(i); 1.116 + if (scanlines_sizes_[i] != scanlines_size) { 1.117 + if (scanlines_[i]) { 1.118 + delete scanlines_[i]; 1.119 + } 1.120 + scanlines_[i] = new uint8* [scanlines_size]; 1.121 + scanlines_sizes_[i] = scanlines_size; 1.122 + } 1.123 + 1.124 + // We allocate padding for the final scanline to pad it up to DCTSIZE bytes 1.125 + // to avoid memory errors, since jpeglib only reads full MCUs blocks. For 1.126 + // the preceding scanlines, the padding is not needed/wanted because the 1.127 + // following addresses will already be valid (they are the initial bytes of 1.128 + // the next scanline) and will be overwritten when jpeglib writes out that 1.129 + // next scanline. 1.130 + int databuf_stride = GetComponentStride(i); 1.131 + int databuf_size = scanlines_size * databuf_stride; 1.132 + if (databuf_strides_[i] != databuf_stride) { 1.133 + if (databuf_[i]) { 1.134 + delete databuf_[i]; 1.135 + } 1.136 + databuf_[i] = new uint8[databuf_size]; 1.137 + databuf_strides_[i] = databuf_stride; 1.138 + } 1.139 + 1.140 + if (GetComponentStride(i) != GetComponentWidth(i)) { 1.141 + has_scanline_padding_ = LIBYUV_TRUE; 1.142 + } 1.143 + } 1.144 + return LIBYUV_TRUE; 1.145 +} 1.146 + 1.147 +static int DivideAndRoundUp(int numerator, int denominator) { 1.148 + return (numerator + denominator - 1) / denominator; 1.149 +} 1.150 + 1.151 +static int DivideAndRoundDown(int numerator, int denominator) { 1.152 + return numerator / denominator; 1.153 +} 1.154 + 1.155 +// Returns width of the last loaded frame. 1.156 +int MJpegDecoder::GetWidth() { 1.157 + return decompress_struct_->image_width; 1.158 +} 1.159 + 1.160 +// Returns height of the last loaded frame. 1.161 +int MJpegDecoder::GetHeight() { 1.162 + return decompress_struct_->image_height; 1.163 +} 1.164 + 1.165 +// Returns format of the last loaded frame. The return value is one of the 1.166 +// kColorSpace* constants. 1.167 +int MJpegDecoder::GetColorSpace() { 1.168 + return decompress_struct_->jpeg_color_space; 1.169 +} 1.170 + 1.171 +// Number of color components in the color space. 1.172 +int MJpegDecoder::GetNumComponents() { 1.173 + return decompress_struct_->num_components; 1.174 +} 1.175 + 1.176 +// Sample factors of the n-th component. 1.177 +int MJpegDecoder::GetHorizSampFactor(int component) { 1.178 + return decompress_struct_->comp_info[component].h_samp_factor; 1.179 +} 1.180 + 1.181 +int MJpegDecoder::GetVertSampFactor(int component) { 1.182 + return decompress_struct_->comp_info[component].v_samp_factor; 1.183 +} 1.184 + 1.185 +int MJpegDecoder::GetHorizSubSampFactor(int component) { 1.186 + return decompress_struct_->max_h_samp_factor / 1.187 + GetHorizSampFactor(component); 1.188 +} 1.189 + 1.190 +int MJpegDecoder::GetVertSubSampFactor(int component) { 1.191 + return decompress_struct_->max_v_samp_factor / 1.192 + GetVertSampFactor(component); 1.193 +} 1.194 + 1.195 +int MJpegDecoder::GetImageScanlinesPerImcuRow() { 1.196 + return decompress_struct_->max_v_samp_factor * DCTSIZE; 1.197 +} 1.198 + 1.199 +int MJpegDecoder::GetComponentScanlinesPerImcuRow(int component) { 1.200 + int vs = GetVertSubSampFactor(component); 1.201 + return DivideAndRoundUp(GetImageScanlinesPerImcuRow(), vs); 1.202 +} 1.203 + 1.204 +int MJpegDecoder::GetComponentWidth(int component) { 1.205 + int hs = GetHorizSubSampFactor(component); 1.206 + return DivideAndRoundUp(GetWidth(), hs); 1.207 +} 1.208 + 1.209 +int MJpegDecoder::GetComponentHeight(int component) { 1.210 + int vs = GetVertSubSampFactor(component); 1.211 + return DivideAndRoundUp(GetHeight(), vs); 1.212 +} 1.213 + 1.214 +// Get width in bytes padded out to a multiple of DCTSIZE 1.215 +int MJpegDecoder::GetComponentStride(int component) { 1.216 + return (GetComponentWidth(component) + DCTSIZE - 1) & ~(DCTSIZE - 1); 1.217 +} 1.218 + 1.219 +int MJpegDecoder::GetComponentSize(int component) { 1.220 + return GetComponentWidth(component) * GetComponentHeight(component); 1.221 +} 1.222 + 1.223 +LIBYUV_BOOL MJpegDecoder::UnloadFrame() { 1.224 +#ifdef HAVE_SETJMP 1.225 + if (setjmp(error_mgr_->setjmp_buffer)) { 1.226 + // We called jpeg_abort_decompress, it experienced an error, and we called 1.227 + // longjmp() and rewound the stack to here. Return error. 1.228 + return LIBYUV_FALSE; 1.229 + } 1.230 +#endif 1.231 + jpeg_abort_decompress(decompress_struct_); 1.232 + return LIBYUV_TRUE; 1.233 +} 1.234 + 1.235 +// TODO(fbarchard): Allow rectangle to be specified: x, y, width, height. 1.236 +LIBYUV_BOOL MJpegDecoder::DecodeToBuffers( 1.237 + uint8** planes, int dst_width, int dst_height) { 1.238 + if (dst_width != GetWidth() || 1.239 + dst_height > GetHeight()) { 1.240 + // ERROR: Bad dimensions 1.241 + return LIBYUV_FALSE; 1.242 + } 1.243 +#ifdef HAVE_SETJMP 1.244 + if (setjmp(error_mgr_->setjmp_buffer)) { 1.245 + // We called into jpeglib, it experienced an error sometime during this 1.246 + // function call, and we called longjmp() and rewound the stack to here. 1.247 + // Return error. 1.248 + return LIBYUV_FALSE; 1.249 + } 1.250 +#endif 1.251 + if (!StartDecode()) { 1.252 + return LIBYUV_FALSE; 1.253 + } 1.254 + SetScanlinePointers(databuf_); 1.255 + int lines_left = dst_height; 1.256 + // Compute amount of lines to skip to implement vertical crop. 1.257 + // TODO(fbarchard): Ensure skip is a multiple of maximum component 1.258 + // subsample. ie 2 1.259 + int skip = (GetHeight() - dst_height) / 2; 1.260 + if (skip > 0) { 1.261 + // There is no API to skip lines in the output data, so we read them 1.262 + // into the temp buffer. 1.263 + while (skip >= GetImageScanlinesPerImcuRow()) { 1.264 + if (!DecodeImcuRow()) { 1.265 + FinishDecode(); 1.266 + return LIBYUV_FALSE; 1.267 + } 1.268 + skip -= GetImageScanlinesPerImcuRow(); 1.269 + } 1.270 + if (skip > 0) { 1.271 + // Have a partial iMCU row left over to skip. Must read it and then 1.272 + // copy the parts we want into the destination. 1.273 + if (!DecodeImcuRow()) { 1.274 + FinishDecode(); 1.275 + return LIBYUV_FALSE; 1.276 + } 1.277 + for (int i = 0; i < num_outbufs_; ++i) { 1.278 + // TODO(fbarchard): Compute skip to avoid this 1.279 + assert(skip % GetVertSubSampFactor(i) == 0); 1.280 + int rows_to_skip = 1.281 + DivideAndRoundDown(skip, GetVertSubSampFactor(i)); 1.282 + int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i) - 1.283 + rows_to_skip; 1.284 + int data_to_skip = rows_to_skip * GetComponentStride(i); 1.285 + CopyPlane(databuf_[i] + data_to_skip, GetComponentStride(i), 1.286 + planes[i], GetComponentWidth(i), 1.287 + GetComponentWidth(i), scanlines_to_copy); 1.288 + planes[i] += scanlines_to_copy * GetComponentWidth(i); 1.289 + } 1.290 + lines_left -= (GetImageScanlinesPerImcuRow() - skip); 1.291 + } 1.292 + } 1.293 + 1.294 + // Read full MCUs but cropped horizontally 1.295 + for (; lines_left > GetImageScanlinesPerImcuRow(); 1.296 + lines_left -= GetImageScanlinesPerImcuRow()) { 1.297 + if (!DecodeImcuRow()) { 1.298 + FinishDecode(); 1.299 + return LIBYUV_FALSE; 1.300 + } 1.301 + for (int i = 0; i < num_outbufs_; ++i) { 1.302 + int scanlines_to_copy = GetComponentScanlinesPerImcuRow(i); 1.303 + CopyPlane(databuf_[i], GetComponentStride(i), 1.304 + planes[i], GetComponentWidth(i), 1.305 + GetComponentWidth(i), scanlines_to_copy); 1.306 + planes[i] += scanlines_to_copy * GetComponentWidth(i); 1.307 + } 1.308 + } 1.309 + 1.310 + if (lines_left > 0) { 1.311 + // Have a partial iMCU row left over to decode. 1.312 + if (!DecodeImcuRow()) { 1.313 + FinishDecode(); 1.314 + return LIBYUV_FALSE; 1.315 + } 1.316 + for (int i = 0; i < num_outbufs_; ++i) { 1.317 + int scanlines_to_copy = 1.318 + DivideAndRoundUp(lines_left, GetVertSubSampFactor(i)); 1.319 + CopyPlane(databuf_[i], GetComponentStride(i), 1.320 + planes[i], GetComponentWidth(i), 1.321 + GetComponentWidth(i), scanlines_to_copy); 1.322 + planes[i] += scanlines_to_copy * GetComponentWidth(i); 1.323 + } 1.324 + } 1.325 + return FinishDecode(); 1.326 +} 1.327 + 1.328 +LIBYUV_BOOL MJpegDecoder::DecodeToCallback(CallbackFunction fn, void* opaque, 1.329 + int dst_width, int dst_height) { 1.330 + if (dst_width != GetWidth() || 1.331 + dst_height > GetHeight()) { 1.332 + // ERROR: Bad dimensions 1.333 + return LIBYUV_FALSE; 1.334 + } 1.335 +#ifdef HAVE_SETJMP 1.336 + if (setjmp(error_mgr_->setjmp_buffer)) { 1.337 + // We called into jpeglib, it experienced an error sometime during this 1.338 + // function call, and we called longjmp() and rewound the stack to here. 1.339 + // Return error. 1.340 + return LIBYUV_FALSE; 1.341 + } 1.342 +#endif 1.343 + if (!StartDecode()) { 1.344 + return LIBYUV_FALSE; 1.345 + } 1.346 + SetScanlinePointers(databuf_); 1.347 + int lines_left = dst_height; 1.348 + // TODO(fbarchard): Compute amount of lines to skip to implement vertical crop 1.349 + int skip = (GetHeight() - dst_height) / 2; 1.350 + if (skip > 0) { 1.351 + while (skip >= GetImageScanlinesPerImcuRow()) { 1.352 + if (!DecodeImcuRow()) { 1.353 + FinishDecode(); 1.354 + return LIBYUV_FALSE; 1.355 + } 1.356 + skip -= GetImageScanlinesPerImcuRow(); 1.357 + } 1.358 + if (skip > 0) { 1.359 + // Have a partial iMCU row left over to skip. 1.360 + if (!DecodeImcuRow()) { 1.361 + FinishDecode(); 1.362 + return LIBYUV_FALSE; 1.363 + } 1.364 + for (int i = 0; i < num_outbufs_; ++i) { 1.365 + // TODO(fbarchard): Compute skip to avoid this 1.366 + assert(skip % GetVertSubSampFactor(i) == 0); 1.367 + int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i)); 1.368 + int data_to_skip = rows_to_skip * GetComponentStride(i); 1.369 + // Change our own data buffer pointers so we can pass them to the 1.370 + // callback. 1.371 + databuf_[i] += data_to_skip; 1.372 + } 1.373 + int scanlines_to_copy = GetImageScanlinesPerImcuRow() - skip; 1.374 + (*fn)(opaque, databuf_, databuf_strides_, scanlines_to_copy); 1.375 + // Now change them back. 1.376 + for (int i = 0; i < num_outbufs_; ++i) { 1.377 + int rows_to_skip = DivideAndRoundDown(skip, GetVertSubSampFactor(i)); 1.378 + int data_to_skip = rows_to_skip * GetComponentStride(i); 1.379 + databuf_[i] -= data_to_skip; 1.380 + } 1.381 + lines_left -= scanlines_to_copy; 1.382 + } 1.383 + } 1.384 + // Read full MCUs until we get to the crop point. 1.385 + for (; lines_left >= GetImageScanlinesPerImcuRow(); 1.386 + lines_left -= GetImageScanlinesPerImcuRow()) { 1.387 + if (!DecodeImcuRow()) { 1.388 + FinishDecode(); 1.389 + return LIBYUV_FALSE; 1.390 + } 1.391 + (*fn)(opaque, databuf_, databuf_strides_, GetImageScanlinesPerImcuRow()); 1.392 + } 1.393 + if (lines_left > 0) { 1.394 + // Have a partial iMCU row left over to decode. 1.395 + if (!DecodeImcuRow()) { 1.396 + FinishDecode(); 1.397 + return LIBYUV_FALSE; 1.398 + } 1.399 + (*fn)(opaque, databuf_, databuf_strides_, lines_left); 1.400 + } 1.401 + return FinishDecode(); 1.402 +} 1.403 + 1.404 +void MJpegDecoder::init_source(j_decompress_ptr cinfo) { 1.405 + fill_input_buffer(cinfo); 1.406 +} 1.407 + 1.408 +boolean MJpegDecoder::fill_input_buffer(j_decompress_ptr cinfo) { 1.409 + BufferVector* buf_vec = (BufferVector*)(cinfo->client_data); 1.410 + if (buf_vec->pos >= buf_vec->len) { 1.411 + assert(0 && "No more data"); 1.412 + // ERROR: No more data 1.413 + return FALSE; 1.414 + } 1.415 + cinfo->src->next_input_byte = buf_vec->buffers[buf_vec->pos].data; 1.416 + cinfo->src->bytes_in_buffer = buf_vec->buffers[buf_vec->pos].len; 1.417 + ++buf_vec->pos; 1.418 + return TRUE; 1.419 +} 1.420 + 1.421 +void MJpegDecoder::skip_input_data(j_decompress_ptr cinfo, 1.422 + long num_bytes) { // NOLINT 1.423 + cinfo->src->next_input_byte += num_bytes; 1.424 +} 1.425 + 1.426 +void MJpegDecoder::term_source(j_decompress_ptr cinfo) { 1.427 + // Nothing to do. 1.428 +} 1.429 + 1.430 +#ifdef HAVE_SETJMP 1.431 +void MJpegDecoder::ErrorHandler(j_common_ptr cinfo) { 1.432 + // This is called when a jpeglib command experiences an error. Unfortunately 1.433 + // jpeglib's error handling model is not very flexible, because it expects the 1.434 + // error handler to not return--i.e., it wants the program to terminate. To 1.435 + // recover from errors we use setjmp() as shown in their example. setjmp() is 1.436 + // C's implementation for the "call with current continuation" functionality 1.437 + // seen in some functional programming languages. 1.438 + // A formatted message can be output, but is unsafe for release. 1.439 +#ifdef DEBUG 1.440 + char buf[JMSG_LENGTH_MAX]; 1.441 + (*cinfo->err->format_message)(cinfo, buf); 1.442 + // ERROR: Error in jpeglib: buf 1.443 +#endif 1.444 + 1.445 + SetJmpErrorMgr* mgr = (SetJmpErrorMgr*)(cinfo->err); 1.446 + // This rewinds the call stack to the point of the corresponding setjmp() 1.447 + // and causes it to return (for a second time) with value 1. 1.448 + longjmp(mgr->setjmp_buffer, 1); 1.449 +} 1.450 +#endif 1.451 + 1.452 +void MJpegDecoder::AllocOutputBuffers(int num_outbufs) { 1.453 + if (num_outbufs != num_outbufs_) { 1.454 + // We could perhaps optimize this case to resize the output buffers without 1.455 + // necessarily having to delete and recreate each one, but it's not worth 1.456 + // it. 1.457 + DestroyOutputBuffers(); 1.458 + 1.459 + scanlines_ = new uint8** [num_outbufs]; 1.460 + scanlines_sizes_ = new int[num_outbufs]; 1.461 + databuf_ = new uint8* [num_outbufs]; 1.462 + databuf_strides_ = new int[num_outbufs]; 1.463 + 1.464 + for (int i = 0; i < num_outbufs; ++i) { 1.465 + scanlines_[i] = NULL; 1.466 + scanlines_sizes_[i] = 0; 1.467 + databuf_[i] = NULL; 1.468 + databuf_strides_[i] = 0; 1.469 + } 1.470 + 1.471 + num_outbufs_ = num_outbufs; 1.472 + } 1.473 +} 1.474 + 1.475 +void MJpegDecoder::DestroyOutputBuffers() { 1.476 + for (int i = 0; i < num_outbufs_; ++i) { 1.477 + delete [] scanlines_[i]; 1.478 + delete [] databuf_[i]; 1.479 + } 1.480 + delete [] scanlines_; 1.481 + delete [] databuf_; 1.482 + delete [] scanlines_sizes_; 1.483 + delete [] databuf_strides_; 1.484 + scanlines_ = NULL; 1.485 + databuf_ = NULL; 1.486 + scanlines_sizes_ = NULL; 1.487 + databuf_strides_ = NULL; 1.488 + num_outbufs_ = 0; 1.489 +} 1.490 + 1.491 +// JDCT_IFAST and do_block_smoothing improve performance substantially. 1.492 +LIBYUV_BOOL MJpegDecoder::StartDecode() { 1.493 + decompress_struct_->raw_data_out = TRUE; 1.494 + decompress_struct_->dct_method = JDCT_IFAST; // JDCT_ISLOW is default 1.495 + decompress_struct_->dither_mode = JDITHER_NONE; 1.496 + // Not applicable to 'raw': 1.497 + decompress_struct_->do_fancy_upsampling = LIBYUV_FALSE; 1.498 + // Only for buffered mode: 1.499 + decompress_struct_->enable_2pass_quant = LIBYUV_FALSE; 1.500 + // Blocky but fast: 1.501 + decompress_struct_->do_block_smoothing = LIBYUV_FALSE; 1.502 + 1.503 + if (!jpeg_start_decompress(decompress_struct_)) { 1.504 + // ERROR: Couldn't start JPEG decompressor"; 1.505 + return LIBYUV_FALSE; 1.506 + } 1.507 + return LIBYUV_TRUE; 1.508 +} 1.509 + 1.510 +LIBYUV_BOOL MJpegDecoder::FinishDecode() { 1.511 + // jpeglib considers it an error if we finish without decoding the whole 1.512 + // image, so we call "abort" rather than "finish". 1.513 + jpeg_abort_decompress(decompress_struct_); 1.514 + return LIBYUV_TRUE; 1.515 +} 1.516 + 1.517 +void MJpegDecoder::SetScanlinePointers(uint8** data) { 1.518 + for (int i = 0; i < num_outbufs_; ++i) { 1.519 + uint8* data_i = data[i]; 1.520 + for (int j = 0; j < scanlines_sizes_[i]; ++j) { 1.521 + scanlines_[i][j] = data_i; 1.522 + data_i += GetComponentStride(i); 1.523 + } 1.524 + } 1.525 +} 1.526 + 1.527 +inline LIBYUV_BOOL MJpegDecoder::DecodeImcuRow() { 1.528 + return (unsigned int)(GetImageScanlinesPerImcuRow()) == 1.529 + jpeg_read_raw_data(decompress_struct_, 1.530 + scanlines_, 1.531 + GetImageScanlinesPerImcuRow()); 1.532 +} 1.533 + 1.534 +// The helper function which recognizes the jpeg sub-sampling type. 1.535 +JpegSubsamplingType MJpegDecoder::JpegSubsamplingTypeHelper( 1.536 + int* subsample_x, int* subsample_y, int number_of_components) { 1.537 + if (number_of_components == 3) { // Color images. 1.538 + if (subsample_x[0] == 1 && subsample_y[0] == 1 && 1.539 + subsample_x[1] == 2 && subsample_y[1] == 2 && 1.540 + subsample_x[2] == 2 && subsample_y[2] == 2) { 1.541 + return kJpegYuv420; 1.542 + } else if (subsample_x[0] == 1 && subsample_y[0] == 1 && 1.543 + subsample_x[1] == 2 && subsample_y[1] == 1 && 1.544 + subsample_x[2] == 2 && subsample_y[2] == 1) { 1.545 + return kJpegYuv422; 1.546 + } else if (subsample_x[0] == 1 && subsample_y[0] == 1 && 1.547 + subsample_x[1] == 1 && subsample_y[1] == 1 && 1.548 + subsample_x[2] == 1 && subsample_y[2] == 1) { 1.549 + return kJpegYuv444; 1.550 + } 1.551 + } else if (number_of_components == 1) { // Grey-scale images. 1.552 + if (subsample_x[0] == 1 && subsample_y[0] == 1) { 1.553 + return kJpegYuv400; 1.554 + } 1.555 + } 1.556 + return kJpegUnknown; 1.557 +} 1.558 + 1.559 +} // namespace libyuv 1.560 +#endif // HAVE_JPEG 1.561 +