michael@0: michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkTypes.h" michael@0: michael@0: // Workaround for: michael@0: // http://connect.microsoft.com/VisualStudio/feedback/details/621653/ michael@0: // http://crbug.com/225822 michael@0: // In VS2010 both intsafe.h and stdint.h define the following without guards. michael@0: // SkTypes brought in windows.h and stdint.h and the following defines are michael@0: // not used by this file. However, they may be re-introduced by wincodec.h. michael@0: #undef INT8_MIN michael@0: #undef INT16_MIN michael@0: #undef INT32_MIN michael@0: #undef INT64_MIN michael@0: #undef INT8_MAX michael@0: #undef UINT8_MAX michael@0: #undef INT16_MAX michael@0: #undef UINT16_MAX michael@0: #undef INT32_MAX michael@0: #undef UINT32_MAX michael@0: #undef INT64_MAX michael@0: #undef UINT64_MAX michael@0: michael@0: #include michael@0: #include "SkAutoCoInitialize.h" michael@0: #include "SkImageDecoder.h" michael@0: #include "SkImageEncoder.h" michael@0: #include "SkIStream.h" michael@0: #include "SkMovie.h" michael@0: #include "SkStream.h" michael@0: #include "SkTScopedComPtr.h" michael@0: #include "SkUnPreMultiply.h" michael@0: michael@0: //All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol. michael@0: //In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported michael@0: //but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2. michael@0: //Undo this #define if it has been done so that we link against the symbols michael@0: //we intended to link against on all SDKs. michael@0: #if defined(CLSID_WICImagingFactory) michael@0: #undef CLSID_WICImagingFactory michael@0: #endif michael@0: michael@0: class SkImageDecoder_WIC : public SkImageDecoder { michael@0: public: michael@0: // Decoding modes corresponding to SkImageDecoder::Mode, plus an extra mode for decoding michael@0: // only the format. michael@0: enum WICModes { michael@0: kDecodeFormat_WICMode, michael@0: kDecodeBounds_WICMode, michael@0: kDecodePixels_WICMode, michael@0: }; michael@0: michael@0: /** michael@0: * Helper function to decode an SkStream. michael@0: * @param stream SkStream to decode. Must be at the beginning. michael@0: * @param bm SkBitmap to decode into. Only used if wicMode is kDecodeBounds_WICMode or michael@0: * kDecodePixels_WICMode, in which case it must not be NULL. michael@0: * @param format Out parameter for the SkImageDecoder::Format of the SkStream. Only used if michael@0: * wicMode is kDecodeFormat_WICMode. michael@0: */ michael@0: bool decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode, Format* format) const; michael@0: michael@0: protected: michael@0: virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE; michael@0: }; michael@0: michael@0: struct FormatConversion { michael@0: GUID fGuidFormat; michael@0: SkImageDecoder::Format fFormat; michael@0: }; michael@0: michael@0: static const FormatConversion gFormatConversions[] = { michael@0: { GUID_ContainerFormatBmp, SkImageDecoder::kBMP_Format }, michael@0: { GUID_ContainerFormatGif, SkImageDecoder::kGIF_Format }, michael@0: { GUID_ContainerFormatIco, SkImageDecoder::kICO_Format }, michael@0: { GUID_ContainerFormatJpeg, SkImageDecoder::kJPEG_Format }, michael@0: { GUID_ContainerFormatPng, SkImageDecoder::kPNG_Format }, michael@0: }; michael@0: michael@0: static SkImageDecoder::Format GuidContainerFormat_to_Format(REFGUID guid) { michael@0: for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) { michael@0: if (IsEqualGUID(guid, gFormatConversions[i].fGuidFormat)) { michael@0: return gFormatConversions[i].fFormat; michael@0: } michael@0: } michael@0: return SkImageDecoder::kUnknown_Format; michael@0: } michael@0: michael@0: bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { michael@0: WICModes wicMode; michael@0: switch (mode) { michael@0: case SkImageDecoder::kDecodeBounds_Mode: michael@0: wicMode = kDecodeBounds_WICMode; michael@0: break; michael@0: case SkImageDecoder::kDecodePixels_Mode: michael@0: wicMode = kDecodePixels_WICMode; michael@0: break; michael@0: } michael@0: return this->decodeStream(stream, bm, wicMode, NULL); michael@0: } michael@0: michael@0: bool SkImageDecoder_WIC::decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode, michael@0: Format* format) const { michael@0: //Initialize COM. michael@0: SkAutoCoInitialize scopedCo; michael@0: if (!scopedCo.succeeded()) { michael@0: return false; michael@0: } michael@0: michael@0: HRESULT hr = S_OK; michael@0: michael@0: //Create Windows Imaging Component ImagingFactory. michael@0: SkTScopedComPtr piImagingFactory; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = CoCreateInstance( michael@0: CLSID_WICImagingFactory michael@0: , NULL michael@0: , CLSCTX_INPROC_SERVER michael@0: , IID_PPV_ARGS(&piImagingFactory) michael@0: ); michael@0: } michael@0: michael@0: //Convert SkStream to IStream. michael@0: SkTScopedComPtr piStream; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = SkIStream::CreateFromSkStream(stream, false, &piStream); michael@0: } michael@0: michael@0: //Make sure we're at the beginning of the stream. michael@0: if (SUCCEEDED(hr)) { michael@0: LARGE_INTEGER liBeginning = { 0 }; michael@0: hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL); michael@0: } michael@0: michael@0: //Create the decoder from the stream content. michael@0: SkTScopedComPtr piBitmapDecoder; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piImagingFactory->CreateDecoderFromStream( michael@0: piStream.get() //Image to be decoded michael@0: , NULL //No particular vendor michael@0: , WICDecodeMetadataCacheOnDemand //Cache metadata when needed michael@0: , &piBitmapDecoder //Pointer to the decoder michael@0: ); michael@0: } michael@0: michael@0: if (kDecodeFormat_WICMode == wicMode) { michael@0: SkASSERT(format != NULL); michael@0: //Get the format michael@0: if (SUCCEEDED(hr)) { michael@0: GUID guidFormat; michael@0: hr = piBitmapDecoder->GetContainerFormat(&guidFormat); michael@0: if (SUCCEEDED(hr)) { michael@0: *format = GuidContainerFormat_to_Format(guidFormat); michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: //Get the first frame from the decoder. michael@0: SkTScopedComPtr piBitmapFrameDecode; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode); michael@0: } michael@0: michael@0: //Get the BitmapSource interface of the frame. michael@0: SkTScopedComPtr piBitmapSourceOriginal; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piBitmapFrameDecode->QueryInterface( michael@0: IID_PPV_ARGS(&piBitmapSourceOriginal) michael@0: ); michael@0: } michael@0: michael@0: //Get the size of the bitmap. michael@0: UINT width; michael@0: UINT height; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piBitmapSourceOriginal->GetSize(&width, &height); michael@0: } michael@0: michael@0: //Exit early if we're only looking for the bitmap bounds. michael@0: if (SUCCEEDED(hr)) { michael@0: bm->setConfig(SkImageInfo::MakeN32Premul(width, height)); michael@0: if (kDecodeBounds_WICMode == wicMode) { michael@0: return true; michael@0: } michael@0: if (!this->allocPixelRef(bm, NULL)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: //Create a format converter. michael@0: SkTScopedComPtr piFormatConverter; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piImagingFactory->CreateFormatConverter(&piFormatConverter); michael@0: } michael@0: michael@0: GUID destinationPixelFormat; michael@0: if (this->getRequireUnpremultipliedColors()) { michael@0: destinationPixelFormat = GUID_WICPixelFormat32bppBGRA; michael@0: } else { michael@0: destinationPixelFormat = GUID_WICPixelFormat32bppPBGRA; michael@0: } michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piFormatConverter->Initialize( michael@0: piBitmapSourceOriginal.get() //Input bitmap to convert michael@0: , destinationPixelFormat //Destination pixel format michael@0: , WICBitmapDitherTypeNone //Specified dither patterm michael@0: , NULL //Specify a particular palette michael@0: , 0.f //Alpha threshold michael@0: , WICBitmapPaletteTypeCustom //Palette translation type michael@0: ); michael@0: } michael@0: michael@0: //Get the BitmapSource interface of the format converter. michael@0: SkTScopedComPtr piBitmapSourceConverted; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piFormatConverter->QueryInterface( michael@0: IID_PPV_ARGS(&piBitmapSourceConverted) michael@0: ); michael@0: } michael@0: michael@0: //Copy the pixels into the bitmap. michael@0: if (SUCCEEDED(hr)) { michael@0: SkAutoLockPixels alp(*bm); michael@0: bm->eraseColor(SK_ColorTRANSPARENT); michael@0: const UINT stride = (UINT) bm->rowBytes(); michael@0: hr = piBitmapSourceConverted->CopyPixels( michael@0: NULL, //Get all the pixels michael@0: stride, michael@0: stride * height, michael@0: reinterpret_cast(bm->getPixels()) michael@0: ); michael@0: michael@0: // Note: we don't need to premultiply here since we specified PBGRA michael@0: if (SkBitmap::ComputeIsOpaque(*bm)) { michael@0: bm->setAlphaType(kOpaque_SkAlphaType); michael@0: } michael@0: } michael@0: michael@0: return SUCCEEDED(hr); michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////// michael@0: michael@0: extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); michael@0: michael@0: SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { michael@0: SkImageDecoder* decoder = image_decoder_from_stream(stream); michael@0: if (NULL == decoder) { michael@0: // If no image decoder specific to the stream exists, use SkImageDecoder_WIC. michael@0: return SkNEW(SkImageDecoder_WIC); michael@0: } else { michael@0: return decoder; michael@0: } michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { michael@0: return NULL; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////// michael@0: michael@0: class SkImageEncoder_WIC : public SkImageEncoder { michael@0: public: michael@0: SkImageEncoder_WIC(Type t) : fType(t) {} michael@0: michael@0: protected: michael@0: virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); michael@0: michael@0: private: michael@0: Type fType; michael@0: }; michael@0: michael@0: bool SkImageEncoder_WIC::onEncode(SkWStream* stream michael@0: , const SkBitmap& bitmapOrig michael@0: , int quality) michael@0: { michael@0: GUID type; michael@0: switch (fType) { michael@0: case kBMP_Type: michael@0: type = GUID_ContainerFormatBmp; michael@0: break; michael@0: case kICO_Type: michael@0: type = GUID_ContainerFormatIco; michael@0: break; michael@0: case kJPEG_Type: michael@0: type = GUID_ContainerFormatJpeg; michael@0: break; michael@0: case kPNG_Type: michael@0: type = GUID_ContainerFormatPng; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: michael@0: //Convert to 8888 if needed. michael@0: const SkBitmap* bitmap; michael@0: SkBitmap bitmapCopy; michael@0: if (kPMColor_SkColorType == bitmapOrig.colorType() && bitmapOrig.isOpaque()) { michael@0: bitmap = &bitmapOrig; michael@0: } else { michael@0: if (!bitmapOrig.copyTo(&bitmapCopy, kPMColor_SkColorType)) { michael@0: return false; michael@0: } michael@0: bitmap = &bitmapCopy; michael@0: } michael@0: michael@0: // We cannot use PBGRA so we need to unpremultiply ourselves michael@0: if (!bitmap->isOpaque()) { michael@0: SkAutoLockPixels alp(*bitmap); michael@0: michael@0: uint8_t* pixels = reinterpret_cast(bitmap->getPixels()); michael@0: for (int y = 0; y < bitmap->height(); ++y) { michael@0: for (int x = 0; x < bitmap->width(); ++x) { michael@0: uint8_t* bytes = pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel(); michael@0: michael@0: SkPMColor* src = reinterpret_cast(bytes); michael@0: SkColor* dst = reinterpret_cast(bytes); michael@0: michael@0: *dst = SkUnPreMultiply::PMColorToColor(*src); michael@0: } michael@0: } michael@0: } michael@0: michael@0: //Initialize COM. michael@0: SkAutoCoInitialize scopedCo; michael@0: if (!scopedCo.succeeded()) { michael@0: return false; michael@0: } michael@0: michael@0: HRESULT hr = S_OK; michael@0: michael@0: //Create Windows Imaging Component ImagingFactory. michael@0: SkTScopedComPtr piImagingFactory; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = CoCreateInstance( michael@0: CLSID_WICImagingFactory michael@0: , NULL michael@0: , CLSCTX_INPROC_SERVER michael@0: , IID_PPV_ARGS(&piImagingFactory) michael@0: ); michael@0: } michael@0: michael@0: //Convert the SkWStream to an IStream. michael@0: SkTScopedComPtr piStream; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = SkWIStream::CreateFromSkWStream(stream, &piStream); michael@0: } michael@0: michael@0: //Create an encode of the appropriate type. michael@0: SkTScopedComPtr piEncoder; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder); michael@0: } michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache); michael@0: } michael@0: michael@0: //Create a the frame. michael@0: SkTScopedComPtr piBitmapFrameEncode; michael@0: SkTScopedComPtr piPropertybag; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag); michael@0: } michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: PROPBAG2 name = { 0 }; michael@0: name.dwType = PROPBAG2_TYPE_DATA; michael@0: name.vt = VT_R4; michael@0: name.pstrName = L"ImageQuality"; michael@0: michael@0: VARIANT value; michael@0: VariantInit(&value); michael@0: value.vt = VT_R4; michael@0: value.fltVal = (FLOAT)(quality / 100.0); michael@0: michael@0: //Ignore result code. michael@0: // This returns E_FAIL if the named property is not in the bag. michael@0: //TODO(bungeman) enumerate the properties, michael@0: // write and set hr iff property exists. michael@0: piPropertybag->Write(1, &name, &value); michael@0: } michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piBitmapFrameEncode->Initialize(piPropertybag.get()); michael@0: } michael@0: michael@0: //Set the size of the frame. michael@0: const UINT width = bitmap->width(); michael@0: const UINT height = bitmap->height(); michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piBitmapFrameEncode->SetSize(width, height); michael@0: } michael@0: michael@0: //Set the pixel format of the frame. michael@0: const WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA; michael@0: WICPixelFormatGUID formatGUID = formatDesired; michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID); michael@0: } michael@0: if (SUCCEEDED(hr)) { michael@0: //Be sure the image format is the one requested. michael@0: hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL; michael@0: } michael@0: michael@0: //Write the pixels into the frame. michael@0: if (SUCCEEDED(hr)) { michael@0: SkAutoLockPixels alp(*bitmap); michael@0: const UINT stride = (UINT) bitmap->rowBytes(); michael@0: hr = piBitmapFrameEncode->WritePixels( michael@0: height michael@0: , stride michael@0: , stride * height michael@0: , reinterpret_cast(bitmap->getPixels())); michael@0: } michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piBitmapFrameEncode->Commit(); michael@0: } michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: hr = piEncoder->Commit(); michael@0: } michael@0: michael@0: return SUCCEEDED(hr); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) { michael@0: switch (t) { michael@0: case SkImageEncoder::kBMP_Type: michael@0: case SkImageEncoder::kICO_Type: michael@0: case SkImageEncoder::kJPEG_Type: michael@0: case SkImageEncoder::kPNG_Type: michael@0: break; michael@0: default: michael@0: return NULL; michael@0: } michael@0: return SkNEW_ARGS(SkImageEncoder_WIC, (t)); michael@0: } michael@0: michael@0: static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_wic_factory); michael@0: michael@0: static SkImageDecoder::Format get_format_wic(SkStreamRewindable* stream) { michael@0: SkImageDecoder::Format format; michael@0: SkImageDecoder_WIC codec; michael@0: if (!codec.decodeStream(stream, NULL, SkImageDecoder_WIC::kDecodeFormat_WICMode, &format)) { michael@0: format = SkImageDecoder::kUnknown_Format; michael@0: } michael@0: return format; michael@0: } michael@0: michael@0: static SkImageDecoder_FormatReg gFormatReg(get_format_wic);