diff options
Diffstat (limited to 'src/ports')
-rw-r--r-- | src/ports/SkImageDecoder_CG.cpp | 255 | ||||
-rw-r--r-- | src/ports/SkImageDecoder_WIC.cpp | 232 | ||||
-rw-r--r-- | src/ports/SkImageDecoder_empty.cpp | 63 |
3 files changed, 542 insertions, 8 deletions
diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp index ead0ed6506..c4446ae16d 100644 --- a/src/ports/SkImageDecoder_CG.cpp +++ b/src/ports/SkImageDecoder_CG.cpp @@ -11,6 +11,7 @@ #include "SkCGUtils.h" #include "SkColorPriv.h" #include "SkData.h" +#include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkMovie.h" #include "SkStream.h" @@ -28,6 +29,210 @@ #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) { @@ -150,13 +355,57 @@ static SkImageEncoder* sk_imageencoder_cg_factory(SkImageEncoder::Type t) { static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_cg_factory); -class SkPNGImageEncoder_CG : public SkImageEncoder_CG { +#ifdef SK_BUILD_FOR_IOS +class SkPNGImageEncoder_IOS : public SkImageEncoder_CG { public: - SkPNGImageEncoder_CG() + SkPNGImageEncoder_IOS() : SkImageEncoder_CG(kPNG_Type) { } }; -DEFINE_ENCODER_CREATOR(PNGImageEncoder_CG); +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); #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp index 43068fc8f6..5febd856d6 100644 --- a/src/ports/SkImageDecoder_WIC.cpp +++ b/src/ports/SkImageDecoder_WIC.cpp @@ -31,6 +31,7 @@ #include <wincodec.h> #include "SkAutoCoInitialize.h" +#include "SkImageDecoder.h" #include "SkImageEncoder.h" #include "SkIStream.h" #include "SkMovie.h" @@ -47,6 +48,222 @@ #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) { @@ -58,10 +275,6 @@ 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); @@ -241,6 +454,15 @@ static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) { static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_wic_factory); -DEFINE_ENCODER_CREATOR(ImageEncoder_WIC); +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); #endif // defined(SK_BUILD_FOR_WIN32) diff --git a/src/ports/SkImageDecoder_empty.cpp b/src/ports/SkImageDecoder_empty.cpp index 33e07acea6..f52dada73b 100644 --- a/src/ports/SkImageDecoder_empty.cpp +++ b/src/ports/SkImageDecoder_empty.cpp @@ -8,11 +8,74 @@ #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. |