| /* | |
| * 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 "include/core/SkTypes.h" | |
| #if defined(SK_BUILD_FOR_WIN) | |
| #include "include/core/SkBitmap.h" | |
| #include "include/core/SkImageEncoder.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/core/SkUnPreMultiply.h" | |
| #include "include/private/SkTemplates.h" | |
| #include "src/core/SkAutoMalloc.h" | |
| #include "src/images/SkImageEncoderPriv.h" | |
| #include "src/utils/win/SkAutoCoInitialize.h" | |
| #include "src/utils/win/SkIStream.h" | |
| #include "src/utils/win/SkTScopedComPtr.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) & 0xFF); | |
| dstRow[1] = (uint8_t) ((bgra >> 8) & 0xFF); | |
| dstRow[2] = (uint8_t) ((bgra >> 16) & 0xFF); | |
| 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) |