• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/core/SkTypes.h"
9 
10 #if defined(SK_BUILD_FOR_WIN)
11 
12 #include "include/core/SkBitmap.h"
13 #include "include/core/SkImageEncoder.h"
14 #include "include/core/SkStream.h"
15 #include "include/core/SkUnPreMultiply.h"
16 #include "include/private/SkTemplates.h"
17 #include "src/core/SkAutoMalloc.h"
18 #include "src/images/SkImageEncoderPriv.h"
19 #include "src/utils/win/SkAutoCoInitialize.h"
20 #include "src/utils/win/SkIStream.h"
21 #include "src/utils/win/SkTScopedComPtr.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 
SkEncodeImageWithWIC(SkWStream * stream,const SkPixmap & pixmap,SkEncodedImageFormat format,int quality)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