Chromium Code Reviews| Index: src/codec/SkJpegCodec.cpp |
| diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp |
| index 84e90d444249142d69a68cd52cab59cebb37c30a..c1ae545b1c7cd4d7c10977a47308b8d1bca7a55b 100644 |
| --- a/src/codec/SkJpegCodec.cpp |
| +++ b/src/codec/SkJpegCodec.cpp |
| @@ -11,6 +11,7 @@ |
| #include "SkJpegUtility_codec.h" |
| #include "SkCodecPriv.h" |
| #include "SkColorPriv.h" |
| +#include "SkScaledCodec.h" |
| #include "SkScanlineDecoder.h" |
| #include "SkStream.h" |
| #include "SkTemplates.h" |
| @@ -146,6 +147,8 @@ SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, |
| JpegDecoderMgr* decoderMgr) |
| : INHERITED(srcInfo, stream) |
| , fDecoderMgr(decoderMgr) |
| + , fPartialNum(1) |
| + , fPartialDenom(1) |
| {} |
| /* |
| @@ -189,6 +192,44 @@ SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const { |
| return SkISize::Make(dinfo.output_width, dinfo.output_height); |
| } |
| +// The swizzler is only used for sampling in the x direction |
| +// when sampling a scanlineDecoder is used. The swizzler is not used in onGetPixels |
| +SkCodec::Result SkJpegCodec::initializeSwizzler(const SkImageInfo& requestedInfo, |
| + void* dst, size_t rowBytes, |
| + const Options& options, |
| + SkPMColor ctable[], |
| + int* ctableCount, |
| + int srcWidth) { |
| + |
| + const SkColorType srcColorType = requestedInfo.colorType(); |
| + SkSwizzler::SrcConfig srcConfig; |
| + switch (srcColorType) { |
| + case kGray_8_SkColorType: |
| + srcConfig = SkSwizzler::kGray; |
| + break; |
| + case kRGBA_8888_SkColorType: |
| + srcConfig = SkSwizzler::kRGBX; |
| + break; |
| + case kBGRA_8888_SkColorType: |
| + srcConfig = SkSwizzler::kBGRX; |
| + break; |
| + case kRGB_565_SkColorType: |
| + srcConfig = SkSwizzler::kRGB_565; |
| + break; |
| + default: |
| + //would have exited before now if the colorType was supported by jpeg |
| + SkASSERT(false); |
| + } |
| + |
| + fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, NULL, requestedInfo, |
| + options.fZeroInitialized, srcWidth)); |
| + if (!fSwizzler) { |
| + // FIXME: CreateSwizzler could fail for another reason. |
| + return kUnimplemented; |
| + } |
| + return kSuccess; |
| +} |
| + |
| /* |
| * Handles rewinding the input stream if it is necessary |
| */ |
| @@ -272,10 +313,10 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) { |
| } |
| /* |
| - * Checks if we can scale to the requested dimensions and scales the dimensions |
| - * if possible |
| + * Checks if we can natively scale to the requested dimensions and natively scales the |
| + * dimensions if possible |
| */ |
| -bool SkJpegCodec::scaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) { |
| +bool SkJpegCodec::nativelyScaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) { |
| // libjpeg-turbo can scale to 1/8, 1/4, 3/8, 1/2, 5/8, 3/4, 7/8, and 1/1 |
| fDecoderMgr->dinfo()->scale_denom = 8; |
| fDecoderMgr->dinfo()->scale_num = 8; |
| @@ -287,7 +328,10 @@ bool SkJpegCodec::scaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) { |
| if (1 == fDecoderMgr->dinfo()->scale_num || |
| dstWidth > fDecoderMgr->dinfo()->output_width || |
| dstHeight > fDecoderMgr->dinfo()->output_height) { |
| - return fDecoderMgr->returnFalse("could not scale to requested dimensions"); |
| + // reset native scale settings on failure because this may be supported by the swizzler |
| + this->fDecoderMgr->dinfo()->scale_num = 8; |
| + turbo_jpeg_calc_output_dimensions(this->fDecoderMgr->dinfo()); |
| + return false; |
| } |
| // Try the next scale |
| @@ -297,6 +341,37 @@ bool SkJpegCodec::scaleToDimensions(uint32_t dstWidth, uint32_t dstHeight) { |
| return true; |
| } |
| +int SkJpegCodec::onPartiallyNativelyScale(float desiredScale) { |
|
msarett
2015/08/04 20:39:36
I think this function is clever :)
|
| + float nativeScales[7] = {0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875}; |
|
scroggo
2015/08/04 20:05:21
nit: I think these can (should?) be static const (
emmaleer
2015/08/05 14:47:51
Acknowledged.
|
| + int numerators[7] = {1, 1, 3, 1, 5, 3, 7}; |
| + int denomonators[7] = {8, 4, 8, 2, 8, 4, 8}; |
| + |
| + int index = 0; |
| + int sampleSize = 1; |
| + float totalScale = nativeScales[index] / sampleSize; |
|
scroggo
2015/08/04 20:05:21
Can this just be "nativeScales[0]"?
Or do you thi
emmaleer
2015/08/05 14:47:51
Yes, it can just be gNativeScales[0]
|
| + |
| + while(totalScale != desiredScale) { |
|
scroggo
2015/08/04 20:05:21
nit: there should be a space between while and (
emmaleer
2015/08/05 14:47:51
Acknowledged.
|
| + if (totalScale < desiredScale) { |
| + // scaling too much, scale less natively |
| + if (index == 7) { |
| + // have tried all possible combinations, partial native scaling not supported |
| + return -1; |
| + } |
| + index++; |
| + } else { |
| + // not scaling enough, scale more by increasing sampleSize |
| + sampleSize++; |
| + } |
| + totalScale = nativeScales[index] / sampleSize; |
| + } |
| + |
| + // partial native scaling supported |
| + fPartialDenom = denomonators[index]; |
| + fPartialNum = numerators[index]; |
| + |
| + return sampleSize; |
| +} |
| + |
| /* |
| * Performs the jpeg decode |
| */ |
| @@ -327,7 +402,7 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, |
| } |
| // Perform the necessary scaling |
| - if (!this->scaleToDimensions(dstInfo.width(), dstInfo.height())) { |
| + if (!this->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) { |
| return fDecoderMgr->returnFailure("cannot scale to requested dims", kInvalidScale); |
| } |
| @@ -394,7 +469,15 @@ public: |
| : INHERITED(dstInfo) |
| , fCodec(codec) |
| , fOpts(opts) |
| - {} |
| + { |
| + if(fCodec->fSwizzler) { |
| + fStorage.reset(codec->fDecoderMgr->dinfo()->output_width * |
|
msarett
2015/08/04 20:39:36
This will always be enough, but is overkill in the
emmaleer
2015/08/05 14:47:51
Acknowledged.
|
| + codec->fDecoderMgr->dinfo()->out_color_components); |
| + fSrcRow = static_cast<uint8_t*>(fStorage.get()); |
| + } else { |
| + fSrcRow = NULL; |
| + } |
| + } |
| virtual ~SkJpegScanlineDecoder() { |
| if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { |
| @@ -413,9 +496,16 @@ public: |
| if (setjmp(fCodec->fDecoderMgr->getJmpBuf())) { |
| return fCodec->fDecoderMgr->returnFailure("setjmp", SkCodec::kInvalidInput); |
| } |
| - |
| // Read rows one at a time |
| - JSAMPLE* dstRow = (JSAMPLE*) dst; |
| + JSAMPLE* dstRow; |
| + if (fCodec->fSwizzler) { |
| + // write data to storage row, then sample using swizzler |
| + dstRow = fSrcRow; |
| + } else { |
| + // write data directly to dst |
| + dstRow = (JSAMPLE*) dst; |
| + } |
| + |
| for (int y = 0; y < count; y++) { |
| // Read row of the image |
| uint32_t rowsDecoded = |
| @@ -432,13 +522,17 @@ public: |
| // Convert to RGBA if necessary |
| if (JCS_CMYK == fCodec->fDecoderMgr->dinfo()->out_color_space) { |
| - convert_CMYK_to_RGBA(dstRow, this->dstInfo().width()); |
| + convert_CMYK_to_RGBA(dstRow, fCodec->fDecoderMgr->dinfo()->output_width); |
|
msarett
2015/08/04 20:39:36
Nice!
|
| } |
| - // Move to the next row |
| - dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes); |
| + if(fCodec->fSwizzler) { |
| + // use swizzler to sample row |
| + fCodec->fSwizzler->swizzle(dst, dstRow); |
| + dst = SkTAddOffset<JSAMPLE>(dst, rowBytes); |
| + } else { |
| + dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes); |
| + } |
| } |
| - |
| return SkCodec::kSuccess; |
| } |
| @@ -464,6 +558,8 @@ public: |
| private: |
| SkAutoTDelete<SkJpegCodec> fCodec; |
| + SkAutoMalloc fStorage; // Only used if sampling is needed |
| + uint8_t* fSrcRow; // Only used if sampling is needed |
| const SkCodec::Options& fOpts; |
| typedef SkScanlineDecoder INHERITED; |
| @@ -499,10 +595,34 @@ SkScanlineDecoder* SkJpegCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, |
| return NULL; |
| } |
| + SkImageInfo scaledInfo = dstInfo; |
| + |
| // Perform the necessary scaling |
| - if (!codec->scaleToDimensions(dstInfo.width(), dstInfo.height())) { |
| - SkCodecPrintf("Cannot scale to output dimensions\n"); |
| - return NULL; |
| + if (!codec->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) { |
| + // full native scaling to dstInfo dimensions not supported |
| + |
| + if (fPartialNum != 1 || fPartialDenom != 1) { |
| + // update num and denom as partiallyNativelyScale was previously called |
|
msarett
2015/08/04 20:39:36
Ah this is the part where we are unhappy. We need
|
| + codec->fDecoderMgr->dinfo()->scale_denom = fPartialDenom; |
| + codec->fDecoderMgr->dinfo()->scale_num = fPartialNum; |
| + turbo_jpeg_calc_output_dimensions(codec->fDecoderMgr->dinfo()); |
| + |
| + // update scanlineDecoder's info to reflect partial native scaling |
| + scaledInfo = dstInfo.makeWH(codec->fDecoderMgr->dinfo()->output_width, |
| + codec->fDecoderMgr->dinfo()->output_height); |
| + } else { |
| + // no native scaling (full or partial) supported |
| + if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { |
| + return NULL; |
| + } |
| + } |
| + // create swizzler for sampling |
| + if (codec->initializeSwizzler(dstInfo, NULL, dstInfo.minRowBytes(), options, ctable, |
| + ctableCount, codec->fDecoderMgr->dinfo()->output_width) |
| + != kSuccess) { |
| + SkCodecPrintf("failed to initialize the swizzler.\n"); |
| + return NULL; |
| + } |
| } |
| // Now, given valid output dimensions, we can start the decompress |
| @@ -512,5 +632,5 @@ SkScanlineDecoder* SkJpegCodec::onGetScanlineDecoder(const SkImageInfo& dstInfo, |
| } |
| // Return the new scanline decoder |
| - return SkNEW_ARGS(SkJpegScanlineDecoder, (dstInfo, codec.detach(), options)); |
| + return SkNEW_ARGS(SkJpegScanlineDecoder, (scaledInfo, codec.detach(), options)); |
| } |