diff options
author | emmaleer <emmaleer@google.com> | 2015-08-13 13:07:03 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-13 13:07:03 -0700 |
commit | d518ea7927f9f4e0ed5b4134d1b4f48243855a47 (patch) | |
tree | b4f71744107a3267e41ee086b6a41164791f6faa | |
parent | 1acf250f922a547018bab25676d63f3bb090be71 (diff) |
SkScaledCodec class
This class does scaling by using a scanlineDecoder.
getScanlines and skipScanlines are used for y sampling,
the swizzler is used for x sampling
this class is currently only working for png and jpeg images
I will update other Codec types to work soon
For SkJpegCodec to implement width wise swizzling it now
uses a swizzler. I ran performance tests on this change.
Here are the performance test results:
https://docs.google.com/a/google.com/spreadsheets/d/1D7-Q_GXD_dI68LZO005NNvb8Wq2Ee0wEBEPG72671yw/edit?usp=sharing
BUG=skia:
Committed: https://skia.googlesource.com/skia/+/0944100ac89f797714eeae0cf2875e2335ff52ee
Review URL: https://codereview.chromium.org/1260673002
-rw-r--r-- | dm/DM.cpp | 7 | ||||
-rw-r--r-- | dm/DMSrcSink.cpp | 24 | ||||
-rw-r--r-- | gyp/codec.gyp | 1 | ||||
-rw-r--r-- | include/codec/SkCodec.h | 2 | ||||
-rw-r--r-- | include/codec/SkScaledCodec.h | 70 | ||||
-rw-r--r-- | include/codec/SkScanlineDecoder.h | 42 | ||||
-rw-r--r-- | src/codec/SkBmpStandardCodec.cpp | 2 | ||||
-rw-r--r-- | src/codec/SkCodec_libgif.cpp | 5 | ||||
-rw-r--r-- | src/codec/SkCodec_libpng.cpp | 33 | ||||
-rw-r--r-- | src/codec/SkCodec_libpng.h | 2 | ||||
-rw-r--r-- | src/codec/SkCodec_wbmp.cpp | 15 | ||||
-rw-r--r-- | src/codec/SkJpegCodec.cpp | 117 | ||||
-rw-r--r-- | src/codec/SkJpegCodec.h | 14 | ||||
-rw-r--r-- | src/codec/SkScaledCodec.cpp | 261 | ||||
-rw-r--r-- | src/codec/SkSwizzler.cpp | 349 | ||||
-rw-r--r-- | src/codec/SkSwizzler.h | 27 |
16 files changed, 762 insertions, 209 deletions
@@ -212,7 +212,12 @@ static void push_codec_srcs(Path path) { // TODO (msarett): Add more scaling tests as we implement more flexible scaling. // TODO (msarett): Implement scaling tests for SkImageDecoder in order to compare with these // tests. SkImageDecoder supports downscales by integer factors. - const float scales[] = { 0.125f, 0.25f, 0.375f, 0.5f, 0.625f, 0.750f, 0.875f, 1.0f }; + // SkJpegCodec natively supports scaling to: 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875 + // 0.1, 0.16, 0.2 etc allow us to test SkScaledCodec with sampleSize 10, 6, 5, etc + // 0.4, 0.7 etc allow to test what happens when the client requests a scale that + // does not exactly match a sampleSize or native scaling capability + const float scales[] = { 0.1f, 0.125f, 0.166f, 0.2f, 0.25f, 0.333f, 0.375f, 0.4f, 0.5f, 0.6f, + 0.625f, 0.750f, 0.8f, 0.875f, 1.0f }; for (float scale : scales) { if (scale != 1.0f && (path.endsWith(".webp") || path.endsWith(".WEBP"))) { diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index f555b9fcbd..9826f97822 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -27,6 +27,7 @@ #include "SkScanlineDecoder.h" #include "SkStream.h" #include "SkXMLWriter.h" +#include "SkScaledCodec.h" DEFINE_bool(multiPage, false, "For document-type backends, render the source" " into multiple pages"); @@ -84,9 +85,13 @@ Error CodecSrc::draw(SkCanvas* canvas) const { if (!encoded) { return SkStringPrintf("Couldn't read %s.", fPath.c_str()); } - SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded)); + SkAutoTDelete<SkCodec> codec(SkScaledCodec::NewFromData(encoded)); if (NULL == codec.get()) { - return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str()); + // scaledCodec not supported, try normal codec + codec.reset(SkCodec::NewFromData(encoded)); + if (NULL == codec.get()) { + return SkStringPrintf("Couldn't create codec for %s.", fPath.c_str()); + } } // Choose the color type to decode to @@ -446,13 +451,16 @@ Error CodecSrc::draw(SkCanvas* canvas) const { SkISize CodecSrc::size() const { SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(fPath.c_str())); - SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded)); - if (NULL != codec) { - SkISize size = codec->getScaledDimensions(fScale); - return size; - } else { - return SkISize::Make(0, 0); + SkAutoTDelete<SkCodec> codec(SkScaledCodec::NewFromData(encoded)); + if (NULL == codec) { + // scaledCodec not supported, try regular codec + codec.reset(SkCodec::NewFromData(encoded)); + if (NULL == codec) { + return SkISize::Make(0, 0); + } } + SkISize size = codec->getScaledDimensions(fScale); + return size; } Name CodecSrc::name() const { diff --git a/gyp/codec.gyp b/gyp/codec.gyp index cc06945ce8..b34c85e7d9 100644 --- a/gyp/codec.gyp +++ b/gyp/codec.gyp @@ -47,6 +47,7 @@ '../src/codec/SkJpegUtility_codec.cpp', '../src/codec/SkMaskSwizzler.cpp', '../src/codec/SkMasks.cpp', + '../src/codec/SkScaledCodec.cpp', '../src/codec/SkScanlineDecoder.cpp', '../src/codec/SkSwizzler.cpp', '../src/codec/SkWebpCodec.cpp', diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h index a0bd18a8e5..3465c1df5b 100644 --- a/include/codec/SkCodec.h +++ b/include/codec/SkCodec.h @@ -51,6 +51,8 @@ public: * Return a size that approximately supports the desired scale factor. * The codec may not be able to scale efficiently to the exact scale * factor requested, so return a size that approximates that scale. + * The returned value is the codec's suggestion for the closest valid + * scale that it can natively support */ SkISize getScaledDimensions(float desiredScale) const { return this->onGetScaledDimensions(desiredScale); diff --git a/include/codec/SkScaledCodec.h b/include/codec/SkScaledCodec.h new file mode 100644 index 0000000000..1bcdf085b2 --- /dev/null +++ b/include/codec/SkScaledCodec.h @@ -0,0 +1,70 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SkScaledCodec_DEFINED +#define SkScaledCodec_DEFINED + +#include "SkCodec.h" +#include "SkScanlineDecoder.h" + +class SkScanlineDecoder; +class SkStream; + +/** + * This class implements scaling, by sampling scanlines in the y direction. + * x-wise sampling is implemented in the swizzler, when getScanlines() is called. + */ +class SkScaledCodec : public SkCodec { +public: + static SkCodec* NewFromStream(SkStream*); + static SkCodec* NewFromData(SkData*); + + virtual ~SkScaledCodec(); + + /** + * returns whether a destination's dimensions are supported for down sampling + */ + static bool DimensionsSupportedForSampling(const SkImageInfo& srcInfo, + const SkImageInfo& dstInfo) { + // heights must be equal as no native y sampling is supported + if (dstInfo.height() != srcInfo.height()) { + return false; + } + // only support down sampling, dstWidth cannot be larger that srcWidth + if(dstInfo.width() > srcInfo.width()) { + return false; + } + return true; + } + + static void ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, + int* sampleSizeX, int* sampleSizeY); + +protected: + /** + * Recommend a set of destination dimensions given a requested scale + */ + SkISize onGetScaledDimensions(float desiredScale) const override; + + Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, SkPMColor*, int*) + override; + SkEncodedFormat onGetEncodedFormat() const override { + return fScanlineDecoder->getEncodedFormat(); + } + + bool onReallyHasAlpha() const override { + return fScanlineDecoder->reallyHasAlpha(); + } + +private: + + SkAutoTDelete<SkScanlineDecoder> fScanlineDecoder; + + explicit SkScaledCodec(SkScanlineDecoder*); + + typedef SkCodec INHERITED; +}; +#endif // SkScaledCodec_DEFINED diff --git a/include/codec/SkScanlineDecoder.h b/include/codec/SkScanlineDecoder.h index c233663dbd..c547f6701f 100644 --- a/include/codec/SkScanlineDecoder.h +++ b/include/codec/SkScanlineDecoder.h @@ -46,6 +46,18 @@ public: virtual ~SkScanlineDecoder() {} /** + * Return a size that approximately supports the desired scale factor. + * The codec may not be able to scale efficiently to the exact scale + * factor requested, so return a size that approximates that scale. + * The returned value is the codec's suggestion for the closest valid + * scale that it can natively support + * FIXME: share this with SkCodec + */ + SkISize getScaledDimensions(float desiredScale) { + return this->onGetScaledDimensions(desiredScale); + } + + /** * Returns the default info, corresponding to the encoded data. */ const SkImageInfo& getInfo() { return fSrcInfo; } @@ -135,14 +147,44 @@ public: return this->onReallyHasAlpha(); } + /** + * Format of the encoded data. + */ + SkEncodedFormat getEncodedFormat() const { return this->onGetEncodedFormat(); } + + /** + * returns true if the image must be scaled, in the y direction, after reading, not during. + * To scale afterwards, we first decode every line and then sample the lines we want afterwards. + * An example is interlaced pngs, where calling getScanlines once (regardless of the count + * used) needs to read the entire image, therefore it is inefficient to call + * getScanlines more than once. Instead, it should only ever be called with all the + * rows needed. + */ + bool requiresPostYSampling() { + return this->onRequiresPostYSampling(); + } + protected: SkScanlineDecoder(const SkImageInfo& srcInfo) : fSrcInfo(srcInfo) , fDstInfo() , fCurrScanline(0) {} + virtual SkISize onGetScaledDimensions(float /* desiredScale */) { + // By default, scaling is not supported. + return this->getInfo().dimensions(); + } + + virtual SkEncodedFormat onGetEncodedFormat() const = 0; + virtual bool onReallyHasAlpha() const { return false; } + /** + * returns true if the image type is hard to sample and must be scaled after reading, not during + * An example is interlaced pngs, where the entire image must be read for each decode + */ + virtual bool onRequiresPostYSampling() { return false; } + const SkImageInfo& dstInfo() const { return fDstInfo; } private: diff --git a/src/codec/SkBmpStandardCodec.cpp b/src/codec/SkBmpStandardCodec.cpp index 08146fc3e4..1dfd04ee21 100644 --- a/src/codec/SkBmpStandardCodec.cpp +++ b/src/codec/SkBmpStandardCodec.cpp @@ -237,7 +237,7 @@ bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, // Create swizzler fSwizzler.reset(SkSwizzler::CreateSwizzler(config, - colorPtr, dstInfo, opts.fZeroInitialized)); + colorPtr, dstInfo, opts.fZeroInitialized, this->getInfo())); if (NULL == fSwizzler.get()) { return false; diff --git a/src/codec/SkCodec_libgif.cpp b/src/codec/SkCodec_libgif.cpp index 2e348cc856..fa267e83e9 100644 --- a/src/codec/SkCodec_libgif.cpp +++ b/src/codec/SkCodec_libgif.cpp @@ -444,11 +444,12 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, // Create the subset swizzler swizzler.reset(SkSwizzler::CreateSwizzler( SkSwizzler::kIndex, colorTable, subsetDstInfo, - zeroInit)); + zeroInit, this->getInfo())); } else { // Create the fully dimensional swizzler swizzler.reset(SkSwizzler::CreateSwizzler( - SkSwizzler::kIndex, colorTable, dstInfo, zeroInit)); + SkSwizzler::kIndex, colorTable, dstInfo, + zeroInit, this->getInfo())); } // Stores output from dgiflib and input to the swizzler diff --git a/src/codec/SkCodec_libpng.cpp b/src/codec/SkCodec_libpng.cpp index c94f371399..159aecd7ef 100644 --- a/src/codec/SkCodec_libpng.cpp +++ b/src/codec/SkCodec_libpng.cpp @@ -11,6 +11,7 @@ #include "SkColorTable.h" #include "SkBitmap.h" #include "SkMath.h" +#include "SkScaledCodec.h" #include "SkScanlineDecoder.h" #include "SkSize.h" #include "SkStream.h" @@ -462,7 +463,7 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, // Create the swizzler. SkPngCodec retains ownership of the color table. const SkPMColor* colors = get_color_ptr(fColorTable.get()); fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo, - options.fZeroInitialized)); + options.fZeroInitialized, this->getInfo())); if (!fSwizzler) { // FIXME: CreateSwizzler could fail for another reason. return kUnimplemented; @@ -582,8 +583,7 @@ public: SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options, - SkPMColor ctable[], int* ctableCount) override - { + SkPMColor ctable[], int* ctableCount) override { if (!fCodec->rewindIfNeeded()) { return SkCodec::kCouldNotRewind; } @@ -594,7 +594,9 @@ public: // Check to see if scaling was requested. if (dstInfo.dimensions() != this->getInfo().dimensions()) { - return SkCodec::kInvalidScale; + if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { + return SkCodec::kInvalidScale; + } } const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable, @@ -604,7 +606,7 @@ public: } fHasAlpha = false; - fStorage.reset(dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig)); + fStorage.reset(this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig)); fSrcRow = static_cast<uint8_t*>(fStorage.get()); return SkCodec::kSuccess; @@ -643,6 +645,11 @@ public: bool onReallyHasAlpha() const override { return fHasAlpha; } + SkEncodedFormat onGetEncodedFormat() const override { + return kPNG_SkEncodedFormat; + } + + private: SkAutoTDelete<SkPngCodec> fCodec; bool fHasAlpha; @@ -673,12 +680,14 @@ public: } if (!conversion_possible(dstInfo, this->getInfo())) { - return SkCodec::kInvalidConversion; + return SkCodec::kInvalidConversion; } // Check to see if scaling was requested. if (dstInfo.dimensions() != this->getInfo().dimensions()) { - return SkCodec::kInvalidScale; + if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { + return SkCodec::kInvalidScale; + } } const SkCodec::Result result = fCodec->initializeSwizzler(dstInfo, options, ctable, @@ -690,7 +699,7 @@ public: fHasAlpha = false; fCurrentRow = 0; fHeight = dstInfo.height(); - fSrcRowBytes = dstInfo.width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig); + fSrcRowBytes = this->getInfo().width() * SkSwizzler::BytesPerPixel(fCodec->fSrcConfig); fGarbageRow.reset(fSrcRowBytes); fGarbageRowPtr = static_cast<uint8_t*>(fGarbageRow.get()); fCanSkipRewind = true; @@ -753,6 +762,14 @@ public: bool onReallyHasAlpha() const override { return fHasAlpha; } + bool onRequiresPostYSampling() override { + return true; + } + + SkEncodedFormat onGetEncodedFormat() const override { + return kPNG_SkEncodedFormat; + } + private: SkAutoTDelete<SkPngCodec> fCodec; bool fHasAlpha; diff --git a/src/codec/SkCodec_libpng.h b/src/codec/SkCodec_libpng.h index 890402200c..e8807499ea 100644 --- a/src/codec/SkCodec_libpng.h +++ b/src/codec/SkCodec_libpng.h @@ -37,6 +37,7 @@ protected: SkEncodedFormat onGetEncodedFormat() const override { return kPNG_SkEncodedFormat; } bool onRewind() override; bool onReallyHasAlpha() const override { return fReallyHasAlpha; } + private: png_structp fPng_ptr; png_infop fInfo_ptr; @@ -52,7 +53,6 @@ private: SkPngCodec(const SkImageInfo&, SkStream*, png_structp, png_infop, int); - // Helper to set up swizzler and color table. Also calls png_read_update_info. Result initializeSwizzler(const SkImageInfo& requestedInfo, const Options&, SkPMColor*, int* ctableCount); diff --git a/src/codec/SkCodec_wbmp.cpp b/src/codec/SkCodec_wbmp.cpp index 6f98294fc6..3081a3bba9 100644 --- a/src/codec/SkCodec_wbmp.cpp +++ b/src/codec/SkCodec_wbmp.cpp @@ -9,6 +9,7 @@ #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkColorTable.h" +#include "SkScaledCodec.h" #include "SkStream.h" #include "SkCodec_wbmp.h" @@ -80,8 +81,8 @@ SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info, case kIndex_8_SkColorType: case kN32_SkColorType: case kGray_8_SkColorType: - return SkSwizzler::CreateSwizzler( - SkSwizzler::kBit, ctable, info, opts.fZeroInitialized); + return SkSwizzler::CreateSwizzler(SkSwizzler::kBit, ctable, info, opts.fZeroInitialized, + this->getInfo()); default: return NULL; } @@ -201,7 +202,9 @@ public: return SkCodec::kUnimplemented; } if (dstInfo.dimensions() != this->getInfo().dimensions()) { - return SkCodec::kInvalidScale; + if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { + return SkCodec::kInvalidScale; + } } if (!valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) { @@ -220,12 +223,16 @@ public: fSwizzler.reset(fCodec->initializeSwizzler(dstInfo, get_color_ptr(fColorTable.get()), options)); if (NULL == fSwizzler.get()) { - return SkCodec::kInvalidInput; + return SkCodec::kInvalidConversion; } return SkCodec::kSuccess; } + SkEncodedFormat onGetEncodedFormat() const { + return kWBMP_SkEncodedFormat; + } + private: SkAutoTDelete<SkWbmpCodec> fCodec; SkAutoTUnref<SkColorTable> fColorTable; diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp index 83656da42d..c094d50a3b 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" @@ -149,6 +150,14 @@ SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, {} /* + * Return the row bytes of a particular image type and width + */ +static int get_row_bytes(const j_decompress_ptr dinfo) { + int colorBytes = (dinfo->out_color_space == JCS_RGB565) ? 2 : dinfo->out_color_components; + return dinfo->output_width * colorBytes; + +} +/* * Return a valid set of output dimensions for this decoder, given an input scale */ SkISize SkJpegCodec::onGetScaledDimensions(float desiredScale) const { @@ -258,10 +267,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; @@ -273,7 +282,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; + chromium_jpeg_calc_output_dimensions(this->fDecoderMgr->dinfo()); + return false; } // Try the next scale @@ -313,7 +325,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); } @@ -381,6 +393,47 @@ public: , fOpts() {} + /* + * Return a valid set of output dimensions for this decoder, given an input scale + */ + SkISize onGetScaledDimensions(float desiredScale) override { + return fCodec->onGetScaledDimensions(desiredScale); + } + + /* + * Create the swizzler based on the encoded format. + * The swizzler is only used for sampling in the x direction. + */ + + SkCodec::Result initializeSwizzler(const SkImageInfo& info, const SkCodec::Options& options) { + SkSwizzler::SrcConfig srcConfig; + switch (info.colorType()) { + 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, info, options.fZeroInitialized, + this->getInfo())); + if (!fSwizzler) { + // FIXME: CreateSwizzler could fail for another reason. + return SkCodec::kUnimplemented; + } + return SkCodec::kSuccess; + } + SkCodec::Result onStart(const SkImageInfo& dstInfo, const SkCodec::Options& options, SkPMColor ctable[], int* ctableCount) override { @@ -401,8 +454,23 @@ public: } // Perform the necessary scaling - if (!fCodec->scaleToDimensions(dstInfo.width(), dstInfo.height())) { - return SkCodec::kInvalidScale; + if (!fCodec->nativelyScaleToDimensions(dstInfo.width(), dstInfo.height())) { + // full native scaling to dstInfo dimensions not supported + + if (!SkScaledCodec::DimensionsSupportedForSampling(this->getInfo(), dstInfo)) { + return SkCodec::kInvalidScale; + } + // create swizzler for sampling + SkCodec::Result result = this->initializeSwizzler(dstInfo, options); + if (SkCodec::kSuccess != result) { + SkCodecPrintf("failed to initialize the swizzler.\n"); + return result; + } + fStorage.reset(get_row_bytes(fCodec->fDecoderMgr->dinfo())); + fSrcRow = static_cast<uint8_t*>(fStorage.get()); + } else { + fSrcRow = NULL; + fSwizzler.reset(NULL); } // Now, given valid output dimensions, we can start the decompress @@ -433,9 +501,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 (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 = @@ -452,13 +527,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); } - // Move to the next row - dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes); + if(fSwizzler) { + // use swizzler to sample row + fSwizzler->swizzle(dst, dstRow); + dst = SkTAddOffset<JSAMPLE>(dst, rowBytes); + } else { + dstRow = SkTAddOffset<JSAMPLE>(dstRow, rowBytes); + } } - return SkCodec::kSuccess; } @@ -466,7 +545,7 @@ public: // TODO (msarett): Make this a member function and avoid reallocating the // memory buffer on each call to skip. #define chromium_jpeg_skip_scanlines(dinfo, count) \ - SkAutoMalloc storage(dinfo->output_width * dinfo->out_color_components); \ + SkAutoMalloc storage(get_row_bytes(dinfo)); \ uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); \ for (int y = 0; y < count; y++) { \ chromium_jpeg_read_scanlines(dinfo, &storagePtr, 1); \ @@ -484,13 +563,16 @@ public: return SkCodec::kSuccess; } -#ifndef TURBO_HAS_SKIP -#undef chromium_jpeg_skip_scanlines -#endif + SkEncodedFormat onGetEncodedFormat() const override { + return kJPEG_SkEncodedFormat; + } private: SkAutoTDelete<SkJpegCodec> fCodec; + SkAutoMalloc fStorage; // Only used if sampling is needed + uint8_t* fSrcRow; // Only used if sampling is needed SkCodec::Options fOpts; + SkAutoTDelete<SkSwizzler> fSwizzler; typedef SkScanlineDecoder INHERITED; }; @@ -502,6 +584,7 @@ SkScanlineDecoder* SkJpegCodec::NewSDFromStream(SkStream* stream) { } const SkImageInfo& srcInfo = codec->getInfo(); + // Return the new scanline decoder return SkNEW_ARGS(SkJpegScanlineDecoder, (srcInfo, codec.detach())); } diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h index 62a5bbc4cf..fac7d96075 100644 --- a/src/codec/SkJpegCodec.h +++ b/src/codec/SkJpegCodec.h @@ -110,19 +110,13 @@ private: bool 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 scaleToDimensions(uint32_t width, uint32_t height); - - /* - * Create the swizzler based on the encoded format - */ - void initializeSwizzler(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, - const Options& options); + bool nativelyScaleToDimensions(uint32_t width, uint32_t height); SkAutoTDelete<JpegDecoderMgr> fDecoderMgr; - + friend class SkJpegScanlineDecoder; typedef SkCodec INHERITED; diff --git a/src/codec/SkScaledCodec.cpp b/src/codec/SkScaledCodec.cpp new file mode 100644 index 0000000000..e0158b6b4e --- /dev/null +++ b/src/codec/SkScaledCodec.cpp @@ -0,0 +1,261 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCodecPriv.h" +#include "SkScaledCodec.h" +#include "SkStream.h" +#include "SkWebpCodec.h" + + +SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { + bool isWebp = SkWebpCodec::IsWebp(stream); + if (!stream->rewind()) { + return NULL; + } + if (isWebp) { + // Webp codec supports scaling and subsetting natively + return SkWebpCodec::NewFromStream(stream); + } + + SkAutoTDelete<SkScanlineDecoder> scanlineDecoder(SkScanlineDecoder::NewFromStream(stream)); + if (NULL == scanlineDecoder) { + return NULL; + } + + // wrap in new SkScaledCodec + return SkNEW_ARGS(SkScaledCodec, (scanlineDecoder.detach())); +} + +SkCodec* SkScaledCodec::NewFromData(SkData* data) { + if (!data) { + return NULL; + } + return NewFromStream(SkNEW_ARGS(SkMemoryStream, (data))); +} + +SkScaledCodec::SkScaledCodec(SkScanlineDecoder* scanlineDecoder) + : INHERITED(scanlineDecoder->getInfo(), NULL) + , fScanlineDecoder(scanlineDecoder) +{} + +SkScaledCodec::~SkScaledCodec() {} + +// returns a scaled dimension based on the original dimension and the sampleSize +// NOTE: we round down here for scaled dimension to match the behavior of SkImageDecoder +static int get_scaled_dimension(int srcDimension, int sampleSize) { + if (sampleSize > srcDimension) { + return 1; + } + return srcDimension / sampleSize; +} + +static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& nativeDims, + const SkISize& scaledCodecDims, float desiredScale) { + if (nativeDims == scaledCodecDims) { + // does not matter which to return if equal. Return here to skip below calculations + return nativeDims; + } + float idealWidth = origDims.width() * desiredScale; + float idealHeight = origDims.height() * desiredScale; + + // calculate difference between native dimensions and ideal dimensions + float nativeWDiff = SkTAbs(idealWidth - nativeDims.width()); + float nativeHDiff = SkTAbs(idealHeight - nativeDims.height()); + float nativeDiff = (nativeWDiff + nativeHDiff) / 2; + + // calculate difference between scaledCodec dimensions and ideal dimensions + float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width()); + float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height()); + float scaledCodecDiff = (scaledCodecWDiff + scaledCodecHDiff) / 2; + + // return dimensions closest to ideal dimensions. + // If the differences are equal, return nativeDims, as native scaling is more efficient. + return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims; + +} +/* + * Return a valid set of output dimensions for this decoder, given an input scale + */ +SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const { + SkISize nativeDimensions = fScanlineDecoder->getScaledDimensions(desiredScale); + // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ... + SkISize scaledCodecDimensions; + if (desiredScale > 0.5f) { + // sampleSize = 1 + scaledCodecDimensions = fScanlineDecoder->getInfo().dimensions(); + } + // sampleSize determines the step size between samples + // Ex: sampleSize = 2, sample every second pixel in x and y directions + int sampleSize = int(1 / desiredScale); + + int scaledWidth = get_scaled_dimension(this->getInfo().width(), sampleSize); + int scaledHeight = get_scaled_dimension(this->getInfo().height(), sampleSize); + + // Return the calculated output dimensions for the given scale + scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight); + + return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions, + scaledCodecDimensions, desiredScale); +} + +// check if scaling to dstInfo size from srcInfo size using sampleSize is possible +static bool scaling_supported(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, + int* sampleX, int* sampleY) { + SkScaledCodec::ComputeSampleSize(dstInfo, srcInfo, sampleX, sampleY); + const int dstWidth = dstInfo.width(); + const int dstHeight = dstInfo.height(); + const int srcWidth = srcInfo.width(); + const int srcHeight = srcInfo.height(); + // only support down sampling, not up sampling + if (dstWidth > srcWidth || dstHeight > srcHeight) { + return false; + } + // check that srcWidth is scaled down by an integer value + if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) { + return false; + } + // check that src height is scaled down by an integer value + if (get_scaled_dimension(srcHeight, *sampleY) != dstHeight) { + return false; + } + // sampleX and sampleY should be equal unless the original sampleSize requested was larger + // than srcWidth or srcHeight. If so, the result of this is dstWidth or dstHeight = 1. + // This functionality allows for tall thin images to still be scaled down by scaling factors. + if (*sampleX != *sampleY){ + if (1 != dstWidth && 1 != dstHeight) { + return false; + } + } + return true; +} + +// calculates sampleSize in x and y direction +void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageInfo& srcInfo, + int* sampleXPtr, int* sampleYPtr) { + int srcWidth = srcInfo.width(); + int dstWidth = dstInfo.width(); + int srcHeight = srcInfo.height(); + int dstHeight = dstInfo.height(); + + int sampleX = srcWidth / dstWidth; + int sampleY = srcHeight / dstHeight; + + // only support down sampling, not up sampling + SkASSERT(dstWidth <= srcWidth); + SkASSERT(dstHeight <= srcHeight); + + // sampleX and sampleY should be equal unless the original sampleSize requested was + // larger than srcWidth or srcHeight. + // If so, the result of this is dstWidth or dstHeight = 1. This functionality + // allows for tall thin images to still be scaled down by scaling factors. + + if (sampleX != sampleY){ + if (1 != dstWidth && 1 != dstHeight) { + + // rounding during onGetScaledDimensions can cause different sampleSizes + // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10 + // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2 = 10 + // correct for this rounding by comparing width to sampleY and height to sampleX + + if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) { + sampleX = sampleY; + } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) { + sampleY = sampleX; + } + } + } + + if (sampleXPtr) { + *sampleXPtr = sampleX; + } + if (sampleYPtr) { + *sampleYPtr = sampleY; + } +} + +// TODO: Implement subsetting in onGetPixels which works when and when not sampling + +SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, + size_t rowBytes, const Options& options, + SkPMColor ctable[], int* ctableCount) { + + if (options.fSubset) { + // Subsets are not supported. + return kUnimplemented; + } + + Result result = fScanlineDecoder->start(requestedInfo, &options, ctable, ctableCount); + if (kSuccess == result) { + // native decode supported + return fScanlineDecoder->getScanlines(dst, requestedInfo.height(), rowBytes); + + } + + if (kInvalidScale != result) { + // no scaling requested + return result; + } + + // scaling requested + int sampleX; + int sampleY; + if (!scaling_supported(requestedInfo, fScanlineDecoder->getInfo(), &sampleX, &sampleY)) { + return kInvalidScale; + } + // set first sample pixel in y direction + int Y0 = sampleY >> 1; + + int dstHeight = requestedInfo.height(); + int srcHeight = fScanlineDecoder->getInfo().height(); + + SkImageInfo info = requestedInfo; + // use original height as scanlineDecoder does not support y sampling natively + info = info.makeWH(requestedInfo.width(), srcHeight); + + // update scanlineDecoder with new info + result = fScanlineDecoder->start(info, &options, ctable, ctableCount); + if (kSuccess != result) { + return result; + } + + const bool requiresPostYSampling = fScanlineDecoder->requiresPostYSampling(); + + if (requiresPostYSampling) { + SkAutoMalloc storage(srcHeight * rowBytes); + uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); + result = fScanlineDecoder->getScanlines(storagePtr, srcHeight, rowBytes); + if (kSuccess != result) { + return result; + } + storagePtr += Y0 * rowBytes; + for (int y = 0; y < dstHeight; y++) { + memcpy(dst, storagePtr, rowBytes); + storagePtr += sampleY * rowBytes; + dst = SkTAddOffset<void>(dst, rowBytes); + } + } else { + // does not require post y sampling + result = fScanlineDecoder->skipScanlines(Y0); + if (kSuccess != result) { + return result; + } + for (int y = 0; y < dstHeight; y++) { + result = fScanlineDecoder->getScanlines(dst, 1, rowBytes); + if (kSuccess != result) { + return result; + } + if (y < dstHeight - 1) { + result = fScanlineDecoder->skipScanlines(sampleY - 1); + if (kSuccess != result) { + return result; + } + } + dst = SkTAddOffset<void>(dst, rowBytes); + } + } + return kSuccess; +} diff --git a/src/codec/SkSwizzler.cpp b/src/codec/SkSwizzler.cpp index b2b7f927ac..741fe18cde 100644 --- a/src/codec/SkSwizzler.cpp +++ b/src/codec/SkSwizzler.cpp @@ -7,6 +7,7 @@ #include "SkCodecPriv.h" #include "SkColorPriv.h" +#include "SkScaledCodec.h" #include "SkSwizzler.h" #include "SkTemplates.h" #include "SkUtils.h" @@ -19,114 +20,107 @@ SkSwizzler::ResultAlpha SkSwizzler::GetResult(uint8_t zeroAlpha, return (((uint16_t) maxAlpha) << 8) | zeroAlpha; } +// samples the row. Does not do anything else but sampling +static SkSwizzler::ResultAlpha sample565(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, + int width, int deltaSrc, int offset, const SkPMColor ctable[]){ + + src += offset; + uint16_t* SK_RESTRICT dst = (uint16_t*) dstRow; + for (int x = 0; x < width; x++) { + dst[x] = src[1] << 8 | src[0]; + src += deltaSrc; + } + // 565 is always opaque + return SkSwizzler::kOpaque_ResultAlpha; +} + // kBit // These routines exclusively choose between white and black #define GRAYSCALE_BLACK 0 #define GRAYSCALE_WHITE 0xFF + +// same as swizzle_bit_to_index and swizzle_bit_to_n32 except for value assigned to dst[x] static SkSwizzler::ResultAlpha swizzle_bit_to_grayscale( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor* /*ctable*/) { + uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; - // Determine how many full bytes are in the row - int bytesInRow = width >> 3; - int i; - for (i = 0; i < bytesInRow; i++) { - U8CPU currByte = src[i]; - for (int j = 0; j < 8; j++) { - dst[j] = ((currByte >> (7 - j)) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK; - } - dst += 8; + // increment src by byte offset and bitIndex by bit offset + src += offset / 8; + int bitIndex = offset % 8; + uint8_t currByte = *src; + for (int x = 0; x < dstWidth; x++) { + dst[x] = ((currByte >> (7-bitIndex)) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK; + int bitOffset = bitIndex + deltaSrc; + bitIndex = bitOffset % 8; + currByte = *(src += bitOffset / 8); } - // Finish the remaining bits - width &= 7; - if (width > 0) { - U8CPU currByte = src[i]; - for (int j = 0; j < width; j++) { - dst[j] = ((currByte >> 7) & 1) ? GRAYSCALE_WHITE : GRAYSCALE_BLACK; - currByte <<= 1; - } - } return SkSwizzler::kOpaque_ResultAlpha; } #undef GRAYSCALE_BLACK #undef GRAYSCALE_WHITE +// same as swizzle_bit_to_grayscale and swizzle_bit_to_n32 except for value assigned to dst[x] static SkSwizzler::ResultAlpha swizzle_bit_to_index( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor* /*ctable*/) { uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; - // Determine how many full bytes are in the row - int bytesInRow = width >> 3; - int i; - for (i = 0; i < bytesInRow; i++) { - U8CPU currByte = src[i]; - for (int j = 0; j < 8; j++) { - dst[j] = (currByte >> (7 - j)) & 1; - } - dst += 8; + // increment src by byte offset and bitIndex by bit offset + src += offset / 8; + int bitIndex = offset % 8; + uint8_t currByte = *src; + for (int x = 0; x < dstWidth; x++) { + dst[x] = ((currByte >> (7-bitIndex)) & 1); + int bitOffset = bitIndex + deltaSrc; + bitIndex = bitOffset % 8; + currByte = *(src += bitOffset / 8); } - // Finish the remaining bits - width &= 7; - if (width > 0) { - U8CPU currByte = src[i]; - for (int j = 0; j < width; j++) { - dst[j] = ((currByte >> 7) & 1); - currByte <<= 1; - } - } return SkSwizzler::kOpaque_ResultAlpha; } +// same as swizzle_bit_to_grayscale and swizzle_bit_to_index except for value assigned to dst[x] static SkSwizzler::ResultAlpha swizzle_bit_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int /*bitsPerPixel*/, const SkPMColor* /*ctable*/) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor* /*ctable*/) { SkPMColor* SK_RESTRICT dst = (SkPMColor*) dstRow; - // Determine how many full bytes are in the row - int bytesInRow = width >> 3; - int i; - for (i = 0; i < bytesInRow; i++) { - U8CPU currByte = src[i]; - for (int j = 0; j < 8; j++) { - dst[j] = ((currByte >> (7 - j)) & 1) ? SK_ColorWHITE : SK_ColorBLACK; - } - dst += 8; + // increment src by byte offset and bitIndex by bit offset + src += offset / 8; + int bitIndex = offset % 8; + uint8_t currByte = *src; + for (int x = 0; x < dstWidth; x++) { + dst[x] = ((currByte >> (7 - bitIndex)) & 1) ? SK_ColorWHITE : SK_ColorBLACK; + int bitOffset = bitIndex + deltaSrc; + bitIndex = bitOffset % 8; + currByte = *(src += bitOffset / 8); } - // Finish the remaining bits - width &= 7; - if (width > 0) { - U8CPU currByte = src[i]; - for (int j = 0; j < width; j++) { - dst[j] = ((currByte >> 7) & 1) ? SK_ColorWHITE : SK_ColorBLACK; - currByte <<= 1; - } - } return SkSwizzler::kOpaque_ResultAlpha; } // kIndex1, kIndex2, kIndex4 static SkSwizzler::ResultAlpha swizzle_small_index_to_index( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bitsPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int bitsPerPixel, int offset, const SkPMColor ctable[]) { + src += offset; uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; INIT_RESULT_ALPHA; const uint32_t pixelsPerByte = 8 / bitsPerPixel; - const size_t rowBytes = compute_row_bytes_ppb(width, pixelsPerByte); + const size_t rowBytes = compute_row_bytes_ppb(dstWidth, pixelsPerByte); const uint8_t mask = (1 << bitsPerPixel) - 1; int x = 0; for (uint32_t byte = 0; byte < rowBytes; byte++) { uint8_t pixelData = src[byte]; - for (uint32_t p = 0; p < pixelsPerByte && x < width; p++) { + for (uint32_t p = 0; p < pixelsPerByte && x < dstWidth; p++) { uint8_t index = (pixelData >> (8 - bitsPerPixel)) & mask; UPDATE_RESULT_ALPHA(ctable[index] >> SK_A32_SHIFT); dst[x] = index; @@ -138,18 +132,19 @@ static SkSwizzler::ResultAlpha swizzle_small_index_to_index( } static SkSwizzler::ResultAlpha swizzle_small_index_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bitsPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int bitsPerPixel, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*) dstRow; INIT_RESULT_ALPHA; const uint32_t pixelsPerByte = 8 / bitsPerPixel; - const size_t rowBytes = compute_row_bytes_ppb(width, pixelsPerByte); + const size_t rowBytes = compute_row_bytes_ppb(dstWidth, pixelsPerByte); const uint8_t mask = (1 << bitsPerPixel) - 1; int x = 0; for (uint32_t byte = 0; byte < rowBytes; byte++) { uint8_t pixelData = src[byte]; - for (uint32_t p = 0; p < pixelsPerByte && x < width; p++) { + for (uint32_t p = 0; p < pixelsPerByte && x < dstWidth; p++) { uint8_t index = (pixelData >> (8 - bitsPerPixel)) & mask; SkPMColor c = ctable[index]; UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT); @@ -164,58 +159,75 @@ static SkSwizzler::ResultAlpha swizzle_small_index_to_n32( // kIndex static SkSwizzler::ResultAlpha swizzle_index_to_index( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; - memcpy(dst, src, width); + INIT_RESULT_ALPHA; // TODO (msarett): Should we skip the loop here and guess that the row is opaque/not opaque? // SkScaledBitmap sampler just guesses that it is opaque. This is dangerous // and probably wrong since gif and bmp (rarely) may have alpha. - INIT_RESULT_ALPHA; - for (int x = 0; x < width; x++) { - UPDATE_RESULT_ALPHA(ctable[src[x]] >> SK_A32_SHIFT); + if (1 == deltaSrc) { + // A non-zero offset is only used when sampling, meaning that deltaSrc will be + // greater than 1. The below loop relies on the fact that src remains unchanged. + SkASSERT(0 == offset); + memcpy(dst, src, dstWidth); + for (int x = 0; x < dstWidth; x++) { + UPDATE_RESULT_ALPHA(ctable[src[x]] >> SK_A32_SHIFT); + } + } else { + for (int x = 0; x < dstWidth; x++) { + dst[x] = *src; + UPDATE_RESULT_ALPHA(ctable[*src] >> SK_A32_SHIFT); + src += deltaSrc; + } } return COMPUTE_RESULT_ALPHA; } static SkSwizzler::ResultAlpha swizzle_index_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; INIT_RESULT_ALPHA; - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[src[x]]; + for (int x = 0; x < dstWidth; x++) { + SkPMColor c = ctable[*src]; UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT); dst[x] = c; + src += deltaSrc; } return COMPUTE_RESULT_ALPHA; } static SkSwizzler::ResultAlpha swizzle_index_to_n32_skipZ( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; INIT_RESULT_ALPHA; - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[src[x]]; + for (int x = 0; x < dstWidth; x++) { + SkPMColor c = ctable[*src]; UPDATE_RESULT_ALPHA(c >> SK_A32_SHIFT); if (c != 0) { dst[x] = c; } + src += deltaSrc; } return COMPUTE_RESULT_ALPHA; } static SkSwizzler::ResultAlpha swizzle_index_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int bytesPerPixel, int offset, const SkPMColor ctable[]) { // FIXME: Support dithering? Requires knowing y, which I think is a bigger // change. + src += offset; uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { dst[x] = SkPixel32ToPixel16(ctable[*src]); src += bytesPerPixel; } @@ -228,29 +240,42 @@ static SkSwizzler::ResultAlpha swizzle_index_to_565( // kGray static SkSwizzler::ResultAlpha swizzle_gray_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < width; x++) { - dst[x] = SkPackARGB32NoCheck(0xFF, src[x], src[x], src[x]); + for (int x = 0; x < dstWidth; x++) { + dst[x] = SkPackARGB32NoCheck(0xFF, *src, *src, *src); + src += deltaSrc; } return SkSwizzler::kOpaque_ResultAlpha; } static SkSwizzler::ResultAlpha swizzle_gray_to_gray( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { - memcpy(dstRow, src, width); + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + + src += offset; + uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; + if (1 == deltaSrc) { + memcpy(dstRow, src, dstWidth); + } else { + for (int x = 0; x < dstWidth; x++) { + dst[x] = src[0]; + src += deltaSrc; + } + } return SkSwizzler::kOpaque_ResultAlpha; } static SkSwizzler::ResultAlpha swizzle_gray_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int bytesPerPixel, int offset, const SkPMColor ctable[]) { // FIXME: Support dithering? + src += offset; uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]); src += bytesPerPixel; } @@ -260,13 +285,14 @@ static SkSwizzler::ResultAlpha swizzle_gray_to_565( // kBGRX static SkSwizzler::ResultAlpha swizzle_bgrx_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { dst[x] = SkPackARGB32NoCheck(0xFF, src[2], src[1], src[0]); - src += bytesPerPixel; + src += deltaSrc; } return SkSwizzler::kOpaque_ResultAlpha; } @@ -274,54 +300,58 @@ static SkSwizzler::ResultAlpha swizzle_bgrx_to_n32( // kBGRA static SkSwizzler::ResultAlpha swizzle_bgra_to_n32_unpremul( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; INIT_RESULT_ALPHA; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { uint8_t alpha = src[3]; UPDATE_RESULT_ALPHA(alpha); dst[x] = SkPackARGB32NoCheck(alpha, src[2], src[1], src[0]); - src += bytesPerPixel; + src += deltaSrc; } return COMPUTE_RESULT_ALPHA; } static SkSwizzler::ResultAlpha swizzle_bgra_to_n32_premul( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; INIT_RESULT_ALPHA; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { uint8_t alpha = src[3]; UPDATE_RESULT_ALPHA(alpha); dst[x] = SkPreMultiplyARGB(alpha, src[2], src[1], src[0]); - src += bytesPerPixel; + src += deltaSrc; } return COMPUTE_RESULT_ALPHA; } // kRGBX static SkSwizzler::ResultAlpha swizzle_rgbx_to_n32( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]); - src += bytesPerPixel; + src += deltaSrc; } return SkSwizzler::kOpaque_ResultAlpha; } static SkSwizzler::ResultAlpha swizzle_rgbx_to_565( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int bytesPerPixel, int offset, const SkPMColor ctable[]) { // FIXME: Support dithering? + src += offset; uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]); src += bytesPerPixel; } @@ -331,48 +361,51 @@ static SkSwizzler::ResultAlpha swizzle_rgbx_to_565( // kRGBA static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; INIT_RESULT_ALPHA; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { unsigned alpha = src[3]; UPDATE_RESULT_ALPHA(alpha); dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); - src += bytesPerPixel; + src += deltaSrc; } return COMPUTE_RESULT_ALPHA; } static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_unpremul( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow); INIT_RESULT_ALPHA; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { unsigned alpha = src[3]; UPDATE_RESULT_ALPHA(alpha); dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]); - src += bytesPerPixel; + src += deltaSrc; } return COMPUTE_RESULT_ALPHA; } static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul_skipZ( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int width, - int bytesPerPixel, const SkPMColor ctable[]) { + void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, + int deltaSrc, int offset, const SkPMColor ctable[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; INIT_RESULT_ALPHA; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { unsigned alpha = src[3]; UPDATE_RESULT_ALPHA(alpha); if (0 != alpha) { dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); } - src += bytesPerPixel; + src += deltaSrc; } return COMPUTE_RESULT_ALPHA; } @@ -385,11 +418,12 @@ static SkSwizzler::ResultAlpha swizzle_rgba_to_n32_premul_skipZ( decide whether to switch to unpremul default. static bool swizzle_rgba_to_n32_unpremul_skipZ(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, - int width, int bitsPerPixel, + int dstWidth, int bitsPerPixel, int offset, const SkPMColor[]) { + src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; unsigned alphaMask = 0xFF; - for (int x = 0; x < width; x++) { + for (int x = 0; x < dstWidth; x++) { unsigned alpha = src[3]; // NOTE: We cheat here. The caller requested unpremul and skip zeroes. It's possible // the color components are not zero, but we skip them anyway, meaning they'll remain @@ -406,9 +440,10 @@ static bool swizzle_rgba_to_n32_unpremul_skipZ(void* SK_RESTRICT dstRow, SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, const SkPMColor* ctable, - const SkImageInfo& info, - SkCodec::ZeroInitialized zeroInit) { - if (info.colorType() == kUnknown_SkColorType || kUnknown == sc) { + const SkImageInfo& dstInfo, + SkCodec::ZeroInitialized zeroInit, + const SkImageInfo& srcInfo) { + if (dstInfo.colorType() == kUnknown_SkColorType || kUnknown == sc) { return NULL; } if ((kIndex == sc || kIndex4 == sc || kIndex2 == sc || kIndex1 == sc) @@ -416,9 +451,10 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, return NULL; } RowProc proc = NULL; + switch (sc) { case kBit: - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_bit_to_n32; break; @@ -435,7 +471,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, case kIndex1: case kIndex2: case kIndex4: - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_small_index_to_n32; break; @@ -447,7 +483,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, } break; case kIndex: - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: // We assume the color premultiplied ctable (or not) as desired. if (SkCodec::kYes_ZeroInitialized == zeroInit) { @@ -469,7 +505,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, } break; case kGray: - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_gray_to_n32; break; @@ -485,7 +521,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, break; case kBGR: case kBGRX: - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_bgrx_to_n32; break; @@ -494,9 +530,9 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, } break; case kBGRA: - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: - switch (info.alphaType()) { + switch (dstInfo.alphaType()) { case kUnpremul_SkAlphaType: proc = &swizzle_bgra_to_n32_unpremul; break; @@ -513,7 +549,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, break; case kRGBX: // TODO: Support other swizzles. - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_rgbx_to_n32; break; @@ -524,9 +560,9 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, } break; case kRGBA: - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: - if (info.alphaType() == kUnpremul_SkAlphaType) { + if (dstInfo.alphaType() == kUnpremul_SkAlphaType) { // Respect zeroInit? proc = &swizzle_rgba_to_n32_unpremul; } else { @@ -542,7 +578,7 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, } break; case kRGB: - switch (info.colorType()) { + switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_rgbx_to_n32; break; @@ -550,6 +586,14 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, break; } break; + case kRGB_565: + switch (dstInfo.colorType()) { + case kRGB_565_SkColorType: + proc = &sample565; + break; + default: + break; + } default: break; } @@ -558,22 +602,31 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, } // Store deltaSrc in bytes if it is an even multiple, otherwise use bits - int deltaSrc = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) : - BitsPerPixel(sc); - return SkNEW_ARGS(SkSwizzler, (proc, ctable, deltaSrc, info)); + int deltaSrc = SkIsAlign8(BitsPerPixel(sc)) ? BytesPerPixel(sc) : BitsPerPixel(sc); + + // get sampleX based on srcInfo and dstInfo dimensions + int sampleX; + SkScaledCodec::ComputeSampleSize(dstInfo, srcInfo, &sampleX, NULL); + + return SkNEW_ARGS(SkSwizzler, (proc, ctable, deltaSrc, dstInfo, sampleX)); } SkSwizzler::SkSwizzler(RowProc proc, const SkPMColor* ctable, - int deltaSrc, const SkImageInfo& info) + int deltaSrc, const SkImageInfo& info, int sampleX) : fRowProc(proc) , fColorTable(ctable) , fDeltaSrc(deltaSrc) , fDstInfo(info) -{} + , fSampleX(sampleX) + , fX0(sampleX == 1 ? 0 : sampleX >> 1) +{ + // check that fX0 is less than original width + SkASSERT(fX0 >= 0 && fX0 < fDstInfo.width() * fSampleX); +} SkSwizzler::ResultAlpha SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { SkASSERT(NULL != dst && NULL != src); - return fRowProc(dst, src, fDstInfo.width(), fDeltaSrc, fColorTable); + return fRowProc(dst, src, fDstInfo.width(), fSampleX * fDeltaSrc, fX0 * fDeltaSrc, fColorTable); } void SkSwizzler::Fill(void* dstStartRow, const SkImageInfo& dstInfo, size_t dstRowBytes, diff --git a/src/codec/SkSwizzler.h b/src/codec/SkSwizzler.h index 2fab7f66b6..b7132ce6d5 100644 --- a/src/codec/SkSwizzler.h +++ b/src/codec/SkSwizzler.h @@ -117,16 +117,22 @@ public: /** * Create a new SkSwizzler. * @param SrcConfig Description of the format of the source. - * @param SkImageInfo dimensions() describe both the src and the dst. - * Other fields describe the dst. + * @param dstInfo describes the destination. * @param ZeroInitialized Whether dst is zero-initialized. The implementation may choose to skip writing zeroes * if set to kYes_ZeroInitialized. + * @param srcInfo is the info of the source. Used to calculate the width samplesize. + * Width sampling is supported by the swizzler, by skipping pixels when + swizzling the row. Height sampling is not supported by the swizzler, + but is implemented in SkScaledCodec. + Sampling in Y can be done by a client with a scanline decoder, + but sampling in X allows the swizzler to skip swizzling pixels and + reading from and writing to memory. * @return A new SkSwizzler or NULL on failure. */ static SkSwizzler* CreateSwizzler(SrcConfig, const SkPMColor* ctable, - const SkImageInfo&, SkCodec::ZeroInitialized); - + const SkImageInfo& dstInfo, SkCodec::ZeroInitialized, + const SkImageInfo& srcInfo); /** * Fill the remainder of the destination with a single color * @@ -181,14 +187,16 @@ private: * Method for converting raw data to Skia pixels. * @param dstRow Row in which to write the resulting pixels. * @param src Row of src data, in format specified by SrcConfig - * @param width Width in pixels + * @param dstWidth Width in pixels of the destination * @param deltaSrc if bitsPerPixel % 8 == 0, deltaSrc is bytesPerPixel * else, deltaSrc is bitsPerPixel * @param ctable Colors (used for kIndex source). + * @param offset The offset before the first pixel to sample. + Is in bytes or bits based on what deltaSrc is in. */ typedef ResultAlpha (*RowProc)(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, + int dstWidth, int deltaSrc, int offset, const SkPMColor ctable[]); const RowProc fRowProc; @@ -199,9 +207,10 @@ private: // deltaSrc is bitsPerPixel const SkImageInfo fDstInfo; int fCurrY; + const int fX0; // first X coord to sample + const int fSampleX; // step between X samples - SkSwizzler(RowProc proc, const SkPMColor* ctable, int deltaSrc, - const SkImageInfo& info); - + SkSwizzler(RowProc proc, const SkPMColor* ctable, int deltaSrc, const SkImageInfo& info, + int sampleX); }; #endif // SkSwizzler_DEFINED |