| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkTypes.h" |
| |
| #if defined(SK_BUILD_FOR_WIN) |
| |
| #include "SkAutoCoInitialize.h" |
| #include "SkAutoMalloc.h" |
| #include "SkBitmap.h" |
| #include "SkImageEncoderPriv.h" |
| #include "SkIStream.h" |
| #include "SkImageEncoder.h" |
| #include "SkStream.h" |
| #include "SkTScopedComPtr.h" |
| #include "SkTemplates.h" |
| #include "SkUnPreMultiply.h" |
| #include <wincodec.h> |
| |
| //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 |
| |
| bool SkEncodeImageWithWIC(SkWStream* stream, const SkPixmap& pixmap, |
| SkEncodedImageFormat format, int quality) { |
| GUID type; |
| switch (format) { |
| case SkEncodedImageFormat::kJPEG: |
| type = GUID_ContainerFormatJpeg; |
| break; |
| case SkEncodedImageFormat::kPNG: |
| type = GUID_ContainerFormatPng; |
| break; |
| default: |
| return false; |
| } |
| SkBitmap bitmapOrig; |
| if (!bitmapOrig.installPixels(pixmap)) { |
| return false; |
| } |
| bitmapOrig.setImmutable(); |
| |
| // First convert to BGRA if necessary. |
| SkBitmap bitmap; |
| if (!bitmap.tryAllocPixels(bitmapOrig.info().makeColorType(kBGRA_8888_SkColorType)) || |
| !bitmapOrig.readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0)) |
| { |
| return false; |
| } |
| |
| // WIC expects unpremultiplied pixels. Unpremultiply if necessary. |
| if (kPremul_SkAlphaType == bitmap.alphaType()) { |
| uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels()); |
| for (int y = 0; y < bitmap.height(); ++y) { |
| for (int x = 0; x < bitmap.width(); ++x) { |
| uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel(); |
| SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes); |
| SkColor* dst = reinterpret_cast<SkColor*>(bytes); |
| *dst = SkUnPreMultiply::PMColorToColor(*src); |
| } |
| } |
| } |
| |
| // Finally, if we are performing a jpeg encode, we must convert to BGR. |
| void* pixels = bitmap.getPixels(); |
| size_t rowBytes = bitmap.rowBytes(); |
| SkAutoMalloc pixelStorage; |
| WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA; |
| if (SkEncodedImageFormat::kJPEG == format) { |
| formatDesired = GUID_WICPixelFormat24bppBGR; |
| rowBytes = SkAlign4(bitmap.width() * 3); |
| pixelStorage.reset(rowBytes * bitmap.height()); |
| for (int y = 0; y < bitmap.height(); y++) { |
| uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes); |
| for (int x = 0; x < bitmap.width(); x++) { |
| uint32_t bgra = *bitmap.getAddr32(x, y); |
| dstRow[0] = (uint8_t) (bgra >> 0); |
| dstRow[1] = (uint8_t) (bgra >> 8); |
| dstRow[2] = (uint8_t) (bgra >> 16); |
| dstRow += 3; |
| } |
| } |
| |
| pixels = pixelStorage.get(); |
| } |
| |
| |
| //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 the SkWStream to an IStream. |
| SkTScopedComPtr<IStream> piStream; |
| if (SUCCEEDED(hr)) { |
| hr = SkWIStream::CreateFromSkWStream(stream, &piStream); |
| } |
| |
| //Create an encode of the appropriate type. |
| SkTScopedComPtr<IWICBitmapEncoder> piEncoder; |
| if (SUCCEEDED(hr)) { |
| hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder); |
| } |
| |
| if (SUCCEEDED(hr)) { |
| hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache); |
| } |
| |
| //Create a the frame. |
| SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode; |
| SkTScopedComPtr<IPropertyBag2> piPropertybag; |
| if (SUCCEEDED(hr)) { |
| hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag); |
| } |
| |
| if (SUCCEEDED(hr)) { |
| PROPBAG2 name; |
| memset(&name, 0, sizeof(name)); |
| name.dwType = PROPBAG2_TYPE_DATA; |
| name.vt = VT_R4; |
| name.pstrName = const_cast<LPOLESTR>(L"ImageQuality"); |
| |
| VARIANT value; |
| VariantInit(&value); |
| value.vt = VT_R4; |
| value.fltVal = (FLOAT)(quality / 100.0); |
| |
| //Ignore result code. |
| // This returns E_FAIL if the named property is not in the bag. |
| //TODO(bungeman) enumerate the properties, |
| // write and set hr iff property exists. |
| piPropertybag->Write(1, &name, &value); |
| } |
| if (SUCCEEDED(hr)) { |
| hr = piBitmapFrameEncode->Initialize(piPropertybag.get()); |
| } |
| |
| //Set the size of the frame. |
| const UINT width = bitmap.width(); |
| const UINT height = bitmap.height(); |
| if (SUCCEEDED(hr)) { |
| hr = piBitmapFrameEncode->SetSize(width, height); |
| } |
| |
| //Set the pixel format of the frame. If native encoded format cannot match BGRA, |
| //it will choose the closest pixel format that it supports. |
| WICPixelFormatGUID formatGUID = formatDesired; |
| if (SUCCEEDED(hr)) { |
| hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID); |
| } |
| if (SUCCEEDED(hr)) { |
| //Be sure the image format is the one requested. |
| hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL; |
| } |
| |
| //Write the pixels into the frame. |
| if (SUCCEEDED(hr)) { |
| hr = piBitmapFrameEncode->WritePixels(height, |
| (UINT) rowBytes, |
| (UINT) rowBytes * height, |
| reinterpret_cast<BYTE*>(pixels)); |
| } |
| |
| if (SUCCEEDED(hr)) { |
| hr = piBitmapFrameEncode->Commit(); |
| } |
| |
| if (SUCCEEDED(hr)) { |
| hr = piEncoder->Commit(); |
| } |
| |
| return SUCCEEDED(hr); |
| } |
| |
| #endif // defined(SK_BUILD_FOR_WIN) |