media/libyuv/source/mjpeg_decoder.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial