Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: src/codec/SkJpegCodec.cpp

Issue 1092303003: Scanline decoding for jpeg (Closed) Base URL: https://skia.googlesource.com/skia.git@index-scanline
Patch Set: Return a reference to ImageInfo Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/codec/SkJpegCodec.h ('k') | tests/CodexTest.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkCodec.h" 8 #include "SkCodec.h"
9 #include "SkJpegCodec.h" 9 #include "SkJpegCodec.h"
10 #include "SkJpegDecoderMgr.h" 10 #include "SkJpegDecoderMgr.h"
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 streamDeleter.detach(); 161 streamDeleter.detach();
162 return codec; 162 return codec;
163 } 163 }
164 return NULL; 164 return NULL;
165 } 165 }
166 166
167 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, 167 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream,
168 JpegDecoderMgr* decoderMgr) 168 JpegDecoderMgr* decoderMgr)
169 : INHERITED(srcInfo, stream) 169 : INHERITED(srcInfo, stream)
170 , fDecoderMgr(decoderMgr) 170 , fDecoderMgr(decoderMgr)
171 , fSwizzler(NULL)
172 , fSrcRowBytes(0)
171 {} 173 {}
172 174
173 /* 175 /*
174 * Return a valid set of output dimensions for this decoder, given an input scal e 176 * Return a valid set of output dimensions for this decoder, given an input scal e
175 */ 177 */
176 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const { 178 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
177 // libjpeg supports scaling by 1/1, 1/2, 1/4, and 1/8, so we will support th ese as well 179 // libjpeg supports scaling by 1/1, 1/2, 1/4, and 1/8, so we will support th ese as well
178 long scale; 180 long scale;
179 if (desiredScale > 0.75f) { 181 if (desiredScale > 0.75f) {
180 scale = 1; 182 scale = 1;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 // Always allow kN32 as the color type 222 // Always allow kN32 as the color type
221 if (kN32_SkColorType == dst.colorType()) { 223 if (kN32_SkColorType == dst.colorType()) {
222 return true; 224 return true;
223 } 225 }
224 226
225 // Otherwise require that the destination color type match our recommendatio n 227 // Otherwise require that the destination color type match our recommendatio n
226 return dst.colorType() == src.colorType(); 228 return dst.colorType() == src.colorType();
227 } 229 }
228 230
229 /* 231 /*
232 * Handles rewinding the input stream if it is necessary
233 */
234 bool SkJpegCodec::handleRewind() {
235 switch(this->rewindIfNeeded()) {
236 case kCouldNotRewind_RewindState:
237 return fDecoderMgr->returnFalse("could not rewind");
238 case kRewound_RewindState: {
239 JpegDecoderMgr* decoderMgr = NULL;
240 if (!ReadHeader(this->stream(), NULL, &decoderMgr)) {
241 return fDecoderMgr->returnFalse("could not rewind");
242 }
243 SkASSERT(NULL != decoderMgr);
244 fDecoderMgr.reset(decoderMgr);
245 return true;
246 }
247 case kNoRewindNecessary_RewindState:
248 return true;
249 default:
250 SkASSERT(false);
251 return false;
252 }
253 }
254
255 /*
256 * Checks if we can scale to the requested dimensions and scales the dimensions
257 * if possible
258 */
259 bool SkJpegCodec::scaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) {
260 // libjpeg can scale to 1/1, 1/2, 1/4, and 1/8
261 SkASSERT(1 == fDecoderMgr->dinfo()->scale_num);
262 SkASSERT(1 == fDecoderMgr->dinfo()->scale_denom);
263 jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
264 while (fDecoderMgr->dinfo()->output_width != dstWidth ||
265 fDecoderMgr->dinfo()->output_height != dstHeight) {
266
267 // Return a failure if we have tried all of the possible scales
268 if (8 == fDecoderMgr->dinfo()->scale_denom ||
269 dstWidth > fDecoderMgr->dinfo()->output_width ||
270 dstHeight > fDecoderMgr->dinfo()->output_height) {
271 return fDecoderMgr->returnFalse("could not scale to requested dimens ions");
272 }
273
274 // Try the next scale
275 fDecoderMgr->dinfo()->scale_denom *= 2;
276 jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
277 }
278 return true;
279 }
280
281 /*
282 * Create the swizzler based on the encoded format
283 */
284 void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo,
285 void* dst, size_t dstRowBytes,
286 const Options& options) {
287 SkSwizzler::SrcConfig srcConfig = get_src_config(*fDecoderMgr->dinfo());
288 fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, NULL, dstInfo, dst, ds tRowBytes,
289 options.fZeroInitialized));
290 fSrcRowBytes = SkSwizzler::BytesPerPixel(srcConfig) * dstInfo.width();
291 }
292
293 /*
230 * Performs the jpeg decode 294 * Performs the jpeg decode
231 */ 295 */
232 SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, 296 SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
233 void* dst, size_t dstRowBytes, 297 void* dst, size_t dstRowBytes,
234 const Options& options, SkPMColor*, int *) { 298 const Options& options, SkPMColor*, int *) {
299
235 // Rewind the stream if needed 300 // Rewind the stream if needed
236 SkCodec::RewindState rewindState = this->rewindIfNeeded(); 301 if (!this->handleRewind()) {
237 if (rewindState == kCouldNotRewind_RewindState) { 302 fDecoderMgr->returnFailure("could not rewind stream", kCouldNotRewind);
238 return kCouldNotRewind;
239 } else if (rewindState == kRewound_RewindState) {
240 JpegDecoderMgr* decoderMgr = NULL;
241 if (!ReadHeader(this->stream(), NULL, &decoderMgr)) {
242 return kCouldNotRewind;
243 }
244 SkASSERT(NULL != decoderMgr);
245 fDecoderMgr.reset(decoderMgr);
246 } 303 }
247 304
248 // Get a pointer to the decompress info since we will use it quite frequentl y 305 // Get a pointer to the decompress info since we will use it quite frequentl y
249 jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); 306 jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo();
250 307
251 // Set the jump location for libjpeg errors 308 // Set the jump location for libjpeg errors
252 if (setjmp(fDecoderMgr->getJmpBuf())) { 309 if (setjmp(fDecoderMgr->getJmpBuf())) {
253 return fDecoderMgr->returnFailure("setjmp", kInvalidInput); 310 return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
254 } 311 }
255 312
256 // Check if we can decode to the requested destination 313 // Check if we can decode to the requested destination
257 if (!conversion_possible(dstInfo, this->getInfo())) { 314 if (!conversion_possible(dstInfo, this->getInfo())) {
258 return fDecoderMgr->returnFailure("conversion_possible", kInvalidConvers ion); 315 return fDecoderMgr->returnFailure("conversion_possible", kInvalidConvers ion);
259 } 316 }
260 // Check if we can scale to the requested dimensions
261 // libjpeg can scale to 1/1, 1/2, 1/4, and 1/8
262 SkASSERT(1 == dinfo->scale_num);
263 SkASSERT(1 == dinfo->scale_denom);
264 jpeg_calc_output_dimensions(dinfo);
265 const uint32_t dstWidth = dstInfo.width();
266 const uint32_t dstHeight = dstInfo.height();
267 while (dinfo->output_width != dstWidth || dinfo->output_height != dstHeight) {
268 317
269 // Return a failure if we have tried all of the possible scales 318 // Perform the necessary scaling
270 if (8 == dinfo->scale_denom || 319 if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) {
271 dstWidth > dinfo->output_width || 320 fDecoderMgr->returnFailure("cannot scale to requested dims", kInvalidSca le);
272 dstHeight > dinfo->output_height) {
273 return fDecoderMgr->returnFailure("cannot scale to requested dims", kInvalidScale);
274 }
275
276 // Try the next scale
277 dinfo->scale_denom *= 2;
278 jpeg_calc_output_dimensions(dinfo);
279 } 321 }
280 322
281 // Now, given valid output dimensions, we can start the decompress 323 // Now, given valid output dimensions, we can start the decompress
282 if (!jpeg_start_decompress(dinfo)) { 324 if (!jpeg_start_decompress(dinfo)) {
283 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); 325 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
284 } 326 }
285 327
286 // Create the swizzler 328 // Create the swizzler
287 SkSwizzler::SrcConfig srcConfig = get_src_config(*dinfo); 329 this->initializeSwizzler(dstInfo, dst, dstRowBytes, options);
288 SkAutoTDelete<SkSwizzler> swizzler(SkSwizzler::CreateSwizzler(srcConfig, NUL L, dstInfo, dst, 330 if (NULL == fSwizzler) {
289 dstRowBytes, options.fZeroInitialized)); 331 return fDecoderMgr->returnFailure("getSwizzler", kUnimplemented);
290 if (NULL == swizzler) {
291 return fDecoderMgr->returnFailure("getSwizzler", kInvalidInput);
292 } 332 }
293 const uint32_t srcBytesPerPixel = SkSwizzler::BytesPerPixel(srcConfig);
294 333
295 // This is usually 1, but can also be 2 or 4. 334 // This is usually 1, but can also be 2 or 4.
296 // If we wanted to always read one row at a time, we could, but we will save space and time 335 // If we wanted to always read one row at a time, we could, but we will save space and time
297 // by using the recommendation from libjpeg. 336 // by using the recommendation from libjpeg.
298 const uint32_t rowsPerDecode = dinfo->rec_outbuf_height; 337 const uint32_t rowsPerDecode = dinfo->rec_outbuf_height;
299 SkASSERT(rowsPerDecode <= 4); 338 SkASSERT(rowsPerDecode <= 4);
300 339
301 // Create a buffer to contain decoded rows (libjpeg requires a 2D array) 340 // Create a buffer to contain decoded rows (libjpeg requires a 2D array)
302 const uint32_t srcRowBytes = srcBytesPerPixel * dstWidth; 341 SkASSERT(0 != fSrcRowBytes);
303 SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, srcRowBytes * row sPerDecode)); 342 SkAutoTDeleteArray<uint8_t> srcBuffer(SkNEW_ARRAY(uint8_t, fSrcRowBytes * ro wsPerDecode));
304 JSAMPLE* srcRows[4]; 343 JSAMPLE* srcRows[4];
305 uint8_t* srcPtr = srcBuffer.get(); 344 uint8_t* srcPtr = srcBuffer.get();
306 for (uint8_t i = 0; i < rowsPerDecode; i++) { 345 for (uint8_t i = 0; i < rowsPerDecode; i++) {
307 srcRows[i] = (JSAMPLE*) srcPtr; 346 srcRows[i] = (JSAMPLE*) srcPtr;
308 srcPtr += srcRowBytes; 347 srcPtr += fSrcRowBytes;
309 } 348 }
310 349
311 // Ensure that we loop enough times to decode all of the rows 350 // Ensure that we loop enough times to decode all of the rows
312 // libjpeg will prevent us from reading past the bottom of the image 351 // libjpeg will prevent us from reading past the bottom of the image
352 uint32_t dstHeight = dstInfo.height();
313 for (uint32_t y = 0; y < dstHeight + rowsPerDecode - 1; y += rowsPerDecode) { 353 for (uint32_t y = 0; y < dstHeight + rowsPerDecode - 1; y += rowsPerDecode) {
314 // Read rows of the image 354 // Read rows of the image
315 uint32_t rowsDecoded = jpeg_read_scanlines(dinfo, srcRows, rowsPerDecode ); 355 uint32_t rowsDecoded = jpeg_read_scanlines(dinfo, srcRows, rowsPerDecode );
316 356
317 // Convert to RGB if necessary 357 // Convert to RGB if necessary
318 if (JCS_CMYK == dinfo->out_color_space) { 358 if (JCS_CMYK == dinfo->out_color_space) {
319 convert_CMYK_to_RGB(srcRows[0], dstWidth * rowsDecoded); 359 convert_CMYK_to_RGB(srcRows[0], dstInfo.width() * rowsDecoded);
320 } 360 }
321 361
322 // Swizzle to output destination 362 // Swizzle to output destination
323 for (uint32_t i = 0; i < rowsDecoded; i++) { 363 for (uint32_t i = 0; i < rowsDecoded; i++) {
324 swizzler->next(srcRows[i]); 364 fSwizzler->next(srcRows[i]);
325 } 365 }
326 366
327 // If we cannot read enough rows, assume the input is incomplete 367 // If we cannot read enough rows, assume the input is incomplete
328 if (rowsDecoded < rowsPerDecode && y + rowsDecoded < dstHeight) { 368 if (rowsDecoded < rowsPerDecode && y + rowsDecoded < dstHeight) {
329 // Fill the remainder of the image with black. This error handling 369 // Fill the remainder of the image with black. This error handling
330 // behavior is unspecified but SkCodec consistently uses black as 370 // behavior is unspecified but SkCodec consistently uses black as
331 // the fill color for opaque images. If the destination is kGray, 371 // the fill color for opaque images. If the destination is kGray,
332 // the low 8 bits of SK_ColorBLACK will be used. Conveniently, 372 // the low 8 bits of SK_ColorBLACK will be used. Conveniently,
333 // these are zeros, which is the representation for black in kGray. 373 // these are zeros, which is the representation for black in kGray.
334 SkSwizzler::Fill(swizzler->getDstRow(), dstInfo, dstRowBytes, 374 SkSwizzler::Fill(fSwizzler->getDstRow(), dstInfo, dstRowBytes,
335 dstHeight - y - rowsDecoded, SK_ColorBLACK, NULL); 375 dstHeight - y - rowsDecoded, SK_ColorBLACK, NULL);
336 376
337 // Prevent libjpeg from failing on incomplete decode 377 // Prevent libjpeg from failing on incomplete decode
338 dinfo->output_scanline = dstHeight; 378 dinfo->output_scanline = dstHeight;
339 379
340 // Finish the decode and indicate that the input was incomplete. 380 // Finish the decode and indicate that the input was incomplete.
341 jpeg_finish_decompress(dinfo); 381 jpeg_finish_decompress(dinfo);
342 return fDecoderMgr->returnFailure("Incomplete image data", kIncomple teInput); 382 return fDecoderMgr->returnFailure("Incomplete image data", kIncomple teInput);
343 } 383 }
344 } 384 }
345 jpeg_finish_decompress(dinfo); 385 jpeg_finish_decompress(dinfo);
346 386
347 return kSuccess; 387 return kSuccess;
348 } 388 }
389
390 /*
391 * Enable scanline decoding for jpegs
392 */
393 class SkJpegScanlineDecoder : public SkScanlineDecoder {
394 public:
395 SkJpegScanlineDecoder(const SkImageInfo& dstInfo, SkJpegCodec* codec)
396 : INHERITED(dstInfo)
397 , fCodec(codec)
398 {
399 fStorage.reset(fCodec->fSrcRowBytes);
400 fSrcRow = static_cast<uint8_t*>(fStorage.get());
401 }
402
403 SkImageGenerator::Result onGetScanlines(void* dst, int count, size_t rowByte s) override {
404 // Set the jump location for libjpeg errors
405 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
406 return fCodec->fDecoderMgr->returnFailure("setjmp", SkImageGenerator ::kInvalidInput);
407 }
408
409 // Read rows one at a time
410 for (int y = 0; y < count; y++) {
411 // Read row of the image
412 uint32_t rowsDecoded = jpeg_read_scanlines(fCodec->fDecoderMgr->dinf o(), &fSrcRow, 1);
413 if (rowsDecoded != 1) {
414 SkSwizzler::Fill(dst, this->dstInfo(), rowBytes, count - y, SK_C olorBLACK, NULL);
415 return SkImageGenerator::kIncompleteInput;
416 }
417
418 // Convert to RGB if necessary
419 if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) {
420 convert_CMYK_to_RGB(fSrcRow, dstInfo().width());
421 }
422
423 // Swizzle to output destination
424 fCodec->fSwizzler->setDstRow(dst);
425 fCodec->fSwizzler->next(fSrcRow);
426 dst = SkTAddOffset<void>(dst, rowBytes);
427 }
428
429 return SkImageGenerator::kSuccess;
430 }
431
432 SkImageGenerator::Result onSkipScanlines(int count) override {
433 // Set the jump location for libjpeg errors
434 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
435 return fCodec->fDecoderMgr->returnFailure("setjmp", SkImageGenerator ::kInvalidInput);
436 }
437
438 // Read rows but ignore the output
439 for (int y = 0; y < count; y++) {
440 jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &fSrcRow, 1);
441 }
442
443 return SkImageGenerator::kSuccess;
444 }
445
446 void onFinish() override {
447 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
448 SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n");
449 return;
450 }
451
452 jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo());
453 }
454
455 private:
456 SkJpegCodec* fCodec; // unowned
457 SkAutoMalloc fStorage;
458 uint8_t* fSrcRow; // ptr into fStorage
459
460 typedef SkScanlineDecoder INHERITED;
461 };
462
463 SkScanlineDecoder* SkJpegCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo,
464 const Options& options, SkPMColor ctable[], int* ctableCount) {
465
466 // Rewind the stream if needed
467 if (!this->handleRewind()) {
468 SkCodecPrintf("Could not rewind\n");
469 return NULL;
470 }
471
472 // Set the jump location for libjpeg errors
473 if (setjmp(fDecoderMgr->getJmpBuf())) {
474 SkCodecPrintf("setjmp: Error from libjpeg\n");
475 return NULL;
476 }
477
478 // Check if we can decode to the requested destination
479 if (!conversion_possible(dstInfo, this->getInfo())) {
480 SkCodecPrintf("Cannot convert to output type\n");
481 return NULL;
482 }
483
484 // Perform the necessary scaling
485 if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) {
486 SkCodecPrintf("Cannot scale ot output dimensions\n");
487 return NULL;
488 }
489
490 // Now, given valid output dimensions, we can start the decompress
491 if (!jpeg_start_decompress(fDecoderMgr->dinfo())) {
492 SkCodecPrintf("start decompress failed\n");
493 return NULL;
494 }
495
496 // Create the swizzler
497 this->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options);
498 if (NULL == fSwizzler) {
499 SkCodecPrintf("Could not create swizzler\n");
500 return NULL;
501 }
502
503 // Return the new scanline decoder
504 return SkNEW_ARGS(SkJpegScanlineDecoder, (dstInfo, this));
505 }
OLDNEW
« no previous file with comments | « src/codec/SkJpegCodec.h ('k') | tests/CodexTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698