diff options
39 files changed, 48 insertions, 6758 deletions
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 9fc4ea5a42..b375e59f45 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -200,7 +200,6 @@ if (PNG_FOUND) add_definitions(-DSK_CODEC_DECODES_PNG) else() remove_srcs(../src/images/*png*) - remove_srcs(../src/images/*ico*) remove_srcs(../src/codec/*Png*) remove_srcs(../src/codec/*Ico*) endif() diff --git a/gm/downsamplebitmap.cpp b/gm/downsamplebitmap.cpp index a99bae0cd9..598382484f 100644 --- a/gm/downsamplebitmap.cpp +++ b/gm/downsamplebitmap.cpp @@ -183,23 +183,15 @@ class DownsampleBitmapImageGM: public DownsampleBitmapGM { DEF_GM( return new DownsampleBitmapTextGM(72, kHigh_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, kHigh_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", kHigh_SkFilterQuality); ) -DEF_GM( return new DownsampleBitmapImageGM("mandrill_132x132_12x12.astc", - kHigh_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapTextGM(72, kMedium_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, kMedium_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", kMedium_SkFilterQuality); ) -DEF_GM( return new DownsampleBitmapImageGM("mandrill_132x132_12x12.astc", - kMedium_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapTextGM(72, kLow_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, kLow_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", kLow_SkFilterQuality); ) -DEF_GM( return new DownsampleBitmapImageGM("mandrill_132x132_12x12.astc", - kLow_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapTextGM(72, kNone_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapCheckerboardGM(512,256, kNone_SkFilterQuality); ) DEF_GM( return new DownsampleBitmapImageGM("mandrill_512.png", kNone_SkFilterQuality); ) -DEF_GM( return new DownsampleBitmapImageGM("mandrill_132x132_12x12.astc", - kNone_SkFilterQuality); ) diff --git a/gm/etc1bitmap.cpp b/gm/etc1bitmap.cpp deleted file mode 100644 index 9d47999151..0000000000 --- a/gm/etc1bitmap.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "gm.h" - -#include "Resources.h" -#include "SkCanvas.h" -#include "SkData.h" -#include "SkImage.h" -#include "SkImageGenerator.h" -#include "SkOSFile.h" -#include "SkTemplates.h" - -#ifndef SK_IGNORE_ETC1_SUPPORT - -#include "etc1.h" - -/** - * Remove the last row and column of ETC1 blocks, effectively - * making a texture that started as power of two into a texture - * that is no longer power of two... - */ -bool slice_etc1_data(void *data, int* width, int* height) { - - // First, parse the data and get to it... - etc1_byte *origData = reinterpret_cast<etc1_byte *>(data); - if (!etc1_pkm_is_valid(origData)) { - return false; - } - - int origW = etc1_pkm_get_width(origData); - int origH = etc1_pkm_get_height(origData); - - int blockWidth = (origW + 3) >> 2; - int blockHeight = (origH + 3) >> 2; - - // Make sure that we have blocks to trim off.. - if (blockWidth < 2 || blockHeight < 2) { - return false; - } - - int newWidth = (blockWidth - 1) << 2; - int newHeight = (blockHeight - 1) << 2; - - size_t newDataSz = etc1_get_encoded_data_size(newWidth, newHeight) + ETC_PKM_HEADER_SIZE; - SkAutoTMalloc<etc1_byte> am(newDataSz); - - etc1_byte* newData = am.get(); - - etc1_pkm_format_header(newData, newWidth, newHeight); - newData += ETC_PKM_HEADER_SIZE; - origData += ETC_PKM_HEADER_SIZE; - - for (int j = 0; j < blockHeight - 1; ++j) { - memcpy(newData, origData, (blockWidth - 1)*ETC1_ENCODED_BLOCK_SIZE); - origData += blockWidth*ETC1_ENCODED_BLOCK_SIZE; - newData += (blockWidth - 1)*ETC1_ENCODED_BLOCK_SIZE; - } - - // Stick the data back whence it came - memcpy(data, am.get(), newDataSz); - *width = newWidth; - *height = newHeight; - - return true; -} -#endif // SK_IGNORE_ETC1_SUPPORT - -namespace skiagm { - -/** - * Test decoding an image from a PKM or KTX file and then - * from compressed ETC1 data. - */ -class ETC1BitmapGM : public GM { -public: - ETC1BitmapGM() { } - virtual ~ETC1BitmapGM() { } - -protected: - SkString onShortName() override { - SkString str = SkString("etc1bitmap_"); - str.append(this->fileExtension()); - return str; - } - - SkISize onISize() override { - return SkISize::Make(128, 128); - } - - virtual SkString fileExtension() const = 0; - - void onDraw(SkCanvas* canvas) override { - SkBitmap bm; - SkString filename = GetResourcePath("mandrill_128."); - filename.append(this->fileExtension()); - sk_sp<SkData> fileData(SkData::MakeFromFileName(filename.c_str())); - if (nullptr == fileData) { - SkDebugf("Could not open the file. Did you forget to set the resourcePath?\n"); - return; - } - - sk_sp<SkImage> image(SkImage::MakeFromEncoded(std::move(fileData))); - if (nullptr == image) { - SkDebugf("Could not decode the ETC file. ETC may not be included in this platform.\n"); - return; - } - canvas->drawImage(image, 0, 0); - } - -private: - typedef GM INHERITED; -}; - -// This class specializes ETC1BitmapGM to load the mandrill_128.pkm file. -class ETC1Bitmap_PKM_GM : public ETC1BitmapGM { -public: - ETC1Bitmap_PKM_GM() : ETC1BitmapGM() { } - virtual ~ETC1Bitmap_PKM_GM() { } - -protected: - - SkString fileExtension() const override { return SkString("pkm"); } - -private: - typedef ETC1BitmapGM INHERITED; -}; - -// This class specializes ETC1BitmapGM to load the mandrill_128.ktx file. -class ETC1Bitmap_KTX_GM : public ETC1BitmapGM { -public: - ETC1Bitmap_KTX_GM() : ETC1BitmapGM() { } - virtual ~ETC1Bitmap_KTX_GM() { } - -protected: - - SkString fileExtension() const override { return SkString("ktx"); } - -private: - typedef ETC1BitmapGM INHERITED; -}; - -// This class specializes ETC1BitmapGM to load the mandrill_128.r11.ktx file. -class ETC1Bitmap_R11_KTX_GM : public ETC1BitmapGM { -public: - ETC1Bitmap_R11_KTX_GM() : ETC1BitmapGM() { } - virtual ~ETC1Bitmap_R11_KTX_GM() { } - -protected: - - SkString fileExtension() const override { return SkString("r11.ktx"); } - -private: - typedef ETC1BitmapGM INHERITED; -}; - -#ifndef SK_IGNORE_ETC1_SUPPORT -/** - * Test decoding an image from a PKM file and then - * from non-power-of-two compressed ETC1 data. First slice - * off a row and column of blocks in order to make it non-power - * of two. - */ -class ETC1Bitmap_NPOT_GM : public GM { -public: - ETC1Bitmap_NPOT_GM() { } - virtual ~ETC1Bitmap_NPOT_GM() { } - -protected: - SkString onShortName() override { - return SkString("etc1bitmap_npot"); - } - - SkISize onISize() override { - return SkISize::Make(124, 124); - } - - void onDraw(SkCanvas* canvas) override { - SkBitmap bm; - SkString pkmFilename = GetResourcePath("mandrill_128.pkm"); - SkAutoDataUnref fileData(SkData::NewFromFileName(pkmFilename.c_str())); - if (nullptr == fileData) { - SkDebugf("Could not open the file. Did you forget to set the resourcePath?\n"); - return; - } - - SkAutoMalloc am(fileData->size()); - memcpy(am.get(), fileData->data(), fileData->size()); - - int width, height; - if (!slice_etc1_data(am.get(), &width, &height)) { - SkDebugf("ETC1 Data is poorly formatted.\n"); - return; - } - - SkASSERT(124 == width); - SkASSERT(124 == height); - - size_t dataSz = etc1_get_encoded_data_size(width, height) + ETC_PKM_HEADER_SIZE; - sk_sp<SkData> nonPOTData(SkData::MakeWithCopy(am.get(), dataSz)); - canvas->drawImage(SkImage::MakeFromEncoded(std::move(nonPOTData)).get(), 0, 0); - } - -private: - typedef GM INHERITED; -}; -#endif // SK_IGNORE_ETC1_SUPPORT - -} // namespace skiagm - -////////////////////////////////////////////////////////////////////////////// - -DEF_GM(return new skiagm::ETC1Bitmap_PKM_GM;) -DEF_GM(return new skiagm::ETC1Bitmap_KTX_GM;) -DEF_GM(return new skiagm::ETC1Bitmap_R11_KTX_GM;) - -#ifndef SK_IGNORE_ETC1_SUPPORT -DEF_GM(return new skiagm::ETC1Bitmap_NPOT_GM;) -#endif // SK_IGNORE_ETC1_SUPPORT diff --git a/gyp/core.gypi b/gyp/core.gypi index 443029d49e..8d558e1d64 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -360,7 +360,6 @@ '<(skia_include_path)/core/SkFontStyle.h', '<(skia_include_path)/core/SkGraphics.h', '<(skia_include_path)/core/SkImage.h', - '<(skia_include_path)/core/SkImageDecoder.h', '<(skia_include_path)/core/SkImageEncoder.h', '<(skia_include_path)/core/SkImageFilter.h', '<(skia_include_path)/core/SkImageInfo.h', diff --git a/gyp/images.gyp b/gyp/images.gyp index 8c5b0a7d1b..53f505d925 100644 --- a/gyp/images.gyp +++ b/gyp/images.gyp @@ -31,36 +31,17 @@ ], 'sources': [ '../include/images/SkForceLinking.h', - '../src/images/SkJpegUtility.h', '../include/images/SkMovie.h', '../include/images/SkPageFlipper.h', - '../src/images/bmpdecoderhelper.cpp', - '../src/images/bmpdecoderhelper.h', - '../src/images/SkForceLinking.cpp', - '../src/images/SkImageDecoder.cpp', '../src/images/SkImageDecoder_FactoryDefault.cpp', - '../src/images/SkImageDecoder_FactoryRegistrar.cpp', - # If decoders are added/removed to/from (all/individual) + # If encoders are added/removed to/from (all/individual) # platform(s), be sure to update SkForceLinking.cpp # so the right decoders will be forced to link. - # IMPORTANT: The build order of the SkImageDecoder_*.cpp files - # defines the order image decoders are tested when decoding a - # stream. The last decoder is the first one tested, so the .cpp - # files should be in listed in order from the least likely to be - # used, to the most likely (jpeg and png should be the last two - # for instance.) As a result, they are deliberately not in - # alphabetical order. - '../src/images/SkImageDecoder_wbmp.cpp', - '../src/images/SkImageDecoder_pkm.cpp', '../src/images/SkImageDecoder_ktx.cpp', - '../src/images/SkImageDecoder_astc.cpp', - '../src/images/SkImageDecoder_libbmp.cpp', - '../src/images/SkImageDecoder_libgif.cpp', - '../src/images/SkImageDecoder_libico.cpp', '../src/images/SkImageDecoder_libwebp.cpp', '../src/images/SkImageDecoder_libjpeg.cpp', '../src/images/SkImageDecoder_libpng.cpp', @@ -72,8 +53,6 @@ '../src/images/SkMovie.cpp', '../src/images/SkMovie_gif.cpp', '../src/images/SkPageFlipper.cpp', - '../src/images/SkScaledBitmapSampler.cpp', - '../src/images/SkScaledBitmapSampler.h', '../src/ports/SkImageDecoder_CG.cpp', '../src/ports/SkImageDecoder_WIC.cpp', @@ -81,8 +60,6 @@ 'conditions': [ [ 'skia_os == "win"', { 'sources!': [ - '../src/images/SkImageDecoder_FactoryDefault.cpp', - '../src/images/SkImageDecoder_libgif.cpp', '../src/images/SkImageDecoder_libpng.cpp', '../src/images/SkMovie_gif.cpp', ], @@ -101,9 +78,7 @@ }], [ 'skia_os in ["mac", "ios"]', { 'sources!': [ - '../src/images/SkImageDecoder_FactoryDefault.cpp', '../src/images/SkImageDecoder_libpng.cpp', - '../src/images/SkImageDecoder_libgif.cpp', '../src/images/SkMovie_gif.cpp', ], },{ #else if skia_os != mac @@ -129,9 +104,7 @@ # The android framework disables these decoders as they are of little use to # Java applications that can't take advantage of the compressed formats. 'sources!': [ - '../src/images/SkImageDecoder_pkm.cpp', '../src/images/SkImageDecoder_ktx.cpp', - '../src/images/SkImageDecoder_astc.cpp', ], }], ], diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index bdb2eb0706..b77e8a7689 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -11,6 +11,7 @@ #include "SkTypes.h" #include "SkBitmap.h" #include "SkDeque.h" +#include "SkImage.h" #include "SkPaint.h" #include "SkRefCnt.h" #include "SkRegion.h" @@ -26,7 +27,6 @@ class SkData; class SkDraw; class SkDrawable; class SkDrawFilter; -class SkImage; class SkImageFilter; class SkMetaData; class SkPath; diff --git a/include/core/SkImageDecoder.h b/include/core/SkImageDecoder.h deleted file mode 100644 index 7a90964b13..0000000000 --- a/include/core/SkImageDecoder.h +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkImageDecoder_DEFINED -#define SkImageDecoder_DEFINED - -#include "SkBitmap.h" -#include "SkImage.h" -#include "SkPngChunkReader.h" -#include "SkRect.h" -#include "SkRefCnt.h" -#include "SkTRegistry.h" -#include "SkTypes.h" - -class SkStream; -class SkStreamRewindable; - -/** \class SkImageDecoder - - DEPRECATED Please use SkImage::NewFromEncoded() or SkImageGenerator::NewFromEncoded(). - - Base class for decoding compressed images into a SkBitmap -*/ -class SkImageDecoder : SkNoncopyable { -public: - virtual ~SkImageDecoder(); - - // TODO (scroggo): Merge with SkEncodedFormat - enum Format { - kUnknown_Format, - kBMP_Format, - kGIF_Format, - kICO_Format, - kJPEG_Format, - kPNG_Format, - kWBMP_Format, - kWEBP_Format, - kPKM_Format, - kKTX_Format, - kASTC_Format, - - kLastKnownFormat = kKTX_Format, - }; - - /** Return the format of image this decoder can decode. If this decoder can decode multiple - formats, kUnknown_Format will be returned. - */ - virtual Format getFormat() const; - - /** If planes or rowBytes is NULL, decodes the header and computes componentSizes - for memory allocation. - Otherwise, decodes the YUV planes into the provided image planes and - updates componentSizes to the final image size. - Returns whether the decoding was successful. - */ - bool decodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], void* planes[3], - size_t rowBytes[3], SkYUVColorSpace*); - - /** Return the format of the SkStreamRewindable or kUnknown_Format if it cannot be determined. - Rewinds the stream before returning. - */ - static Format GetStreamFormat(SkStreamRewindable*); - - /** Return a readable string of the Format provided. - */ - static const char* GetFormatName(Format); - - /** Return a readable string of the value returned by getFormat(). - */ - const char* getFormatName() const; - - /** Whether the decoder should skip writing zeroes to output if possible. - */ - bool getSkipWritingZeroes() const { return fSkipWritingZeroes; } - - /** Set to true if the decoder should skip writing any zeroes when - creating the output image. - This is a hint that may not be respected by the decoder. - It should only be used if it is known that the memory to write - to has already been set to 0; otherwise the resulting image will - have garbage. - This is ideal for images that contain a lot of completely transparent - pixels, but may be a performance hit for an image that has only a - few transparent pixels. - The default is false. - */ - void setSkipWritingZeroes(bool skip) { fSkipWritingZeroes = skip; } - - /** Returns true if the decoder should try to dither the resulting image. - The default setting is true. - */ - bool getDitherImage() const { return fDitherImage; } - - /** Set to true if the the decoder should try to dither the resulting image. - The default setting is true. - */ - void setDitherImage(bool dither) { fDitherImage = dither; } - - /** Returns true if the decoder should try to decode the - resulting image to a higher quality even at the expense of - the decoding speed. - */ - bool getPreferQualityOverSpeed() const { return fPreferQualityOverSpeed; } - - /** Set to true if the the decoder should try to decode the - resulting image to a higher quality even at the expense of - the decoding speed. - */ - void setPreferQualityOverSpeed(bool qualityOverSpeed) { - fPreferQualityOverSpeed = qualityOverSpeed; - } - - /** Set to true to require the decoder to return a bitmap with unpremultiplied - colors. The default is false, meaning the resulting bitmap will have its - colors premultiplied. - NOTE: Passing true to this function may result in a bitmap which cannot - be properly used by Skia. - */ - void setRequireUnpremultipliedColors(bool request) { - fRequireUnpremultipliedColors = request; - } - - /** Returns true if the decoder will only return bitmaps with unpremultiplied - colors. - */ - bool getRequireUnpremultipliedColors() const { return fRequireUnpremultipliedColors; } - - SkPngChunkReader* getPeeker() const { return fPeeker; } - SkPngChunkReader* setPeeker(SkPngChunkReader*); - - /** - * By default, the codec will try to comply with the "pref" colortype - * that is passed to decode() or decodeSubset(). However, this can be called - * to override that, causing the codec to try to match the src depth instead - * (as shown below). - * - * src_8Index -> kIndex_8_SkColorType - * src_8Gray -> kN32_SkColorType - * src_8bpc -> kN32_SkColorType - */ - void setPreserveSrcDepth(bool preserve) { - fPreserveSrcDepth = preserve; - } - - SkBitmap::Allocator* getAllocator() const { return fAllocator; } - SkBitmap::Allocator* setAllocator(SkBitmap::Allocator*); - - // sample-size, if set to > 1, tells the decoder to return a smaller than - // original bitmap, sampling 1 pixel for every size pixels. e.g. if sample - // size is set to 3, then the returned bitmap will be 1/3 as wide and high, - // and will contain 1/9 as many pixels as the original. - // Note: this is a hint, and the codec may choose to ignore this, or only - // approximate the sample size. - int getSampleSize() const { return fSampleSize; } - void setSampleSize(int size); - - /** Reset the sampleSize to its default of 1 - */ - void resetSampleSize() { this->setSampleSize(1); } - - /** Decoding is synchronous, but for long decodes, a different thread can - call this method safely. This sets a state that the decoders will - periodically check, and if they see it changed to cancel, they will - cancel. This will result in decode() returning false. However, there is - no guarantee that the decoder will see the state change in time, so - it is possible that cancelDecode() will be called, but will be ignored - and decode() will return true (assuming no other problems were - encountered). - - This state is automatically reset at the beginning of decode(). - */ - void cancelDecode() { - // now the subclass must query shouldCancelDecode() to be informed - // of the request - fShouldCancelDecode = true; - } - - /** Passed to the decode method. If kDecodeBounds_Mode is passed, then - only the bitmap's info need be set. If kDecodePixels_Mode - is passed, then the bitmap must have pixels or a pixelRef. - */ - enum Mode { - kDecodeBounds_Mode, //!< only return info in bitmap - kDecodePixels_Mode //!< return entire bitmap (including pixels) - }; - - /** Result of a decode. If read as a boolean, a partial success is - considered a success (true). - */ - enum Result { - kFailure = 0, //!< Image failed to decode. bitmap will be - // unchanged. - kPartialSuccess = 1, //!< Part of the image decoded. The rest is - // filled in automatically - kSuccess = 2 //!< The entire image was decoded, if Mode is - // kDecodePixels_Mode, or the bounds were - // decoded, in kDecodeBounds_Mode. - }; - - /** Given a stream, decode it into the specified bitmap. - If the decoder can decompress the image, it calls bitmap.setInfo(), - and then if the Mode is kDecodePixels_Mode, call allocPixelRef(), - which will allocated a pixelRef. To access the pixel memory, the codec - needs to call lockPixels/unlockPixels on the - bitmap. It can then set the pixels with the decompressed image. - * If the image cannot be decompressed, return kFailure. After the - * decoding, the function converts the decoded colortype in bitmap - * to pref if possible. Whether a conversion is feasible is - * tested by Bitmap::canCopyTo(pref). - - If an SkBitmap::Allocator is installed via setAllocator, it will be - used to allocate the pixel memory. A clever allocator can be used - to allocate the memory from a cache, volatile memory, or even from - an existing bitmap's memory. - - If an SkPngChunkReader is installed via setPeeker, it may be used to - peek into meta data during the decode. - */ - Result decode(SkStream*, SkBitmap* bitmap, SkColorType pref, Mode); - Result decode(SkStream* stream, SkBitmap* bitmap, Mode mode) { - return this->decode(stream, bitmap, kUnknown_SkColorType, mode); - } - - /** Given a stream, this will try to find an appropriate decoder object. - If none is found, the method returns NULL. - - DEPRECATED Please use SkImage::NewFromEncoded() or SkImageGenerator::NewFromEncoded(). - */ - static SkImageDecoder* Factory(SkStreamRewindable*); - - /** Decode the image stored in the specified file, and store the result - in bitmap. Return true for success or false on failure. - - @param pref Prefer this colortype. - - @param format On success, if format is non-null, it is set to the format - of the decoded file. On failure it is ignored. - - DEPRECATED Do not use. - */ - static bool DecodeFile(const char file[], SkBitmap* bitmap, SkColorType pref, Mode, - Format* format = NULL); - static bool DecodeFile(const char file[], SkBitmap* bitmap) { - return DecodeFile(file, bitmap, kUnknown_SkColorType, kDecodePixels_Mode, NULL); - } - - /** Decode the image stored in the specified memory buffer, and store the - result in bitmap. Return true for success or false on failure. - - @param pref Prefer this colortype. - - @param format On success, if format is non-null, it is set to the format - of the decoded buffer. On failure it is ignored. - - DEPRECATED Please use SkImage::NewFromEncoded() or SkImageGenerator::NewFromEncoded(). - */ - static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap, SkColorType pref, - Mode, Format* format = NULL); - static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap){ - return DecodeMemory(buffer, size, bitmap, kUnknown_SkColorType, kDecodePixels_Mode, NULL); - } - - /** Decode the image stored in the specified SkStreamRewindable, and store the result - in bitmap. Return true for success or false on failure. - - @param pref Prefer this colortype. - - @param format On success, if format is non-null, it is set to the format - of the decoded stream. On failure it is ignored. - - DEPRECATED Please use SkImage::NewFromEncoded() or SkImageGenerator::NewFromEncoded(). - */ - static bool DecodeStream(SkStreamRewindable* stream, SkBitmap* bitmap, SkColorType pref, Mode, - Format* format = NULL); - static bool DecodeStream(SkStreamRewindable* stream, SkBitmap* bitmap) { - return DecodeStream(stream, bitmap, kUnknown_SkColorType, kDecodePixels_Mode, NULL); - } - -protected: - // must be overridden in subclasses. This guy is called by decode(...) - virtual Result onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0; - - /** If planes or rowBytes is NULL, decodes the header and computes componentSizes - for memory allocation. - Otherwise, decodes the YUV planes into the provided image planes and - updates componentSizes to the final image size. - Returns whether the decoding was successful. - */ - virtual bool onDecodeYUV8Planes(SkStream*, SkISize[3] /*componentSizes*/, - void*[3] /*planes*/, size_t[3] /*rowBytes*/, - SkYUVColorSpace*) { - return false; - } - - /** - * Copy all fields on this decoder to the other decoder. Used by subclasses - * to decode a subimage using a different decoder, but with the same settings. - */ - void copyFieldsToOther(SkImageDecoder* other); - - /** Can be queried from within onDecode, to see if the user (possibly in - a different thread) has requested the decode to cancel. If this returns - true, your onDecode() should stop and return false. - Each subclass needs to decide how often it can query this, to balance - responsiveness with performance. - - Calling this outside of onDecode() may return undefined values. - */ - -public: - bool shouldCancelDecode() const { return fShouldCancelDecode; } - -protected: - SkImageDecoder(); - - /** - * Return the default preference being used by the current or latest call to decode. - */ - SkColorType getDefaultPref() { return fDefaultPref; } - - /* Helper for subclasses. Call this to allocate the pixel memory given the bitmap's info. - Returns true on success. This method handles checking for an optional Allocator. - */ - bool allocPixelRef(SkBitmap*, SkColorTable*) const; - - /** - * The raw data of the src image. - */ - enum SrcDepth { - // Color-indexed. - kIndex_SrcDepth, - // Grayscale in 8 bits. - k8BitGray_SrcDepth, - // 8 bits per component. Used for 24 bit if there is no alpha. - k32Bit_SrcDepth, - }; - /** The subclass, inside onDecode(), calls this to determine the colorType of - the returned bitmap. SrcDepth and hasAlpha reflect the raw data of the - src image. This routine returns the caller's preference given - srcDepth and hasAlpha, or kUnknown_SkColorType if there is no preference. - */ - SkColorType getPrefColorType(SrcDepth, bool hasAlpha) const; - -private: - SkPngChunkReader* fPeeker; - SkBitmap::Allocator* fAllocator; - int fSampleSize; - SkColorType fDefaultPref; // use if fUsePrefTable is false - bool fPreserveSrcDepth; - bool fDitherImage; - bool fSkipWritingZeroes; - mutable bool fShouldCancelDecode; - bool fPreferQualityOverSpeed; - bool fRequireUnpremultipliedColors; -}; - -/** Calling newDecoder with a stream returns a new matching imagedecoder - instance, or NULL if none can be found. The caller must manage its ownership - of the stream as usual, calling unref() when it is done, as the returned - decoder may have called ref() (and if so, the decoder is responsible for - balancing its ownership when it is destroyed). - */ -class SkImageDecoderFactory : public SkRefCnt { -public: - - - virtual SkImageDecoder* newDecoder(SkStreamRewindable*) = 0; - -private: - typedef SkRefCnt INHERITED; -}; - -class SkDefaultImageDecoderFactory : SkImageDecoderFactory { -public: - // calls SkImageDecoder::Factory(stream) - virtual SkImageDecoder* newDecoder(SkStreamRewindable* stream) { - return SkImageDecoder::Factory(stream); - } -}; - -// This macro declares a global (i.e., non-class owned) creation entry point -// for each decoder (e.g., CreateJPEGImageDecoder) -#define DECLARE_DECODER_CREATOR(codec) \ - SkImageDecoder *Create ## codec (); - -// This macro defines the global creation entry point for each decoder. Each -// decoder implementation that registers with the decoder factory must call it. -#define DEFINE_DECODER_CREATOR(codec) \ - SkImageDecoder* Create##codec() { return new Sk##codec; } - -// All the decoders known by Skia. Note that, depending on the compiler settings, -// not all of these will be available -DECLARE_DECODER_CREATOR(BMPImageDecoder); -DECLARE_DECODER_CREATOR(GIFImageDecoder); -DECLARE_DECODER_CREATOR(ICOImageDecoder); -DECLARE_DECODER_CREATOR(JPEGImageDecoder); -DECLARE_DECODER_CREATOR(PNGImageDecoder); -DECLARE_DECODER_CREATOR(WBMPImageDecoder); -DECLARE_DECODER_CREATOR(WEBPImageDecoder); -DECLARE_DECODER_CREATOR(PKMImageDecoder); -DECLARE_DECODER_CREATOR(KTXImageDecoder); -DECLARE_DECODER_CREATOR(ASTCImageDecoder); - -// Typedefs to make registering decoder and formatter callbacks easier. -// These have to be defined outside SkImageDecoder. :( -typedef SkTRegistry<SkImageDecoder*(*)(SkStreamRewindable*)> SkImageDecoder_DecodeReg; -typedef SkTRegistry<SkImageDecoder::Format(*)(SkStreamRewindable*)> SkImageDecoder_FormatReg; - -#endif diff --git a/include/core/SkImageEncoder.h b/include/core/SkImageEncoder.h index bb3341f836..1ccfae0bf9 100644 --- a/include/core/SkImageEncoder.h +++ b/include/core/SkImageEncoder.h @@ -110,8 +110,12 @@ DECLARE_ENCODER_CREATOR(PNGImageEncoder); DECLARE_ENCODER_CREATOR(KTXImageEncoder); DECLARE_ENCODER_CREATOR(WEBPImageEncoder); -#ifdef SK_BUILD_FOR_IOS -DECLARE_ENCODER_CREATOR(PNGImageEncoder_IOS); +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) +DECLARE_ENCODER_CREATOR(PNGImageEncoder_CG); +#endif + +#if defined(SK_BUILD_FOR_WIN) +DECLARE_ENCODER_CREATOR(ImageEncoder_WIC); #endif // Typedef to make registering encoder callback easier diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index 6c0dda0d3e..0cf9411f56 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -8,20 +8,23 @@ #ifndef SkPicture_DEFINED #define SkPicture_DEFINED -#include "SkImageDecoder.h" #include "SkRefCnt.h" +#include "SkRect.h" #include "SkTypes.h" class GrContext; class SkBigPicture; class SkBitmap; class SkCanvas; +class SkPath; class SkPictureData; class SkPixelSerializer; +class SkReadBuffer; class SkRefCntSet; class SkStream; class SkTypefacePlayback; class SkWStream; +class SkWriteBuffer; struct SkPictInfo; #define SK_SUPPORT_LEGACY_PICTURE_PTR diff --git a/include/core/SkPngChunkReader.h b/include/core/SkPngChunkReader.h index f424dd8cfc..0cd6634bce 100644 --- a/include/core/SkPngChunkReader.h +++ b/include/core/SkPngChunkReader.h @@ -16,7 +16,7 @@ * * Base class for optional callbacks to retrieve meta/chunk data out of a PNG * encoded image as it is being decoded. - * Used by SkImageDecoder and SkCodec. + * Used by SkCodec. */ class SkPngChunkReader : public SkRefCnt { public: diff --git a/include/core/SkWriteBuffer.h b/include/core/SkWriteBuffer.h index 8e4607887d..6e9d043aeb 100644 --- a/include/core/SkWriteBuffer.h +++ b/include/core/SkWriteBuffer.h @@ -10,6 +10,7 @@ #define SkWriteBuffer_DEFINED #include "SkData.h" +#include "SkImage.h" #include "SkPath.h" #include "SkPicture.h" #include "SkPixelSerializer.h" diff --git a/include/effects/SkImageSource.h b/include/effects/SkImageSource.h index ab6e0c161f..c18e2e57b5 100644 --- a/include/effects/SkImageSource.h +++ b/include/effects/SkImageSource.h @@ -8,10 +8,9 @@ #ifndef SkImageSource_DEFINED #define SkImageSource_DEFINED +#include "SkImage.h" #include "SkImageFilter.h" -class SkImage; - class SK_API SkImageSource : public SkImageFilter { public: static SkImageFilter* Create(SkImage*); diff --git a/src/android/SkBitmapRegionDecoder.cpp b/src/android/SkBitmapRegionDecoder.cpp index 712034ba44..101efbda45 100644 --- a/src/android/SkBitmapRegionDecoder.cpp +++ b/src/android/SkBitmapRegionDecoder.cpp @@ -11,7 +11,6 @@ #include "SkAndroidCodec.h" #include "SkCodec.h" #include "SkCodecPriv.h" -#include "SkImageDecoder.h" SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( SkData* data, Strategy strategy) { diff --git a/src/core/SkBigPicture.h b/src/core/SkBigPicture.h index 2e42213539..0834709f8a 100644 --- a/src/core/SkBigPicture.h +++ b/src/core/SkBigPicture.h @@ -10,9 +10,11 @@ #include "SkOncePtr.h" #include "SkPicture.h" +#include "SkRect.h" #include "SkTemplates.h" class SkBBoxHierarchy; +class SkMatrix; class SkRecord; // An implementation of SkPicture supporting an arbitrary number of drawing commands. diff --git a/src/core/SkLayerInfo.h b/src/core/SkLayerInfo.h index 04ae1794a2..aa19ecbd0c 100644 --- a/src/core/SkLayerInfo.h +++ b/src/core/SkLayerInfo.h @@ -9,6 +9,8 @@ #define SkLayerInfo_DEFINED #include "SkBigPicture.h" +#include "SkMatrix.h" +#include "SkPaint.h" #include "SkTArray.h" // This class stores information about the saveLayer/restore pairs found diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h index a606681896..4f4317c298 100644 --- a/src/gpu/GrLayerCache.h +++ b/src/gpu/GrLayerCache.h @@ -16,6 +16,7 @@ #include "SkChecksum.h" #include "SkImageFilter.h" #include "SkMessageBus.h" +#include "SkPaint.h" #include "SkPicture.h" #include "SkTDynamicHash.h" diff --git a/src/images/SkForceLinking.cpp b/src/images/SkForceLinking.cpp index 55b7021432..05fc7e08a6 100644 --- a/src/images/SkForceLinking.cpp +++ b/src/images/SkForceLinking.cpp @@ -5,8 +5,8 @@ * found in the LICENSE file. */ +#include "SkImageEncoder.h" #include "SkForceLinking.h" -#include "SkImageDecoder.h" // This method is required to fool the linker into not discarding the pre-main // initialization and registration of the decoder classes. Passing true will @@ -14,26 +14,22 @@ int SkForceLinking(bool doNotPassTrue) { if (doNotPassTrue) { SkASSERT(false); - CreateJPEGImageDecoder(); - CreateWEBPImageDecoder(); - CreateBMPImageDecoder(); - CreateICOImageDecoder(); - CreateWBMPImageDecoder(); + CreateJPEGImageEncoder(); + CreateWEBPImageEncoder(); + // Only link hardware texture codecs on platforms that build them. See images.gyp #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK - CreatePKMImageDecoder(); - CreateKTXImageDecoder(); - CreateASTCImageDecoder(); + CreateKTXImageEncoder(); #endif - // Only link GIF and PNG on platforms that build them. See images.gyp + #if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_IOS) - CreateGIFImageDecoder(); + CreatePNGImageEncoder(); #endif -#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_IOS) - CreatePNGImageDecoder(); +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + CreatePNGImageEncoder_CG(); #endif -#if defined(SK_BUILD_FOR_IOS) - CreatePNGImageEncoder_IOS(); +#if defined(SK_BUILD_FOR_WIN) + CreateImageEncoder_WIC(); #endif return -1; } diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp deleted file mode 100644 index 221faf74d5..0000000000 --- a/src/images/SkImageDecoder.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkImageDecoder.h" -#include "SkBitmap.h" -#include "SkImagePriv.h" -#include "SkPixelRef.h" -#include "SkStream.h" -#include "SkTemplates.h" -#include "SkCanvas.h" - -SkImageDecoder::SkImageDecoder() - : fPeeker(nullptr) - , fAllocator(nullptr) - , fSampleSize(1) - , fDefaultPref(kUnknown_SkColorType) - , fPreserveSrcDepth(false) - , fDitherImage(true) - , fSkipWritingZeroes(false) - , fPreferQualityOverSpeed(false) - , fRequireUnpremultipliedColors(false) { -} - -SkImageDecoder::~SkImageDecoder() { - SkSafeUnref(fPeeker); - SkSafeUnref(fAllocator); -} - -void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) { - if (nullptr == other) { - return; - } - other->setPeeker(fPeeker); - other->setAllocator(fAllocator); - other->setSampleSize(fSampleSize); - other->setPreserveSrcDepth(fPreserveSrcDepth); - other->setDitherImage(fDitherImage); - other->setSkipWritingZeroes(fSkipWritingZeroes); - other->setPreferQualityOverSpeed(fPreferQualityOverSpeed); - other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors); -} - -SkImageDecoder::Format SkImageDecoder::getFormat() const { - return kUnknown_Format; -} - -const char* SkImageDecoder::getFormatName() const { - return GetFormatName(this->getFormat()); -} - -const char* SkImageDecoder::GetFormatName(Format format) { - switch (format) { - case kUnknown_Format: - return "Unknown Format"; - case kBMP_Format: - return "BMP"; - case kGIF_Format: - return "GIF"; - case kICO_Format: - return "ICO"; - case kPKM_Format: - return "PKM"; - case kKTX_Format: - return "KTX"; - case kASTC_Format: - return "ASTC"; - case kJPEG_Format: - return "JPEG"; - case kPNG_Format: - return "PNG"; - case kWBMP_Format: - return "WBMP"; - case kWEBP_Format: - return "WEBP"; - default: - SkDEBUGFAIL("Invalid format type!"); - } - return "Unknown Format"; -} - -SkPngChunkReader* SkImageDecoder::setPeeker(SkPngChunkReader* peeker) { - SkRefCnt_SafeAssign(fPeeker, peeker); - return peeker; -} - -SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) { - SkRefCnt_SafeAssign(fAllocator, alloc); - return alloc; -} - -void SkImageDecoder::setSampleSize(int size) { - if (size < 1) { - size = 1; - } - fSampleSize = size; -} - -bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap, - SkColorTable* ctable) const { - return bitmap->tryAllocPixels(fAllocator, ctable); -} - -/////////////////////////////////////////////////////////////////////////////// - -SkColorType SkImageDecoder::getPrefColorType(SrcDepth srcDepth, bool srcHasAlpha) const { - SkColorType ct = fDefaultPref; - if (fPreserveSrcDepth) { - switch (srcDepth) { - case kIndex_SrcDepth: - ct = kIndex_8_SkColorType; - break; - case k8BitGray_SrcDepth: - ct = kN32_SkColorType; - break; - case k32Bit_SrcDepth: - ct = kN32_SkColorType; - break; - } - } - return ct; -} - -SkImageDecoder::Result SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, SkColorType pref, - Mode mode) { - // we reset this to false before calling onDecode - fShouldCancelDecode = false; - // assign this, for use by getPrefColorType(), in case fUsePrefTable is false - fDefaultPref = pref; - - // pass a temporary bitmap, so that if we return false, we are assured of - // leaving the caller's bitmap untouched. - SkBitmap tmp; - const Result result = this->onDecode(stream, &tmp, mode); - if (kFailure != result) { - bm->swap(tmp); - } - return result; -} - -/////////////////////////////////////////////////////////////////////////////// - -bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, SkColorType pref, Mode mode, - Format* format) { - SkASSERT(file); - SkASSERT(bm); - - SkAutoTDelete<SkStreamRewindable> stream(SkStream::NewFromFile(file)); - if (stream.get()) { - if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) { - if (SkPixelRef* pr = bm->pixelRef()) { - pr->setURI(file); - } - return true; - } - } - return false; -} - -bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm, SkColorType pref, - Mode mode, Format* format) { - if (0 == size) { - return false; - } - SkASSERT(buffer); - - SkMemoryStream stream(buffer, size); - return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format); -} - -bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkColorType pref, - Mode mode, Format* format) { - SkASSERT(stream); - SkASSERT(bm); - - bool success = false; - SkImageDecoder* codec = SkImageDecoder::Factory(stream); - - if (codec) { - success = codec->decode(stream, bm, pref, mode) != kFailure; - if (success && format) { - *format = codec->getFormat(); - if (kUnknown_Format == *format) { - if (stream->rewind()) { - *format = GetStreamFormat(stream); - } - } - } - delete codec; - } - return success; -} - -bool SkImageDecoder::decodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], void* planes[3], - size_t rowBytes[3], SkYUVColorSpace* colorSpace) { - // we reset this to false before calling onDecodeYUV8Planes - fShouldCancelDecode = false; - - return this->onDecodeYUV8Planes(stream, componentSizes, planes, rowBytes, colorSpace); -} diff --git a/src/images/SkImageDecoder_FactoryDefault.cpp b/src/images/SkImageDecoder_FactoryDefault.cpp index 77c0a0ac57..ef8ddda830 100644 --- a/src/images/SkImageDecoder_FactoryDefault.cpp +++ b/src/images/SkImageDecoder_FactoryDefault.cpp @@ -6,18 +6,9 @@ * found in the LICENSE file. */ -#include "SkImageDecoder.h" #include "SkMovie.h" #include "SkStream.h" -extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); - -SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { - return image_decoder_from_stream(stream); -} - -///////////////////////////////////////////////////////////////////////// - typedef SkTRegistry<SkMovie*(*)(SkStreamRewindable*)> MovieReg; SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { diff --git a/src/images/SkImageDecoder_FactoryRegistrar.cpp b/src/images/SkImageDecoder_FactoryRegistrar.cpp deleted file mode 100644 index 36034d20ad..0000000000 --- a/src/images/SkImageDecoder_FactoryRegistrar.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkErrorInternals.h" -#include "SkImageDecoder.h" -#include "SkStream.h" -#include "SkTRegistry.h" - -// This file is used for registration of SkImageDecoders. It also holds a function -// for checking all the the registered SkImageDecoders for one that matches an -// input SkStreamRewindable. - -template SkImageDecoder_DecodeReg* SkImageDecoder_DecodeReg::gHead; - -SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); - -SkImageDecoder* image_decoder_from_stream(SkStreamRewindable* stream) { - SkImageDecoder* codec = nullptr; - const SkImageDecoder_DecodeReg* curr = SkImageDecoder_DecodeReg::Head(); - while (curr) { - codec = curr->factory()(stream); - // we rewind here, because we promise later when we call "decode", that - // the stream will be at its beginning. - bool rewindSuceeded = stream->rewind(); - - // our image decoder's require that rewind is supported so we fail early - // if we are given a stream that does not support rewinding. - if (!rewindSuceeded) { - SkDEBUGF(("Unable to rewind the image stream.")); - delete codec; - return nullptr; - } - - if (codec) { - return codec; - } - curr = curr->next(); - } - return nullptr; -} - -template SkImageDecoder_FormatReg* SkImageDecoder_FormatReg::gHead; - -SkImageDecoder::Format SkImageDecoder::GetStreamFormat(SkStreamRewindable* stream) { - const SkImageDecoder_FormatReg* curr = SkImageDecoder_FormatReg::Head(); - while (curr != nullptr) { - Format format = curr->factory()(stream); - if (!stream->rewind()) { - SkErrorInternals::SetError(kInvalidOperation_SkError, - "Unable to rewind the image stream\n"); - return kUnknown_Format; - } - if (format != kUnknown_Format) { - return format; - } - curr = curr->next(); - } - return kUnknown_Format; -} diff --git a/src/images/SkImageDecoder_astc.cpp b/src/images/SkImageDecoder_astc.cpp deleted file mode 100644 index 30d65f1f0f..0000000000 --- a/src/images/SkImageDecoder_astc.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkData.h" -#include "SkEndian.h" -#include "SkColorPriv.h" -#include "SkImageDecoder.h" -#include "SkScaledBitmapSampler.h" -#include "SkStream.h" -#include "SkStreamPriv.h" -#include "SkTypes.h" - -#include "SkTextureCompressor.h" - -class SkASTCImageDecoder : public SkImageDecoder { -public: - SkASTCImageDecoder() { } - - Format getFormat() const override { - return kASTC_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; - -private: - typedef SkImageDecoder INHERITED; -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -static const uint32_t kASTCMagicNumber = 0x5CA1AB13; - -static inline int read_24bit(const uint8_t* buf) { - // Assume everything is little endian... - return - static_cast<int>(buf[0]) | - (static_cast<int>(buf[1]) << 8) | - (static_cast<int>(buf[2]) << 16); -} - -SkImageDecoder::Result SkASTCImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { - auto data = SkCopyStreamToData(stream); - if (!data || !data->size()) { - return kFailure; - } - - unsigned char* buf = (unsigned char*) data->data(); - - // Make sure that the magic header is there... - SkASSERT(SkEndian_SwapLE32(*(reinterpret_cast<uint32_t*>(buf))) == kASTCMagicNumber); - - // Advance past the magic header - buf += 4; - - const int blockDimX = buf[0]; - const int blockDimY = buf[1]; - const int blockDimZ = buf[2]; - - if (1 != blockDimZ) { - // We don't support decoding 3D - return kFailure; - } - - // Choose the proper ASTC format - SkTextureCompressor::Format astcFormat; - if (4 == blockDimX && 4 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_4x4_Format; - } else if (5 == blockDimX && 4 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_5x4_Format; - } else if (5 == blockDimX && 5 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_5x5_Format; - } else if (6 == blockDimX && 5 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_6x5_Format; - } else if (6 == blockDimX && 6 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_6x6_Format; - } else if (8 == blockDimX && 5 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_8x5_Format; - } else if (8 == blockDimX && 6 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_8x6_Format; - } else if (8 == blockDimX && 8 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_8x8_Format; - } else if (10 == blockDimX && 5 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_10x5_Format; - } else if (10 == blockDimX && 6 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_10x6_Format; - } else if (10 == blockDimX && 8 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_10x8_Format; - } else if (10 == blockDimX && 10 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_10x10_Format; - } else if (12 == blockDimX && 10 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_12x10_Format; - } else if (12 == blockDimX && 12 == blockDimY) { - astcFormat = SkTextureCompressor::kASTC_12x12_Format; - } else { - // We don't support any other block dimensions.. - return kFailure; - } - - // Advance buf past the block dimensions - buf += 3; - - // Read the width/height/depth from the buffer... - const int width = read_24bit(buf); - const int height = read_24bit(buf + 3); - const int depth = read_24bit(buf + 6); - - if (1 != depth) { - // We don't support decoding 3D. - return kFailure; - } - - // Advance the buffer past the image dimensions - buf += 9; - - // Setup the sampler... - SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); - - // Determine the alpha of the bitmap... - SkAlphaType alphaType = kOpaque_SkAlphaType; - if (this->getRequireUnpremultipliedColors()) { - alphaType = kUnpremul_SkAlphaType; - } else { - alphaType = kPremul_SkAlphaType; - } - - // Set the config... - bm->setInfo(SkImageInfo::MakeN32(sampler.scaledWidth(), sampler.scaledHeight(), alphaType)); - - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - - if (!this->allocPixelRef(bm, nullptr)) { - return kFailure; - } - - // Lock the pixels, since we're about to write to them... - SkAutoLockPixels alp(*bm); - - if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, *this)) { - return kFailure; - } - - // ASTC Data is encoded as RGBA pixels, so we should extract it as such - int nPixels = width * height; - SkAutoMalloc outRGBAData(nPixels * 4); - uint8_t *outRGBADataPtr = reinterpret_cast<uint8_t *>(outRGBAData.get()); - - // Decode ASTC - if (!SkTextureCompressor::DecompressBufferFromFormat( - outRGBADataPtr, width*4, buf, width, height, astcFormat)) { - return kFailure; - } - - // Set each of the pixels... - const int srcRowBytes = width * 4; - const int dstHeight = sampler.scaledHeight(); - const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBADataPtr); - srcRow += sampler.srcY0() * srcRowBytes; - for (int y = 0; y < dstHeight; ++y) { - sampler.next(srcRow); - srcRow += sampler.srcDY() * srcRowBytes; - } - - return kSuccess; -} - -///////////////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(ASTCImageDecoder); -///////////////////////////////////////////////////////////////////////////////////////// - -static bool is_astc(SkStreamRewindable* stream) { - // Read the ASTC header and make sure it's valid. - uint32_t magic; - if (stream->read((void*)&magic, 4) != 4) { - return false; - } - - return kASTCMagicNumber == SkEndian_SwapLE32(magic); -} - -static SkImageDecoder* sk_libastc_dfactory(SkStreamRewindable* stream) { - if (is_astc(stream)) { - return new SkASTCImageDecoder; - } - return nullptr; -} - -static SkImageDecoder_DecodeReg gReg(sk_libastc_dfactory); - -static SkImageDecoder::Format get_format_astc(SkStreamRewindable* stream) { - if (is_astc(stream)) { - return SkImageDecoder::kASTC_Format; - } - return SkImageDecoder::kUnknown_Format; -} - -static SkImageDecoder_FormatReg gFormatReg(get_format_astc); diff --git a/src/images/SkImageDecoder_ktx.cpp b/src/images/SkImageDecoder_ktx.cpp index 156674565c..79f0293c2a 100644 --- a/src/images/SkImageDecoder_ktx.cpp +++ b/src/images/SkImageDecoder_ktx.cpp @@ -6,10 +6,9 @@ */ #include "SkColorPriv.h" -#include "SkImageDecoder.h" +#include "SkImageEncoder.h" #include "SkImageGenerator.h" #include "SkPixelRef.h" -#include "SkScaledBitmapSampler.h" #include "SkStream.h" #include "SkStreamPriv.h" #include "SkTypes.h" @@ -17,230 +16,14 @@ #include "ktx.h" #include "etc1.h" -///////////////////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// -// KTX Image decoder -// --- +// KTX Image Encoder +// // KTX is a general texture data storage file format ratified by the Khronos Group. As an // overview, a KTX file contains all of the appropriate values needed to fully specify a // texture in an OpenGL application, including the use of compressed data. // -// This decoder is meant to be used with an SkDiscardablePixelRef so that GPU backends -// can sniff the data before creating a texture. If they encounter a compressed format -// that they understand, they can then upload the data directly to the GPU. Otherwise, -// they will decode the data into a format that Skia supports. - -class SkKTXImageDecoder : public SkImageDecoder { -public: - SkKTXImageDecoder() { } - - Format getFormat() const override { - return kKTX_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; - -private: - typedef SkImageDecoder INHERITED; -}; - -SkImageDecoder::Result SkKTXImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { - // TODO: Implement SkStream::copyToData() that's cheap for memory and file streams - auto data = SkCopyStreamToData(stream); - if (nullptr == data) { - return kFailure; - } - - SkKTXFile ktxFile(data.get()); - if (!ktxFile.valid()) { - return kFailure; - } - - const unsigned short width = ktxFile.width(); - const unsigned short height = ktxFile.height(); - - // Set a flag if our source is premultiplied alpha - const SkString premulKey("KTXPremultipliedAlpha"); - const bool bSrcIsPremul = ktxFile.getValueForKey(premulKey) == SkString("True"); - - // Setup the sampler... - SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); - - // Determine the alpha of the bitmap... - SkAlphaType alphaType = kOpaque_SkAlphaType; - if (ktxFile.isRGBA8()) { - if (this->getRequireUnpremultipliedColors()) { - alphaType = kUnpremul_SkAlphaType; - // If the client wants unpremul colors and we only have - // premul, then we cannot honor their wish. - if (bSrcIsPremul) { - return kFailure; - } - } else { - alphaType = kPremul_SkAlphaType; - } - } - - // Search through the compressed formats to see if the KTX file is holding - // compressed data - bool ktxIsCompressed = false; - SkTextureCompressor::Format ktxCompressedFormat; - for (int i = 0; i < SkTextureCompressor::kFormatCnt; ++i) { - SkTextureCompressor::Format fmt = static_cast<SkTextureCompressor::Format>(i); - if (ktxFile.isCompressedFormat(fmt)) { - ktxIsCompressed = true; - ktxCompressedFormat = fmt; - break; - } - } - - // If the compressed format is a grayscale image, then setup the bitmap properly... - bool isCompressedAlpha = ktxIsCompressed && - ((SkTextureCompressor::kLATC_Format == ktxCompressedFormat) || - (SkTextureCompressor::kR11_EAC_Format == ktxCompressedFormat)); - - // Set the image dimensions and underlying pixel type. - if (isCompressedAlpha) { - const int w = sampler.scaledWidth(); - const int h = sampler.scaledHeight(); - bm->setInfo(SkImageInfo::MakeA8(w, h)); - } else { - const int w = sampler.scaledWidth(); - const int h = sampler.scaledHeight(); - bm->setInfo(SkImageInfo::MakeN32(w, h, alphaType)); - } - - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - - // If we've made it this far, then we know how to grok the data. - if (!this->allocPixelRef(bm, nullptr)) { - return kFailure; - } - - // Lock the pixels, since we're about to write to them... - SkAutoLockPixels alp(*bm); - - if (isCompressedAlpha) { - if (!sampler.begin(bm, SkScaledBitmapSampler::kGray, *this)) { - return kFailure; - } - - // Alpha data is only a single byte per pixel. - int nPixels = width * height; - SkAutoMalloc outRGBData(nPixels); - uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get()); - - // Decode the compressed format - const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); - if (!SkTextureCompressor::DecompressBufferFromFormat( - outRGBDataPtr, width, buf, width, height, ktxCompressedFormat)) { - return kFailure; - } - - // Set each of the pixels... - const int srcRowBytes = width; - const int dstHeight = sampler.scaledHeight(); - const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr); - srcRow += sampler.srcY0() * srcRowBytes; - for (int y = 0; y < dstHeight; ++y) { - sampler.next(srcRow); - srcRow += sampler.srcDY() * srcRowBytes; - } - - return kSuccess; - - } else if (ktxFile.isCompressedFormat(SkTextureCompressor::kETC1_Format)) { - if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { - return kFailure; - } - - // ETC1 Data is encoded as RGB pixels, so we should extract it as such - int nPixels = width * height; - SkAutoMalloc outRGBData(nPixels * 3); - uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get()); - - // Decode ETC1 - const uint8_t *buf = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); - if (!SkTextureCompressor::DecompressBufferFromFormat( - outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) { - return kFailure; - } - - // Set each of the pixels... - const int srcRowBytes = width * 3; - const int dstHeight = sampler.scaledHeight(); - const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr); - srcRow += sampler.srcY0() * srcRowBytes; - for (int y = 0; y < dstHeight; ++y) { - sampler.next(srcRow); - srcRow += sampler.srcDY() * srcRowBytes; - } - - return kSuccess; - - } else if (ktxFile.isRGB8()) { - - // Uncompressed RGB data (without alpha) - if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { - return kFailure; - } - - // Just need to read RGB pixels - const int srcRowBytes = width * 3; - const int dstHeight = sampler.scaledHeight(); - const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); - srcRow += sampler.srcY0() * srcRowBytes; - for (int y = 0; y < dstHeight; ++y) { - sampler.next(srcRow); - srcRow += sampler.srcDY() * srcRowBytes; - } - - return kSuccess; - - } else if (ktxFile.isRGBA8()) { - - // Uncompressed RGBA data - - // If we know that the image contains premultiplied alpha, then - // we need to turn off the premultiplier - SkScaledBitmapSampler::Options opts (*this); - if (bSrcIsPremul) { - SkASSERT(bm->alphaType() == kPremul_SkAlphaType); - SkASSERT(!this->getRequireUnpremultipliedColors()); - - opts.fPremultiplyAlpha = false; - } - - if (!sampler.begin(bm, SkScaledBitmapSampler::kRGBA, opts)) { - return kFailure; - } - - // Just need to read RGBA pixels - const int srcRowBytes = width * 4; - const int dstHeight = sampler.scaledHeight(); - const uint8_t *srcRow = reinterpret_cast<const uint8_t *>(ktxFile.pixelData()); - srcRow += sampler.srcY0() * srcRowBytes; - for (int y = 0; y < dstHeight; ++y) { - sampler.next(srcRow); - srcRow += sampler.srcDY() * srcRowBytes; - } - - return kSuccess; - } - - return kFailure; -} - -/////////////////////////////////////////////////////////////////////////////// - -// KTX Image Encoder -// // This encoder takes a best guess at how to encode the bitmap passed to it. If // there is an installed discardable pixel ref with existing PKM data, then we // will repurpose the existing ETC1 data into a KTX file. If the data contains @@ -304,28 +87,11 @@ bool SkKTXImageEncoder::encodePKM(SkWStream* stream, const SkData *data) { } ///////////////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(KTXImageDecoder); DEFINE_ENCODER_CREATOR(KTXImageEncoder); ///////////////////////////////////////////////////////////////////////////////////////// -static SkImageDecoder* sk_libktx_dfactory(SkStreamRewindable* stream) { - if (SkKTXFile::is_ktx(stream)) { - return new SkKTXImageDecoder; - } - return nullptr; -} - -static SkImageDecoder::Format get_format_ktx(SkStreamRewindable* stream) { - if (SkKTXFile::is_ktx(stream)) { - return SkImageDecoder::kKTX_Format; - } - return SkImageDecoder::kUnknown_Format; -} - SkImageEncoder* sk_libktx_efactory(SkImageEncoder::Type t) { return (SkImageEncoder::kKTX_Type == t) ? new SkKTXImageEncoder : nullptr; } -static SkImageDecoder_DecodeReg gReg(sk_libktx_dfactory); -static SkImageDecoder_FormatReg gFormatReg(get_format_ktx); static SkImageEncoder_EncodeReg gEReg(sk_libktx_efactory); diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp deleted file mode 100644 index b9359bea7a..0000000000 --- a/src/images/SkImageDecoder_libbmp.cpp +++ /dev/null @@ -1,166 +0,0 @@ - -/* - * Copyright 2007 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "bmpdecoderhelper.h" -#include "SkColorPriv.h" -#include "SkData.h" -#include "SkImageDecoder.h" -#include "SkScaledBitmapSampler.h" -#include "SkStream.h" -#include "SkStreamPriv.h" -#include "SkTDArray.h" - -class SkBMPImageDecoder : public SkImageDecoder { -public: - SkBMPImageDecoder() {} - - Format getFormat() const override { - return kBMP_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) override; - -private: - typedef SkImageDecoder INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(BMPImageDecoder); -/////////////////////////////////////////////////////////////////////////////// - -static bool is_bmp(SkStreamRewindable* stream) { - static const char kBmpMagic[] = { 'B', 'M' }; - - - char buffer[sizeof(kBmpMagic)]; - - return stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) && - !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic)); -} - -static SkImageDecoder* sk_libbmp_dfactory(SkStreamRewindable* stream) { - if (is_bmp(stream)) { - return new SkBMPImageDecoder; - } - return nullptr; -} - -static SkImageDecoder_DecodeReg gReg(sk_libbmp_dfactory); - -static SkImageDecoder::Format get_format_bmp(SkStreamRewindable* stream) { - if (is_bmp(stream)) { - return SkImageDecoder::kBMP_Format; - } - return SkImageDecoder::kUnknown_Format; -} - -static SkImageDecoder_FormatReg gFormatReg(get_format_bmp); - -/////////////////////////////////////////////////////////////////////////////// - -class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback { -public: - // we don't copy the bitmap, just remember the pointer - SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {} - - // override from BmpDecoderCallback - virtual uint8* SetSize(int width, int height) { - fWidth = width; - fHeight = height; - if (fJustBounds) { - return nullptr; - } - - fRGB.setCount(width * height * 3); // 3 == r, g, b - return fRGB.begin(); - } - - int width() const { return fWidth; } - int height() const { return fHeight; } - const uint8_t* rgb() const { return fRGB.begin(); } - -private: - SkTDArray<uint8_t> fRGB; - int fWidth; - int fHeight; - bool fJustBounds; -}; - -SkImageDecoder::Result SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { - // First read the entire stream, so that all of the data can be passed to - // the BmpDecoderHelper. - - auto data = SkCopyStreamToData(stream); - if (!data) { - return kFailure; - } - - // Byte length of all of the data. - const size_t length = data->size(); - if (0 == length) { - return kFailure; - } - - const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode; - SkBmpDecoderCallback callback(justBounds); - - // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...] - { - image_codec::BmpDecoderHelper helper; - const int max_pixels = 16383*16383; // max width*height - if (!helper.DecodeImage((const char*) data->data(), length, - max_pixels, &callback)) { - return kFailure; - } - } - - // we don't need this anymore, so free it now (before we try to allocate - // the bitmap's pixels) rather than waiting for its destructor - data.reset(nullptr); - - int width = callback.width(); - int height = callback.height(); - SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, false); - - // only accept prefConfig if it makes sense for us - if (kARGB_4444_SkColorType != colorType && kRGB_565_SkColorType != colorType) { - colorType = kN32_SkColorType; - } - - SkScaledBitmapSampler sampler(width, height, getSampleSize()); - - bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), - colorType, kOpaque_SkAlphaType)); - - if (justBounds) { - return kSuccess; - } - - if (!this->allocPixelRef(bm, nullptr)) { - return kFailure; - } - - SkAutoLockPixels alp(*bm); - - if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { - return kFailure; - } - - const int srcRowBytes = width * 3; - const int dstHeight = sampler.scaledHeight(); - const uint8_t* srcRow = callback.rgb(); - - srcRow += sampler.srcY0() * srcRowBytes; - for (int y = 0; y < dstHeight; y++) { - sampler.next(srcRow); - srcRow += sampler.srcDY() * srcRowBytes; - } - return kSuccess; -} diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp deleted file mode 100644 index 2677b13073..0000000000 --- a/src/images/SkImageDecoder_libgif.cpp +++ /dev/null @@ -1,541 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkColor.h" -#include "SkColorPriv.h" -#include "SkColorTable.h" -#include "SkImageDecoder.h" -#include "SkRTConf.h" -#include "SkScaledBitmapSampler.h" -#include "SkStream.h" -#include "SkTemplates.h" -#include "SkUtils.h" - -#include "gif_lib.h" - -class SkGIFImageDecoder : public SkImageDecoder { -public: - Format getFormat() const override { - return kGIF_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) override; - -private: - typedef SkImageDecoder INHERITED; -}; - -static const uint8_t gStartingIterlaceYValue[] = { - 0, 4, 2, 1 -}; -static const uint8_t gDeltaIterlaceYValue[] = { - 8, 8, 4, 2 -}; - -SK_CONF_DECLARE(bool, c_suppressGIFImageDecoderWarnings, - "images.gif.suppressDecoderWarnings", true, - "Suppress GIF warnings and errors when calling image decode " - "functions."); - - -/* Implement the GIF interlace algorithm in an iterator. - 1) grab every 8th line beginning at 0 - 2) grab every 8th line beginning at 4 - 3) grab every 4th line beginning at 2 - 4) grab every 2nd line beginning at 1 -*/ -class GifInterlaceIter { -public: - GifInterlaceIter(int height) : fHeight(height) { - fStartYPtr = gStartingIterlaceYValue; - fDeltaYPtr = gDeltaIterlaceYValue; - - fCurrY = *fStartYPtr++; - fDeltaY = *fDeltaYPtr++; - } - - int currY() const { - SkASSERT(fStartYPtr); - SkASSERT(fDeltaYPtr); - return fCurrY; - } - - void next() { - SkASSERT(fStartYPtr); - SkASSERT(fDeltaYPtr); - - int y = fCurrY + fDeltaY; - // We went from an if statement to a while loop so that we iterate - // through fStartYPtr until a valid row is found. This is so that images - // that are smaller than 5x5 will not trash memory. - while (y >= fHeight) { - if (gStartingIterlaceYValue + - SK_ARRAY_COUNT(gStartingIterlaceYValue) == fStartYPtr) { - // we done - SkDEBUGCODE(fStartYPtr = nullptr;) - SkDEBUGCODE(fDeltaYPtr = nullptr;) - y = 0; - } else { - y = *fStartYPtr++; - fDeltaY = *fDeltaYPtr++; - } - } - fCurrY = y; - } - -private: - const int fHeight; - int fCurrY; - int fDeltaY; - const uint8_t* fStartYPtr; - const uint8_t* fDeltaYPtr; -}; - -/////////////////////////////////////////////////////////////////////////////// - -static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out, - int size) { - SkStream* stream = (SkStream*) fileType->UserData; - return (int) stream->read(out, size); -} - -void CheckFreeExtension(SavedImage* Image) { - if (Image->ExtensionBlocks) { -#if GIFLIB_MAJOR < 5 - FreeExtension(Image); -#else - GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks); -#endif - } -} - -// return nullptr on failure -static const ColorMapObject* find_colormap(const GifFileType* gif) { - const ColorMapObject* cmap = gif->Image.ColorMap; - if (nullptr == cmap) { - cmap = gif->SColorMap; - } - - if (nullptr == cmap) { - // no colormap found - return nullptr; - } - // some sanity checks - if (cmap && ((unsigned)cmap->ColorCount > 256 || - cmap->ColorCount != (1 << cmap->BitsPerPixel))) { - cmap = nullptr; - } - return cmap; -} - -// return -1 if not found (i.e. we're completely opaque) -static int find_transpIndex(const SavedImage& image, int colorCount) { - int transpIndex = -1; - for (int i = 0; i < image.ExtensionBlockCount; ++i) { - const ExtensionBlock* eb = image.ExtensionBlocks + i; - if (eb->Function == 0xF9 && eb->ByteCount == 4) { - if (eb->Bytes[0] & 1) { - transpIndex = (unsigned char)eb->Bytes[3]; - // check for valid transpIndex - if (transpIndex >= colorCount) { - transpIndex = -1; - } - break; - } - } - } - return transpIndex; -} - -static SkImageDecoder::Result error_return(const SkBitmap& bm, const char msg[]) { - if (!c_suppressGIFImageDecoderWarnings) { - SkDebugf("libgif error [%s] bitmap [%d %d] pixels %p colortable %p\n", - msg, bm.width(), bm.height(), bm.getPixels(), - bm.getColorTable()); - } - return SkImageDecoder::kFailure; -} - -static void gif_warning(const SkBitmap& bm, const char msg[]) { - if (!c_suppressGIFImageDecoderWarnings) { - SkDebugf("libgif warning [%s] bitmap [%d %d] pixels %p colortable %p\n", - msg, bm.width(), bm.height(), bm.getPixels(), - bm.getColorTable()); - } -} - -/** - * Skip rows in the source gif image. - * @param gif Source image. - * @param dst Scratch output needed by gif library call. Must be >= width bytes. - * @param width Bytes per row in the source image. - * @param rowsToSkip Number of rows to skip. - * @return True on success, false on GIF_ERROR. - */ -static bool skip_src_rows(GifFileType* gif, uint8_t* dst, int width, int rowsToSkip) { - for (int i = 0; i < rowsToSkip; i++) { - if (DGifGetLine(gif, dst, width) == GIF_ERROR) { - return false; - } - } - return true; -} - -/** - * GIFs with fewer then 256 color entries will sometimes index out of - * bounds of the color table (this is malformed, but libgif does not - * check sicne it is rare). This function checks for this error and - * fixes it. This makes the output image consistantly deterministic. - */ -static void sanitize_indexed_bitmap(SkBitmap* bm) { - if ((kIndex_8_SkColorType == bm->colorType()) && !(bm->empty())) { - SkAutoLockPixels alp(*bm); - if (bm->getPixels()) { - SkColorTable* ct = bm->getColorTable(); // Index8 must have it. - SkASSERT(ct != nullptr); - uint32_t count = ct->count(); - SkASSERT(count > 0); - SkASSERT(count <= 0x100); - if (count != 0x100) { // Full colortables can't go wrong. - // Count is a power of 2; asserted elsewhere. - uint8_t byteMask = (~(count - 1)); - bool warning = false; - uint8_t* addr = static_cast<uint8_t*>(bm->getPixels()); - int height = bm->height(); - int width = bm->width(); - size_t rowBytes = bm->rowBytes(); - while (--height >= 0) { - uint8_t* ptr = addr; - int x = width; - while (--x >= 0) { - if (0 != ((*ptr) & byteMask)) { - warning = true; - *ptr = 0; - } - ++ptr; - } - addr += rowBytes; - } - if (warning) { - gif_warning(*bm, "Index out of bounds."); - } - } - } - } -} - -namespace { -// This function is a template argument, so can't be static. -int close_gif(GifFileType* gif) { -#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0) - return DGifCloseFile(gif); -#else - return DGifCloseFile(gif, nullptr); -#endif -} -}//namespace - -SkImageDecoder::Result SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) { -#if GIFLIB_MAJOR < 5 - GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc); -#else - GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, nullptr); -#endif - if (nullptr == gif) { - return error_return(*bm, "DGifOpen"); - } - - SkAutoTCallIProc<GifFileType, close_gif> acp(gif); - - SavedImage temp_save; - temp_save.ExtensionBlocks=nullptr; - temp_save.ExtensionBlockCount=0; - SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save); - - int width, height; - GifRecordType recType; - GifByteType *extData; -#if GIFLIB_MAJOR >= 5 - int extFunction; -#endif - int transpIndex = -1; // -1 means we don't have it (yet) - int fillIndex = gif->SBackGroundColor; - - do { - if (DGifGetRecordType(gif, &recType) == GIF_ERROR) { - return error_return(*bm, "DGifGetRecordType"); - } - - switch (recType) { - case IMAGE_DESC_RECORD_TYPE: { - if (DGifGetImageDesc(gif) == GIF_ERROR) { - return error_return(*bm, "IMAGE_DESC_RECORD_TYPE"); - } - - if (gif->ImageCount < 1) { // sanity check - return error_return(*bm, "ImageCount < 1"); - } - - width = gif->SWidth; - height = gif->SHeight; - - SavedImage* image = &gif->SavedImages[gif->ImageCount-1]; - const GifImageDesc& desc = image->ImageDesc; - - int imageLeft = desc.Left; - int imageTop = desc.Top; - const int innerWidth = desc.Width; - const int innerHeight = desc.Height; - if (innerWidth <= 0 || innerHeight <= 0) { - return error_return(*bm, "invalid dimensions"); - } - - // check for valid descriptor - if (innerWidth > width) { - gif_warning(*bm, "image too wide, expanding output to size"); - width = innerWidth; - imageLeft = 0; - } else if (imageLeft + innerWidth > width) { - gif_warning(*bm, "shifting image left to fit"); - imageLeft = width - innerWidth; - } else if (imageLeft < 0) { - gif_warning(*bm, "shifting image right to fit"); - imageLeft = 0; - } - - - if (innerHeight > height) { - gif_warning(*bm, "image too tall, expanding output to size"); - height = innerHeight; - imageTop = 0; - } else if (imageTop + innerHeight > height) { - gif_warning(*bm, "shifting image up to fit"); - imageTop = height - innerHeight; - } else if (imageTop < 0) { - gif_warning(*bm, "shifting image down to fit"); - imageTop = 0; - } - - SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); - - bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), - kIndex_8_SkColorType, kPremul_SkAlphaType)); - - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - - - // now we decode the colortable - int colorCount = 0; - { - // Declare colorPtr here for scope. - SkPMColor colorPtr[256]; // storage for worst-case - const ColorMapObject* cmap = find_colormap(gif); - if (cmap != nullptr) { - SkASSERT(cmap->ColorCount == (1 << (cmap->BitsPerPixel))); - colorCount = cmap->ColorCount; - if (colorCount > 256) { - colorCount = 256; // our kIndex8 can't support more - } - for (int index = 0; index < colorCount; index++) { - colorPtr[index] = SkPackARGB32(0xFF, - cmap->Colors[index].Red, - cmap->Colors[index].Green, - cmap->Colors[index].Blue); - } - } else { - // find_colormap() returned nullptr. Some (rare, broken) - // GIFs don't have a color table, so we force one. - gif_warning(*bm, "missing colormap"); - colorCount = 256; - sk_memset32(colorPtr, SK_ColorWHITE, colorCount); - } - transpIndex = find_transpIndex(temp_save, colorCount); - if (transpIndex >= 0) { - colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor - fillIndex = transpIndex; - } else if (fillIndex >= colorCount) { - // gif->SBackGroundColor should be less than colorCount. - fillIndex = 0; // If not, fix it. - } - - SkAutoTUnref<SkColorTable> ctable(new SkColorTable(colorPtr, colorCount)); - if (!this->allocPixelRef(bm, ctable)) { - return error_return(*bm, "allocPixelRef"); - } - } - - // abort if either inner dimension is <= 0 - if (innerWidth <= 0 || innerHeight <= 0) { - return error_return(*bm, "non-pos inner width/height"); - } - - SkAutoLockPixels alp(*bm); - - SkAutoTMalloc<uint8_t> storage(innerWidth); - uint8_t* scanline = storage.get(); - - // GIF has an option to store the scanlines of an image, plus a larger background, - // filled by a fill color. In this case, we will use a subset of the larger bitmap - // for sampling. - SkBitmap subset; - SkBitmap* workingBitmap; - // are we only a subset of the total bounds? - if ((imageTop | imageLeft) > 0 || - innerWidth < width || innerHeight < height) { - // Fill the background. - memset(bm->getPixels(), fillIndex, bm->getSize()); - - // Create a subset of the bitmap. - SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX(), - imageTop / sampler.srcDY(), - innerWidth / sampler.srcDX(), - innerHeight / sampler.srcDY())); - if (!bm->extractSubset(&subset, subsetRect)) { - return error_return(*bm, "Extract failed."); - } - // Update the sampler. We'll now be only sampling into the subset. - sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize()); - workingBitmap = ⊂ - } else { - workingBitmap = bm; - } - - // bm is already locked, but if we had to take a subset, it must be locked also, - // so that getPixels() will point to its pixels. - SkAutoLockPixels alpWorking(*workingBitmap); - - if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) { - return error_return(*bm, "Sampler failed to begin."); - } - - // now decode each scanline - if (gif->Image.Interlace) { - // Iterate over the height of the source data. The sampler will - // take care of skipping unneeded rows. - GifInterlaceIter iter(innerHeight); - for (int y = 0; y < innerHeight; y++) { - if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { - gif_warning(*bm, "interlace DGifGetLine"); - memset(scanline, fillIndex, innerWidth); - for (; y < innerHeight; y++) { - sampler.sampleInterlaced(scanline, iter.currY()); - iter.next(); - } - return kPartialSuccess; - } - sampler.sampleInterlaced(scanline, iter.currY()); - iter.next(); - } - } else { - // easy, non-interlace case - const int outHeight = workingBitmap->height(); - skip_src_rows(gif, scanline, innerWidth, sampler.srcY0()); - for (int y = 0; y < outHeight; y++) { - if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) { - gif_warning(*bm, "DGifGetLine"); - memset(scanline, fillIndex, innerWidth); - for (; y < outHeight; y++) { - sampler.next(scanline); - } - return kPartialSuccess; - } - // scanline now contains the raw data. Sample it. - sampler.next(scanline); - if (y < outHeight - 1) { - skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1); - } - } - // skip the rest of the rows (if any) - int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1; - SkASSERT(read <= innerHeight); - skip_src_rows(gif, scanline, innerWidth, innerHeight - read); - } - sanitize_indexed_bitmap(bm); - return kSuccess; - } break; - - case EXTENSION_RECORD_TYPE: -#if GIFLIB_MAJOR < 5 - if (DGifGetExtension(gif, &temp_save.Function, - &extData) == GIF_ERROR) { -#else - if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) { -#endif - return error_return(*bm, "DGifGetExtension"); - } - - while (extData != nullptr) { - /* Create an extension block with our data */ -#if GIFLIB_MAJOR < 5 - if (AddExtensionBlock(&temp_save, extData[0], - &extData[1]) == GIF_ERROR) { -#else - if (GifAddExtensionBlock(&temp_save.ExtensionBlockCount, - &temp_save.ExtensionBlocks, - extFunction, - extData[0], - &extData[1]) == GIF_ERROR) { -#endif - return error_return(*bm, "AddExtensionBlock"); - } - if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) { - return error_return(*bm, "DGifGetExtensionNext"); - } -#if GIFLIB_MAJOR < 5 - temp_save.Function = 0; -#endif - } - break; - - case TERMINATE_RECORD_TYPE: - break; - - default: /* Should be trapped by DGifGetRecordType */ - break; - } - } while (recType != TERMINATE_RECORD_TYPE); - - sanitize_indexed_bitmap(bm); - return kSuccess; -} - -/////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(GIFImageDecoder); -/////////////////////////////////////////////////////////////////////////////// - -static bool is_gif(SkStreamRewindable* stream) { - char buf[GIF_STAMP_LEN]; - if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) { - if (memcmp(GIF_STAMP, buf, GIF_STAMP_LEN) == 0 || - memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 || - memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) { - return true; - } - } - return false; -} - -static SkImageDecoder* sk_libgif_dfactory(SkStreamRewindable* stream) { - if (is_gif(stream)) { - return new SkGIFImageDecoder; - } - return nullptr; -} - -static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory); - -static SkImageDecoder::Format get_format_gif(SkStreamRewindable* stream) { - if (is_gif(stream)) { - return SkImageDecoder::kGIF_Format; - } - return SkImageDecoder::kUnknown_Format; -} - -static SkImageDecoder_FormatReg gFormatReg(get_format_gif); diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp deleted file mode 100644 index ff04d74d06..0000000000 --- a/src/images/SkImageDecoder_libico.cpp +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkColorPriv.h" -#include "SkData.h" -#include "SkImageDecoder.h" -#include "SkStream.h" -#include "SkStreamPriv.h" -#include "SkTypes.h" - -class SkICOImageDecoder : public SkImageDecoder { -public: - SkICOImageDecoder(); - - Format getFormat() const override { - return kICO_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; - -private: - typedef SkImageDecoder INHERITED; -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -//read bytes starting from the begin-th index in the buffer -//read in Intel order, and return an integer - -#define readByte(buffer,begin) buffer[begin] -#define read2Bytes(buffer,begin) buffer[begin]+SkLeftShift(buffer[begin+1],8) -#define read4Bytes(buffer,begin) buffer[begin]+SkLeftShift(buffer[begin+1],8)+SkLeftShift(buffer[begin+2],16)+SkLeftShift(buffer[begin+3],24) - -///////////////////////////////////////////////////////////////////////////////////////// - -SkICOImageDecoder::SkICOImageDecoder() -{ -} - -//helpers - my function pointer will call one of these, depending on the bitCount, each time through the inner loop -static void editPixelBit1(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); -static void editPixelBit4(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); -static void editPixelBit8(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); -static void editPixelBit24(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); -static void editPixelBit32(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors); - - -static int calculateRowBytesFor8888(int w, int bitCount) -{ - // Default rowBytes is w << 2 for kARGB_8888 - // In the case of a 4 bit image with an odd width, we need to add some - // so we can go off the end of the drawn bitmap. - // Add 4 to ensure that it is still a multiple of 4. - if (4 == bitCount && (w & 0x1)) { - return (w + 1) << 2; - } - // Otherwise return 0, which will allow it to be calculated automatically. - return 0; -} - -SkImageDecoder::Result SkICOImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { - auto data = SkCopyStreamToData(stream); - if (!data) { - return kFailure; - } - - const size_t length = data->size(); - // Check that the buffer is large enough to read the directory header - if (length < 6) { - return kFailure; - } - - unsigned char* buf = (unsigned char*) data->data(); - - //these should always be the same - should i use for error checking? - what about files that have some - //incorrect values, but still decode properly? - int reserved = read2Bytes(buf, 0); // 0 - int type = read2Bytes(buf, 2); // 1 - if (reserved != 0 || type != 1) { - return kFailure; - } - - int count = read2Bytes(buf, 4); - // Check that there are directory entries - if (count < 1) { - return kFailure; - } - - // Check that buffer is large enough to read directory entries. - // We are guaranteed that count is at least 1. We might as well assume - // count is 1 because this deprecated decoder only looks at the first - // directory entry. - if (length < (size_t)(6 + count*16)) { - return kFailure; - } - - //skip ahead to the correct header - //commented out lines are not used, but if i switch to other read method, need to know how many to skip - //otherwise, they could be used for error checking - int w = readByte(buf, 6); - int h = readByte(buf, 7); - SkASSERT(w >= 0 && h >= 0); - int colorCount = readByte(buf, 8); - //int reservedToo = readByte(buf, 9 + choice*16); //0 - //int planes = read2Bytes(buf, 10 + choice*16); //1 - but often 0 - //int fakeBitCount = read2Bytes(buf, 12 + choice*16); //should be real - usually 0 - const size_t size = read4Bytes(buf, 14); //matters? - const size_t offset = read4Bytes(buf, 18); - // promote the sum to 64-bits to avoid overflow - // Check that buffer is large enough to read image data - if (offset > length || size > length || ((uint64_t)offset + size) > length) { - return kFailure; - } - - // Check to see if this is a PNG image inside the ICO - { - SkMemoryStream subStream(buf + offset, size, false); - SkAutoTDelete<SkImageDecoder> otherDecoder(SkImageDecoder::Factory(&subStream)); - if (otherDecoder.get() != nullptr) { - // Disallow nesting ICO files within one another - // FIXME: Can ICO files contain other formats besides PNG? - if (otherDecoder->getFormat() == SkImageDecoder::kICO_Format) { - return kFailure; - } - // Set fields on the other decoder to be the same as this one. - this->copyFieldsToOther(otherDecoder.get()); - const Result result = otherDecoder->decode(&subStream, bm, this->getDefaultPref(), - mode); - // FIXME: Should we just return result here? Is it possible that data that looked like - // a subimage was not, but was actually a valid ICO? - if (result != kFailure) { - return result; - } - } - } - - //int infoSize = read4Bytes(buf, offset); //40 - //int width = read4Bytes(buf, offset+4); //should == w - //int height = read4Bytes(buf, offset+8); //should == 2*h - //int planesToo = read2Bytes(buf, offset+12); //should == 1 (does it?) - - // For ico images, only a byte is used to store each dimension - // 0 is used to represent 256 - if (w == 0) { - w = 256; - } - if (h == 0) { - h = 256; - } - - // Check that buffer is large enough to read the bit depth - if (length < offset + 16) { - return kFailure; - } - int bitCount = read2Bytes(buf, offset+14); - - void (*placePixel)(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) = nullptr; - switch (bitCount) - { - case 1: - placePixel = &editPixelBit1; - colorCount = 2; - break; - case 4: - placePixel = &editPixelBit4; - colorCount = 16; - break; - case 8: - placePixel = &editPixelBit8; - colorCount = 256; - break; - case 24: - placePixel = &editPixelBit24; - colorCount = 0; - break; - case 32: - placePixel = &editPixelBit32; - colorCount = 0; - break; - default: - SkDEBUGF(("Decoding %ibpp is unimplemented\n", bitCount)); - return kFailure; - } - - //these should all be zero, but perhaps are not - need to check - //int compression = read4Bytes(buf, offset+16); //0 - //int imageSize = read4Bytes(buf, offset+20); //0 - sometimes has a value - //int xPixels = read4Bytes(buf, offset+24); //0 - //int yPixels = read4Bytes(buf, offset+28); //0 - //int colorsUsed = read4Bytes(buf, offset+32) //0 - might have an actual value though - //int colorsImportant = read4Bytes(buf, offset+36); //0 - - int begin = SkToInt(offset + 40); - // Check that the buffer is large enough to read the color table - // For bmp-in-icos, there should be 4 bytes per color - if (length < (size_t) (begin + 4*colorCount)) { - return kFailure; - } - - //this array represents the colortable - //if i allow other types of bitmaps, it may actually be used as a part of the bitmap - SkPMColor* colors = nullptr; - int blue, green, red; - if (colorCount) - { - colors = new SkPMColor[colorCount]; - for (int j = 0; j < colorCount; j++) - { - //should this be a function - maybe a #define? - blue = readByte(buf, begin + 4*j); - green = readByte(buf, begin + 4*j + 1); - red = readByte(buf, begin + 4*j + 2); - colors[j] = SkPackARGB32(0xFF, red & 0xFF, green & 0xFF, blue & 0xFF); - } - } - int bitWidth = w*bitCount; - int test = bitWidth & 0x1F; - int mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1); //either 0xFFFFFFFF or 0 - int lineBitWidth = (bitWidth & 0xFFFFFFE0) + (0x20 & mask); - int lineWidth = lineBitWidth/bitCount; - - int xorOffset = begin + colorCount*4; //beginning of the color bitmap - //other read method means we will just be here already - int andOffset = xorOffset + ((lineWidth*h*bitCount) >> 3); - - /*int */test = w & 0x1F; //the low 5 bits - we are rounding up to the next 32 (2^5) - /*int */mask = -(((test >> 4) | (test >> 3) | (test >> 2) | (test >> 1) | test) & 0x1); //either 0xFFFFFFFF or 0 - int andLineWidth = (w & 0xFFFFFFE0) + (0x20 & mask); - //if we allow different Configs, everything is the same til here - //change the config, and use different address getter, and place index vs color, and add the color table - //FIXME: what is the tradeoff in size? - //if the andbitmap (mask) is all zeroes, then we can easily do an index bitmap - //however, with small images with large colortables, maybe it's better to still do argb_8888 - - bm->setInfo(SkImageInfo::MakeN32Premul(w, h), calculateRowBytesFor8888(w, bitCount)); - - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - delete[] colors; - return kSuccess; - } - - if (!this->allocPixelRef(bm, nullptr)) - { - delete[] colors; - return kFailure; - } - - // The AND mask is a 1-bit alpha mask for each pixel that comes after the - // XOR mask in the bmp. If we check that the largest AND offset is safe, - // it should mean all other buffer accesses will be at smaller indices and - // will therefore be safe. - size_t maxAndOffset = andOffset + ((andLineWidth*(h-1)+(w-1)) >> 3); - if (length <= maxAndOffset) { - return kFailure; - } - - // Here we assert that all reads from the buffer using the XOR offset are - // less than the AND offset. This should be guaranteed based on the above - // calculations. -#ifdef SK_DEBUG - int maxPixelNum = lineWidth*(h-1)+w-1; - int maxByte; - switch (bitCount) { - case 1: - maxByte = maxPixelNum >> 3; - break; - case 4: - maxByte = maxPixelNum >> 1; - break; - case 8: - maxByte = maxPixelNum; - break; - case 24: - maxByte = maxPixelNum * 3 + 2; - break; - case 32: - maxByte = maxPixelNum * 4 + 3; - break; - default: - SkASSERT(false); - return kFailure; - } - int maxXOROffset = xorOffset + maxByte; - SkASSERT(maxXOROffset < andOffset); -#endif - - SkAutoLockPixels alp(*bm); - - for (int y = 0; y < h; y++) - { - for (int x = 0; x < w; x++) - { - //U32* address = bm->getAddr32(x, y); - - //check the alpha bit first, but pass it along to the function to figure out how to deal with it - int andPixelNo = andLineWidth*(h-y-1)+x; - //only need to get a new alphaByte when x %8 == 0 - //but that introduces an if and a mod - probably much slower - //that's ok, it's just a read of an array, not a stream - int alphaByte = readByte(buf, andOffset + (andPixelNo >> 3)); - int shift = 7 - (andPixelNo & 0x7); - int m = 1 << shift; - - int pixelNo = lineWidth*(h-y-1)+x; - placePixel(pixelNo, buf, xorOffset, x, y, w, bm, alphaByte, m, shift, colors); - - } - } - - delete [] colors; - //ensure we haven't read off the end? - //of course this doesn't help us if the andOffset was a lie... - //return andOffset + (andLineWidth >> 3) <= length; - return kSuccess; -} //onDecode - -//function to place the pixel, determined by the bitCount -static void editPixelBit1(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) -{ - // note that this should be the same as/similar to the AND bitmap - SkPMColor* address = bm->getAddr32(x,y); - int byte = readByte(buf, xorOffset + (pixelNo >> 3)); - int colorBit; - int alphaBit; - // Read all of the bits in this byte. - int i = x + 8; - // Pin to the width so we do not write outside the bounds of - // our color table. - i = i > w ? w : i; - // While loop to check all 8 bits individually. - while (x < i) - { - - colorBit = (byte & m) >> shift; - alphaBit = (alphaByte & m) >> shift; - *address = (alphaBit-1)&(colors[colorBit]); - x++; - // setup for the next pixel - address = address + 1; - m = m >> 1; - shift -= 1; - } - x--; -} -static void editPixelBit4(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) -{ - SkPMColor* address = bm->getAddr32(x, y); - int byte = readByte(buf, xorOffset + (pixelNo >> 1)); - int pixel = (byte >> 4) & 0xF; - int alphaBit = (alphaByte & m) >> shift; - *address = (alphaBit-1)&(colors[pixel]); - x++; - //if w is odd, x may be the same as w, which means we are writing to an unused portion of the bitmap - //but that's okay, since i've added an extra rowByte for just this purpose - address = address + 1; - pixel = byte & 0xF; - m = m >> 1; - alphaBit = (alphaByte & m) >> (shift-1); - //speed up trick here - *address = (alphaBit-1)&(colors[pixel]); -} - -static void editPixelBit8(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) -{ - SkPMColor* address = bm->getAddr32(x, y); - int pixel = readByte(buf, xorOffset + pixelNo); - int alphaBit = (alphaByte & m) >> shift; - *address = (alphaBit-1)&(colors[pixel]); -} - -static void editPixelBit24(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) -{ - SkPMColor* address = bm->getAddr32(x, y); - int blue = readByte(buf, xorOffset + 3*pixelNo); - int green = readByte(buf, xorOffset + 3*pixelNo + 1); - int red = readByte(buf, xorOffset + 3*pixelNo + 2); - int alphaBit = (alphaByte & m) >> shift; - //alphaBit == 1 => alpha = 0 - int alpha = (alphaBit-1) & 0xFF; - *address = SkPreMultiplyARGB(alpha, red, green, blue); -} - -static void editPixelBit32(const int pixelNo, const unsigned char* buf, - const int xorOffset, int& x, int y, const int w, - SkBitmap* bm, int alphaByte, int m, int shift, SkPMColor* colors) -{ - SkPMColor* address = bm->getAddr32(x, y); - int blue = readByte(buf, xorOffset + 4*pixelNo); - int green = readByte(buf, xorOffset + 4*pixelNo + 1); - int red = readByte(buf, xorOffset + 4*pixelNo + 2); - int alphaBit = (alphaByte & m) >> shift; -#if 1 // don't trust the alphaBit for 32bit images <mrr> - alphaBit = 0; -#endif - int alpha = readByte(buf, xorOffset + 4*pixelNo + 3) & ((alphaBit-1)&0xFF); - *address = SkPreMultiplyARGB(alpha, red, green, blue); -} - -/////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(ICOImageDecoder); -///////////////////////////////////////////////////////////////////////////////////////// - -static bool is_ico(SkStreamRewindable* stream) { - // Check to see if the first four bytes are 0,0,1,0 - // FIXME: Is that required and sufficient? - char buf[4]; - if (stream->read((void*)buf, 4) != 4) { - return false; - } - int reserved = read2Bytes(buf, 0); - int type = read2Bytes(buf, 2); - return 0 == reserved && 1 == type; -} - -static SkImageDecoder* sk_libico_dfactory(SkStreamRewindable* stream) { - if (is_ico(stream)) { - return new SkICOImageDecoder; - } - return nullptr; -} - -static SkImageDecoder_DecodeReg gReg(sk_libico_dfactory); - -static SkImageDecoder::Format get_format_ico(SkStreamRewindable* stream) { - if (is_ico(stream)) { - return SkImageDecoder::kICO_Format; - } - return SkImageDecoder::kUnknown_Format; -} - -static SkImageDecoder_FormatReg gFormatReg(get_format_ico); diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp index 89bfefcd45..fd10bdbdf6 100644 --- a/src/images/SkImageDecoder_libjpeg.cpp +++ b/src/images/SkImageDecoder_libjpeg.cpp @@ -6,13 +6,10 @@ */ -#include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkJpegUtility.h" #include "SkColorPriv.h" #include "SkDither.h" -#include "SkMSAN.h" -#include "SkScaledBitmapSampler.h" #include "SkStream.h" #include "SkTemplates.h" #include "SkTime.h" @@ -28,730 +25,12 @@ extern "C" { #include "jerror.h" } -// These enable timing code that report milliseconds for an encoding/decoding +// These enable timing code that report milliseconds for an encoding //#define TIME_ENCODE -//#define TIME_DECODE // this enables our rgb->yuv code, which is faster than libjpeg on ARM #define WE_CONVERT_TO_YUV -// If ANDROID_RGB is defined by in the jpeg headers it indicates that jpeg offers -// support for two additional formats (1) JCS_RGBA_8888 and (2) JCS_RGB_565. - -#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS true -#define DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS true -SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderWarnings, - "images.jpeg.suppressDecoderWarnings", - DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_WARNINGS, - "Suppress most JPG warnings when calling decode functions."); -SK_CONF_DECLARE(bool, c_suppressJPEGImageDecoderErrors, - "images.jpeg.suppressDecoderErrors", - DEFAULT_FOR_SUPPRESS_JPEG_IMAGE_DECODER_ERRORS, - "Suppress most JPG error messages when decode " - "function fails."); - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -static void do_nothing_emit_message(jpeg_common_struct*, int) { - /* do nothing */ -} -static void do_nothing_output_message(j_common_ptr) { - /* do nothing */ -} - -static void initialize_info(jpeg_decompress_struct* cinfo, skjpeg_source_mgr* src_mgr) { - SkASSERT(cinfo != nullptr); - SkASSERT(src_mgr != nullptr); - jpeg_create_decompress(cinfo); - cinfo->src = src_mgr; - /* To suppress warnings with a SK_DEBUG binary, set the - * environment variable "skia_images_jpeg_suppressDecoderWarnings" - * to "true". Inside a program that links to skia: - * SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true); */ - if (c_suppressJPEGImageDecoderWarnings) { - cinfo->err->emit_message = &do_nothing_emit_message; - } - /* To suppress error messages with a SK_DEBUG binary, set the - * environment variable "skia_images_jpeg_suppressDecoderErrors" - * to "true". Inside a program that links to skia: - * SK_CONF_SET("images.jpeg.suppressDecoderErrors", true); */ - if (c_suppressJPEGImageDecoderErrors) { - cinfo->err->output_message = &do_nothing_output_message; - } -} - -class SkJPEGImageDecoder : public SkImageDecoder { -public: - - Format getFormat() const override { - return kJPEG_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; - bool onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], - void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) override; - -private: - - /** - * Determine the appropriate bitmap colortype and out_color_space based on - * both the preference of the caller and the jpeg_color_space on the - * jpeg_decompress_struct passed in. - * Must be called after jpeg_read_header. - */ - SkColorType getBitmapColorType(jpeg_decompress_struct*); - - typedef SkImageDecoder INHERITED; -}; - -////////////////////////////////////////////////////////////////////////// - -/* Automatically clean up after throwing an exception */ -class JPEGAutoClean { -public: - JPEGAutoClean(): cinfo_ptr(nullptr) {} - ~JPEGAutoClean() { - if (cinfo_ptr) { - jpeg_destroy_decompress(cinfo_ptr); - } - } - void set(jpeg_decompress_struct* info) { - cinfo_ptr = info; - } -private: - jpeg_decompress_struct* cinfo_ptr; -}; - -/////////////////////////////////////////////////////////////////////////////// - -/* If we need to better match the request, we might examine the image and - output dimensions, and determine if the downsampling jpeg provided is - not sufficient. If so, we can recompute a modified sampleSize value to - make up the difference. - - To skip this additional scaling, just set sampleSize = 1; below. - */ -static int recompute_sampleSize(int sampleSize, - const jpeg_decompress_struct& cinfo) { - return sampleSize * cinfo.output_width / cinfo.image_width; -} - -static bool valid_output_dimensions(const jpeg_decompress_struct& cinfo) { - /* These are initialized to 0, so if they have non-zero values, we assume - they are "valid" (i.e. have been computed by libjpeg) - */ - return 0 != cinfo.output_width && 0 != cinfo.output_height; -} - -static bool skip_src_rows(jpeg_decompress_struct* cinfo, void* buffer, int count) { - for (int i = 0; i < count; i++) { - JSAMPLE* rowptr = (JSAMPLE*)buffer; - int row_count = jpeg_read_scanlines(cinfo, &rowptr, 1); - if (1 != row_count) { - return false; - } - } - return true; -} - -/////////////////////////////////////////////////////////////////////////////// - -// This guy exists just to aid in debugging, as it allows debuggers to just -// set a break-point in one place to see all error exists. -static void print_jpeg_decoder_errors(const jpeg_decompress_struct& cinfo, - int width, int height, const char caller[]) { - if (!(c_suppressJPEGImageDecoderErrors)) { - char buffer[JMSG_LENGTH_MAX]; - cinfo.err->format_message((const j_common_ptr)&cinfo, buffer); - SkDebugf("libjpeg error %d <%s> from %s [%d %d]\n", - cinfo.err->msg_code, buffer, caller, width, height); - } -} - -static bool return_false(const jpeg_decompress_struct& cinfo, - const char caller[]) { - print_jpeg_decoder_errors(cinfo, 0, 0, caller); - return false; -} - -static SkImageDecoder::Result return_failure(const jpeg_decompress_struct& cinfo, - const SkBitmap& bm, const char caller[]) { - print_jpeg_decoder_errors(cinfo, bm.width(), bm.height(), caller); - return SkImageDecoder::kFailure; -} - -/////////////////////////////////////////////////////////////////////////////// - -// Convert a scanline of CMYK samples to RGBX in place. Note that this -// method moves the "scanline" pointer in its processing -static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) { - // At this point we've received CMYK pixels from libjpeg. We - // perform a crude conversion to RGB (based on the formulae - // from easyrgb.com): - // CMYK -> CMY - // C = ( C * (1 - K) + K ) // for each CMY component - // CMY -> RGB - // R = ( 1 - C ) * 255 // for each RGB component - // Unfortunately we are seeing inverted CMYK so all the original terms - // are 1-. This yields: - // CMYK -> CMY - // C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K - // The conversion from CMY->RGB remains the same - for (unsigned int x = 0; x < width; ++x, scanline += 4) { - scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]); - scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]); - scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]); - scanline[3] = 255; - } -} - -/** - * Common code for setting the error manager. - */ -static void set_error_mgr(jpeg_decompress_struct* cinfo, skjpeg_error_mgr* errorManager) { - SkASSERT(cinfo != nullptr); - SkASSERT(errorManager != nullptr); - cinfo->err = jpeg_std_error(errorManager); - errorManager->error_exit = skjpeg_error_exit; -} - -/** - * Common code for setting the dct method. - */ -static void set_dct_method(const SkImageDecoder& decoder, jpeg_decompress_struct* cinfo) { - SkASSERT(cinfo != nullptr); - cinfo->dct_method = JDCT_ISLOW; -} - -SkColorType SkJPEGImageDecoder::getBitmapColorType(jpeg_decompress_struct* cinfo) { - SkASSERT(cinfo != nullptr); - - SrcDepth srcDepth = k32Bit_SrcDepth; - if (JCS_GRAYSCALE == cinfo->jpeg_color_space) { - srcDepth = k8BitGray_SrcDepth; - } - - SkColorType colorType = this->getPrefColorType(srcDepth, /*hasAlpha*/ false); - switch (colorType) { - case kAlpha_8_SkColorType: - // Only respect A8 colortype if the original is grayscale, - // in which case we will treat the grayscale as alpha - // values. - if (cinfo->jpeg_color_space != JCS_GRAYSCALE) { - colorType = kN32_SkColorType; - } - break; - case kN32_SkColorType: - // Fall through. - case kARGB_4444_SkColorType: - // Fall through. - case kRGB_565_SkColorType: - // These are acceptable destination colortypes. - break; - default: - // Force all other colortypes to 8888. - colorType = kN32_SkColorType; - break; - } - - switch (cinfo->jpeg_color_space) { - case JCS_CMYK: - // Fall through. - case JCS_YCCK: - // libjpeg cannot convert from CMYK or YCCK to RGB - here we set up - // so libjpeg will give us CMYK samples back and we will later - // manually convert them to RGB - cinfo->out_color_space = JCS_CMYK; - break; - case JCS_GRAYSCALE: - if (kAlpha_8_SkColorType == colorType) { - cinfo->out_color_space = JCS_GRAYSCALE; - break; - } - // The data is JCS_GRAYSCALE, but the caller wants some sort of RGB - // colortype. Fall through to set to the default. - default: - cinfo->out_color_space = JCS_RGB; - break; - } - return colorType; -} - -/** - * Based on the colortype and dither mode, adjust out_color_space and - * dither_mode of cinfo. Only does work in ANDROID_RGB - */ -static void adjust_out_color_space_and_dither(jpeg_decompress_struct* cinfo, - SkColorType colorType, - const SkImageDecoder& decoder) { - SkASSERT(cinfo != nullptr); -#ifdef ANDROID_RGB - cinfo->dither_mode = JDITHER_NONE; - if (JCS_CMYK == cinfo->out_color_space) { - return; - } - switch (colorType) { - case kN32_SkColorType: - cinfo->out_color_space = JCS_RGBA_8888; - break; - case kRGB_565_SkColorType: - cinfo->out_color_space = JCS_RGB_565; - if (decoder.getDitherImage()) { - cinfo->dither_mode = JDITHER_ORDERED; - } - break; - default: - break; - } -#endif -} - -/** - Sets all pixels in given bitmap to SK_ColorWHITE for all rows >= y. - Used when decoding fails partway through reading scanlines to fill - remaining lines. */ -static void fill_below_level(int y, SkBitmap* bitmap) { - SkIRect rect = SkIRect::MakeLTRB(0, y, bitmap->width(), bitmap->height()); - SkCanvas canvas(*bitmap); - canvas.clipRect(SkRect::Make(rect)); - canvas.drawColor(SK_ColorWHITE); -} - -/** - * Get the config and bytes per pixel of the source data. Return - * whether the data is supported. - */ -static bool get_src_config(const jpeg_decompress_struct& cinfo, - SkScaledBitmapSampler::SrcConfig* sc, - int* srcBytesPerPixel) { - SkASSERT(sc != nullptr && srcBytesPerPixel != nullptr); - if (JCS_CMYK == cinfo.out_color_space) { - // In this case we will manually convert the CMYK values to RGB - *sc = SkScaledBitmapSampler::kRGBX; - // The CMYK work-around relies on 4 components per pixel here - *srcBytesPerPixel = 4; - } else if (3 == cinfo.out_color_components && JCS_RGB == cinfo.out_color_space) { - *sc = SkScaledBitmapSampler::kRGB; - *srcBytesPerPixel = 3; -#ifdef ANDROID_RGB - } else if (JCS_RGBA_8888 == cinfo.out_color_space) { - *sc = SkScaledBitmapSampler::kRGBX; - *srcBytesPerPixel = 4; - } else if (JCS_RGB_565 == cinfo.out_color_space) { - *sc = SkScaledBitmapSampler::kRGB_565; - *srcBytesPerPixel = 2; -#endif - } else if (1 == cinfo.out_color_components && - JCS_GRAYSCALE == cinfo.out_color_space) { - *sc = SkScaledBitmapSampler::kGray; - *srcBytesPerPixel = 1; - } else { - return false; - } - return true; -} - -SkImageDecoder::Result SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { -#ifdef TIME_DECODE - SkAutoTime atm("JPEG Decode"); -#endif - - JPEGAutoClean autoClean; - - jpeg_decompress_struct cinfo; - skjpeg_source_mgr srcManager(stream, this); - - skjpeg_error_mgr errorManager; - set_error_mgr(&cinfo, &errorManager); - - // All objects need to be instantiated before this setjmp call so that - // they will be cleaned up properly if an error occurs. - if (setjmp(errorManager.fJmpBuf)) { - return return_failure(cinfo, *bm, "setjmp"); - } - - initialize_info(&cinfo, &srcManager); - autoClean.set(&cinfo); - - int status = jpeg_read_header(&cinfo, true); - if (status != JPEG_HEADER_OK) { - return return_failure(cinfo, *bm, "read_header"); - } - - /* Try to fulfill the requested sampleSize. Since jpeg can do it (when it - can) much faster that we, just use their num/denom api to approximate - the size. - */ - int sampleSize = this->getSampleSize(); - - set_dct_method(*this, &cinfo); - - SkASSERT(1 == cinfo.scale_num); - cinfo.scale_denom = sampleSize; - - const SkColorType colorType = this->getBitmapColorType(&cinfo); - const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ? - kPremul_SkAlphaType : kOpaque_SkAlphaType; - - adjust_out_color_space_and_dither(&cinfo, colorType, *this); - - if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) { - // Assume an A8 bitmap is not opaque to avoid the check of each - // individual pixel. It is very unlikely to be opaque, since - // an opaque A8 bitmap would not be very interesting. - // Otherwise, a jpeg image is opaque. - bool success = bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height, - colorType, alphaType)); - return success ? kSuccess : kFailure; - } - - /* image_width and image_height are the original dimensions, available - after jpeg_read_header(). To see the scaled dimensions, we have to call - jpeg_start_decompress(), and then read output_width and output_height. - */ - if (!jpeg_start_decompress(&cinfo)) { - /* If we failed here, we may still have enough information to return - to the caller if they just wanted (subsampled bounds). If sampleSize - was 1, then we would have already returned. Thus we just check if - we're in kDecodeBounds_Mode, and that we have valid output sizes. - - One reason to fail here is that we have insufficient stream data - to complete the setup. However, output dimensions seem to get - computed very early, which is why this special check can pay off. - */ - if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) { - SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height, - recompute_sampleSize(sampleSize, cinfo)); - // Assume an A8 bitmap is not opaque to avoid the check of each - // individual pixel. It is very unlikely to be opaque, since - // an opaque A8 bitmap would not be very interesting. - // Otherwise, a jpeg image is opaque. - bool success = bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(), - colorType, alphaType)); - return success ? kSuccess : kFailure; - } else { - return return_failure(cinfo, *bm, "start_decompress"); - } - } - sampleSize = recompute_sampleSize(sampleSize, cinfo); - - SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize); - // Assume an A8 bitmap is not opaque to avoid the check of each - // individual pixel. It is very unlikely to be opaque, since - // an opaque A8 bitmap would not be very interesting. - // Otherwise, a jpeg image is opaque. - bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), - colorType, alphaType)); - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - if (!this->allocPixelRef(bm, nullptr)) { - return return_failure(cinfo, *bm, "allocPixelRef"); - } - - SkAutoLockPixels alp(*bm); - -#ifdef ANDROID_RGB - /* short-circuit the SkScaledBitmapSampler when possible, as this gives - a significant performance boost. - */ - if (sampleSize == 1 && - ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) || - (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565))) - { - JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels(); - INT32 const bpr = bm->rowBytes(); - - while (cinfo.output_scanline < cinfo.output_height) { - int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); - if (0 == row_count) { - // if row_count == 0, then we didn't get a scanline, - // so return early. We will return a partial image. - fill_below_level(cinfo.output_scanline, bm); - cinfo.output_scanline = cinfo.output_height; - jpeg_finish_decompress(&cinfo); - return kPartialSuccess; - } - if (this->shouldCancelDecode()) { - return return_failure(cinfo, *bm, "shouldCancelDecode"); - } - rowptr += bpr; - } - jpeg_finish_decompress(&cinfo); - return kSuccess; - } -#endif - - // check for supported formats - SkScaledBitmapSampler::SrcConfig sc; - int srcBytesPerPixel; - - if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) { - return return_failure(cinfo, *bm, "jpeg colorspace"); - } - - if (!sampler.begin(bm, sc, *this)) { - return return_failure(cinfo, *bm, "sampler.begin"); - } - - SkAutoTMalloc<uint8_t> srcStorage(cinfo.output_width * srcBytesPerPixel); - uint8_t* srcRow = srcStorage.get(); - - // Possibly skip initial rows [sampler.srcY0] - if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) { - return return_failure(cinfo, *bm, "skip rows"); - } - - // now loop through scanlines until y == bm->height() - 1 - for (int y = 0;; y++) { - JSAMPLE* rowptr = (JSAMPLE*)srcRow; - int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1); - sk_msan_mark_initialized(srcRow, srcRow + cinfo.output_width * srcBytesPerPixel, - "skbug.com/4550"); - if (0 == row_count) { - // if row_count == 0, then we didn't get a scanline, - // so return early. We will return a partial image. - fill_below_level(y, bm); - cinfo.output_scanline = cinfo.output_height; - jpeg_finish_decompress(&cinfo); - return kPartialSuccess; - } - if (this->shouldCancelDecode()) { - return return_failure(cinfo, *bm, "shouldCancelDecode"); - } - - if (JCS_CMYK == cinfo.out_color_space) { - convert_CMYK_to_RGB(srcRow, cinfo.output_width); - } - - - sampler.next(srcRow); - if (bm->height() - 1 == y) { - // we're done - break; - } - - if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) { - return return_failure(cinfo, *bm, "skip rows"); - } - } - - // we formally skip the rest, so we don't get a complaint from libjpeg - if (!skip_src_rows(&cinfo, srcRow, - cinfo.output_height - cinfo.output_scanline)) { - return return_failure(cinfo, *bm, "skip rows"); - } - jpeg_finish_decompress(&cinfo); - - return kSuccess; -} - -/////////////////////////////////////////////////////////////////////////////// - -enum SizeType { - kSizeForMemoryAllocation_SizeType, - kActualSize_SizeType -}; - -static SkISize compute_yuv_size(const jpeg_decompress_struct& info, int component, - SizeType sizeType) { - if (sizeType == kSizeForMemoryAllocation_SizeType) { - return SkISize::Make(info.cur_comp_info[component]->width_in_blocks * DCTSIZE, - info.cur_comp_info[component]->height_in_blocks * DCTSIZE); - } - return SkISize::Make(info.cur_comp_info[component]->downsampled_width, - info.cur_comp_info[component]->downsampled_height); -} - -static bool appears_to_be_yuv(const jpeg_decompress_struct& info) { - return (info.jpeg_color_space == JCS_YCbCr) - && (DCTSIZE == 8) - && (info.num_components == 3) - && (info.comps_in_scan >= info.num_components) - && (info.scale_denom <= 8) - && (info.cur_comp_info[0]) - && (info.cur_comp_info[1]) - && (info.cur_comp_info[2]) - && (info.cur_comp_info[1]->h_samp_factor == 1) - && (info.cur_comp_info[1]->v_samp_factor == 1) - && (info.cur_comp_info[2]->h_samp_factor == 1) - && (info.cur_comp_info[2]->v_samp_factor == 1); -} - -static void update_components_sizes(const jpeg_decompress_struct& cinfo, SkISize componentSizes[3], - SizeType sizeType) { - SkASSERT(appears_to_be_yuv(cinfo)); - for (int i = 0; i < 3; ++i) { - componentSizes[i] = compute_yuv_size(cinfo, i, sizeType); - } -} - -static bool output_raw_data(jpeg_decompress_struct& cinfo, void* planes[3], size_t rowBytes[3]) { - SkASSERT(appears_to_be_yuv(cinfo)); - // U size and V size have to be the same if we're calling output_raw_data() - SkISize uvSize = compute_yuv_size(cinfo, 1, kSizeForMemoryAllocation_SizeType); - SkASSERT(uvSize == compute_yuv_size(cinfo, 2, kSizeForMemoryAllocation_SizeType)); - - JSAMPARRAY bufferraw[3]; - JSAMPROW bufferraw2[32]; - bufferraw[0] = &bufferraw2[0]; // Y channel rows (8 or 16) - bufferraw[1] = &bufferraw2[16]; // U channel rows (8) - bufferraw[2] = &bufferraw2[24]; // V channel rows (8) - int yWidth = cinfo.output_width; - int yHeight = cinfo.output_height; - int yMaxH = yHeight - 1; - int v = cinfo.cur_comp_info[0]->v_samp_factor; - int uvMaxH = uvSize.height() - 1; - JSAMPROW outputY = static_cast<JSAMPROW>(planes[0]); - JSAMPROW outputU = static_cast<JSAMPROW>(planes[1]); - JSAMPROW outputV = static_cast<JSAMPROW>(planes[2]); - size_t rowBytesY = rowBytes[0]; - size_t rowBytesU = rowBytes[1]; - size_t rowBytesV = rowBytes[2]; - - int yScanlinesToRead = DCTSIZE * v; - SkAutoMalloc lastRowStorage(rowBytesY * 4); - JSAMPROW yLastRow = (JSAMPROW)lastRowStorage.get(); - JSAMPROW uLastRow = yLastRow + rowBytesY; - JSAMPROW vLastRow = uLastRow + rowBytesY; - JSAMPROW dummyRow = vLastRow + rowBytesY; - - while (cinfo.output_scanline < cinfo.output_height) { - // Request 8 or 16 scanlines: returns 0 or more scanlines. - bool hasYLastRow(false), hasUVLastRow(false); - // Assign 8 or 16 rows of memory to read the Y channel. - for (int i = 0; i < yScanlinesToRead; ++i) { - int scanline = (cinfo.output_scanline + i); - if (scanline < yMaxH) { - bufferraw2[i] = &outputY[scanline * rowBytesY]; - } else if (scanline == yMaxH) { - bufferraw2[i] = yLastRow; - hasYLastRow = true; - } else { - bufferraw2[i] = dummyRow; - } - } - int scaledScanline = cinfo.output_scanline / v; - // Assign 8 rows of memory to read the U and V channels. - for (int i = 0; i < 8; ++i) { - int scanline = (scaledScanline + i); - if (scanline < uvMaxH) { - bufferraw2[16 + i] = &outputU[scanline * rowBytesU]; - bufferraw2[24 + i] = &outputV[scanline * rowBytesV]; - } else if (scanline == uvMaxH) { - bufferraw2[16 + i] = uLastRow; - bufferraw2[24 + i] = vLastRow; - hasUVLastRow = true; - } else { - bufferraw2[16 + i] = dummyRow; - bufferraw2[24 + i] = dummyRow; - } - } - JDIMENSION scanlinesRead = jpeg_read_raw_data(&cinfo, bufferraw, yScanlinesToRead); - - if (scanlinesRead == 0) { - return false; - } - - if (hasYLastRow) { - memcpy(&outputY[yMaxH * rowBytesY], yLastRow, yWidth); - } - if (hasUVLastRow) { - memcpy(&outputU[uvMaxH * rowBytesU], uLastRow, uvSize.width()); - memcpy(&outputV[uvMaxH * rowBytesV], vLastRow, uvSize.width()); - } - } - - cinfo.output_scanline = SkMin32(cinfo.output_scanline, cinfo.output_height); - - return true; -} - -bool SkJPEGImageDecoder::onDecodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], - void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) { -#ifdef TIME_DECODE - SkAutoTime atm("JPEG YUV8 Decode"); -#endif - if (this->getSampleSize() != 1) { - return false; // Resizing not supported - } - - JPEGAutoClean autoClean; - - jpeg_decompress_struct cinfo; - skjpeg_source_mgr srcManager(stream, this); - - skjpeg_error_mgr errorManager; - set_error_mgr(&cinfo, &errorManager); - - // All objects need to be instantiated before this setjmp call so that - // they will be cleaned up properly if an error occurs. - if (setjmp(errorManager.fJmpBuf)) { - return return_false(cinfo, "setjmp YUV8"); - } - - initialize_info(&cinfo, &srcManager); - autoClean.set(&cinfo); - - int status = jpeg_read_header(&cinfo, true); - if (status != JPEG_HEADER_OK) { - return return_false(cinfo, "read_header YUV8"); - } - - if (!appears_to_be_yuv(cinfo)) { - // It's not an error to not be encoded in YUV, so no need to use return_false() - return false; - } - - cinfo.out_color_space = JCS_YCbCr; - cinfo.raw_data_out = TRUE; - - if (!planes || !planes[0] || !rowBytes || !rowBytes[0]) { // Compute size only - update_components_sizes(cinfo, componentSizes, kSizeForMemoryAllocation_SizeType); - return true; - } - - set_dct_method(*this, &cinfo); - - SkASSERT(1 == cinfo.scale_num); - cinfo.scale_denom = 1; - -#ifdef ANDROID_RGB - cinfo.dither_mode = JDITHER_NONE; -#endif - - /* image_width and image_height are the original dimensions, available - after jpeg_read_header(). To see the scaled dimensions, we have to call - jpeg_start_decompress(), and then read output_width and output_height. - */ - if (!jpeg_start_decompress(&cinfo)) { - return return_false(cinfo, "start_decompress YUV8"); - } - - // Seems like jpeg_start_decompress is updating our opinion of whether cinfo represents YUV. - // Again, not really an error. - if (!appears_to_be_yuv(cinfo)) { - return false; - } - - if (!output_raw_data(cinfo, planes, rowBytes)) { - return return_false(cinfo, "output_raw_data"); - } - - update_components_sizes(cinfo, componentSizes, kActualSize_SizeType); - jpeg_finish_decompress(&cinfo); - - if (nullptr != colorSpace) { - *colorSpace = kJPEG_SkYUVColorSpace; - } - - return true; -} - /////////////////////////////////////////////////////////////////////////////// #include "SkColorPriv.h" @@ -993,45 +272,11 @@ protected: }; /////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(JPEGImageDecoder); DEFINE_ENCODER_CREATOR(JPEGImageEncoder); /////////////////////////////////////////////////////////////////////////////// -static bool is_jpeg(SkStreamRewindable* stream) { - static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF }; - static const size_t HEADER_SIZE = sizeof(gHeader); - - char buffer[HEADER_SIZE]; - size_t len = stream->read(buffer, HEADER_SIZE); - - if (len != HEADER_SIZE) { - return false; // can't read enough - } - if (memcmp(buffer, gHeader, HEADER_SIZE)) { - return false; - } - return true; -} - - -static SkImageDecoder* sk_libjpeg_dfactory(SkStreamRewindable* stream) { - if (is_jpeg(stream)) { - return new SkJPEGImageDecoder; - } - return nullptr; -} - -static SkImageDecoder::Format get_format_jpeg(SkStreamRewindable* stream) { - if (is_jpeg(stream)) { - return SkImageDecoder::kJPEG_Format; - } - return SkImageDecoder::kUnknown_Format; -} - static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) { return (SkImageEncoder::kJPEG_Type == t) ? new SkJPEGImageEncoder : nullptr; } -static SkImageDecoder_DecodeReg gDReg(sk_libjpeg_dfactory); -static SkImageDecoder_FormatReg gFormatReg(get_format_jpeg); static SkImageEncoder_EncodeReg gEReg(sk_libjpeg_efactory); diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp index cd8152a36b..c3df5d10a8 100644 --- a/src/images/SkImageDecoder_libpng.cpp +++ b/src/images/SkImageDecoder_libpng.cpp @@ -5,14 +5,12 @@ * found in the LICENSE file. */ -#include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkColor.h" #include "SkColorPriv.h" #include "SkDither.h" #include "SkMath.h" #include "SkRTConf.h" -#include "SkScaledBitmapSampler.h" #include "SkStream.h" #include "SkTemplates.h" #include "SkUtils.h" @@ -44,88 +42,10 @@ SK_CONF_DECLARE(bool, c_suppressPNGImageDecoderWarnings, "Suppress most PNG warnings when calling image decode " "functions."); -class SkPNGImageIndex { -public: - // Takes ownership of stream. - SkPNGImageIndex(SkStreamRewindable* stream, png_structp png_ptr, png_infop info_ptr) - : fStream(stream) - , fPng_ptr(png_ptr) - , fInfo_ptr(info_ptr) - , fColorType(kUnknown_SkColorType) { - SkASSERT(stream != nullptr); - } - ~SkPNGImageIndex() { - if (fPng_ptr) { - png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); - } - } - - SkAutoTDelete<SkStreamRewindable> fStream; - png_structp fPng_ptr; - png_infop fInfo_ptr; - SkColorType fColorType; -}; - -class SkPNGImageDecoder : public SkImageDecoder { -public: - SkPNGImageDecoder() { - fImageIndex = nullptr; - } - Format getFormat() const override { - return kPNG_Format; - } - - virtual ~SkPNGImageDecoder() { delete fImageIndex; } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; - -private: - SkPNGImageIndex* fImageIndex; - - bool onDecodeInit(SkStream* stream, png_structp *png_ptrp, png_infop *info_ptrp); - bool decodePalette(png_structp png_ptr, png_infop info_ptr, int bitDepth, - bool * SK_RESTRICT hasAlphap, bool *reallyHasAlphap, - SkColorTable **colorTablep); - bool getBitmapColorType(png_structp, png_infop, SkColorType*, bool* hasAlpha, - SkPMColor* theTranspColor); - - typedef SkImageDecoder INHERITED; -}; - -#ifndef png_jmpbuf -# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) -#endif - -#define PNG_BYTES_TO_CHECK 4 - -/* Automatically clean up after throwing an exception */ -struct PNGAutoClean { - PNGAutoClean(png_structp p, png_infop i): png_ptr(p), info_ptr(i) {} - ~PNGAutoClean() { - png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); - } -private: - png_structp png_ptr; - png_infop info_ptr; -}; - -static void sk_read_fn(png_structp png_ptr, png_bytep data, png_size_t length) { - SkStream* sk_stream = (SkStream*) png_get_io_ptr(png_ptr); - size_t bytes = sk_stream->read(data, length); - if (bytes != length) { - png_error(png_ptr, "Read Error!"); - } -} +/////////////////////////////////////////////////////////////////////////////// -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED -static int sk_read_user_chunk(png_structp png_ptr, png_unknown_chunkp chunk) { - SkPngChunkReader* peeker = (SkPngChunkReader*)png_get_user_chunk_ptr(png_ptr); - // readChunk() returning true means continue decoding - return peeker->readChunk((const char*)chunk->name, chunk->data, chunk->size) ? - 1 : -1; -} -#endif +#include "SkColorPriv.h" +#include "SkUnPreMultiply.h" static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { if (!c_suppressPNGImageDecoderWarnings) { @@ -134,577 +54,6 @@ static void sk_error_fn(png_structp png_ptr, png_const_charp msg) { longjmp(png_jmpbuf(png_ptr), 1); } -static void skip_src_rows(png_structp png_ptr, uint8_t storage[], int count) { - for (int i = 0; i < count; i++) { - uint8_t* tmp = storage; - png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); - } -} - -static bool pos_le(int value, int max) { - return value > 0 && value <= max; -} - -static bool substituteTranspColor(SkBitmap* bm, SkPMColor match) { - SkASSERT(bm->colorType() == kN32_SkColorType); - - bool reallyHasAlpha = false; - - for (int y = bm->height() - 1; y >= 0; --y) { - SkPMColor* p = bm->getAddr32(0, y); - for (int x = bm->width() - 1; x >= 0; --x) { - if (match == *p) { - *p = 0; - reallyHasAlpha = true; - } - p += 1; - } - } - return reallyHasAlpha; -} - -static bool canUpscalePaletteToConfig(SkColorType dstColorType, bool srcHasAlpha) { - switch (dstColorType) { - case kN32_SkColorType: - case kARGB_4444_SkColorType: - return true; - case kRGB_565_SkColorType: - // only return true if the src is opaque (since 565 is opaque) - return !srcHasAlpha; - default: - return false; - } -} - -// call only if color_type is PALETTE. Returns true if the ctable has alpha -static bool hasTransparencyInPalette(png_structp png_ptr, png_infop info_ptr) { - png_bytep trans; - int num_trans; - - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, nullptr); - return num_trans > 0; - } - return false; -} - -void do_nothing_warning_fn(png_structp, png_const_charp) { - /* do nothing */ -} - -bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp, - png_infop *info_ptrp) { - /* Create and initialize the png_struct with the desired error handler - * functions. If you want to use the default stderr and longjump method, - * you can supply nullptr for the last three parameters. We also supply the - * the compiler header file version, so that we know if the application - * was compiled with a compatible version of the library. */ - - png_error_ptr user_warning_fn = - (c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : nullptr; - /* nullptr means to leave as default library behavior. */ - /* c_suppressPNGImageDecoderWarnings default depends on SK_DEBUG. */ - /* To suppress warnings with a SK_DEBUG binary, set the - * environment variable "skia_images_png_suppressDecoderWarnings" - * to "true". Inside a program that links to skia: - * SK_CONF_SET("images.png.suppressDecoderWarnings", true); */ - - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, - nullptr, sk_error_fn, user_warning_fn); - // png_voidp user_error_ptr, user_error_fn, user_warning_fn); - if (png_ptr == nullptr) { - return false; - } - - *png_ptrp = png_ptr; - - /* Allocate/initialize the memory for image information. */ - png_infop info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == nullptr) { - png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); - return false; - } - *info_ptrp = info_ptr; - - /* Set error handling if you are using the setjmp/longjmp method (this is - * the normal method of doing things with libpng). REQUIRED unless you - * set up your own error handlers in the png_create_read_struct() earlier. - */ - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); - return false; - } - - /* If you are using replacement read functions, instead of calling - * png_init_io() here you would call: - */ - png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn); - /* where user_io_ptr is a structure you want available to the callbacks */ - /* If we have already read some of the signature */ -// png_set_sig_bytes(png_ptr, 0 /* sig_read */ ); - -#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED - // hookup our peeker so we can see any user-chunks the caller may be interested in - png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0); - if (this->getPeeker()) { - png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk); - } -#endif - /* The call to png_read_info() gives us all of the information from the - * PNG file before the first IDAT (image data chunk). */ - png_read_info(png_ptr, info_ptr); - png_uint_32 origWidth, origHeight; - int bitDepth, colorType; - png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, - &colorType, int_p_NULL, int_p_NULL, int_p_NULL); - - /* tell libpng to strip 16 bit/color files down to 8 bits/color */ - if (bitDepth == 16) { - png_set_strip_16(png_ptr); - } -#ifdef PNG_READ_PACK_SUPPORTED - /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single - * byte into separate bytes (useful for paletted and grayscale images). */ - if (bitDepth < 8) { - png_set_packing(png_ptr); - } -#endif - /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ - if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) { - png_set_expand_gray_1_2_4_to_8(png_ptr); - } - - return true; -} - -SkImageDecoder::Result SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap, - Mode mode) { - png_structp png_ptr; - png_infop info_ptr; - - if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) { - return kFailure; - } - - PNGAutoClean autoClean(png_ptr, info_ptr); - - if (setjmp(png_jmpbuf(png_ptr))) { - return kFailure; - } - - png_uint_32 origWidth, origHeight; - int bitDepth, pngColorType, interlaceType; - png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, - &pngColorType, &interlaceType, int_p_NULL, int_p_NULL); - - SkColorType colorType; - bool hasAlpha = false; - SkPMColor theTranspColor = 0; // 0 tells us not to try to match - - if (!this->getBitmapColorType(png_ptr, info_ptr, &colorType, &hasAlpha, &theTranspColor)) { - return kFailure; - } - - SkAlphaType alphaType = this->getRequireUnpremultipliedColors() ? - kUnpremul_SkAlphaType : kPremul_SkAlphaType; - const int sampleSize = this->getSampleSize(); - SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); - decodedBitmap->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(), - colorType, alphaType)); - - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - - // from here down we are concerned with colortables and pixels - - // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype - // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we - // draw lots faster if we can flag the bitmap has being opaque - bool reallyHasAlpha = false; - SkColorTable* colorTable = nullptr; - - if (pngColorType == PNG_COLOR_TYPE_PALETTE) { - decodePalette(png_ptr, info_ptr, bitDepth, &hasAlpha, &reallyHasAlpha, &colorTable); - } - - SkAutoUnref aur(colorTable); - - if (!this->allocPixelRef(decodedBitmap, - kIndex_8_SkColorType == colorType ? colorTable : nullptr)) { - return kFailure; - } - - SkAutoLockPixels alp(*decodedBitmap); - - // Repeat setjmp, otherwise variables declared since the last call (e.g. alp - // and aur) won't get their destructors called in case of a failure. - if (setjmp(png_jmpbuf(png_ptr))) { - return kFailure; - } - - /* Turn on interlace handling. REQUIRED if you are not using - * png_read_image(). To see how to handle interlacing passes, - * see the png_read_row() method below: - */ - const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ? - png_set_interlace_handling(png_ptr) : 1; - - /* Optional call to gamma correct and add the background to the palette - * and update info structure. REQUIRED if you are expecting libpng to - * update the palette for you (ie you selected such a transform above). - */ - png_read_update_info(png_ptr, info_ptr); - - if ((kAlpha_8_SkColorType == colorType || kIndex_8_SkColorType == colorType) && - 1 == sampleSize) { - if (kAlpha_8_SkColorType == colorType) { - // For an A8 bitmap, we assume there is an alpha for speed. It is - // possible the bitmap is opaque, but that is an unlikely use case - // since it would not be very interesting. - reallyHasAlpha = true; - // A8 is only allowed if the original was GRAY. - SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType); - } - for (int i = 0; i < number_passes; i++) { - for (png_uint_32 y = 0; y < origHeight; y++) { - uint8_t* bmRow = decodedBitmap->getAddr8(0, y); - png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); - } - } - } else { - SkScaledBitmapSampler::SrcConfig sc; - int srcBytesPerPixel = 4; - - if (colorTable != nullptr) { - sc = SkScaledBitmapSampler::kIndex; - srcBytesPerPixel = 1; - } else if (kAlpha_8_SkColorType == colorType) { - // A8 is only allowed if the original was GRAY. - SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType); - sc = SkScaledBitmapSampler::kGray; - srcBytesPerPixel = 1; - } else if (hasAlpha) { - sc = SkScaledBitmapSampler::kRGBA; - } else { - sc = SkScaledBitmapSampler::kRGBX; - } - - /* We have to pass the colortable explicitly, since we may have one - even if our decodedBitmap doesn't, due to the request that we - upscale png's palette to a direct model - */ - const SkPMColor* colors = colorTable ? colorTable->readColors() : nullptr; - if (!sampler.begin(decodedBitmap, sc, *this, colors)) { - return kFailure; - } - const int height = decodedBitmap->height(); - - if (number_passes > 1) { - SkAutoTMalloc<uint8_t> storage(origWidth * origHeight * srcBytesPerPixel); - uint8_t* base = storage.get(); - size_t rowBytes = origWidth * srcBytesPerPixel; - - for (int i = 0; i < number_passes; i++) { - uint8_t* row = base; - for (png_uint_32 y = 0; y < origHeight; y++) { - uint8_t* bmRow = row; - png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1); - row += rowBytes; - } - } - // now sample it - base += sampler.srcY0() * rowBytes; - for (int y = 0; y < height; y++) { - reallyHasAlpha |= sampler.next(base); - base += sampler.srcDY() * rowBytes; - } - } else { - SkAutoTMalloc<uint8_t> storage(origWidth * srcBytesPerPixel); - uint8_t* srcRow = storage.get(); - skip_src_rows(png_ptr, srcRow, sampler.srcY0()); - - for (int y = 0; y < height; y++) { - uint8_t* tmp = srcRow; - png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1); - reallyHasAlpha |= sampler.next(srcRow); - if (y < height - 1) { - skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1); - } - } - - // skip the rest of the rows (if any) - png_uint_32 read = (height - 1) * sampler.srcDY() + - sampler.srcY0() + 1; - SkASSERT(read <= origHeight); - skip_src_rows(png_ptr, srcRow, origHeight - read); - } - } - - /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ - png_read_end(png_ptr, info_ptr); - - if (0 != theTranspColor) { - reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor); - } - if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) { - switch (decodedBitmap->colorType()) { - case kIndex_8_SkColorType: - // Fall through. - case kARGB_4444_SkColorType: - // We have chosen not to support unpremul for these colortypes. - return kFailure; - default: { - // Fall through to finish the decode. This colortype either - // supports unpremul or it is irrelevant because it has no - // alpha (or only alpha). - // These brackets prevent a warning. - } - } - } - - if (!reallyHasAlpha) { - decodedBitmap->setAlphaType(kOpaque_SkAlphaType); - } - return kSuccess; -} - - - -bool SkPNGImageDecoder::getBitmapColorType(png_structp png_ptr, png_infop info_ptr, - SkColorType* colorTypep, - bool* hasAlphap, - SkPMColor* SK_RESTRICT theTranspColorp) { - png_uint_32 origWidth, origHeight; - int bitDepth, colorType; - png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, - &colorType, int_p_NULL, int_p_NULL, int_p_NULL); - -#ifdef PNG_sBIT_SUPPORTED - // check for sBIT chunk data, in case we should disable dithering because - // our data is not truely 8bits per component - png_color_8p sig_bit; - if (this->getDitherImage() && png_get_sBIT(png_ptr, info_ptr, &sig_bit)) { -#if 0 - SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green, - sig_bit->blue, sig_bit->alpha); -#endif - // 0 seems to indicate no information available - if (pos_le(sig_bit->red, SK_R16_BITS) && - pos_le(sig_bit->green, SK_G16_BITS) && - pos_le(sig_bit->blue, SK_B16_BITS)) { - this->setDitherImage(false); - } - } -#endif - - if (colorType == PNG_COLOR_TYPE_PALETTE) { - bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr); - *colorTypep = this->getPrefColorType(kIndex_SrcDepth, paletteHasAlpha); - // now see if we can upscale to their requested colortype - if (!canUpscalePaletteToConfig(*colorTypep, paletteHasAlpha)) { - *colorTypep = kIndex_8_SkColorType; - } - } else { - png_color_16p transpColor = nullptr; - int numTransp = 0; - - png_get_tRNS(png_ptr, info_ptr, nullptr, &numTransp, &transpColor); - - bool valid = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS); - - if (valid && numTransp == 1 && transpColor != nullptr) { - /* Compute our transparent color, which we'll match against later. - We don't really handle 16bit components properly here, since we - do our compare *after* the values have been knocked down to 8bit - which means we will find more matches than we should. The real - fix seems to be to see the actual 16bit components, do the - compare, and then knock it down to 8bits ourselves. - */ - if (colorType & PNG_COLOR_MASK_COLOR) { - if (16 == bitDepth) { - *theTranspColorp = SkPackARGB32(0xFF, transpColor->red >> 8, - transpColor->green >> 8, - transpColor->blue >> 8); - } else { - /* We apply the mask because in a very small - number of corrupt PNGs, (transpColor->red > 255) - and (bitDepth == 8), for certain versions of libpng. */ - *theTranspColorp = SkPackARGB32(0xFF, - 0xFF & (transpColor->red), - 0xFF & (transpColor->green), - 0xFF & (transpColor->blue)); - } - } else { // gray - if (16 == bitDepth) { - *theTranspColorp = SkPackARGB32(0xFF, transpColor->gray >> 8, - transpColor->gray >> 8, - transpColor->gray >> 8); - } else { - /* We apply the mask because in a very small - number of corrupt PNGs, (transpColor->red > - 255) and (bitDepth == 8), for certain versions - of libpng. For safety we assume the same could - happen with a grayscale PNG. */ - *theTranspColorp = SkPackARGB32(0xFF, - 0xFF & (transpColor->gray), - 0xFF & (transpColor->gray), - 0xFF & (transpColor->gray)); - } - } - } - - if (valid || - PNG_COLOR_TYPE_RGB_ALPHA == colorType || - PNG_COLOR_TYPE_GRAY_ALPHA == colorType) { - *hasAlphap = true; - } - - SrcDepth srcDepth = k32Bit_SrcDepth; - if (PNG_COLOR_TYPE_GRAY == colorType) { - srcDepth = k8BitGray_SrcDepth; - // Remove this assert, which fails on desk_pokemonwiki.skp - //SkASSERT(!*hasAlphap); - } - - *colorTypep = this->getPrefColorType(srcDepth, *hasAlphap); - // now match the request against our capabilities - if (*hasAlphap) { - if (*colorTypep != kARGB_4444_SkColorType) { - *colorTypep = kN32_SkColorType; - } - } else { - if (kAlpha_8_SkColorType == *colorTypep) { - if (k8BitGray_SrcDepth != srcDepth) { - // Converting a non grayscale image to A8 is not currently supported. - *colorTypep = kN32_SkColorType; - } - } else if (*colorTypep != kRGB_565_SkColorType && - *colorTypep != kARGB_4444_SkColorType) { - *colorTypep = kN32_SkColorType; - } - } - } - - // sanity check for size - { - int64_t size = sk_64_mul(origWidth, origHeight); - // now check that if we are 4-bytes per pixel, we also don't overflow - if (size < 0 || size > (0x7FFFFFFF >> 2)) { - return false; - } - } - - // If the image has alpha and the decoder wants unpremultiplied - // colors, the only supported colortype is 8888. - if (this->getRequireUnpremultipliedColors() && *hasAlphap) { - *colorTypep = kN32_SkColorType; - } - - if (fImageIndex != nullptr) { - if (kUnknown_SkColorType == fImageIndex->fColorType) { - // This is the first time for this subset decode. From now on, - // all decodes must be in the same colortype. - fImageIndex->fColorType = *colorTypep; - } else if (fImageIndex->fColorType != *colorTypep) { - // Requesting a different colortype for a subsequent decode is not - // supported. Report failure before we make changes to png_ptr. - return false; - } - } - - bool convertGrayToRGB = PNG_COLOR_TYPE_GRAY == colorType && *colorTypep != kAlpha_8_SkColorType; - - // Unless the user is requesting A8, convert a grayscale image into RGB. - // GRAY_ALPHA will always be converted to RGB - if (convertGrayToRGB || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { - png_set_gray_to_rgb(png_ptr); - } - - // Add filler (or alpha) byte (after each RGB triplet) if necessary. - if (colorType == PNG_COLOR_TYPE_RGB || convertGrayToRGB) { - png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); - } - - return true; -} - -typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); - -bool SkPNGImageDecoder::decodePalette(png_structp png_ptr, png_infop info_ptr, - int bitDepth, bool *hasAlphap, - bool *reallyHasAlphap, - SkColorTable **colorTablep) { - int numPalette; - png_colorp palette; - png_bytep trans; - int numTrans; - - png_get_PLTE(png_ptr, info_ptr, &palette, &numPalette); - - SkPMColor colorStorage[256]; // worst-case storage - SkPMColor* colorPtr = colorStorage; - - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_get_tRNS(png_ptr, info_ptr, &trans, &numTrans, nullptr); - *hasAlphap = (numTrans > 0); - } else { - numTrans = 0; - } - - // check for bad images that might make us crash - if (numTrans > numPalette) { - numTrans = numPalette; - } - - int index = 0; - int transLessThanFF = 0; - - // Choose which function to use to create the color table. If the final destination's - // colortype is unpremultiplied, the color table will store unpremultiplied colors. - PackColorProc proc; - if (this->getRequireUnpremultipliedColors()) { - proc = &SkPackARGB32NoCheck; - } else { - proc = &SkPreMultiplyARGB; - } - for (; index < numTrans; index++) { - transLessThanFF |= (int)*trans - 0xFF; - *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue); - palette++; - } - bool reallyHasAlpha = (transLessThanFF < 0); - - for (; index < numPalette; index++) { - *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); - palette++; - } - - /* BUGGY IMAGE WORKAROUND - - Invalid images could contain pixel values that are greater than the number of palette - entries. Since we use pixel values as indices into the palette this could result in reading - beyond the end of the palette which could leak the contents of uninitialized memory. To - ensure this doesn't happen, we grow the colortable to the maximum size that can be - addressed by the bitdepth of the image and fill it with the last palette color or black if - the palette is empty (really broken image). - */ - int colorCount = SkTMax(numPalette, 1 << SkTMin(bitDepth, 8)); - SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0); - for (; index < colorCount; index++) { - *colorPtr++ = lastColor; - } - - *colorTablep = new SkColorTable(colorStorage, colorCount); - *reallyHasAlphap = reallyHasAlpha; - return true; -} - -/////////////////////////////////////////////////////////////////////////////// - -#include "SkColorPriv.h" -#include "SkUnPreMultiply.h" - static void sk_write_fn(png_structp png_ptr, png_bytep data, png_size_t len) { SkWStream* sk_stream = (SkWStream*)png_get_io_ptr(png_ptr); if (!sk_stream->write(data, len)) { @@ -985,37 +334,11 @@ bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap, } /////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(PNGImageDecoder); DEFINE_ENCODER_CREATOR(PNGImageEncoder); /////////////////////////////////////////////////////////////////////////////// -static bool is_png(SkStreamRewindable* stream) { - char buf[PNG_BYTES_TO_CHECK]; - if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK && - !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) { - return true; - } - return false; -} - -SkImageDecoder* sk_libpng_dfactory(SkStreamRewindable* stream) { - if (is_png(stream)) { - return new SkPNGImageDecoder; - } - return nullptr; -} - -static SkImageDecoder::Format get_format_png(SkStreamRewindable* stream) { - if (is_png(stream)) { - return SkImageDecoder::kPNG_Format; - } - return SkImageDecoder::kUnknown_Format; -} - SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) { return (SkImageEncoder::kPNG_Type == t) ? new SkPNGImageEncoder : nullptr; } -static SkImageDecoder_DecodeReg gDReg(sk_libpng_dfactory); -static SkImageDecoder_FormatReg gFormatReg(get_format_png); static SkImageEncoder_EncodeReg gEReg(sk_libpng_efactory); diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp index 2db08cee83..116608a253 100644 --- a/src/images/SkImageDecoder_libwebp.cpp +++ b/src/images/SkImageDecoder_libwebp.cpp @@ -14,10 +14,9 @@ * limitations under the License. */ -#include "SkImageDecoder.h" +#include "SkBitmap.h" #include "SkImageEncoder.h" #include "SkColorPriv.h" -#include "SkScaledBitmapSampler.h" #include "SkStream.h" #include "SkTemplates.h" #include "SkUtils.h" @@ -32,299 +31,9 @@ extern "C" { // If moving libwebp out of skia source tree, path for webp headers must be // updated accordingly. Here, we enforce using local copy in webp sub-directory. -#include "webp/decode.h" #include "webp/encode.h" } -// this enables timing code to report milliseconds for a decode -//#define TIME_DECODE - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -// Define VP8 I/O on top of Skia stream - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -static const size_t WEBP_VP8_HEADER_SIZE = 64; -static const size_t WEBP_IDECODE_BUFFER_SZ = (1 << 16); - -// Parse headers of RIFF container, and check for valid Webp (VP8) content. -static bool webp_parse_header(SkStream* stream, int* width, int* height, int* alpha) { - unsigned char buffer[WEBP_VP8_HEADER_SIZE]; - size_t bytesToRead = WEBP_VP8_HEADER_SIZE; - size_t totalBytesRead = 0; - do { - unsigned char* dst = buffer + totalBytesRead; - const size_t bytesRead = stream->read(dst, bytesToRead); - if (0 == bytesRead) { - SkASSERT(stream->isAtEnd()); - break; - } - bytesToRead -= bytesRead; - totalBytesRead += bytesRead; - SkASSERT(bytesToRead + totalBytesRead == WEBP_VP8_HEADER_SIZE); - } while (!stream->isAtEnd() && bytesToRead > 0); - - WebPBitstreamFeatures features; - VP8StatusCode status = WebPGetFeatures(buffer, totalBytesRead, &features); - if (VP8_STATUS_OK != status) { - return false; // Invalid WebP file. - } - *width = features.width; - *height = features.height; - *alpha = features.has_alpha; - - // sanity check for image size that's about to be decoded. - { - int64_t size = sk_64_mul(*width, *height); - if (!sk_64_isS32(size)) { - return false; - } - // now check that if we are 4-bytes per pixel, we also don't overflow - if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) { - return false; - } - } - return true; -} - -class SkWEBPImageDecoder: public SkImageDecoder { -public: - SkWEBPImageDecoder() { - fOrigWidth = 0; - fOrigHeight = 0; - fHasAlpha = 0; - } - - Format getFormat() const override { - return kWEBP_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; - -private: - /** - * Called when determining the output config to request to webp. - * If the image does not have alpha, there is no need to premultiply. - * If the caller wants unpremultiplied colors, that is respected. - */ - bool shouldPremultiply() const { - return SkToBool(fHasAlpha) && !this->getRequireUnpremultipliedColors(); - } - - bool setDecodeConfig(SkBitmap* decodedBitmap, int width, int height); - - SkAutoTDelete<SkStream> fInputStream; - int fOrigWidth; - int fOrigHeight; - int fHasAlpha; - - typedef SkImageDecoder INHERITED; -}; - -////////////////////////////////////////////////////////////////////////// - -#ifdef TIME_DECODE - -#include "SkTime.h" - -class AutoTimeMillis { -public: - AutoTimeMillis(const char label[]) : - fLabel(label) { - if (nullptr == fLabel) { - fLabel = ""; - } - fNow = SkTime::GetMSecs(); - } - ~AutoTimeMillis() { - SkDebugf("---- Time (ms): %s %d\n", fLabel, SkTime::GetMSecs() - fNow); - } -private: - const char* fLabel; - SkMSec fNow; -}; - -#endif - -/////////////////////////////////////////////////////////////////////////////// - -// This guy exists just to aid in debugging, as it allows debuggers to just -// set a break-point in one place to see all error exists. -static void print_webp_error(const SkBitmap& bm, const char msg[]) { - SkDEBUGF(("libwebp error %s [%d %d]", msg, bm.width(), bm.height())); -} - -static SkImageDecoder::Result return_failure(const SkBitmap& bm, const char msg[]) { - print_webp_error(bm, msg); - return SkImageDecoder::kFailure; // must always return kFailure -} - -/////////////////////////////////////////////////////////////////////////////// - -static WEBP_CSP_MODE webp_decode_mode(const SkBitmap* decodedBitmap, bool premultiply) { - WEBP_CSP_MODE mode = MODE_LAST; - const SkColorType ct = decodedBitmap->colorType(); - - if (ct == kN32_SkColorType) { - #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) - mode = premultiply ? MODE_bgrA : MODE_BGRA; - #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) - mode = premultiply ? MODE_rgbA : MODE_RGBA; - #else - #error "Skia uses BGRA or RGBA byte order" - #endif - } else if (ct == kARGB_4444_SkColorType) { - mode = premultiply ? MODE_rgbA_4444 : MODE_RGBA_4444; - } else if (ct == kRGB_565_SkColorType) { - mode = MODE_RGB_565; - } - SkASSERT(MODE_LAST != mode); - return mode; -} - -// Incremental WebP image decoding. Reads input buffer of 64K size iteratively -// and decodes this block to appropriate color-space as per config object. -static bool webp_idecode(SkStream* stream, WebPDecoderConfig* config) { - WebPIDecoder* idec = WebPIDecode(nullptr, 0, config); - if (nullptr == idec) { - WebPFreeDecBuffer(&config->output); - return false; - } - - if (!stream->rewind()) { - SkDebugf("Failed to rewind webp stream!"); - return false; - } - const size_t readBufferSize = stream->hasLength() ? - SkTMin(stream->getLength(), WEBP_IDECODE_BUFFER_SZ) : WEBP_IDECODE_BUFFER_SZ; - SkAutoTMalloc<unsigned char> srcStorage(readBufferSize); - unsigned char* input = srcStorage.get(); - if (nullptr == input) { - WebPIDelete(idec); - WebPFreeDecBuffer(&config->output); - return false; - } - - bool success = true; - VP8StatusCode status = VP8_STATUS_SUSPENDED; - do { - const size_t bytesRead = stream->read(input, readBufferSize); - if (0 == bytesRead) { - success = false; - break; - } - - status = WebPIAppend(idec, input, bytesRead); - if (VP8_STATUS_OK != status && VP8_STATUS_SUSPENDED != status) { - success = false; - break; - } - } while (VP8_STATUS_OK != status); - srcStorage.reset(); - WebPIDelete(idec); - WebPFreeDecBuffer(&config->output); - - return success; -} - -static bool webp_get_config_resize(WebPDecoderConfig* config, - SkBitmap* decodedBitmap, - int width, int height, bool premultiply) { - WEBP_CSP_MODE mode = webp_decode_mode(decodedBitmap, premultiply); - if (MODE_LAST == mode) { - return false; - } - - if (0 == WebPInitDecoderConfig(config)) { - return false; - } - - config->output.colorspace = mode; - config->output.u.RGBA.rgba = (uint8_t*)decodedBitmap->getPixels(); - config->output.u.RGBA.stride = (int) decodedBitmap->rowBytes(); - config->output.u.RGBA.size = decodedBitmap->getSize(); - config->output.is_external_memory = 1; - - if (width != decodedBitmap->width() || height != decodedBitmap->height()) { - config->options.use_scaling = 1; - config->options.scaled_width = decodedBitmap->width(); - config->options.scaled_height = decodedBitmap->height(); - } - - return true; -} - -bool SkWEBPImageDecoder::setDecodeConfig(SkBitmap* decodedBitmap, int width, int height) { - SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, SkToBool(fHasAlpha)); - - // YUV converter supports output in RGB565, RGBA4444 and RGBA8888 formats. - if (fHasAlpha) { - if (colorType != kARGB_4444_SkColorType) { - colorType = kN32_SkColorType; - } - } else { - if (colorType != kRGB_565_SkColorType && colorType != kARGB_4444_SkColorType) { - colorType = kN32_SkColorType; - } - } - - SkAlphaType alphaType = kOpaque_SkAlphaType; - if (SkToBool(fHasAlpha)) { - if (this->getRequireUnpremultipliedColors()) { - alphaType = kUnpremul_SkAlphaType; - } else { - alphaType = kPremul_SkAlphaType; - } - } - return decodedBitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); -} - -SkImageDecoder::Result SkWEBPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, - Mode mode) { -#ifdef TIME_DECODE - AutoTimeMillis atm("WEBP Decode"); -#endif - - int origWidth, origHeight, hasAlpha; - if (!webp_parse_header(stream, &origWidth, &origHeight, &hasAlpha)) { - return kFailure; - } - this->fHasAlpha = hasAlpha; - - const int sampleSize = this->getSampleSize(); - SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize); - if (!setDecodeConfig(decodedBitmap, sampler.scaledWidth(), - sampler.scaledHeight())) { - return kFailure; - } - - // If only bounds are requested, done - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - - if (!this->allocPixelRef(decodedBitmap, nullptr)) { - return return_failure(*decodedBitmap, "allocPixelRef"); - } - - SkAutoLockPixels alp(*decodedBitmap); - - WebPDecoderConfig config; - if (!webp_get_config_resize(&config, decodedBitmap, origWidth, origHeight, - this->shouldPremultiply())) { - return kFailure; - } - - // Decode the WebP image data stream using WebP incremental decoding. - return webp_idecode(stream, &config) ? kSuccess : kFailure; -} - -/////////////////////////////////////////////////////////////////////////////// - #include "SkUnPreMultiply.h" typedef void (*ScanlineImporter)(const uint8_t* in, uint8_t* out, int width, @@ -528,32 +237,11 @@ bool SkWEBPImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bm, /////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(WEBPImageDecoder); DEFINE_ENCODER_CREATOR(WEBPImageEncoder); /////////////////////////////////////////////////////////////////////////////// -static SkImageDecoder* sk_libwebp_dfactory(SkStreamRewindable* stream) { - int width, height, hasAlpha; - if (!webp_parse_header(stream, &width, &height, &hasAlpha)) { - return nullptr; - } - - // Magic matches, call decoder - return new SkWEBPImageDecoder; -} - -static SkImageDecoder::Format get_format_webp(SkStreamRewindable* stream) { - int width, height, hasAlpha; - if (webp_parse_header(stream, &width, &height, &hasAlpha)) { - return SkImageDecoder::kWEBP_Format; - } - return SkImageDecoder::kUnknown_Format; -} - static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) { return (SkImageEncoder::kWEBP_Type == t) ? new SkWEBPImageEncoder : nullptr; } -static SkImageDecoder_DecodeReg gDReg(sk_libwebp_dfactory); -static SkImageDecoder_FormatReg gFormatReg(get_format_webp); static SkImageEncoder_EncodeReg gEReg(sk_libwebp_efactory); diff --git a/src/images/SkImageDecoder_pkm.cpp b/src/images/SkImageDecoder_pkm.cpp deleted file mode 100644 index af68f20d97..0000000000 --- a/src/images/SkImageDecoder_pkm.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkColorPriv.h" -#include "SkData.h" -#include "SkImageDecoder.h" -#include "SkScaledBitmapSampler.h" -#include "SkStream.h" -#include "SkStreamPriv.h" -#include "SkTextureCompressor.h" -#include "SkTypes.h" - -#include "etc1.h" - -class SkPKMImageDecoder : public SkImageDecoder { -public: - SkPKMImageDecoder() { } - - Format getFormat() const override { - return kPKM_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; - -private: - typedef SkImageDecoder INHERITED; -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -SkImageDecoder::Result SkPKMImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { - sk_sp<SkData> data(SkCopyStreamToData(stream)); - if (!data || !data->size()) { - return kFailure; - } - - unsigned char* buf = (unsigned char*) data->data(); - - // Make sure original PKM header is there... - SkASSERT(etc1_pkm_is_valid(buf)); - - const unsigned short width = etc1_pkm_get_width(buf); - const unsigned short height = etc1_pkm_get_height(buf); - - // Setup the sampler... - SkScaledBitmapSampler sampler(width, height, this->getSampleSize()); - - // Set the config... - bm->setInfo(SkImageInfo::MakeN32(sampler.scaledWidth(), sampler.scaledHeight(), - kOpaque_SkAlphaType)); - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - - if (!this->allocPixelRef(bm, nullptr)) { - return kFailure; - } - - // Lock the pixels, since we're about to write to them... - SkAutoLockPixels alp(*bm); - - if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) { - return kFailure; - } - - // Advance buffer past the header - buf += ETC_PKM_HEADER_SIZE; - - // ETC1 Data is encoded as RGB pixels, so we should extract it as such - int nPixels = width * height; - SkAutoMalloc outRGBData(nPixels * 3); - uint8_t *outRGBDataPtr = reinterpret_cast<uint8_t *>(outRGBData.get()); - - // Decode ETC1 - if (!SkTextureCompressor::DecompressBufferFromFormat( - outRGBDataPtr, width*3, buf, width, height, SkTextureCompressor::kETC1_Format)) { - return kFailure; - } - - // Set each of the pixels... - const int srcRowBytes = width * 3; - const int dstHeight = sampler.scaledHeight(); - const uint8_t *srcRow = reinterpret_cast<uint8_t *>(outRGBDataPtr); - srcRow += sampler.srcY0() * srcRowBytes; - for (int y = 0; y < dstHeight; ++y) { - sampler.next(srcRow); - srcRow += sampler.srcDY() * srcRowBytes; - } - - return kSuccess; -} - -///////////////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(PKMImageDecoder); -///////////////////////////////////////////////////////////////////////////////////////// - -static bool is_pkm(SkStreamRewindable* stream) { - // Read the PKM header and make sure it's valid. - unsigned char buf[ETC_PKM_HEADER_SIZE]; - if (stream->read((void*)buf, ETC_PKM_HEADER_SIZE) != ETC_PKM_HEADER_SIZE) { - return false; - } - - return SkToBool(etc1_pkm_is_valid(buf)); -} - -static SkImageDecoder* sk_libpkm_dfactory(SkStreamRewindable* stream) { - if (is_pkm(stream)) { - return new SkPKMImageDecoder; - } - return nullptr; -} - -static SkImageDecoder_DecodeReg gReg(sk_libpkm_dfactory); - -static SkImageDecoder::Format get_format_pkm(SkStreamRewindable* stream) { - if (is_pkm(stream)) { - return SkImageDecoder::kPKM_Format; - } - return SkImageDecoder::kUnknown_Format; -} - -static SkImageDecoder_FormatReg gFormatReg(get_format_pkm); diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp deleted file mode 100644 index 335966b29a..0000000000 --- a/src/images/SkImageDecoder_wbmp.cpp +++ /dev/null @@ -1,173 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkImageDecoder.h" -#include "SkColor.h" -#include "SkColorPriv.h" -#include "SkMath.h" -#include "SkStream.h" -#include "SkTemplates.h" -#include "SkUtils.h" - -class SkWBMPImageDecoder : public SkImageDecoder { -public: - Format getFormat() const override { - return kWBMP_Format; - } - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode) override; - -private: - typedef SkImageDecoder INHERITED; -}; - -static bool read_byte(SkStream* stream, uint8_t* data) -{ - return stream->read(data, 1) == 1; -} - -static bool read_mbf(SkStream* stream, int* value) -{ - int n = 0; - uint8_t data; - do { - if (!read_byte(stream, &data)) { - return false; - } - n = (n << 7) | (data & 0x7F); - } while (data & 0x80); - - *value = n; - return true; -} - -struct wbmp_head { - int fWidth; - int fHeight; - - bool init(SkStream* stream) - { - uint8_t data; - - if (!read_byte(stream, &data) || data != 0) { // unknown type - return false; - } - if (!read_byte(stream, &data) || (data & 0x9F)) { // skip fixed header - return false; - } - if (!read_mbf(stream, &fWidth) || (unsigned)fWidth > 0xFFFF) { - return false; - } - if (!read_mbf(stream, &fHeight) || (unsigned)fHeight > 0xFFFF) { - return false; - } - return fWidth != 0 && fHeight != 0; - } -}; - -static void expand_bits_to_bytes(uint8_t dst[], const uint8_t src[], int bits) -{ - int bytes = bits >> 3; - - for (int i = 0; i < bytes; i++) { - unsigned mask = *src++; - dst[0] = (mask >> 7) & 1; - dst[1] = (mask >> 6) & 1; - dst[2] = (mask >> 5) & 1; - dst[3] = (mask >> 4) & 1; - dst[4] = (mask >> 3) & 1; - dst[5] = (mask >> 2) & 1; - dst[6] = (mask >> 1) & 1; - dst[7] = (mask >> 0) & 1; - dst += 8; - } - - bits &= 7; - if (bits > 0) { - unsigned mask = *src; - do { - *dst++ = (mask >> 7) & 1; - mask <<= 1; - } while (--bits != 0); - } -} - -SkImageDecoder::Result SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap, - Mode mode) -{ - wbmp_head head; - - if (!head.init(stream)) { - return kFailure; - } - - int width = head.fWidth; - int height = head.fHeight; - - decodedBitmap->setInfo(SkImageInfo::Make(width, height, - kIndex_8_SkColorType, kOpaque_SkAlphaType)); - - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - - const SkPMColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; - SkColorTable* ct = new SkColorTable(colors, 2); - SkAutoUnref aur(ct); - - if (!this->allocPixelRef(decodedBitmap, ct)) { - return kFailure; - } - - SkAutoLockPixels alp(*decodedBitmap); - - uint8_t* dst = decodedBitmap->getAddr8(0, 0); - // store the 1-bit valuess at the end of our pixels, so we won't stomp - // on them before we're read them. Just trying to avoid a temp allocation - size_t srcRB = SkAlign8(width) >> 3; - size_t srcSize = height * srcRB; - uint8_t* src = dst + decodedBitmap->getSize() - srcSize; - if (stream->read(src, srcSize) != srcSize) { - return kFailure; - } - - for (int y = 0; y < height; y++) - { - expand_bits_to_bytes(dst, src, width); - dst += decodedBitmap->rowBytes(); - src += srcRB; - } - - return kSuccess; -} - -/////////////////////////////////////////////////////////////////////////////// -DEFINE_DECODER_CREATOR(WBMPImageDecoder); -/////////////////////////////////////////////////////////////////////////////// - -static SkImageDecoder* sk_wbmp_dfactory(SkStreamRewindable* stream) { - wbmp_head head; - - if (head.init(stream)) { - return new SkWBMPImageDecoder; - } - return nullptr; -} - -static SkImageDecoder::Format get_format_wbmp(SkStreamRewindable* stream) { - wbmp_head head; - if (head.init(stream)) { - return SkImageDecoder::kWBMP_Format; - } - return SkImageDecoder::kUnknown_Format; -} - -static SkImageDecoder_DecodeReg gDReg(sk_wbmp_dfactory); -static SkImageDecoder_FormatReg gFormatReg(get_format_wbmp); diff --git a/src/images/SkJpegUtility.cpp b/src/images/SkJpegUtility.cpp index f1a32cae10..ab8486bcf6 100644 --- a/src/images/SkJpegUtility.cpp +++ b/src/images/SkJpegUtility.cpp @@ -8,108 +8,6 @@ #include "SkJpegUtility.h" -///////////////////////////////////////////////////////////////////// -static void sk_init_source(j_decompress_ptr cinfo) { - skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src; - src->next_input_byte = (const JOCTET*)src->fBuffer; - src->bytes_in_buffer = 0; -#ifdef SK_JPEG_INDEX_SUPPORTED - src->current_offset = 0; -#endif - if (!src->fStream->rewind()) { - SkDebugf("xxxxxxxxxxxxxx failure to rewind\n"); - cinfo->err->error_exit((j_common_ptr)cinfo); - } -} - -#ifdef SK_JPEG_INDEX_SUPPORTED -static boolean sk_seek_input_data(j_decompress_ptr cinfo, long byte_offset) { - skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src; - size_t bo = (size_t) byte_offset; - - if (bo > src->current_offset) { - (void)src->fStream->skip(bo - src->current_offset); - } else { - if (!src->fStream->rewind()) { - SkDebugf("xxxxxxxxxxxxxx failure to rewind\n"); - cinfo->err->error_exit((j_common_ptr)cinfo); - return false; - } - (void)src->fStream->skip(bo); - } - - src->current_offset = bo; - src->next_input_byte = (const JOCTET*)src->fBuffer; - src->bytes_in_buffer = 0; - return true; -} -#endif - -static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) { - skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src; - if (src->fDecoder != nullptr && src->fDecoder->shouldCancelDecode()) { - return FALSE; - } - size_t bytes = src->fStream->read(src->fBuffer, skjpeg_source_mgr::kBufferSize); - // note that JPEG is happy with less than the full read, - // as long as the result is non-zero - if (bytes == 0) { - return FALSE; - } - -#ifdef SK_JPEG_INDEX_SUPPORTED - src->current_offset += bytes; -#endif - src->next_input_byte = (const JOCTET*)src->fBuffer; - src->bytes_in_buffer = bytes; - return TRUE; -} - -static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) { - skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src; - - if (num_bytes > (long)src->bytes_in_buffer) { - size_t bytesToSkip = num_bytes - src->bytes_in_buffer; - while (bytesToSkip > 0) { - size_t bytes = src->fStream->skip(bytesToSkip); - if (bytes <= 0 || bytes > bytesToSkip) { -// SkDebugf("xxxxxxxxxxxxxx failure to skip request %d returned %d\n", bytesToSkip, bytes); - cinfo->err->error_exit((j_common_ptr)cinfo); - return; - } -#ifdef SK_JPEG_INDEX_SUPPORTED - src->current_offset += bytes; -#endif - bytesToSkip -= bytes; - } - src->next_input_byte = (const JOCTET*)src->fBuffer; - src->bytes_in_buffer = 0; - } else { - src->next_input_byte += num_bytes; - src->bytes_in_buffer -= num_bytes; - } -} - -static void sk_term_source(j_decompress_ptr /*cinfo*/) {} - - -/////////////////////////////////////////////////////////////////////////////// - -skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder) - : fStream(stream) - , fDecoder(decoder) { - - init_source = sk_init_source; - fill_input_buffer = sk_fill_input_buffer; - skip_input_data = sk_skip_input_data; - resync_to_restart = jpeg_resync_to_restart; - term_source = sk_term_source; -#ifdef SK_JPEG_INDEX_SUPPORTED - seek_input_data = sk_seek_input_data; -#endif -// SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize); -} - /////////////////////////////////////////////////////////////////////////////// static void sk_init_destination(j_compress_ptr cinfo) { diff --git a/src/images/SkJpegUtility.h b/src/images/SkJpegUtility.h index 1a763f843c..c84465289c 100644 --- a/src/images/SkJpegUtility.h +++ b/src/images/SkJpegUtility.h @@ -10,7 +10,6 @@ #ifndef SkJpegUtility_DEFINED #define SkJpegUtility_DEFINED -#include "SkImageDecoder.h" #include "SkStream.h" extern "C" { @@ -30,23 +29,6 @@ struct skjpeg_error_mgr : jpeg_error_mgr { void skjpeg_error_exit(j_common_ptr cinfo); -/////////////////////////////////////////////////////////////////////////// -/* Our source struct for directing jpeg to our stream object. -*/ -struct skjpeg_source_mgr : jpeg_source_mgr { - skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder); - - // Unowned. - SkStream* fStream; - // Unowned pointer to the decoder, used to check if the decoding process - // has been cancelled. - SkImageDecoder* fDecoder; - enum { - kBufferSize = 1024 - }; - char fBuffer[kBufferSize]; -}; - ///////////////////////////////////////////////////////////////////////////// /* Our destination struct for directing decompressed pixels to our stream * object. diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp deleted file mode 100644 index 5ffd648893..0000000000 --- a/src/images/SkScaledBitmapSampler.cpp +++ /dev/null @@ -1,877 +0,0 @@ -/* - * Copyright 2007 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#include "SkScaledBitmapSampler.h" -#include "SkBitmap.h" -#include "SkColorPriv.h" -#include "SkDither.h" -#include "SkTypes.h" - -// 8888 - -static bool Sample_Gray_D8888(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < width; x++) { - dst[x] = SkPackARGB32(0xFF, src[0], src[0], src[0]); - src += deltaSrc; - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_gray_to_8888_proc(const SkScaledBitmapSampler::Options& opts) { - // Dither, unpremul, and skipZeroes have no effect - return Sample_Gray_D8888; -} - -static bool Sample_RGBx_D8888(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - for (int x = 0; x < width; x++) { - dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]); - src += deltaSrc; - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_RGBx_to_8888_proc(const SkScaledBitmapSampler::Options& opts) { - // Dither, unpremul, and skipZeroes have no effect - return Sample_RGBx_D8888; -} - -static bool Sample_RGBA_D8888(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - unsigned alphaMask = 0xFF; - for (int x = 0; x < width; x++) { - unsigned alpha = src[3]; - dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); - src += deltaSrc; - alphaMask &= alpha; - } - return alphaMask != 0xFF; -} - -static bool Sample_RGBA_D8888_Unpremul(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, - const SkPMColor[]) { - uint32_t* SK_RESTRICT dst = reinterpret_cast<uint32_t*>(dstRow); - unsigned alphaMask = 0xFF; - for (int x = 0; x < width; x++) { - unsigned alpha = src[3]; - dst[x] = SkPackARGB32NoCheck(alpha, src[0], src[1], src[2]); - src += deltaSrc; - alphaMask &= alpha; - } - return alphaMask != 0xFF; -} - -static bool Sample_RGBA_D8888_SkipZ(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, - const SkPMColor[]) { - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - unsigned alphaMask = 0xFF; - for (int x = 0; x < width; x++) { - unsigned alpha = src[3]; - if (0 != alpha) { - dst[x] = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); - } - src += deltaSrc; - alphaMask &= alpha; - } - return alphaMask != 0xFF; -} - -static SkScaledBitmapSampler::RowProc -get_RGBA_to_8888_proc(const SkScaledBitmapSampler::Options& opts) { - // Dither has no effect. - if (!opts.fPremultiplyAlpha) { - // We could check each component for a zero, at the expense of extra checks. - // For now, just return unpremul. - return Sample_RGBA_D8888_Unpremul; - } - // Supply the versions that premultiply the colors - if (opts.fSkipZeros) { - return Sample_RGBA_D8888_SkipZ; - } - return Sample_RGBA_D8888; -} - -// 565 - -static bool Sample_Gray_D565(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < width; x++) { - dst[x] = SkPack888ToRGB16(src[0], src[0], src[0]); - src += deltaSrc; - } - return false; -} - -static bool Sample_Gray_D565_D(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, const SkPMColor[]) { - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - DITHER_565_SCAN(y); - for (int x = 0; x < width; x++) { - dst[x] = SkDitherRGBTo565(src[0], src[0], src[0], DITHER_VALUE(x)); - src += deltaSrc; - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_gray_to_565_proc(const SkScaledBitmapSampler::Options& opts) { - // Unpremul and skip zeroes make no difference - if (opts.fDither) { - return Sample_Gray_D565_D; - } - return Sample_Gray_D565; -} - -static bool Sample_RGBx_D565(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < width; x++) { - dst[x] = SkPack888ToRGB16(src[0], src[1], src[2]); - src += deltaSrc; - } - return false; -} - -static bool Sample_RGBx_D565_D(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, - const SkPMColor[]) { - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - DITHER_565_SCAN(y); - for (int x = 0; x < width; x++) { - dst[x] = SkDitherRGBTo565(src[0], src[1], src[2], DITHER_VALUE(x)); - src += deltaSrc; - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_RGBx_to_565_proc(const SkScaledBitmapSampler::Options& opts) { - // Unpremul and skip zeroes make no difference - if (opts.fDither) { - return Sample_RGBx_D565_D; - } - return Sample_RGBx_D565; -} - - -static bool Sample_D565_D565(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - uint16_t* SK_RESTRICT castedSrc = (uint16_t*) src; - for (int x = 0; x < width; x++) { - dst[x] = castedSrc[0]; - castedSrc += deltaSrc >> 1; - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_565_to_565_proc(const SkScaledBitmapSampler::Options& opts) { - // Unpremul, dither, and skip zeroes have no effect - return Sample_D565_D565; -} - -// 4444 - -static bool Sample_Gray_D4444(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - for (int x = 0; x < width; x++) { - unsigned gray = src[0] >> 4; - dst[x] = SkPackARGB4444(0xF, gray, gray, gray); - src += deltaSrc; - } - return false; -} - -static bool Sample_Gray_D4444_D(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, const SkPMColor[]) { - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - DITHER_4444_SCAN(y); - for (int x = 0; x < width; x++) { - dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[0], src[0], - DITHER_VALUE(x)); - src += deltaSrc; - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_gray_to_4444_proc(const SkScaledBitmapSampler::Options& opts) { - // Skip zeroes and unpremul make no difference - if (opts.fDither) { - return Sample_Gray_D4444_D; - } - return Sample_Gray_D4444; -} - -static bool Sample_RGBx_D4444(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - for (int x = 0; x < width; x++) { - dst[x] = SkPackARGB4444(0xF, src[0] >> 4, src[1] >> 4, src[2] >> 4); - src += deltaSrc; - } - return false; -} - -static bool Sample_RGBx_D4444_D(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, const SkPMColor[]) { - SkPMColor16* dst = (SkPMColor16*)dstRow; - DITHER_4444_SCAN(y); - - for (int x = 0; x < width; x++) { - dst[x] = SkDitherARGB32To4444(0xFF, src[0], src[1], src[2], - DITHER_VALUE(x)); - src += deltaSrc; - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_RGBx_to_4444_proc(const SkScaledBitmapSampler::Options& opts) { - // Skip zeroes and unpremul make no difference - if (opts.fDither) { - return Sample_RGBx_D4444_D; - } - return Sample_RGBx_D4444; -} - -static bool Sample_RGBA_D4444(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - unsigned alphaMask = 0xFF; - - for (int x = 0; x < width; x++) { - unsigned alpha = src[3]; - SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); - dst[x] = SkPixel32ToPixel4444(c); - src += deltaSrc; - alphaMask &= alpha; - } - return alphaMask != 0xFF; -} - -static bool Sample_RGBA_D4444_SkipZ(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, - const SkPMColor[]) { - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - unsigned alphaMask = 0xFF; - - for (int x = 0; x < width; x++) { - unsigned alpha = src[3]; - if (alpha != 0) { - SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); - dst[x] = SkPixel32ToPixel4444(c); - } - src += deltaSrc; - alphaMask &= alpha; - } - return alphaMask != 0xFF; -} - - -static bool Sample_RGBA_D4444_D(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, - const SkPMColor[]) { - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - unsigned alphaMask = 0xFF; - DITHER_4444_SCAN(y); - - for (int x = 0; x < width; x++) { - unsigned alpha = src[3]; - SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); - dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x)); - src += deltaSrc; - alphaMask &= alpha; - } - return alphaMask != 0xFF; -} - -static bool Sample_RGBA_D4444_D_SkipZ(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, - const SkPMColor[]) { - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - unsigned alphaMask = 0xFF; - DITHER_4444_SCAN(y); - - for (int x = 0; x < width; x++) { - unsigned alpha = src[3]; - if (alpha != 0) { - SkPMColor c = SkPreMultiplyARGB(alpha, src[0], src[1], src[2]); - dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x)); - } - src += deltaSrc; - alphaMask &= alpha; - } - return alphaMask != 0xFF; -} - -static SkScaledBitmapSampler::RowProc -get_RGBA_to_4444_proc(const SkScaledBitmapSampler::Options& opts) { - if (!opts.fPremultiplyAlpha) { - // Unpremultiplied is not supported for 4444 - return nullptr; - } - if (opts.fSkipZeros) { - if (opts.fDither) { - return Sample_RGBA_D4444_D_SkipZ; - } - return Sample_RGBA_D4444_SkipZ; - } - if (opts.fDither) { - return Sample_RGBA_D4444_D; - } - return Sample_RGBA_D4444; -} - -// Index - -#define A32_MASK_IN_PLACE (SkPMColor)(SK_A32_MASK << SK_A32_SHIFT) - -static bool Sample_Index_D8888(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor ctable[]) { - - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - SkPMColor cc = A32_MASK_IN_PLACE; - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[*src]; - cc &= c; - dst[x] = c; - src += deltaSrc; - } - return cc != A32_MASK_IN_PLACE; -} - -static bool Sample_Index_D8888_SkipZ(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, - const SkPMColor ctable[]) { - - SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; - SkPMColor cc = A32_MASK_IN_PLACE; - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[*src]; - cc &= c; - if (c != 0) { - dst[x] = c; - } - src += deltaSrc; - } - return cc != A32_MASK_IN_PLACE; -} - -static SkScaledBitmapSampler::RowProc -get_index_to_8888_proc(const SkScaledBitmapSampler::Options& opts) { - // The caller is expected to have created the source colortable - // properly with respect to opts.fPremultiplyAlpha, so premul makes - // no difference here. - // Dither makes no difference - if (opts.fSkipZeros) { - return Sample_Index_D8888_SkipZ; - } - return Sample_Index_D8888; -} - -static bool Sample_Index_D565(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor ctable[]) { - - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - for (int x = 0; x < width; x++) { - dst[x] = SkPixel32ToPixel16(ctable[*src]); - src += deltaSrc; - } - return false; -} - -static bool Sample_Index_D565_D(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, int width, - int deltaSrc, int y, const SkPMColor ctable[]) { - - uint16_t* SK_RESTRICT dst = (uint16_t*)dstRow; - DITHER_565_SCAN(y); - - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[*src]; - dst[x] = SkDitherRGBTo565(SkGetPackedR32(c), SkGetPackedG32(c), - SkGetPackedB32(c), DITHER_VALUE(x)); - src += deltaSrc; - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_index_to_565_proc(const SkScaledBitmapSampler::Options& opts) { - // Unpremultiplied and skip zeroes make no difference - if (opts.fDither) { - return Sample_Index_D565_D; - } - return Sample_Index_D565; -} - -static bool Sample_Index_D4444(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, int width, - int deltaSrc, int y, const SkPMColor ctable[]) { - - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - SkPMColor cc = A32_MASK_IN_PLACE; - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[*src]; - cc &= c; - dst[x] = SkPixel32ToPixel4444(c); - src += deltaSrc; - } - return cc != A32_MASK_IN_PLACE; -} - -static bool Sample_Index_D4444_D(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, int width, - int deltaSrc, int y, const SkPMColor ctable[]) { - - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - SkPMColor cc = A32_MASK_IN_PLACE; - DITHER_4444_SCAN(y); - - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[*src]; - cc &= c; - dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x)); - src += deltaSrc; - } - return cc != A32_MASK_IN_PLACE; -} - -static bool Sample_Index_D4444_SkipZ(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, int width, - int deltaSrc, int y, const SkPMColor ctable[]) { - - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - SkPMColor cc = A32_MASK_IN_PLACE; - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[*src]; - cc &= c; - if (c != 0) { - dst[x] = SkPixel32ToPixel4444(c); - } - src += deltaSrc; - } - return cc != A32_MASK_IN_PLACE; -} - -static bool Sample_Index_D4444_D_SkipZ(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, int width, - int deltaSrc, int y, const SkPMColor ctable[]) { - - SkPMColor16* SK_RESTRICT dst = (SkPMColor16*)dstRow; - SkPMColor cc = A32_MASK_IN_PLACE; - DITHER_4444_SCAN(y); - - for (int x = 0; x < width; x++) { - SkPMColor c = ctable[*src]; - cc &= c; - if (c != 0) { - dst[x] = SkDitherARGB32To4444(c, DITHER_VALUE(x)); - } - src += deltaSrc; - } - return cc != A32_MASK_IN_PLACE; -} - -static SkScaledBitmapSampler::RowProc -get_index_to_4444_proc(const SkScaledBitmapSampler::Options& opts) { - // Unpremul not allowed - if (!opts.fPremultiplyAlpha) { - return nullptr; - } - if (opts.fSkipZeros) { - if (opts.fDither) { - return Sample_Index_D4444_D_SkipZ; - } - return Sample_Index_D4444_SkipZ; - } - if (opts.fDither) { - return Sample_Index_D4444_D; - } - return Sample_Index_D4444; -} - -static bool Sample_Index_DI(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, const SkPMColor[]) { - if (1 == deltaSrc) { - memcpy(dstRow, src, width); - } else { - uint8_t* SK_RESTRICT dst = (uint8_t*)dstRow; - for (int x = 0; x < width; x++) { - dst[x] = src[0]; - src += deltaSrc; - } - } - return false; -} - -static SkScaledBitmapSampler::RowProc -get_index_to_index_proc(const SkScaledBitmapSampler::Options& opts) { - // Unpremul not allowed - if (!opts.fPremultiplyAlpha) { - return nullptr; - } - // Ignore dither and skip zeroes - return Sample_Index_DI; -} - -// A8 -static bool Sample_Gray_DA8(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int, - const SkPMColor[]) { - // Sampling Gray to A8 uses the same function as Index to Index8, - // except we assume that there is alpha for speed, since an A8 - // bitmap with no alpha is not interesting. - (void) Sample_Index_DI(dstRow, src, width, deltaSrc, /* y unused */ 0, - /* ctable unused */ nullptr); - return true; -} - -static SkScaledBitmapSampler::RowProc -get_gray_to_A8_proc(const SkScaledBitmapSampler::Options& opts) { - if (!opts.fPremultiplyAlpha) { - return nullptr; - } - // Ignore skip and dither. - return Sample_Gray_DA8; -} - -typedef SkScaledBitmapSampler::RowProc (*RowProcChooser)(const SkScaledBitmapSampler::Options&); -/////////////////////////////////////////////////////////////////////////////// - -#include "SkScaledBitmapSampler.h" - -SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height, - int sampleSize) { - fCTable = nullptr; - fDstRow = nullptr; - fRowProc = nullptr; - - if (width <= 0 || height <= 0) { - sk_throw(); - } - - SkDEBUGCODE(fSampleMode = kUninitialized_SampleMode); - - if (sampleSize <= 1) { - fScaledWidth = width; - fScaledHeight = height; - fX0 = fY0 = 0; - fDX = fDY = 1; - return; - } - - int dx = SkMin32(sampleSize, width); - int dy = SkMin32(sampleSize, height); - - fScaledWidth = width / dx; - fScaledHeight = height / dy; - - SkASSERT(fScaledWidth > 0); - SkASSERT(fScaledHeight > 0); - - fX0 = dx >> 1; - fY0 = dy >> 1; - - SkASSERT(fX0 >= 0 && fX0 < width); - SkASSERT(fY0 >= 0 && fY0 < height); - - fDX = dx; - fDY = dy; - - SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width); - SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height); -} - -bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, - const Options& opts, - const SkPMColor ctable[]) { - static const RowProcChooser gProcChoosers[] = { - get_gray_to_8888_proc, - get_RGBx_to_8888_proc, - get_RGBA_to_8888_proc, - get_index_to_8888_proc, - nullptr, // 565 to 8888 - - get_gray_to_565_proc, - get_RGBx_to_565_proc, - get_RGBx_to_565_proc, // The source alpha will be ignored. - get_index_to_565_proc, - get_565_to_565_proc, - - get_gray_to_4444_proc, - get_RGBx_to_4444_proc, - get_RGBA_to_4444_proc, - get_index_to_4444_proc, - nullptr, // 565 to 4444 - - nullptr, // gray to index - nullptr, // rgbx to index - nullptr, // rgba to index - get_index_to_index_proc, - nullptr, // 565 to index - - get_gray_to_A8_proc, - nullptr, // rgbx to a8 - nullptr, // rgba to a8 - nullptr, // index to a8 - nullptr, // 565 to a8 - }; - - // The jump between dst configs in the table - static const int gProcDstConfigSpan = 5; - static_assert(SK_ARRAY_COUNT(gProcChoosers) == 5 * gProcDstConfigSpan, - "gProcs_has_the_wrong_number_of_entries"); - - fCTable = ctable; - - int index = 0; - switch (sc) { - case SkScaledBitmapSampler::kGray: - fSrcPixelSize = 1; - index += 0; - break; - case SkScaledBitmapSampler::kRGB: - fSrcPixelSize = 3; - index += 1; - break; - case SkScaledBitmapSampler::kRGBX: - fSrcPixelSize = 4; - index += 1; - break; - case SkScaledBitmapSampler::kRGBA: - fSrcPixelSize = 4; - index += 2; - break; - case SkScaledBitmapSampler::kIndex: - fSrcPixelSize = 1; - index += 3; - break; - case SkScaledBitmapSampler::kRGB_565: - fSrcPixelSize = 2; - index += 4; - break; - default: - return false; - } - - switch (dst->colorType()) { - case kN32_SkColorType: - index += 0 * gProcDstConfigSpan; - break; - case kRGB_565_SkColorType: - index += 1 * gProcDstConfigSpan; - break; - case kARGB_4444_SkColorType: - index += 2 * gProcDstConfigSpan; - break; - case kIndex_8_SkColorType: - index += 3 * gProcDstConfigSpan; - break; - case kAlpha_8_SkColorType: - index += 4 * gProcDstConfigSpan; - break; - default: - return false; - } - - RowProcChooser chooser = gProcChoosers[index]; - if (nullptr == chooser) { - fRowProc = nullptr; - } else { - fRowProc = chooser(opts); - } - fDstRow = (char*)dst->getPixels(); - fDstRowBytes = dst->rowBytes(); - fCurrY = 0; - return fRowProc != nullptr; -} - -bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, - const SkImageDecoder& decoder, - const SkPMColor ctable[]) { - return this->begin(dst, sc, Options(decoder), ctable); -} - -bool SkScaledBitmapSampler::next(const uint8_t* SK_RESTRICT src) { - SkASSERT(kInterlaced_SampleMode != fSampleMode); - SkDEBUGCODE(fSampleMode = kConsecutive_SampleMode); - SkASSERT((unsigned)fCurrY < (unsigned)fScaledHeight); - - bool hadAlpha = fRowProc(fDstRow, src + fX0 * fSrcPixelSize, fScaledWidth, - fDX * fSrcPixelSize, fCurrY, fCTable); - fDstRow += fDstRowBytes; - fCurrY += 1; - return hadAlpha; -} - -bool SkScaledBitmapSampler::sampleInterlaced(const uint8_t* SK_RESTRICT src, int srcY) { - SkASSERT(kConsecutive_SampleMode != fSampleMode); - SkDEBUGCODE(fSampleMode = kInterlaced_SampleMode); - // Any line that should be a part of the destination can be created by the formula: - // fY0 + (some multiplier) * fDY - // so if srcY - fY0 is not an integer multiple of fDY that srcY will be skipped. - const int srcYMinusY0 = srcY - fY0; - if (srcYMinusY0 % fDY != 0) { - // This line is not part of the output, so return false for alpha, since we have - // not added an alpha to the output. - return false; - } - // Unlike in next(), where the data is used sequentially, this function skips around, - // so fDstRow and fCurrY are never updated. fDstRow must always be the starting point - // of the destination bitmap's pixels, which is used to calculate the destination row - // each time this function is called. - const int dstY = srcYMinusY0 / fDY; - if (dstY >= fScaledHeight) { - return false; - } - char* dstRow = fDstRow + dstY * fDstRowBytes; - return fRowProc(dstRow, src + fX0 * fSrcPixelSize, fScaledWidth, - fDX * fSrcPixelSize, dstY, fCTable); -} - -#ifdef SK_DEBUG -// The following code is for a test to ensure that changing the method to get the right row proc -// did not change the row proc unintentionally. Tested by ImageDecodingTest.cpp - -// friend of SkScaledBitmapSampler solely for the purpose of accessing fRowProc. -class RowProcTester { -public: - static SkScaledBitmapSampler::RowProc getRowProc(const SkScaledBitmapSampler& sampler) { - return sampler.fRowProc; - } -}; - - -// Table showing the expected RowProc for each combination of inputs. -// Table formated as follows: -// Each group of 5 consecutive rows represents sampling from a single -// SkScaledBitmapSampler::SrcConfig. -// Within each set, each row represents a different destination SkBitmap::Config -// Each column represents a different combination of dither and unpremul. -// D = dither ~D = no dither -// U = unpremul ~U = no unpremul -// ~D~U D~U ~DU DU -SkScaledBitmapSampler::RowProc gTestProcs[] = { - // Gray - Sample_Gray_DA8, Sample_Gray_DA8, nullptr, nullptr, // to A8 - nullptr, nullptr, nullptr, nullptr, // to Index8 - Sample_Gray_D565, Sample_Gray_D565_D, Sample_Gray_D565, Sample_Gray_D565_D, // to 565 - Sample_Gray_D4444, Sample_Gray_D4444_D, Sample_Gray_D4444, Sample_Gray_D4444_D, // to 4444 - Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, Sample_Gray_D8888, // to 8888 - // Index - nullptr, nullptr, nullptr, nullptr, // to A8 - Sample_Index_DI, Sample_Index_DI, nullptr, nullptr, // to Index8 - Sample_Index_D565, Sample_Index_D565_D, Sample_Index_D565, Sample_Index_D565_D, // to 565 - Sample_Index_D4444, Sample_Index_D4444_D, nullptr, nullptr, // to 4444 - Sample_Index_D8888, Sample_Index_D8888, Sample_Index_D8888, Sample_Index_D8888, // to 8888 - // RGB - nullptr, nullptr, nullptr, nullptr, // to A8 - nullptr, nullptr, nullptr, nullptr, // to Index8 - Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565 - Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444 - Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888 - // RGBx is the same as RGB - nullptr, nullptr, nullptr, nullptr, // to A8 - nullptr, nullptr, nullptr, nullptr, // to Index8 - Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565 - Sample_RGBx_D4444, Sample_RGBx_D4444_D, Sample_RGBx_D4444, Sample_RGBx_D4444_D, // to 4444 - Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, Sample_RGBx_D8888, // to 8888 - // RGBA - nullptr, nullptr, nullptr, nullptr, // to A8 - nullptr, nullptr, nullptr, nullptr, // to Index8 - Sample_RGBx_D565, Sample_RGBx_D565_D, Sample_RGBx_D565, Sample_RGBx_D565_D, // to 565 - Sample_RGBA_D4444, Sample_RGBA_D4444_D, nullptr, nullptr, // to 4444 - Sample_RGBA_D8888, Sample_RGBA_D8888, Sample_RGBA_D8888_Unpremul, Sample_RGBA_D8888_Unpremul, // to 8888 - // RGB_565 - nullptr, nullptr, nullptr, nullptr, // to A8 - nullptr, nullptr, nullptr, nullptr, // to Index8 - Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, Sample_D565_D565, // to 565 - nullptr, nullptr, nullptr, nullptr, // to 4444 - nullptr, nullptr, nullptr, nullptr, // to 8888 -}; - -// Dummy class that allows instantiation of an ImageDecoder, so begin can query its fields. -class DummyDecoder : public SkImageDecoder { -public: - DummyDecoder() {} -protected: - Result onDecode(SkStream*, SkBitmap*, SkImageDecoder::Mode) override { - return kFailure; - } -}; - -void test_row_proc_choice(); -void test_row_proc_choice() { - const SkColorType colorTypes[] = { - kAlpha_8_SkColorType, kIndex_8_SkColorType, kRGB_565_SkColorType, kARGB_4444_SkColorType, - kN32_SkColorType - }; - - SkBitmap dummyBitmap; - DummyDecoder dummyDecoder; - size_t procCounter = 0; - for (int sc = SkScaledBitmapSampler::kGray; sc <= SkScaledBitmapSampler::kRGB_565; ++sc) { - for (size_t c = 0; c < SK_ARRAY_COUNT(colorTypes); ++c) { - for (int unpremul = 0; unpremul <= 1; ++unpremul) { - for (int dither = 0; dither <= 1; ++dither) { - // Arbitrary width/height/sampleSize to allow SkScaledBitmapSampler to - // be considered valid. - SkScaledBitmapSampler sampler(10, 10, 1); - dummyBitmap.setInfo(SkImageInfo::Make(10, 10, - colorTypes[c], kPremul_SkAlphaType)); - dummyDecoder.setDitherImage(SkToBool(dither)); - dummyDecoder.setRequireUnpremultipliedColors(SkToBool(unpremul)); - sampler.begin(&dummyBitmap, (SkScaledBitmapSampler::SrcConfig) sc, - dummyDecoder); - SkScaledBitmapSampler::RowProc expected = gTestProcs[procCounter]; - SkScaledBitmapSampler::RowProc actual = RowProcTester::getRowProc(sampler); - SkASSERT(expected == actual); - procCounter++; - } - } - } - } - SkASSERT(SK_ARRAY_COUNT(gTestProcs) == procCounter); -} -#endif // SK_DEBUG diff --git a/src/images/SkScaledBitmapSampler.h b/src/images/SkScaledBitmapSampler.h deleted file mode 100644 index 198dc07572..0000000000 --- a/src/images/SkScaledBitmapSampler.h +++ /dev/null @@ -1,107 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkScaledBitmapSampler_DEFINED -#define SkScaledBitmapSampler_DEFINED - -#include "SkTypes.h" -#include "SkColor.h" -#include "SkImageDecoder.h" - -class SkBitmap; - -class SkScaledBitmapSampler { -public: - SkScaledBitmapSampler(int origWidth, int origHeight, int cellSize); - - int scaledWidth() const { return fScaledWidth; } - int scaledHeight() const { return fScaledHeight; } - - int srcY0() const { return fY0; } - int srcDX() const { return fDX; } - int srcDY() const { return fDY; } - - enum SrcConfig { - kGray, // 1 byte per pixel - kIndex, // 1 byte per pixel - kRGB, // 3 bytes per pixel - kRGBX, // 4 byes per pixel (ignore 4th) - kRGBA, // 4 bytes per pixel - kRGB_565 // 2 bytes per pixel - }; - - struct Options { - bool fDither; - bool fPremultiplyAlpha; - bool fSkipZeros; - explicit Options(const SkImageDecoder &dec) - : fDither(dec.getDitherImage()) - , fPremultiplyAlpha(!dec.getRequireUnpremultipliedColors()) - , fSkipZeros(dec.getSkipWritingZeroes()) - { } - }; - - // Given a dst bitmap (with pixels already allocated) and a src-config, - // prepares iterator to process the src colors and write them into dst. - // Returns false if the request cannot be fulfulled. - bool begin(SkBitmap* dst, SrcConfig sc, const SkImageDecoder& decoder, - const SkPMColor* = nullptr); - bool begin(SkBitmap* dst, SrcConfig sc, const Options& opts, - const SkPMColor* = nullptr); - // call with row of src pixels, for y = 0...scaledHeight-1. - // returns true if the row had non-opaque alpha in it - bool next(const uint8_t* SK_RESTRICT src); - - // Like next(), but specifies the y value of the source row, so the - // rows can come in any order. If the row is not part of the output - // sample, it will be skipped. Only sampleInterlaced OR next should - // be called for one SkScaledBitmapSampler. - bool sampleInterlaced(const uint8_t* SK_RESTRICT src, int srcY); - - typedef bool (*RowProc)(void* SK_RESTRICT dstRow, - const uint8_t* SK_RESTRICT src, - int width, int deltaSrc, int y, - const SkPMColor[]); - -private: - int fScaledWidth; - int fScaledHeight; - - int fX0; // first X coord to sample - int fY0; // first Y coord (scanline) to sample - int fDX; // step between X samples - int fDY; // step between Y samples - -#ifdef SK_DEBUG - // Keep track of whether the caller is using next or sampleInterlaced. - // Only one can be used per sampler. - enum SampleMode { - kUninitialized_SampleMode, - kConsecutive_SampleMode, - kInterlaced_SampleMode, - }; - - SampleMode fSampleMode; -#endif - - // setup state - char* fDstRow; // points into bitmap's pixels - size_t fDstRowBytes; - int fCurrY; // used for dithering - int fSrcPixelSize; // 1, 3, 4 - RowProc fRowProc; - - // optional reference to the src colors if the src is a palette model - const SkPMColor* fCTable; - -#ifdef SK_DEBUG - // Helper class allowing a test to have access to fRowProc. - friend class RowProcTester; -#endif -}; - -#endif diff --git a/src/images/bmpdecoderhelper.cpp b/src/images/bmpdecoderhelper.cpp deleted file mode 100644 index 9171b5d527..0000000000 --- a/src/images/bmpdecoderhelper.cpp +++ /dev/null @@ -1,369 +0,0 @@ - -/* - * Copyright 2007 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -// Author: cevans@google.com (Chris Evans) - -#include "bmpdecoderhelper.h" - -namespace image_codec { - -static const int kBmpHeaderSize = 14; -static const int kBmpInfoSize = 40; -static const int kBmpOS2InfoSize = 12; -static const int kMaxDim = SHRT_MAX / 2; - -bool BmpDecoderHelper::DecodeImage(const char* p, - size_t len, - int max_pixels, - BmpDecoderCallback* callback) { - data_ = reinterpret_cast<const uint8*>(p); - pos_ = 0; - len_ = len; - inverted_ = true; - // Parse the header structure. - if (len < kBmpHeaderSize + 4) { - return false; - } - GetShort(); // Signature. - GetInt(); // Size. - GetInt(); // Reserved. - int offset = GetInt(); - // Parse the info structure. - int infoSize = GetInt(); - if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) { - return false; - } - int cols = 0; - int comp = 0; - int colLen = 4; - if (infoSize >= kBmpInfoSize) { - if (len < kBmpHeaderSize + kBmpInfoSize) { - return false; - } - width_ = GetInt(); - height_ = GetInt(); - GetShort(); // Planes. - bpp_ = GetShort(); - comp = GetInt(); - GetInt(); // Size. - GetInt(); // XPPM. - GetInt(); // YPPM. - cols = GetInt(); - GetInt(); // Important colours. - } else { - if (len < kBmpHeaderSize + kBmpOS2InfoSize) { - return false; - } - colLen = 3; - width_ = GetShort(); - height_ = GetShort(); - GetShort(); // Planes. - bpp_ = GetShort(); - } - if (height_ < 0) { - height_ = -height_; - inverted_ = false; - } - if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) { - return false; - } - if (width_ * height_ > max_pixels) { - return false; - } - if (cols < 0 || cols > 256) { - return false; - } - // Allocate then read in the colour map. - if (cols == 0 && bpp_ <= 8) { - cols = 1 << bpp_; - } - if (bpp_ <= 8 || cols > 0) { - uint8* colBuf = new uint8[256 * 3]; - memset(colBuf, '\0', 256 * 3); - colTab_.reset(colBuf); - } - if (cols > 0) { - if (pos_ + (cols * colLen) > len_) { - return false; - } - for (int i = 0; i < cols; ++i) { - int base = i * 3; - colTab_[base + 2] = GetByte(); - colTab_[base + 1] = GetByte(); - colTab_[base] = GetByte(); - if (colLen == 4) { - GetByte(); - } - } - } - // Read in the compression data if necessary. - redBits_ = 0x7c00; - greenBits_ = 0x03e0; - blueBits_ = 0x001f; - bool rle = false; - if (comp == 1 || comp == 2) { - rle = true; - } else if (comp == 3) { - if (pos_ + 12 > len_) { - return false; - } - redBits_ = GetInt() & 0xffff; - greenBits_ = GetInt() & 0xffff; - blueBits_ = GetInt() & 0xffff; - } - redShiftRight_ = CalcShiftRight(redBits_); - greenShiftRight_ = CalcShiftRight(greenBits_); - blueShiftRight_ = CalcShiftRight(blueBits_); - redShiftLeft_ = CalcShiftLeft(redBits_); - greenShiftLeft_ = CalcShiftLeft(greenBits_); - blueShiftLeft_ = CalcShiftLeft(blueBits_); - rowPad_ = 0; - pixelPad_ = 0; - int rowLen; - if (bpp_ == 32) { - rowLen = width_ * 4; - pixelPad_ = 1; - } else if (bpp_ == 24) { - rowLen = width_ * 3; - } else if (bpp_ == 16) { - rowLen = width_ * 2; - } else if (bpp_ == 8) { - rowLen = width_; - } else if (bpp_ == 4) { - rowLen = width_ / 2; - if (width_ & 1) { - rowLen++; - } - } else if (bpp_ == 1) { - rowLen = width_ / 8; - if (width_ & 7) { - rowLen++; - } - } else { - return false; - } - // Round the rowLen up to a multiple of 4. - if (rowLen % 4 != 0) { - rowPad_ = 4 - (rowLen % 4); - rowLen += rowPad_; - } - - if (offset > 0 && (size_t)offset > pos_ && (size_t)offset < len_) { - pos_ = offset; - } - // Deliberately off-by-one; a load of BMPs seem to have their last byte - // missing. - if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) { - return false; - } - - output_ = callback->SetSize(width_, height_); - if (nullptr == output_) { - return true; // meaning we succeeded, but they want us to stop now - } - - if (rle && (bpp_ == 4 || bpp_ == 8)) { - DoRLEDecode(); - } else { - DoStandardDecode(); - } - return true; -} - -void BmpDecoderHelper::DoRLEDecode() { - static const uint8 RLE_ESCAPE = 0; - static const uint8 RLE_EOL = 0; - static const uint8 RLE_EOF = 1; - static const uint8 RLE_DELTA = 2; - int x = 0; - int y = height_ - 1; - while (pos_ + 1 < len_) { - uint8 cmd = GetByte(); - if (cmd != RLE_ESCAPE) { - uint8 pixels = GetByte(); - int num = 0; - uint8 col = pixels; - while (cmd-- && x < width_) { - if (bpp_ == 4) { - if (num & 1) { - col = pixels & 0xf; - } else { - col = pixels >> 4; - } - } - PutPixel(x++, y, col); - num++; - } - } else { - cmd = GetByte(); - if (cmd == RLE_EOF) { - return; - } else if (cmd == RLE_EOL) { - x = 0; - y--; - if (y < 0) { - return; - } - } else if (cmd == RLE_DELTA) { - if (pos_ + 1 < len_) { - uint8 dx = GetByte(); - uint8 dy = GetByte(); - x += dx; - if (x > width_) { - x = width_; - } - y -= dy; - if (y < 0) { - return; - } - } - } else { - int num = 0; - int bytesRead = 0; - uint8 val = 0; - while (cmd-- && pos_ < len_) { - if (bpp_ == 8 || !(num & 1)) { - val = GetByte(); - bytesRead++; - } - uint8 col = val; - if (bpp_ == 4) { - if (num & 1) { - col = col & 0xf; - } else { - col >>= 4; - } - } - if (x < width_) { - PutPixel(x++, y, col); - } - num++; - } - // All pixel runs must be an even number of bytes - skip a byte if we - // read an odd number. - if ((bytesRead & 1) && pos_ < len_) { - GetByte(); - } - } - } - } -} - -void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) { - CHECK(x >= 0 && x < width_); - CHECK(y >= 0 && y < height_); - if (!inverted_) { - y = height_ - (y + 1); - } - - int base = ((y * width_) + x) * 3; - int colBase = col * 3; - output_[base] = colTab_[colBase]; - output_[base + 1] = colTab_[colBase + 1]; - output_[base + 2] = colTab_[colBase + 2]; -} - -void BmpDecoderHelper::DoStandardDecode() { - int row = 0; - uint8 currVal = 0; - for (int h = height_ - 1; h >= 0; h--, row++) { - int realH = h; - if (!inverted_) { - realH = height_ - (h + 1); - } - uint8* line = output_ + (3 * width_ * realH); - for (int w = 0; w < width_; w++) { - if (bpp_ >= 24) { - line[2] = GetByte(); - line[1] = GetByte(); - line[0] = GetByte(); - } else if (bpp_ == 16) { - uint32 val = GetShort(); - line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_; - line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_; - line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_; - } else if (bpp_ <= 8) { - uint8 col; - if (bpp_ == 8) { - col = GetByte(); - } else if (bpp_ == 4) { - if ((w % 2) == 0) { - currVal = GetByte(); - col = currVal >> 4; - } else { - col = currVal & 0xf; - } - } else { - if ((w % 8) == 0) { - currVal = GetByte(); - } - int bit = w & 7; - col = ((currVal >> (7 - bit)) & 1); - } - int base = col * 3; - line[0] = colTab_[base]; - line[1] = colTab_[base + 1]; - line[2] = colTab_[base + 2]; - } - line += 3; - for (int i = 0; i < pixelPad_; ++i) { - GetByte(); - } - } - for (int i = 0; i < rowPad_; ++i) { - GetByte(); - } - } -} - -int BmpDecoderHelper::GetInt() { - uint8 b1 = GetByte(); - uint8 b2 = GetByte(); - uint8 b3 = GetByte(); - uint8 b4 = GetByte(); - return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); -} - -int BmpDecoderHelper::GetShort() { - uint8 b1 = GetByte(); - uint8 b2 = GetByte(); - return b1 | (b2 << 8); -} - -uint8 BmpDecoderHelper::GetByte() { - CHECK(pos_ <= len_); - // We deliberately allow this off-by-one access to cater for BMPs with their - // last byte missing. - if (pos_ == len_) { - return 0; - } - return data_[pos_++]; -} - -int BmpDecoderHelper::CalcShiftRight(uint32 mask) { - int ret = 0; - while (mask != 0 && !(mask & 1)) { - mask >>= 1; - ret++; - } - return ret; -} - -int BmpDecoderHelper::CalcShiftLeft(uint32 mask) { - int ret = 0; - while (mask != 0 && !(mask & 1)) { - mask >>= 1; - } - while (mask != 0 && !(mask & 0x80)) { - mask <<= 1; - ret++; - } - return ret; -} - -} // namespace image_codec diff --git a/src/images/bmpdecoderhelper.h b/src/images/bmpdecoderhelper.h deleted file mode 100644 index b448734bbc..0000000000 --- a/src/images/bmpdecoderhelper.h +++ /dev/null @@ -1,116 +0,0 @@ - -/* - * Copyright 2007 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef IMAGE_CODEC_BMPDECODERHELPER_H__ -#define IMAGE_CODEC_BMPDECODERHELPER_H__ - -/////////////////////////////////////////////////////////////////////////////// -// this section is my current "glue" between google3 code and android. -// will be fixed soon - -#include "SkTypes.h" -#include <limits.h> -#define DISALLOW_EVIL_CONSTRUCTORS(name) -#define CHECK(predicate) SkASSERT(predicate) -typedef uint8_t uint8; -typedef uint32_t uint32; - -template <typename T> class scoped_array { -private: - T* ptr_; - scoped_array(scoped_array const&); - scoped_array& operator=(const scoped_array&); - -public: - explicit scoped_array(T* p = 0) : ptr_(p) {} - ~scoped_array() { - delete[] ptr_; - } - - void reset(T* p = 0) { - if (p != ptr_) { - delete[] ptr_; - ptr_ = p; - } - } - - T& operator[](int i) const { - return ptr_[i]; - } -}; - -/////////////////////////////////////////////////////////////////////////////// - -namespace image_codec { - -class BmpDecoderCallback { - public: - BmpDecoderCallback() { } - virtual ~BmpDecoderCallback() {} - - /** - * This is called once for an image. It is passed the width and height and - * should return the address of a buffer that is large enough to store - * all of the resulting pixels (widht * height * 3 bytes). If it returns nullptr, - * then the decoder will abort, but return true, as the caller has received - * valid dimensions. - */ - virtual uint8* SetSize(int width, int height) = 0; - - private: - DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderCallback); -}; - -class BmpDecoderHelper { - public: - BmpDecoderHelper() { } - ~BmpDecoderHelper() { } - bool DecodeImage(const char* data, - size_t len, - int max_pixels, - BmpDecoderCallback* callback); - - private: - DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderHelper); - - void DoRLEDecode(); - void DoStandardDecode(); - void PutPixel(int x, int y, uint8 col); - - int GetInt(); - int GetShort(); - uint8 GetByte(); - int CalcShiftRight(uint32 mask); - int CalcShiftLeft(uint32 mask); - - const uint8* data_; - size_t pos_; - size_t len_; - int width_; - int height_; - int bpp_; - int pixelPad_; - int rowPad_; - scoped_array<uint8> colTab_; - uint32 redBits_; - uint32 greenBits_; - uint32 blueBits_; - int redShiftRight_; - int greenShiftRight_; - int blueShiftRight_; - int redShiftLeft_; - int greenShiftLeft_; - int blueShiftLeft_; - uint8* output_; - bool inverted_; -}; - -} // namespace - -#endif diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp index c4446ae16d..ead0ed6506 100644 --- a/src/ports/SkImageDecoder_CG.cpp +++ b/src/ports/SkImageDecoder_CG.cpp @@ -11,7 +11,6 @@ #include "SkCGUtils.h" #include "SkColorPriv.h" #include "SkData.h" -#include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkMovie.h" #include "SkStream.h" @@ -29,210 +28,6 @@ #include <MobileCoreServices/MobileCoreServices.h> #endif -static void data_unref_proc(void* skdata, const void*, size_t) { - SkASSERT(skdata); - static_cast<SkData*>(skdata)->unref(); -} - -static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) { - // TODO: use callbacks, so we don't have to load all the data into RAM - SkData* skdata = SkCopyStreamToData(stream).release(); - if (!skdata) { - return nullptr; - } - - return CGDataProviderCreateWithData(skdata, skdata->data(), skdata->size(), data_unref_proc); -} - -static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) { - CGDataProviderRef data = SkStreamToDataProvider(stream); - if (!data) { - return nullptr; - } - CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0); - CGDataProviderRelease(data); - return imageSrc; -} - -class SkImageDecoder_CG : public SkImageDecoder { -protected: - virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode); -}; - -static void argb_4444_force_opaque(void* row, int count) { - uint16_t* row16 = (uint16_t*)row; - for (int i = 0; i < count; ++i) { - row16[i] |= 0xF000; - } -} - -static void argb_8888_force_opaque(void* row, int count) { - // can use RGBA or BGRA, they have the same shift for alpha - const uint32_t alphaMask = 0xFF << SK_RGBA_A32_SHIFT; - uint32_t* row32 = (uint32_t*)row; - for (int i = 0; i < count; ++i) { - row32[i] |= alphaMask; - } -} - -static void alpha_8_force_opaque(void* row, int count) { - memset(row, 0xFF, count); -} - -static void force_opaque(SkBitmap* bm) { - SkAutoLockPixels alp(*bm); - if (!bm->getPixels()) { - return; - } - - void (*proc)(void*, int); - switch (bm->colorType()) { - case kARGB_4444_SkColorType: - proc = argb_4444_force_opaque; - break; - case kRGBA_8888_SkColorType: - case kBGRA_8888_SkColorType: - proc = argb_8888_force_opaque; - break; - case kAlpha_8_SkColorType: - proc = alpha_8_force_opaque; - break; - default: - return; - } - - char* row = (char*)bm->getPixels(); - for (int y = 0; y < bm->height(); ++y) { - proc(row, bm->width()); - row += bm->rowBytes(); - } - bm->setAlphaType(kOpaque_SkAlphaType); -} - -#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) - -class AutoCFDataRelease { - CFDataRef fDR; -public: - AutoCFDataRelease(CFDataRef dr) : fDR(dr) {} - ~AutoCFDataRelease() { if (fDR) { CFRelease(fDR); } } - - operator CFDataRef () { return fDR; } -}; - -static bool colorspace_is_sRGB(CGColorSpaceRef cs) { -#ifdef SK_BUILD_FOR_IOS - return true; // iOS seems to define itself to always return sRGB <reed> -#else - AutoCFDataRelease data(CGColorSpaceCopyICCProfile(cs)); - if (data) { - // found by inspection -- need a cleaner way to sniff a profile - const CFIndex ICC_PROFILE_OFFSET_TO_SRGB_TAG = 52; - - if (CFDataGetLength(data) >= ICC_PROFILE_OFFSET_TO_SRGB_TAG + 4) { - return !memcmp(CFDataGetBytePtr(data) + ICC_PROFILE_OFFSET_TO_SRGB_TAG, "sRGB", 4); - } - } - return false; -#endif -} - -SkImageDecoder::Result SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { - CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); - - if (nullptr == imageSrc) { - return kFailure; - } - SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); - - CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, nullptr); - if (nullptr == image) { - return kFailure; - } - SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image); - - const int width = SkToInt(CGImageGetWidth(image)); - const int height = SkToInt(CGImageGetHeight(image)); - SkColorProfileType cpType = kLinear_SkColorProfileType; - - CGColorSpaceRef cs = CGImageGetColorSpace(image); - if (cs) { - CGColorSpaceModel m = CGColorSpaceGetModel(cs); - if (kCGColorSpaceModelRGB == m && colorspace_is_sRGB(cs)) { - cpType = kSRGB_SkColorProfileType; - } - } - - SkAlphaType at = kPremul_SkAlphaType; - switch (CGImageGetAlphaInfo(image)) { - case kCGImageAlphaNone: - case kCGImageAlphaNoneSkipLast: - case kCGImageAlphaNoneSkipFirst: - at = kOpaque_SkAlphaType; - break; - default: - break; - } - - bm->setInfo(SkImageInfo::Make(width, height, kN32_SkColorType, at, cpType)); - if (SkImageDecoder::kDecodeBounds_Mode == mode) { - return kSuccess; - } - - if (!this->allocPixelRef(bm, nullptr)) { - return kFailure; - } - - SkAutoLockPixels alp(*bm); - - if (!SkCopyPixelsFromCGImage(bm->info(), bm->rowBytes(), bm->getPixels(), image)) { - return kFailure; - } - - CGImageAlphaInfo info = CGImageGetAlphaInfo(image); - switch (info) { - case kCGImageAlphaNone: - case kCGImageAlphaNoneSkipLast: - case kCGImageAlphaNoneSkipFirst: - // We're opaque, but we can't rely on the data always having 0xFF - // in the alpha slot (which Skia wants), so we have to ram it in - // ourselves. - force_opaque(bm); - break; - default: - // we don't know if we're opaque or not, so compute it. - if (SkBitmap::ComputeIsOpaque(*bm)) { - bm->setAlphaType(kOpaque_SkAlphaType); - } - } - if (!bm->isOpaque() && this->getRequireUnpremultipliedColors()) { - // CGBitmapContext does not support unpremultiplied, so the image has been premultiplied. - // Convert to unpremultiplied. - for (int i = 0; i < width; ++i) { - for (int j = 0; j < height; ++j) { - uint32_t* addr = bm->getAddr32(i, j); - *addr = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(*addr); - } - } - bm->setAlphaType(kUnpremul_SkAlphaType); - } - return kSuccess; -} - -/////////////////////////////////////////////////////////////////////////////// - -extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); - -SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { - SkImageDecoder* decoder = image_decoder_from_stream(stream); - if (nullptr == decoder) { - // If no image decoder specific to the stream exists, use SkImageDecoder_CG. - return new SkImageDecoder_CG; - } else { - return decoder; - } -} - ///////////////////////////////////////////////////////////////////////// SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { @@ -355,57 +150,13 @@ static SkImageEncoder* sk_imageencoder_cg_factory(SkImageEncoder::Type t) { static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_cg_factory); -#ifdef SK_BUILD_FOR_IOS -class SkPNGImageEncoder_IOS : public SkImageEncoder_CG { +class SkPNGImageEncoder_CG : public SkImageEncoder_CG { public: - SkPNGImageEncoder_IOS() + SkPNGImageEncoder_CG() : SkImageEncoder_CG(kPNG_Type) { } }; -DEFINE_ENCODER_CREATOR(PNGImageEncoder_IOS); -#endif - -struct FormatConversion { - CFStringRef fUTType; - SkImageDecoder::Format fFormat; -}; - -// Array of the types supported by the decoder. -static const FormatConversion gFormatConversions[] = { - { kUTTypeBMP, SkImageDecoder::kBMP_Format }, - { kUTTypeGIF, SkImageDecoder::kGIF_Format }, - { kUTTypeICO, SkImageDecoder::kICO_Format }, - { kUTTypeJPEG, SkImageDecoder::kJPEG_Format }, - // Also include JPEG2000 - { kUTTypeJPEG2000, SkImageDecoder::kJPEG_Format }, - { kUTTypePNG, SkImageDecoder::kPNG_Format }, -}; - -static SkImageDecoder::Format UTType_to_Format(const CFStringRef uttype) { - for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) { - if (CFStringCompare(uttype, gFormatConversions[i].fUTType, 0) == kCFCompareEqualTo) { - return gFormatConversions[i].fFormat; - } - } - return SkImageDecoder::kUnknown_Format; -} - -static SkImageDecoder::Format get_format_cg(SkStreamRewindable* stream) { - CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); - - if (nullptr == imageSrc) { - return SkImageDecoder::kUnknown_Format; - } - - SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); - const CFStringRef name = CGImageSourceGetType(imageSrc); - if (nullptr == name) { - return SkImageDecoder::kUnknown_Format; - } - return UTType_to_Format(name); -} - -static SkImageDecoder_FormatReg gFormatReg(get_format_cg); +DEFINE_ENCODER_CREATOR(PNGImageEncoder_CG); #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp index 5febd856d6..43068fc8f6 100644 --- a/src/ports/SkImageDecoder_WIC.cpp +++ b/src/ports/SkImageDecoder_WIC.cpp @@ -31,7 +31,6 @@ #include <wincodec.h> #include "SkAutoCoInitialize.h" -#include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkIStream.h" #include "SkMovie.h" @@ -48,222 +47,6 @@ #undef CLSID_WICImagingFactory #endif -class SkImageDecoder_WIC : public SkImageDecoder { -public: - // Decoding modes corresponding to SkImageDecoder::Mode, plus an extra mode for decoding - // only the format. - enum WICModes { - kDecodeFormat_WICMode, - kDecodeBounds_WICMode, - kDecodePixels_WICMode, - }; - - /** - * Helper function to decode an SkStream. - * @param stream SkStream to decode. Must be at the beginning. - * @param bm SkBitmap to decode into. Only used if wicMode is kDecodeBounds_WICMode or - * kDecodePixels_WICMode, in which case it must not be nullptr. - * @param format Out parameter for the SkImageDecoder::Format of the SkStream. Only used if - * wicMode is kDecodeFormat_WICMode. - */ - bool decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode, Format* format) const; - -protected: - Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) override; -}; - -struct FormatConversion { - GUID fGuidFormat; - SkImageDecoder::Format fFormat; -}; - -static const FormatConversion gFormatConversions[] = { - { GUID_ContainerFormatBmp, SkImageDecoder::kBMP_Format }, - { GUID_ContainerFormatGif, SkImageDecoder::kGIF_Format }, - { GUID_ContainerFormatIco, SkImageDecoder::kICO_Format }, - { GUID_ContainerFormatJpeg, SkImageDecoder::kJPEG_Format }, - { GUID_ContainerFormatPng, SkImageDecoder::kPNG_Format }, -}; - -static SkImageDecoder::Format GuidContainerFormat_to_Format(REFGUID guid) { - for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) { - if (IsEqualGUID(guid, gFormatConversions[i].fGuidFormat)) { - return gFormatConversions[i].fFormat; - } - } - return SkImageDecoder::kUnknown_Format; -} - -SkImageDecoder::Result SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { - WICModes wicMode; - switch (mode) { - case SkImageDecoder::kDecodeBounds_Mode: - wicMode = kDecodeBounds_WICMode; - break; - case SkImageDecoder::kDecodePixels_Mode: - wicMode = kDecodePixels_WICMode; - break; - } - return this->decodeStream(stream, bm, wicMode, nullptr) ? kSuccess : kFailure; -} - -bool SkImageDecoder_WIC::decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode, - Format* format) const { - //Initialize COM. - SkAutoCoInitialize scopedCo; - if (!scopedCo.succeeded()) { - return false; - } - - HRESULT hr = S_OK; - - //Create Windows Imaging Component ImagingFactory. - SkTScopedComPtr<IWICImagingFactory> piImagingFactory; - if (SUCCEEDED(hr)) { - hr = CoCreateInstance( - CLSID_WICImagingFactory - , nullptr - , CLSCTX_INPROC_SERVER - , IID_PPV_ARGS(&piImagingFactory) - ); - } - - //Convert SkStream to IStream. - SkTScopedComPtr<IStream> piStream; - if (SUCCEEDED(hr)) { - hr = SkIStream::CreateFromSkStream(stream, false, &piStream); - } - - //Make sure we're at the beginning of the stream. - if (SUCCEEDED(hr)) { - LARGE_INTEGER liBeginning = { 0 }; - hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, nullptr); - } - - //Create the decoder from the stream content. - SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder; - if (SUCCEEDED(hr)) { - hr = piImagingFactory->CreateDecoderFromStream( - piStream.get() //Image to be decoded - , nullptr //No particular vendor - , WICDecodeMetadataCacheOnDemand //Cache metadata when needed - , &piBitmapDecoder //Pointer to the decoder - ); - } - - if (kDecodeFormat_WICMode == wicMode) { - SkASSERT(format != nullptr); - //Get the format - if (SUCCEEDED(hr)) { - GUID guidFormat; - hr = piBitmapDecoder->GetContainerFormat(&guidFormat); - if (SUCCEEDED(hr)) { - *format = GuidContainerFormat_to_Format(guidFormat); - return true; - } - } - return false; - } - - //Get the first frame from the decoder. - SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode; - if (SUCCEEDED(hr)) { - hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode); - } - - //Get the BitmapSource interface of the frame. - SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal; - if (SUCCEEDED(hr)) { - hr = piBitmapFrameDecode->QueryInterface( - IID_PPV_ARGS(&piBitmapSourceOriginal) - ); - } - - //Get the size of the bitmap. - UINT width; - UINT height; - if (SUCCEEDED(hr)) { - hr = piBitmapSourceOriginal->GetSize(&width, &height); - } - - //Exit early if we're only looking for the bitmap bounds. - if (SUCCEEDED(hr)) { - bm->setInfo(SkImageInfo::MakeN32Premul(width, height)); - if (kDecodeBounds_WICMode == wicMode) { - return true; - } - if (!this->allocPixelRef(bm, nullptr)) { - return false; - } - } - - //Create a format converter. - SkTScopedComPtr<IWICFormatConverter> piFormatConverter; - if (SUCCEEDED(hr)) { - hr = piImagingFactory->CreateFormatConverter(&piFormatConverter); - } - - GUID destinationPixelFormat; - if (this->getRequireUnpremultipliedColors()) { - destinationPixelFormat = GUID_WICPixelFormat32bppBGRA; - } else { - destinationPixelFormat = GUID_WICPixelFormat32bppPBGRA; - } - - if (SUCCEEDED(hr)) { - hr = piFormatConverter->Initialize( - piBitmapSourceOriginal.get() //Input bitmap to convert - , destinationPixelFormat //Destination pixel format - , WICBitmapDitherTypeNone //Specified dither patterm - , nullptr //Specify a particular palette - , 0.f //Alpha threshold - , WICBitmapPaletteTypeCustom //Palette translation type - ); - } - - //Get the BitmapSource interface of the format converter. - SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted; - if (SUCCEEDED(hr)) { - hr = piFormatConverter->QueryInterface( - IID_PPV_ARGS(&piBitmapSourceConverted) - ); - } - - //Copy the pixels into the bitmap. - if (SUCCEEDED(hr)) { - SkAutoLockPixels alp(*bm); - bm->eraseColor(SK_ColorTRANSPARENT); - const UINT stride = (UINT) bm->rowBytes(); - hr = piBitmapSourceConverted->CopyPixels( - nullptr, //Get all the pixels - stride, - stride * height, - reinterpret_cast<BYTE *>(bm->getPixels()) - ); - - // Note: we don't need to premultiply here since we specified PBGRA - if (SkBitmap::ComputeIsOpaque(*bm)) { - bm->setAlphaType(kOpaque_SkAlphaType); - } - } - - return SUCCEEDED(hr); -} - -///////////////////////////////////////////////////////////////////////// - -extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); - -SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { - SkImageDecoder* decoder = image_decoder_from_stream(stream); - if (nullptr == decoder) { - // If no image decoder specific to the stream exists, use SkImageDecoder_WIC. - return new SkImageDecoder_WIC; - } else { - return decoder; - } -} - ///////////////////////////////////////////////////////////////////////// SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { @@ -275,6 +58,10 @@ SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { class SkImageEncoder_WIC : public SkImageEncoder { public: SkImageEncoder_WIC(Type t) : fType(t) {} + + // DO NOT USE this constructor. This exists only so SkForceLinking can + // link the WIC image encoder. + SkImageEncoder_WIC() {} protected: virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); @@ -454,15 +241,6 @@ static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) { static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_wic_factory); -static SkImageDecoder::Format get_format_wic(SkStreamRewindable* stream) { - SkImageDecoder::Format format; - SkImageDecoder_WIC codec; - if (!codec.decodeStream(stream, nullptr, SkImageDecoder_WIC::kDecodeFormat_WICMode, &format)) { - format = SkImageDecoder::kUnknown_Format; - } - return format; -} - -static SkImageDecoder_FormatReg gFormatReg(get_format_wic); +DEFINE_ENCODER_CREATOR(ImageEncoder_WIC); #endif // defined(SK_BUILD_FOR_WIN32) diff --git a/src/ports/SkImageDecoder_empty.cpp b/src/ports/SkImageDecoder_empty.cpp index f52dada73b..33e07acea6 100644 --- a/src/ports/SkImageDecoder_empty.cpp +++ b/src/ports/SkImageDecoder_empty.cpp @@ -8,74 +8,11 @@ #include "SkBitmap.h" #include "SkImage.h" -#include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkMovie.h" #include "SkPixelSerializer.h" #include "SkStream.h" -class SkColorTable; -class SkPngChunkReader; - -// Empty implementations for SkImageDecoder. - -SkImageDecoder::SkImageDecoder() {} - -SkImageDecoder::~SkImageDecoder() {} - -SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable*) { - return nullptr; -} - -void SkImageDecoder::copyFieldsToOther(SkImageDecoder* ) {} - -bool SkImageDecoder::DecodeFile(const char[], SkBitmap*, SkColorType, Mode, Format*) { - return false; -} - -SkImageDecoder::Result SkImageDecoder::decode(SkStream*, SkBitmap*, SkColorType, Mode) { - return kFailure; -} - -bool SkImageDecoder::DecodeStream(SkStreamRewindable*, SkBitmap*, SkColorType, Mode, Format*) { - return false; -} - -bool SkImageDecoder::DecodeMemory(const void*, size_t, SkBitmap*, SkColorType, Mode, Format*) { - return false; -} - -bool SkImageDecoder::decodeYUV8Planes(SkStream*, SkISize[3], void*[3], - size_t[3], SkYUVColorSpace*) { - return false; -} - -SkImageDecoder::Format SkImageDecoder::getFormat() const { - return kUnknown_Format; -} - -SkImageDecoder::Format SkImageDecoder::GetStreamFormat(SkStreamRewindable*) { - return kUnknown_Format; -} - -const char* SkImageDecoder::GetFormatName(Format) { - return nullptr; -} - -SkPngChunkReader* SkImageDecoder::setPeeker(SkPngChunkReader*) { - return nullptr; -} - -SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator*) { - return nullptr; -} - -void SkImageDecoder::setSampleSize(int) {} - -bool SkImageDecoder::allocPixelRef(SkBitmap*, SkColorTable*) const { - return false; -} - ///////////////////////////////////////////////////////////////////////// // Empty implementation for SkMovie. |