1 2 /* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 #define WIN32_LEAN_AND_MEAN 11 #include <Windows.h> 12 #include <wincodec.h> 13 #include "SkAutoCoInitialize.h" 14 #include "SkImageDecoder.h" 15 #include "SkImageEncoder.h" 16 #include "SkIStream.h" 17 #include "SkMovie.h" 18 #include "SkStream.h" 19 #include "SkTScopedComPtr.h" 20 #include "SkUnPreMultiply.h" 21 22 //All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol. 23 //In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported 24 //but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2. 25 //Undo this #define if it has been done so that we link against the symbols 26 //we intended to link against on all SDKs. 27 #if defined(CLSID_WICImagingFactory) 28 #undef CLSID_WICImagingFactory 29 #endif 30 31 class SkImageDecoder_WIC : public SkImageDecoder { 32 protected: 33 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode); 34 }; 35 36 bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { 37 //Initialize COM. 38 SkAutoCoInitialize scopedCo; 39 if (!scopedCo.succeeded()) { 40 return false; 41 } 42 43 HRESULT hr = S_OK; 44 45 //Create Windows Imaging Component ImagingFactory. 46 SkTScopedComPtr<IWICImagingFactory> piImagingFactory; 47 if (SUCCEEDED(hr)) { 48 hr = CoCreateInstance( 49 CLSID_WICImagingFactory 50 , NULL 51 , CLSCTX_INPROC_SERVER 52 , IID_PPV_ARGS(&piImagingFactory) 53 ); 54 } 55 56 //Convert SkStream to IStream. 57 SkTScopedComPtr<IStream> piStream; 58 if (SUCCEEDED(hr)) { 59 hr = SkIStream::CreateFromSkStream(stream, false, &piStream); 60 } 61 62 //Make sure we're at the beginning of the stream. 63 if (SUCCEEDED(hr)) { 64 LARGE_INTEGER liBeginning = { 0 }; 65 hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL); 66 } 67 68 //Create the decoder from the stream content. 69 SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder; 70 if (SUCCEEDED(hr)) { 71 hr = piImagingFactory->CreateDecoderFromStream( 72 piStream.get() //Image to be decoded 73 , NULL //No particular vendor 74 , WICDecodeMetadataCacheOnDemand //Cache metadata when needed 75 , &piBitmapDecoder //Pointer to the decoder 76 ); 77 } 78 79 //Get the first frame from the decoder. 80 SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode; 81 if (SUCCEEDED(hr)) { 82 hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode); 83 } 84 85 //Get the BitmapSource interface of the frame. 86 SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal; 87 if (SUCCEEDED(hr)) { 88 hr = piBitmapFrameDecode->QueryInterface( 89 IID_PPV_ARGS(&piBitmapSourceOriginal) 90 ); 91 } 92 93 //Get the size of the bitmap. 94 UINT width; 95 UINT height; 96 if (SUCCEEDED(hr)) { 97 hr = piBitmapSourceOriginal->GetSize(&width, &height); 98 } 99 100 //Exit early if we're only looking for the bitmap bounds. 101 if (SUCCEEDED(hr)) { 102 bm->setConfig(SkBitmap::kARGB_8888_Config, width, height); 103 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 104 return true; 105 } 106 if (!this->allocPixelRef(bm, NULL)) { 107 return false; 108 } 109 } 110 111 //Create a format converter. 112 SkTScopedComPtr<IWICFormatConverter> piFormatConverter; 113 if (SUCCEEDED(hr)) { 114 hr = piImagingFactory->CreateFormatConverter(&piFormatConverter); 115 } 116 117 if (SUCCEEDED(hr)) { 118 hr = piFormatConverter->Initialize( 119 piBitmapSourceOriginal.get() //Input bitmap to convert 120 , GUID_WICPixelFormat32bppPBGRA //Destination pixel format 121 , WICBitmapDitherTypeNone //Specified dither patterm 122 , NULL //Specify a particular palette 123 , 0.f //Alpha threshold 124 , WICBitmapPaletteTypeCustom //Palette translation type 125 ); 126 } 127 128 //Get the BitmapSource interface of the format converter. 129 SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted; 130 if (SUCCEEDED(hr)) { 131 hr = piFormatConverter->QueryInterface( 132 IID_PPV_ARGS(&piBitmapSourceConverted) 133 ); 134 } 135 136 //Copy the pixels into the bitmap. 137 if (SUCCEEDED(hr)) { 138 SkAutoLockPixels alp(*bm); 139 bm->eraseColor(SK_ColorTRANSPARENT); 140 const int stride = bm->rowBytes(); 141 hr = piBitmapSourceConverted->CopyPixels( 142 NULL, //Get all the pixels 143 stride, 144 stride * height, 145 reinterpret_cast<BYTE *>(bm->getPixels()) 146 ); 147 148 // Note: we don't need to premultiply here since we specified PBGRA 149 bm->computeAndSetOpaquePredicate(); 150 } 151 152 return SUCCEEDED(hr); 153 } 154 155 ///////////////////////////////////////////////////////////////////////// 156 157 SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { 158 return SkNEW(SkImageDecoder_WIC); 159 } 160 161 ///////////////////////////////////////////////////////////////////////// 162 163 SkMovie* SkMovie::DecodeStream(SkStream* stream) { 164 return NULL; 165 } 166 167 ///////////////////////////////////////////////////////////////////////// 168 169 class SkImageEncoder_WIC : public SkImageEncoder { 170 public: 171 SkImageEncoder_WIC(Type t) : fType(t) {} 172 173 protected: 174 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); 175 176 private: 177 Type fType; 178 }; 179 180 bool SkImageEncoder_WIC::onEncode(SkWStream* stream 181 , const SkBitmap& bitmapOrig 182 , int quality) 183 { 184 GUID type; 185 switch (fType) { 186 case kJPEG_Type: 187 type = GUID_ContainerFormatJpeg; 188 break; 189 case kPNG_Type: 190 type = GUID_ContainerFormatPng; 191 break; 192 default: 193 return false; 194 } 195 196 //Convert to 8888 if needed. 197 const SkBitmap* bitmap; 198 SkBitmap bitmapCopy; 199 if (SkBitmap::kARGB_8888_Config == bitmapOrig.config() && bitmapOrig.isOpaque()) { 200 bitmap = &bitmapOrig; 201 } else { 202 if (!bitmapOrig.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config)) { 203 return false; 204 } 205 bitmap = &bitmapCopy; 206 } 207 208 // We cannot use PBGRA so we need to unpremultiply ourselves 209 if (!bitmap->isOpaque()) { 210 SkAutoLockPixels alp(*bitmap); 211 212 uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap->getPixels()); 213 for (int y = 0; y < bitmap->height(); ++y) { 214 for (int x = 0; x < bitmap->width(); ++x) { 215 uint8_t* bytes = pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel(); 216 217 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes); 218 SkColor* dst = reinterpret_cast<SkColor*>(bytes); 219 220 *dst = SkUnPreMultiply::PMColorToColor(*src); 221 } 222 } 223 } 224 225 //Initialize COM. 226 SkAutoCoInitialize scopedCo; 227 if (!scopedCo.succeeded()) { 228 return false; 229 } 230 231 HRESULT hr = S_OK; 232 233 //Create Windows Imaging Component ImagingFactory. 234 SkTScopedComPtr<IWICImagingFactory> piImagingFactory; 235 if (SUCCEEDED(hr)) { 236 hr = CoCreateInstance( 237 CLSID_WICImagingFactory 238 , NULL 239 , CLSCTX_INPROC_SERVER 240 , IID_PPV_ARGS(&piImagingFactory) 241 ); 242 } 243 244 //Convert the SkWStream to an IStream. 245 SkTScopedComPtr<IStream> piStream; 246 if (SUCCEEDED(hr)) { 247 hr = SkWIStream::CreateFromSkWStream(stream, &piStream); 248 } 249 250 //Create an encode of the appropriate type. 251 SkTScopedComPtr<IWICBitmapEncoder> piEncoder; 252 if (SUCCEEDED(hr)) { 253 hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder); 254 } 255 256 if (SUCCEEDED(hr)) { 257 hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache); 258 } 259 260 //Create a the frame. 261 SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode; 262 SkTScopedComPtr<IPropertyBag2> piPropertybag; 263 if (SUCCEEDED(hr)) { 264 hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag); 265 } 266 267 if (SUCCEEDED(hr)) { 268 PROPBAG2 name = { 0 }; 269 name.dwType = PROPBAG2_TYPE_DATA; 270 name.vt = VT_R4; 271 name.pstrName = L"ImageQuality"; 272 273 VARIANT value; 274 VariantInit(&value); 275 value.vt = VT_R4; 276 value.fltVal = (FLOAT)(quality / 100.0); 277 278 //Ignore result code. 279 // This returns E_FAIL if the named property is not in the bag. 280 //TODO(bungeman) enumerate the properties, 281 // write and set hr iff property exists. 282 piPropertybag->Write(1, &name, &value); 283 } 284 if (SUCCEEDED(hr)) { 285 hr = piBitmapFrameEncode->Initialize(piPropertybag.get()); 286 } 287 288 //Set the size of the frame. 289 const UINT width = bitmap->width(); 290 const UINT height = bitmap->height(); 291 if (SUCCEEDED(hr)) { 292 hr = piBitmapFrameEncode->SetSize(width, height); 293 } 294 295 //Set the pixel format of the frame. 296 const WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA; 297 WICPixelFormatGUID formatGUID = formatDesired; 298 if (SUCCEEDED(hr)) { 299 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID); 300 } 301 if (SUCCEEDED(hr)) { 302 //Be sure the image format is the one requested. 303 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL; 304 } 305 306 //Write the pixels into the frame. 307 if (SUCCEEDED(hr)) { 308 SkAutoLockPixels alp(*bitmap); 309 hr = piBitmapFrameEncode->WritePixels( 310 height 311 , bitmap->rowBytes() 312 , bitmap->rowBytes()*height 313 , reinterpret_cast<BYTE*>(bitmap->getPixels())); 314 } 315 316 if (SUCCEEDED(hr)) { 317 hr = piBitmapFrameEncode->Commit(); 318 } 319 320 if (SUCCEEDED(hr)) { 321 hr = piEncoder->Commit(); 322 } 323 324 return SUCCEEDED(hr); 325 } 326 327 SkImageEncoder* SkImageEncoder::Create(Type t) { 328 switch (t) { 329 case kJPEG_Type: 330 case kPNG_Type: 331 break; 332 default: 333 return NULL; 334 } 335 return SkNEW_ARGS(SkImageEncoder_WIC, (t)); 336 } 337