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