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

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

Issue 1260673002: SkScaledCodec class (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Move Jpeg Swizzler to ScanlineDecoder Created 5 years, 4 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
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"
11 #include "SkJpegUtility_codec.h" 11 #include "SkJpegUtility_codec.h"
12 #include "SkCodecPriv.h" 12 #include "SkCodecPriv.h"
13 #include "SkColorPriv.h" 13 #include "SkColorPriv.h"
14 #include "SkScaledCodec.h"
14 #include "SkScanlineDecoder.h" 15 #include "SkScanlineDecoder.h"
15 #include "SkStream.h" 16 #include "SkStream.h"
16 #include "SkTemplates.h" 17 #include "SkTemplates.h"
17 #include "SkTypes.h" 18 #include "SkTypes.h"
18 19
19 // stdio is needed for libjpeg-turbo 20 // stdio is needed for libjpeg-turbo
20 #include <stdio.h> 21 #include <stdio.h>
21 22
22 extern "C" { 23 extern "C" {
23 #include "jpeglibmangler.h" 24 #include "jpeglibmangler.h"
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
142 return NULL; 143 return NULL;
143 } 144 }
144 145
145 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, 146 SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream,
146 JpegDecoderMgr* decoderMgr) 147 JpegDecoderMgr* decoderMgr)
147 : INHERITED(srcInfo, stream) 148 : INHERITED(srcInfo, stream)
148 , fDecoderMgr(decoderMgr) 149 , fDecoderMgr(decoderMgr)
149 {} 150 {}
150 151
151 /* 152 /*
153 * Return the row bytes of a particular image type and width
154 */
155 static int get_row_bytes(const j_decompress_ptr dinfo) {
156 int colorBytes = (dinfo->out_color_space == JCS_RGB565) ? 2 : dinfo->out_col or_components;
157 return dinfo->output_width * colorBytes;
158
159 }
160 /*
152 * Return a valid set of output dimensions for this decoder, given an input scal e 161 * Return a valid set of output dimensions for this decoder, given an input scal e
153 */ 162 */
154 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const { 163 SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const {
155 // libjpeg-turbo supports scaling by 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1, so we will 164 // libjpeg-turbo supports scaling by 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1, so we will
156 // support these as well 165 // support these as well
157 long num; 166 long num;
158 long denom = 8; 167 long denom = 8;
159 if (desiredScale > 0.875f) { 168 if (desiredScale > 0.875f) {
160 num = 8; 169 num = 8;
161 } else if (desiredScale > 0.75f) { 170 } else if (desiredScale > 0.75f) {
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 // much faster than decoding to color and then converting 274 // much faster than decoding to color and then converting
266 fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE; 275 fDecoderMgr->dinfo()->out_color_space = JCS_GRAYSCALE;
267 } 276 }
268 return true; 277 return true;
269 default: 278 default:
270 return false; 279 return false;
271 } 280 }
272 } 281 }
273 282
274 /* 283 /*
275 * Checks if we can scale to the requested dimensions and scales the dimensions 284 * Checks if we can natively scale to the requested dimensions and natively scal es the
276 * if possible 285 * dimensions if possible
277 */ 286 */
278 bool SkJpegCodec::scaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) { 287 bool SkJpegCodec::nativelyScaleToDimensions(uint32_t dstWidth, uint32_t dstHeigh t) {
279 // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1 288 // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1
280 fDecoderMgr->dinfo()->scale_denom = 8; 289 fDecoderMgr->dinfo()->scale_denom = 8;
281 fDecoderMgr->dinfo()->scale_num = 8; 290 fDecoderMgr->dinfo()->scale_num = 8;
282 turbo_jpeg_calc_output_dimensions(fDecoderMgr->dinfo()); 291 turbo_jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
283 while (fDecoderMgr->dinfo()->output_width != dstWidth || 292 while (fDecoderMgr->dinfo()->output_width != dstWidth ||
284 fDecoderMgr->dinfo()->output_height != dstHeight) { 293 fDecoderMgr->dinfo()->output_height != dstHeight) {
285 294
286 // Return a failure if we have tried all of the possible scales 295 // Return a failure if we have tried all of the possible scales
287 if (1 == fDecoderMgr->dinfo()->scale_num || 296 if (1 == fDecoderMgr->dinfo()->scale_num ||
288 dstWidth > fDecoderMgr->dinfo()->output_width || 297 dstWidth > fDecoderMgr->dinfo()->output_width ||
289 dstHeight > fDecoderMgr->dinfo()->output_height) { 298 dstHeight > fDecoderMgr->dinfo()->output_height) {
290 return fDecoderMgr->returnFalse("could not scale to requested dimens ions"); 299 // reset native scale settings on failure because this may be suppor ted by the swizzler
300 this->fDecoderMgr->dinfo()->scale_num = 8;
301 turbo_jpeg_calc_output_dimensions(this->fDecoderMgr->dinfo());
302 return false;
291 } 303 }
292 304
293 // Try the next scale 305 // Try the next scale
294 fDecoderMgr->dinfo()->scale_num -= 1; 306 fDecoderMgr->dinfo()->scale_num -= 1;
295 turbo_jpeg_calc_output_dimensions(fDecoderMgr->dinfo()); 307 turbo_jpeg_calc_output_dimensions(fDecoderMgr->dinfo());
296 } 308 }
297 return true; 309 return true;
298 } 310 }
299 311
300 /* 312 /*
(...skipping 19 matching lines...) Expand all
320 if (setjmp(fDecoderMgr->getJmpBuf())) { 332 if (setjmp(fDecoderMgr->getJmpBuf())) {
321 return fDecoderMgr->returnFailure("setjmp", kInvalidInput); 333 return fDecoderMgr->returnFailure("setjmp", kInvalidInput);
322 } 334 }
323 335
324 // Check if we can decode to the requested destination and set the output co lor space 336 // Check if we can decode to the requested destination and set the output co lor space
325 if (!this->setOutputColorSpace(dstInfo)) { 337 if (!this->setOutputColorSpace(dstInfo)) {
326 return fDecoderMgr->returnFailure("conversion_possible", kInvalidConvers ion); 338 return fDecoderMgr->returnFailure("conversion_possible", kInvalidConvers ion);
327 } 339 }
328 340
329 // Perform the necessary scaling 341 // Perform the necessary scaling
330 if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) { 342 if (!this->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) {
331 return fDecoderMgr->returnFailure("cannot scale to requested dims", kInv alidScale); 343 return fDecoderMgr->returnFailure("cannot scale to requested dims", kInv alidScale);
332 } 344 }
333 345
334 // Now, given valid output dimensions, we can start the decompress 346 // Now, given valid output dimensions, we can start the decompress
335 if (!turbo_jpeg_start_decompress(dinfo)) { 347 if (!turbo_jpeg_start_decompress(dinfo)) {
336 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); 348 return fDecoderMgr->returnFailure("startDecompress", kInvalidInput);
337 } 349 }
338 350
339 // The recommended output buffer height should always be 1 in high quality m odes. 351 // The recommended output buffer height should always be 1 in high quality m odes.
340 // If it's not, we want to know because it means our strategy is not optimal . 352 // If it's not, we want to know because it means our strategy is not optimal .
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
388 * Enable scanline decoding for jpegs 400 * Enable scanline decoding for jpegs
389 */ 401 */
390 class SkJpegScanlineDecoder : public SkScanlineDecoder { 402 class SkJpegScanlineDecoder : public SkScanlineDecoder {
391 public: 403 public:
392 SkJpegScanlineDecoder(const SkImageInfo& srcInfo, SkJpegCodec* codec) 404 SkJpegScanlineDecoder(const SkImageInfo& srcInfo, SkJpegCodec* codec)
393 : INHERITED(srcInfo) 405 : INHERITED(srcInfo)
394 , fCodec(codec) 406 , fCodec(codec)
395 , fOpts() 407 , fOpts()
396 {} 408 {}
397 409
410 /*
411 * Return a valid set of output dimensions for this decoder, given an input s cale
412 */
413 SkISize onGetScaledDimensions(float desiredScale) override {
414 return fCodec->onGetScaledDimensions(desiredScale);
415 }
416
417 /*
418 * Create the swizzler based on the encoded format.
419 * The swizzler is only used for sampling in the x direction.
420 */
421
422 SkCodec::Result initializeSwizzler(const SkImageInfo& info, const SkCodec::O ptions& options) {
423 SkSwizzler::SrcConfig srcConfig;
424 switch (info.colorType()) {
425 case kGray_8_SkColorType:
426 srcConfig = SkSwizzler::kGray;
427 break;
428 case kRGBA_8888_SkColorType:
429 srcConfig = SkSwizzler::kRGBX;
430 break;
431 case kBGRA_8888_SkColorType:
432 srcConfig = SkSwizzler::kBGRX;
433 break;
434 case kRGB_565_SkColorType:
435 srcConfig = SkSwizzler::kRGB_565;
436 break;
437 default:
438 //would have exited before now if the colorType was supported by jpeg
439 SkASSERT(false);
440 }
441
442 fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, NULL, info, option s.fZeroInitialized,
443 this->getInfo().width()));
444 if (!fSwizzler) {
445 // FIXME: CreateSwizzler could fail for another reason.
446 return SkCodec::kUnimplemented;
447 }
448 return SkCodec::kSuccess;
449 }
450
398 SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options, 451 SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options,
399 SkPMColor ctable[], int* ctableCount) override { 452 SkPMColor ctable[], int* ctableCount) override {
400 453
401 // Rewind the stream if needed 454 // Rewind the stream if needed
402 if (!fCodec->handleRewind()) { 455 if (!fCodec->handleRewind()) {
403 return SkCodec::kCouldNotRewind; 456 return SkCodec::kCouldNotRewind;
404 } 457 }
405 458
406 // Set the jump location for libjpeg errors 459 // Set the jump location for libjpeg errors
407 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { 460 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
408 SkCodecPrintf("setjmp: Error from libjpeg\n"); 461 SkCodecPrintf("setjmp: Error from libjpeg\n");
409 return SkCodec::kInvalidInput; 462 return SkCodec::kInvalidInput;
410 } 463 }
411 464
412 // Check if we can decode to the requested destination and set the outpu t color space 465 // Check if we can decode to the requested destination and set the outpu t color space
413 if (!fCodec->setOutputColorSpace(dstInfo)) { 466 if (!fCodec->setOutputColorSpace(dstInfo)) {
414 return SkCodec::kInvalidConversion; 467 return SkCodec::kInvalidConversion;
415 } 468 }
416 469
417 // Perform the necessary scaling 470 // Perform the necessary scaling
418 if (!fCodec->scaleToDimensions(dstInfo.width(), dstInfo.height())) { 471 if (!fCodec->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height() )) {
419 return SkCodec::kInvalidScale; 472 // full native scaling to dstInfo dimensions not supported
473
474 if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) {
475 return SkCodec::kInvalidScale;
476 }
477 // create swizzler for sampling
478 if (this->initializeSwizzler(dstInfo, options) != SkCodec::kSuccess) {
scroggo 2015/08/06 15:09:11 initializeSwizzler may return kUnimplemented, and
emmaleer 2015/08/06 18:59:52 I've changed this to check the result from initial
scroggo 2015/08/06 20:37:59 Your approach makes sense. My approach would have
emmaleer 2015/08/07 18:38:56 Acknowledged.
479 SkCodecPrintf("failed to initialize the swizzler.\n");
480 return SkCodec::kInvalidScale;
481 }
482 fStorage.reset(get_row_bytes(fCodec->fDecoderMgr->dinfo()));
483 fSrcRow = static_cast<uint8_t*>(fStorage.get());
420 } 484 }
scroggo 2015/08/06 15:09:10 Should we set fSrcRow to NULL otherwise? Same ques
emmaleer 2015/08/06 18:59:52 Acknowledged.
421 485
422 // Now, given valid output dimensions, we can start the decompress 486 // Now, given valid output dimensions, we can start the decompress
423 if (!turbo_jpeg_start_decompress(fCodec->fDecoderMgr->dinfo())) { 487 if (!turbo_jpeg_start_decompress(fCodec->fDecoderMgr->dinfo())) {
424 SkCodecPrintf("start decompress failed\n"); 488 SkCodecPrintf("start decompress failed\n");
425 return SkCodec::kInvalidInput; 489 return SkCodec::kInvalidInput;
426 } 490 }
427 491
428 fOpts = options; 492 fOpts = options;
429 493
430 return SkCodec::kSuccess; 494 return SkCodec::kSuccess;
431 } 495 }
432 496
433 virtual ~SkJpegScanlineDecoder() { 497 virtual ~SkJpegScanlineDecoder() {
434 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { 498 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
435 SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n"); 499 SkCodecPrintf("setjmp: Error in libjpeg finish_decompress\n");
436 return; 500 return;
437 } 501 }
438 502
439 // We may not have decoded the entire image. Prevent libjpeg-turbo from failing on a 503 // We may not have decoded the entire image. Prevent libjpeg-turbo from failing on a
440 // partial decode. 504 // partial decode.
441 fCodec->fDecoderMgr->dinfo()->output_scanline = fCodec->getInfo().height (); 505 fCodec->fDecoderMgr->dinfo()->output_scanline = fCodec->getInfo().height ();
442 turbo_jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo()); 506 turbo_jpeg_finish_decompress(fCodec->fDecoderMgr->dinfo());
443 } 507 }
444 508
445 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri de { 509 SkCodec::Result onGetScanlines(void* dst, int count, size_t rowBytes) overri de {
446 // Set the jump location for libjpeg errors 510 // Set the jump location for libjpeg errors
447 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { 511 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
448 return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvali dInput); 512 return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvali dInput);
449 } 513 }
514 // Read rows one at a time
515 JSAMPLE* dstRow;
516 if (fSwizzler) {
517 // write data to storage row, then sample using swizzler
518 dstRow = fSrcRow;
519 } else {
520 // write data directly to dst
521 dstRow = (JSAMPLE*) dst;
522 }
450 523
451 // Read rows one at a time
452 JSAMPLE* dstRow = (JSAMPLE*) dst;
453 for (int y = 0; y < count; y++) { 524 for (int y = 0; y < count; y++) {
454 // Read row of the image 525 // Read row of the image
455 uint32_t rowsDecoded = 526 uint32_t rowsDecoded =
456 turbo_jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &dst Row, 1); 527 turbo_jpeg_read_scanlines(fCodec->fDecoderMgr->dinfo(), &dst Row, 1);
457 if (rowsDecoded != 1) { 528 if (rowsDecoded != 1) {
458 if (SkCodec::kNo_ZeroInitialized == fOpts.fZeroInitialized || 529 if (SkCodec::kNo_ZeroInitialized == fOpts.fZeroInitialized ||
459 kN32_SkColorType == this->dstInfo().colorType()) { 530 kN32_SkColorType == this->dstInfo().colorType()) {
460 SkSwizzler::Fill(dstRow, this->dstInfo(), rowBytes, 531 SkSwizzler::Fill(dstRow, this->dstInfo(), rowBytes,
461 count - y, SK_ColorBLACK, NULL); 532 count - y, SK_ColorBLACK, NULL);
462 } 533 }
463 fCodec->fDecoderMgr->dinfo()->output_scanline = this->dstInfo(). height(); 534 fCodec->fDecoderMgr->dinfo()->output_scanline = this->dstInfo(). height();
464 return SkCodec::kIncompleteInput; 535 return SkCodec::kIncompleteInput;
465 } 536 }
466 537
467 // Convert to RGBA if necessary 538 // Convert to RGBA if necessary
468 if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) { 539 if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) {
469 convert_CMYK_to_RGBA(dstRow, this->dstInfo().width()); 540 convert_CMYK_to_RGBA(dstRow, fCodec->fDecoderMgr->dinfo()->outpu t_width);
470 } 541 }
471 542
472 // Move to the next row 543 if(fSwizzler) {
473 dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes); 544 // use swizzler to sample row
545 fSwizzler->swizzle(dst, dstRow);
546 dst = SkTAddOffset<JSAMPLE>(dst, rowBytes);
547 } else {
548 dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes);
549 }
474 } 550 }
475
476 return SkCodec::kSuccess; 551 return SkCodec::kSuccess;
477 } 552 }
478 553
479 #ifndef TURBO_HAS_SKIP 554 #ifndef TURBO_HAS_SKIP
480 #define turbo_jpeg_skip_scanlines(dinfo, count) \ 555 #define turbo_jpeg_skip_scanlines(dinfo, count) \
481 SkAutoMalloc storage(dinfo->output_width * dinfo->out_color_components); \ 556 SkAutoMalloc storage(get_row_bytes(dinfo)); \
482 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); \ 557 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); \
483 for (int y = 0; y < count; y++) { \ 558 for (int y = 0; y < count; y++) { \
484 turbo_jpeg_read_scanlines(dinfo, &storagePtr, 1); \ 559 turbo_jpeg_read_scanlines(dinfo, &storagePtr, 1); \
485 } 560 }
486 #endif 561 #endif
487 562
488 SkCodec::Result onSkipScanlines(int count) override { 563 SkCodec::Result onSkipScanlines(int count) override {
489 // Set the jump location for libjpeg errors 564 // Set the jump location for libjpeg errors
490 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { 565 if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) {
491 return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvali dInput); 566 return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvali dInput);
492 } 567 }
493 568
494 turbo_jpeg_skip_scanlines(fCodec->fDecoderMgr->dinfo(), count); 569 turbo_jpeg_skip_scanlines(fCodec->fDecoderMgr->dinfo(), count);
495 570
496 return SkCodec::kSuccess; 571 return SkCodec::kSuccess;
497 } 572 }
498 573
574 SkEncodedFormat onGetEncodedFormat() const override {
575 return kJPEG_SkEncodedFormat;
576 }
577
499 private: 578 private:
500 SkAutoTDelete<SkJpegCodec> fCodec; 579 SkAutoTDelete<SkJpegCodec> fCodec;
580 SkAutoMalloc fStorage; // Only used if sampling is needed
581 uint8_t* fSrcRow; // Only used if sampling is needed
501 SkCodec::Options fOpts; 582 SkCodec::Options fOpts;
583 SkAutoTDelete<SkSwizzler> fSwizzler;
502 584
503 typedef SkScanlineDecoder INHERITED; 585 typedef SkScanlineDecoder INHERITED;
504 }; 586 };
505 587
506 SkScanlineDecoder* SkJpegCodec::NewSDFromStream(SkStream* stream) { 588 SkScanlineDecoder* SkJpegCodec::NewSDFromStream(SkStream* stream) {
507 SkAutoTDelete<SkJpegCodec> codec(static_cast<SkJpegCodec*>(SkJpegCodec::NewF romStream(stream))); 589 SkAutoTDelete<SkJpegCodec> codec(static_cast<SkJpegCodec*>(SkJpegCodec::NewF romStream(stream)));
508 if (!codec) { 590 if (!codec) {
509 return NULL; 591 return NULL;
510 } 592 }
511 593
512 const SkImageInfo& srcInfo = codec->getInfo(); 594 const SkImageInfo& srcInfo = codec->getInfo();
595
513 // Return the new scanline decoder 596 // Return the new scanline decoder
514 return SkNEW_ARGS(SkJpegScanlineDecoder, (srcInfo, codec.detach())); 597 return SkNEW_ARGS(SkJpegScanlineDecoder, (srcInfo, codec.detach()));
515 } 598 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698