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_WIN) 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 52 bool 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; 160 memset(&name, 0, sizeof(name)); 161 name.dwType = PROPBAG2_TYPE_DATA; 162 name.vt = VT_R4; 163 name.pstrName = const_cast<LPOLESTR>(L"ImageQuality"); 164 165 VARIANT value; 166 VariantInit(&value); 167 value.vt = VT_R4; 168 value.fltVal = (FLOAT)(quality / 100.0); 169 170 //Ignore result code. 171 // This returns E_FAIL if the named property is not in the bag. 172 //TODO(bungeman) enumerate the properties, 173 // write and set hr iff property exists. 174 piPropertybag->Write(1, &name, &value); 175 } 176 if (SUCCEEDED(hr)) { 177 hr = piBitmapFrameEncode->Initialize(piPropertybag.get()); 178 } 179 180 //Set the size of the frame. 181 const UINT width = bitmap.width(); 182 const UINT height = bitmap.height(); 183 if (SUCCEEDED(hr)) { 184 hr = piBitmapFrameEncode->SetSize(width, height); 185 } 186 187 //Set the pixel format of the frame. If native encoded format cannot match BGRA, 188 //it will choose the closest pixel format that it supports. 189 WICPixelFormatGUID formatGUID = formatDesired; 190 if (SUCCEEDED(hr)) { 191 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID); 192 } 193 if (SUCCEEDED(hr)) { 194 //Be sure the image format is the one requested. 195 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL; 196 } 197 198 //Write the pixels into the frame. 199 if (SUCCEEDED(hr)) { 200 hr = piBitmapFrameEncode->WritePixels(height, 201 (UINT) rowBytes, 202 (UINT) rowBytes * height, 203 reinterpret_cast<BYTE*>(pixels)); 204 } 205 206 if (SUCCEEDED(hr)) { 207 hr = piBitmapFrameEncode->Commit(); 208 } 209 210 if (SUCCEEDED(hr)) { 211 hr = piEncoder->Commit(); 212 } 213 214 return SUCCEEDED(hr); 215 } 216 217 #endif // defined(SK_BUILD_FOR_WIN) 218