• 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_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 
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 (!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