Thu, 22 Jan 2015 13:21:57 +0100
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 |