• 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 "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 
SkEncodeImageWithWIC(SkWStream * stream,const SkPixmap & pixmap,SkEncodedImageFormat format,int quality)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 (!bitmapOrig.copyTo(&bitmap, kBGRA_8888_SkColorType)) {
74         return false;
75     }
76 
77     // WIC expects unpremultiplied pixels.  Unpremultiply if necessary.
78     if (kPremul_SkAlphaType == bitmap.alphaType()) {
79         uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
80         for (int y = 0; y < bitmap.height(); ++y) {
81             for (int x = 0; x < bitmap.width(); ++x) {
82                 uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel();
83                 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
84                 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
85                 *dst = SkUnPreMultiply::PMColorToColor(*src);
86             }
87         }
88     }
89 
90     // Finally, if we are performing a jpeg encode, we must convert to BGR.
91     void* pixels = bitmap.getPixels();
92     size_t rowBytes = bitmap.rowBytes();
93     SkAutoMalloc pixelStorage;
94     WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
95     if (SkEncodedImageFormat::kJPEG == format) {
96         formatDesired = GUID_WICPixelFormat24bppBGR;
97         rowBytes = SkAlign4(bitmap.width() * 3);
98         pixelStorage.reset(rowBytes * bitmap.height());
99         for (int y = 0; y < bitmap.height(); y++) {
100             uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes);
101             for (int x = 0; x < bitmap.width(); x++) {
102                 uint32_t bgra = *bitmap.getAddr32(x, y);
103                 dstRow[0] = (uint8_t) (bgra >>  0);
104                 dstRow[1] = (uint8_t) (bgra >>  8);
105                 dstRow[2] = (uint8_t) (bgra >> 16);
106                 dstRow += 3;
107             }
108         }
109 
110         pixels = pixelStorage.get();
111     }
112 
113 
114     //Initialize COM.
115     SkAutoCoInitialize scopedCo;
116     if (!scopedCo.succeeded()) {
117         return false;
118     }
119 
120     HRESULT hr = S_OK;
121 
122     //Create Windows Imaging Component ImagingFactory.
123     SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
124     if (SUCCEEDED(hr)) {
125         hr = CoCreateInstance(
126             CLSID_WICImagingFactory
127             , nullptr
128             , CLSCTX_INPROC_SERVER
129             , IID_PPV_ARGS(&piImagingFactory)
130         );
131     }
132 
133     //Convert the SkWStream to an IStream.
134     SkTScopedComPtr<IStream> piStream;
135     if (SUCCEEDED(hr)) {
136         hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
137     }
138 
139     //Create an encode of the appropriate type.
140     SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
141     if (SUCCEEDED(hr)) {
142         hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
143     }
144 
145     if (SUCCEEDED(hr)) {
146         hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
147     }
148 
149     //Create a the frame.
150     SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
151     SkTScopedComPtr<IPropertyBag2> piPropertybag;
152     if (SUCCEEDED(hr)) {
153         hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
154     }
155 
156     if (SUCCEEDED(hr)) {
157         PROPBAG2 name = { 0 };
158         name.dwType = PROPBAG2_TYPE_DATA;
159         name.vt = VT_R4;
160         name.pstrName = L"ImageQuality";
161 
162         VARIANT value;
163         VariantInit(&value);
164         value.vt = VT_R4;
165         value.fltVal = (FLOAT)(quality / 100.0);
166 
167         //Ignore result code.
168         //  This returns E_FAIL if the named property is not in the bag.
169         //TODO(bungeman) enumerate the properties,
170         //  write and set hr iff property exists.
171         piPropertybag->Write(1, &name, &value);
172     }
173     if (SUCCEEDED(hr)) {
174         hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
175     }
176 
177     //Set the size of the frame.
178     const UINT width = bitmap.width();
179     const UINT height = bitmap.height();
180     if (SUCCEEDED(hr)) {
181         hr = piBitmapFrameEncode->SetSize(width, height);
182     }
183 
184     //Set the pixel format of the frame.  If native encoded format cannot match BGRA,
185     //it will choose the closest pixel format that it supports.
186     WICPixelFormatGUID formatGUID = formatDesired;
187     if (SUCCEEDED(hr)) {
188         hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
189     }
190     if (SUCCEEDED(hr)) {
191         //Be sure the image format is the one requested.
192         hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
193     }
194 
195     //Write the pixels into the frame.
196     if (SUCCEEDED(hr)) {
197         hr = piBitmapFrameEncode->WritePixels(height,
198                                               (UINT) rowBytes,
199                                               (UINT) rowBytes * height,
200                                               reinterpret_cast<BYTE*>(pixels));
201     }
202 
203     if (SUCCEEDED(hr)) {
204         hr = piBitmapFrameEncode->Commit();
205     }
206 
207     if (SUCCEEDED(hr)) {
208         hr = piEncoder->Commit();
209     }
210 
211     return SUCCEEDED(hr);
212 }
213 
214 #endif // defined(SK_BUILD_FOR_WIN32)
215