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()
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); )
+++ /dev/null
-/*
- * 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
'<(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',
],
'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',
'../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',
'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',
],
}],
[ '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
# 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',
],
}],
],
#include "SkTypes.h"
#include "SkBitmap.h"
#include "SkDeque.h"
+#include "SkImage.h"
#include "SkPaint.h"
#include "SkRefCnt.h"
#include "SkRegion.h"
class SkDraw;
class SkDrawable;
class SkDrawFilter;
-class SkImage;
class SkImageFilter;
class SkMetaData;
class SkPath;
+++ /dev/null
-/*
- * 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
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
#ifndef SkPicture_DEFINED
#define SkPicture_DEFINED
-#include "SkImageDecoder.h"
#include "SkRefCnt.h"
#include "SkTypes.h"
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;
+struct SkRect;
#define SK_SUPPORT_LEGACY_PICTURE_PTR
*
* 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:
#define SkWriteBuffer_DEFINED
#include "SkData.h"
+#include "SkImage.h"
#include "SkPath.h"
#include "SkPicture.h"
#include "SkPixelSerializer.h"
#include "SkAndroidCodec.h"
#include "SkCodec.h"
#include "SkCodecPriv.h"
-#include "SkImageDecoder.h"
SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create(
SkData* data, Strategy strategy) {
#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.
#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
#include "SkChecksum.h"
#include "SkImageFilter.h"
#include "SkMessageBus.h"
+#include "SkPaint.h"
#include "SkPicture.h"
#include "SkTDynamicHash.h"
* 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
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;
}
+++ /dev/null
-/*
- * 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);
-}
* 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) {
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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);
*/
#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"
#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
}
/////////////////////////////////////////////////////////////////////////////////////////
-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);
+++ /dev/null
-
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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);
*/
-#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"
#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"
};
///////////////////////////////////////////////////////////////////////////////
-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);
* 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"
"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) {
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)) {
}
///////////////////////////////////////////////////////////////////////////////
-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);
* 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"
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,
///////////////////////////////////////////////////////////////////////////////
-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);
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-
-/*
- * 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);
#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) {
#ifndef SkJpegUtility_DEFINED
#define SkJpegUtility_DEFINED
-#include "SkImageDecoder.h"
#include "SkStream.h"
extern "C" {
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.
+++ /dev/null
-/*
- * 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
+++ /dev/null
-
-/*
- * 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
+++ /dev/null
-
-/*
- * 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
+++ /dev/null
-
-/*
- * 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
#include "SkCGUtils.h"
#include "SkColorPriv.h"
#include "SkData.h"
-#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkMovie.h"
#include "SkStream.h"
#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) {
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)
#include <wincodec.h>
#include "SkAutoCoInitialize.h"
-#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkIStream.h"
#include "SkMovie.h"
#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) {
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);
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)
#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.