/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/core/SkStream.h" #include "include/ports/SkImageGeneratorWIC.h" #include "include/private/SkTemplates.h" #include "src/utils/win/SkIStream.h" #include "src/utils/win/SkTScopedComPtr.h" #include // All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol. // In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported // but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2. // Undo this #define if it has been done so that we link against the symbols // we intended to link against on all SDKs. #if defined(CLSID_WICImagingFactory) #undef CLSID_WICImagingFactory #endif namespace { class ImageGeneratorWIC : public SkImageGenerator { public: /* * Takes ownership of the imagingFactory * Takes ownership of the imageSource */ ImageGeneratorWIC(const SkImageInfo& info, IWICImagingFactory* imagingFactory, IWICBitmapSource* imageSource, sk_sp); protected: sk_sp onRefEncodedData() override; bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options&) override; private: SkTScopedComPtr fImagingFactory; SkTScopedComPtr fImageSource; sk_sp fData; using INHERITED = SkImageGenerator; }; } // namespace std::unique_ptr SkImageGeneratorWIC::MakeFromEncodedWIC(sk_sp data) { // Create Windows Imaging Component ImagingFactory. SkTScopedComPtr imagingFactory; HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&imagingFactory)); if (FAILED(hr)) { return nullptr; } // Create an IStream. SkTScopedComPtr iStream; // Note that iStream will take ownership of the new memory stream because // we set |deleteOnRelease| to true. hr = SkIStream::CreateFromSkStream(std::make_unique(data), &iStream); if (FAILED(hr)) { return nullptr; } // Create the decoder from the stream. SkTScopedComPtr decoder; hr = imagingFactory->CreateDecoderFromStream(iStream.get(), nullptr, WICDecodeMetadataCacheOnDemand, &decoder); if (FAILED(hr)) { return nullptr; } // Select the first frame from the decoder. SkTScopedComPtr imageFrame; hr = decoder->GetFrame(0, &imageFrame); if (FAILED(hr)) { return nullptr; } // Treat the frame as an image source. SkTScopedComPtr imageSource; hr = imageFrame->QueryInterface(IID_PPV_ARGS(&imageSource)); if (FAILED(hr)) { return nullptr; } // Get the size of the image. UINT width; UINT height; hr = imageSource->GetSize(&width, &height); if (FAILED(hr)) { return nullptr; } // Get the encoded pixel format. WICPixelFormatGUID format; hr = imageSource->GetPixelFormat(&format); if (FAILED(hr)) { return nullptr; } // Recommend kOpaque if the image is opaque and kPremul otherwise. // FIXME: We are stuck recommending kPremul for all indexed formats // (Ex: GUID_WICPixelFormat8bppIndexed) because we don't have // a way to check if the image has alpha. SkAlphaType alphaType = kPremul_SkAlphaType; if (GUID_WICPixelFormat16bppBGR555 == format || GUID_WICPixelFormat16bppBGR565 == format || GUID_WICPixelFormat32bppBGR101010 == format || GUID_WICPixelFormatBlackWhite == format || GUID_WICPixelFormat2bppGray == format || GUID_WICPixelFormat4bppGray == format || GUID_WICPixelFormat8bppGray == format || GUID_WICPixelFormat16bppGray == format || GUID_WICPixelFormat16bppGrayFixedPoint == format || GUID_WICPixelFormat16bppGrayHalf == format || GUID_WICPixelFormat32bppGrayFloat == format || GUID_WICPixelFormat32bppGrayFixedPoint == format || GUID_WICPixelFormat32bppRGBE == format || GUID_WICPixelFormat24bppRGB == format || GUID_WICPixelFormat24bppBGR == format || GUID_WICPixelFormat32bppBGR == format || GUID_WICPixelFormat48bppRGB == format || GUID_WICPixelFormat48bppBGR == format || GUID_WICPixelFormat48bppRGBFixedPoint == format || GUID_WICPixelFormat48bppBGRFixedPoint == format || GUID_WICPixelFormat48bppRGBHalf == format || GUID_WICPixelFormat64bppRGBFixedPoint == format || GUID_WICPixelFormat64bppRGBHalf == format || GUID_WICPixelFormat96bppRGBFixedPoint == format || GUID_WICPixelFormat128bppRGBFloat == format || GUID_WICPixelFormat128bppRGBFixedPoint == format || GUID_WICPixelFormat32bppRGB == format || GUID_WICPixelFormat64bppRGB == format || GUID_WICPixelFormat96bppRGBFloat == format || GUID_WICPixelFormat32bppCMYK == format || GUID_WICPixelFormat64bppCMYK == format || GUID_WICPixelFormat8bppY == format || GUID_WICPixelFormat8bppCb == format || GUID_WICPixelFormat8bppCr == format || GUID_WICPixelFormat16bppCbCr == format) { alphaType = kOpaque_SkAlphaType; } // FIXME: If we change the implementation to handle swizzling ourselves, // we can support more output formats. SkImageInfo info = SkImageInfo::MakeS32(width, height, alphaType); return std::unique_ptr( new ImageGeneratorWIC(info, imagingFactory.release(), imageSource.release(), std::move(data))); } ImageGeneratorWIC::ImageGeneratorWIC(const SkImageInfo& info, IWICImagingFactory* imagingFactory, IWICBitmapSource* imageSource, sk_sp data) : INHERITED(info) , fImagingFactory(imagingFactory) , fImageSource(imageSource) , fData(std::move(data)) {} sk_sp ImageGeneratorWIC::onRefEncodedData() { return fData; } bool ImageGeneratorWIC::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, const Options&) { if (kN32_SkColorType != info.colorType()) { return false; } // Create a format converter. SkTScopedComPtr formatConverter; HRESULT hr = fImagingFactory->CreateFormatConverter(&formatConverter); if (FAILED(hr)) { return false; } GUID format = GUID_WICPixelFormat32bppPBGRA; if (kUnpremul_SkAlphaType == info.alphaType()) { format = GUID_WICPixelFormat32bppBGRA; } hr = formatConverter->Initialize(fImageSource.get(), format, WICBitmapDitherTypeNone, nullptr, 0.0, WICBitmapPaletteTypeCustom); if (FAILED(hr)) { return false; } // Treat the format converter as an image source. SkTScopedComPtr formatConverterSrc; hr = formatConverter->QueryInterface(IID_PPV_ARGS(&formatConverterSrc)); if (FAILED(hr)) { return false; } // Set the destination pixels. hr = formatConverterSrc->CopyPixels(nullptr, (UINT) rowBytes, (UINT) rowBytes * info.height(), (BYTE*) pixels); return SUCCEEDED(hr); }