1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkTypes.h" 9 10#if defined(SK_BUILD_FOR_WIN32) 11 12// Workaround for: 13// http://connect.microsoft.com/VisualStudio/feedback/details/621653/ 14// http://crbug.com/225822 15// In VS2010 both intsafe.h and stdint.h define the following without guards. 16// SkTypes brought in windows.h and stdint.h and the following defines are 17// not used by this file. However, they may be re-introduced by wincodec.h. 18#undef INT8_MIN 19#undef INT16_MIN 20#undef INT32_MIN 21#undef INT64_MIN 22#undef INT8_MAX 23#undef UINT8_MAX 24#undef INT16_MAX 25#undef UINT16_MAX 26#undef INT32_MAX 27#undef UINT32_MAX 28#undef INT64_MAX 29#undef UINT64_MAX 30 31#include "SkAutoCoInitialize.h" 32#include "SkAutoMalloc.h" 33#include "SkBitmap.h" 34#include "SkImageEncoderPriv.h" 35#include "SkIStream.h" 36#include "SkImageEncoder.h" 37#include "SkStream.h" 38#include "SkTScopedComPtr.h" 39#include "SkTemplates.h" 40#include "SkUnPreMultiply.h" 41#include <wincodec.h> 42 43//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol. 44//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported 45//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2. 46//Undo this #define if it has been done so that we link against the symbols 47//we intended to link against on all SDKs. 48#if defined(CLSID_WICImagingFactory) 49#undef CLSID_WICImagingFactory 50#endif 51 52bool SkEncodeImageWithWIC(SkWStream* stream, const SkPixmap& pixmap, 53 SkEncodedImageFormat format, int quality) { 54 GUID type; 55 switch (format) { 56 case SkEncodedImageFormat::kJPEG: 57 type = GUID_ContainerFormatJpeg; 58 break; 59 case SkEncodedImageFormat::kPNG: 60 type = GUID_ContainerFormatPng; 61 break; 62 default: 63 return false; 64 } 65 SkBitmap bitmapOrig; 66 if (!bitmapOrig.installPixels(pixmap)) { 67 return false; 68 } 69 bitmapOrig.setImmutable(); 70 71 // First convert to BGRA if necessary. 72 SkBitmap bitmap; 73 if (!bitmap.tryAllocPixels(bitmapOrig.info().makeColorType(kBGRA_8888_SkColorType)) || 74 !bitmapOrig.readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0)) 75 { 76 return false; 77 } 78 79 // WIC expects unpremultiplied pixels. Unpremultiply if necessary. 80 if (kPremul_SkAlphaType == bitmap.alphaType()) { 81 uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels()); 82 for (int y = 0; y < bitmap.height(); ++y) { 83 for (int x = 0; x < bitmap.width(); ++x) { 84 uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel(); 85 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes); 86 SkColor* dst = reinterpret_cast<SkColor*>(bytes); 87 *dst = SkUnPreMultiply::PMColorToColor(*src); 88 } 89 } 90 } 91 92 // Finally, if we are performing a jpeg encode, we must convert to BGR. 93 void* pixels = bitmap.getPixels(); 94 size_t rowBytes = bitmap.rowBytes(); 95 SkAutoMalloc pixelStorage; 96 WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA; 97 if (SkEncodedImageFormat::kJPEG == format) { 98 formatDesired = GUID_WICPixelFormat24bppBGR; 99 rowBytes = SkAlign4(bitmap.width() * 3); 100 pixelStorage.reset(rowBytes * bitmap.height()); 101 for (int y = 0; y < bitmap.height(); y++) { 102 uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes); 103 for (int x = 0; x < bitmap.width(); x++) { 104 uint32_t bgra = *bitmap.getAddr32(x, y); 105 dstRow[0] = (uint8_t) (bgra >> 0); 106 dstRow[1] = (uint8_t) (bgra >> 8); 107 dstRow[2] = (uint8_t) (bgra >> 16); 108 dstRow += 3; 109 } 110 } 111 112 pixels = pixelStorage.get(); 113 } 114 115 116 //Initialize COM. 117 SkAutoCoInitialize scopedCo; 118 if (!scopedCo.succeeded()) { 119 return false; 120 } 121 122 HRESULT hr = S_OK; 123 124 //Create Windows Imaging Component ImagingFactory. 125 SkTScopedComPtr<IWICImagingFactory> piImagingFactory; 126 if (SUCCEEDED(hr)) { 127 hr = CoCreateInstance( 128 CLSID_WICImagingFactory 129 , nullptr 130 , CLSCTX_INPROC_SERVER 131 , IID_PPV_ARGS(&piImagingFactory) 132 ); 133 } 134 135 //Convert the SkWStream to an IStream. 136 SkTScopedComPtr<IStream> piStream; 137 if (SUCCEEDED(hr)) { 138 hr = SkWIStream::CreateFromSkWStream(stream, &piStream); 139 } 140 141 //Create an encode of the appropriate type. 142 SkTScopedComPtr<IWICBitmapEncoder> piEncoder; 143 if (SUCCEEDED(hr)) { 144 hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder); 145 } 146 147 if (SUCCEEDED(hr)) { 148 hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache); 149 } 150 151 //Create a the frame. 152 SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode; 153 SkTScopedComPtr<IPropertyBag2> piPropertybag; 154 if (SUCCEEDED(hr)) { 155 hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag); 156 } 157 158 if (SUCCEEDED(hr)) { 159 PROPBAG2 name = { 0 }; 160 name.dwType = PROPBAG2_TYPE_DATA; 161 name.vt = VT_R4; 162 name.pstrName = L"ImageQuality"; 163 164 VARIANT value; 165 VariantInit(&value); 166 value.vt = VT_R4; 167 value.fltVal = (FLOAT)(quality / 100.0); 168 169 //Ignore result code. 170 // This returns E_FAIL if the named property is not in the bag. 171 //TODO(bungeman) enumerate the properties, 172 // write and set hr iff property exists. 173 piPropertybag->Write(1, &name, &value); 174 } 175 if (SUCCEEDED(hr)) { 176 hr = piBitmapFrameEncode->Initialize(piPropertybag.get()); 177 } 178 179 //Set the size of the frame. 180 const UINT width = bitmap.width(); 181 const UINT height = bitmap.height(); 182 if (SUCCEEDED(hr)) { 183 hr = piBitmapFrameEncode->SetSize(width, height); 184 } 185 186 //Set the pixel format of the frame. If native encoded format cannot match BGRA, 187 //it will choose the closest pixel format that it supports. 188 WICPixelFormatGUID formatGUID = formatDesired; 189 if (SUCCEEDED(hr)) { 190 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID); 191 } 192 if (SUCCEEDED(hr)) { 193 //Be sure the image format is the one requested. 194 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL; 195 } 196 197 //Write the pixels into the frame. 198 if (SUCCEEDED(hr)) { 199 hr = piBitmapFrameEncode->WritePixels(height, 200 (UINT) rowBytes, 201 (UINT) rowBytes * height, 202 reinterpret_cast<BYTE*>(pixels)); 203 } 204 205 if (SUCCEEDED(hr)) { 206 hr = piBitmapFrameEncode->Commit(); 207 } 208 209 if (SUCCEEDED(hr)) { 210 hr = piEncoder->Commit(); 211 } 212 213 return SUCCEEDED(hr); 214} 215 216#endif // defined(SK_BUILD_FOR_WIN32) 217