blob: 6524526bdbda52b3e1bf2a8059843d964e6bf03d [file] [log] [blame]
bungeman@google.com242bb892011-06-22 20:42:34 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * 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.
bungeman@google.com242bb892011-06-22 20:42:34 +00006 */
7
bungeman@google.com85302962013-10-11 20:04:08 +00008#include "SkTypes.h"
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
mtklein1ee76512015-11-02 10:20:27 -080010#if defined(SK_BUILD_FOR_WIN32)
11
bungeman@google.com85302962013-10-11 20:04:08 +000012// 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
bungeman@google.com242bb892011-06-22 20:42:34 +000031#include <wincodec.h>
bungeman@google.com9df621d2011-06-23 21:43:52 +000032#include "SkAutoCoInitialize.h"
msarettd442d322016-03-25 11:08:14 -070033#include "SkBitmap.h"
bungeman@google.com242bb892011-06-22 20:42:34 +000034#include "SkImageEncoder.h"
bungeman@google.com9df621d2011-06-23 21:43:52 +000035#include "SkIStream.h"
bungeman@google.com242bb892011-06-22 20:42:34 +000036#include "SkStream.h"
bungeman@google.com9df621d2011-06-23 21:43:52 +000037#include "SkTScopedComPtr.h"
robertphillips@google.com59bfb122012-11-08 15:06:52 +000038#include "SkUnPreMultiply.h"
bungeman@google.com242bb892011-06-22 20:42:34 +000039
bungeman@google.comc18143e2013-01-11 20:02:32 +000040//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
41//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
42//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
43//Undo this #define if it has been done so that we link against the symbols
44//we intended to link against on all SDKs.
45#if defined(CLSID_WICImagingFactory)
46#undef CLSID_WICImagingFactory
47#endif
48
bungeman@google.com242bb892011-06-22 20:42:34 +000049class SkImageEncoder_WIC : public SkImageEncoder {
50public:
51 SkImageEncoder_WIC(Type t) : fType(t) {}
halcanary9d524f22016-03-29 09:03:52 -070052
bungeman@google.com242bb892011-06-22 20:42:34 +000053protected:
54 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
55
56private:
57 Type fType;
58};
59
60bool SkImageEncoder_WIC::onEncode(SkWStream* stream
bungeman@google.com22b49502011-08-01 19:37:43 +000061 , const SkBitmap& bitmapOrig
bungeman@google.com242bb892011-06-22 20:42:34 +000062 , int quality)
63{
64 GUID type;
65 switch (fType) {
66 case kJPEG_Type:
67 type = GUID_ContainerFormatJpeg;
68 break;
69 case kPNG_Type:
70 type = GUID_ContainerFormatPng;
71 break;
72 default:
73 return false;
74 }
75
msarett36c38cb2016-08-15 18:52:17 -070076 // First convert to BGRA if necessary.
77 SkBitmap bitmap;
78 if (!bitmapOrig.copyTo(&bitmap, kBGRA_8888_SkColorType)) {
79 return false;
bungeman@google.com22b49502011-08-01 19:37:43 +000080 }
81
msarett36c38cb2016-08-15 18:52:17 -070082 // WIC expects unpremultiplied pixels. Unpremultiply if necessary.
83 if (kPremul_SkAlphaType == bitmap.alphaType()) {
84 uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
85 for (int y = 0; y < bitmap.height(); ++y) {
86 for (int x = 0; x < bitmap.width(); ++x) {
87 uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel();
robertphillips@google.com59bfb122012-11-08 15:06:52 +000088 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
89 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
robertphillips@google.com59bfb122012-11-08 15:06:52 +000090 *dst = SkUnPreMultiply::PMColorToColor(*src);
91 }
92 }
93 }
94
msarett36c38cb2016-08-15 18:52:17 -070095 // Finally, if we are performing a jpeg encode, we must convert to BGR.
96 void* pixels = bitmap.getPixels();
97 size_t rowBytes = bitmap.rowBytes();
98 SkAutoMalloc pixelStorage;
99 WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
100 if (kJPEG_Type == fType) {
101 formatDesired = GUID_WICPixelFormat24bppBGR;
102 rowBytes = SkAlign4(bitmap.width() * 3);
103 pixelStorage.reset(rowBytes * bitmap.height());
104 for (int y = 0; y < bitmap.height(); y++) {
105 uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes);
106 for (int x = 0; x < bitmap.width(); x++) {
107 uint32_t bgra = *bitmap.getAddr32(x, y);
108 dstRow[0] = (uint8_t) (bgra >> 0);
109 dstRow[1] = (uint8_t) (bgra >> 8);
110 dstRow[2] = (uint8_t) (bgra >> 16);
111 dstRow += 3;
112 }
113 }
114
115 pixels = pixelStorage.get();
116 }
117
118
bungeman@google.com242bb892011-06-22 20:42:34 +0000119 //Initialize COM.
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000120 SkAutoCoInitialize scopedCo;
121 if (!scopedCo.succeeded()) {
122 return false;
123 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000124
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000125 HRESULT hr = S_OK;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000126
bungeman@google.com242bb892011-06-22 20:42:34 +0000127 //Create Windows Imaging Component ImagingFactory.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000128 SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
bungeman@google.com242bb892011-06-22 20:42:34 +0000129 if (SUCCEEDED(hr)) {
130 hr = CoCreateInstance(
131 CLSID_WICImagingFactory
halcanary96fcdcc2015-08-27 07:41:13 -0700132 , nullptr
bungeman@google.com242bb892011-06-22 20:42:34 +0000133 , CLSCTX_INPROC_SERVER
134 , IID_PPV_ARGS(&piImagingFactory)
135 );
136 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000137
bungeman@google.com9df621d2011-06-23 21:43:52 +0000138 //Convert the SkWStream to an IStream.
139 SkTScopedComPtr<IStream> piStream;
bungeman@google.com242bb892011-06-22 20:42:34 +0000140 if (SUCCEEDED(hr)) {
bungeman@google.com9df621d2011-06-23 21:43:52 +0000141 hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
bungeman@google.com242bb892011-06-22 20:42:34 +0000142 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000143
bungeman@google.com242bb892011-06-22 20:42:34 +0000144 //Create an encode of the appropriate type.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000145 SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
bungeman@google.com242bb892011-06-22 20:42:34 +0000146 if (SUCCEEDED(hr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700147 hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
bungeman@google.com242bb892011-06-22 20:42:34 +0000148 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000149
bungeman@google.com242bb892011-06-22 20:42:34 +0000150 if (SUCCEEDED(hr)) {
151 hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
152 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000153
bungeman@google.com242bb892011-06-22 20:42:34 +0000154 //Create a the frame.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000155 SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
156 SkTScopedComPtr<IPropertyBag2> piPropertybag;
bungeman@google.com242bb892011-06-22 20:42:34 +0000157 if (SUCCEEDED(hr)) {
158 hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
159 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000160
bungeman@google.com242bb892011-06-22 20:42:34 +0000161 if (SUCCEEDED(hr)) {
162 PROPBAG2 name = { 0 };
163 name.dwType = PROPBAG2_TYPE_DATA;
164 name.vt = VT_R4;
165 name.pstrName = L"ImageQuality";
rmistry@google.comd6176b02012-08-23 18:14:13 +0000166
bungeman@google.com242bb892011-06-22 20:42:34 +0000167 VARIANT value;
168 VariantInit(&value);
169 value.vt = VT_R4;
170 value.fltVal = (FLOAT)(quality / 100.0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000171
bungeman@google.com242bb892011-06-22 20:42:34 +0000172 //Ignore result code.
173 // This returns E_FAIL if the named property is not in the bag.
174 //TODO(bungeman) enumerate the properties,
175 // write and set hr iff property exists.
176 piPropertybag->Write(1, &name, &value);
177 }
178 if (SUCCEEDED(hr)) {
179 hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
180 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000181
bungeman@google.com242bb892011-06-22 20:42:34 +0000182 //Set the size of the frame.
msarett36c38cb2016-08-15 18:52:17 -0700183 const UINT width = bitmap.width();
184 const UINT height = bitmap.height();
bungeman@google.com242bb892011-06-22 20:42:34 +0000185 if (SUCCEEDED(hr)) {
186 hr = piBitmapFrameEncode->SetSize(width, height);
187 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000188
msarette820dfe2016-03-18 12:13:47 -0700189 //Set the pixel format of the frame. If native encoded format cannot match BGRA,
190 //it will choose the closest pixel format that it supports.
bungeman@google.com242bb892011-06-22 20:42:34 +0000191 WICPixelFormatGUID formatGUID = formatDesired;
192 if (SUCCEEDED(hr)) {
193 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
194 }
msarett60316a92016-03-21 13:35:43 -0700195 if (SUCCEEDED(hr)) {
196 //Be sure the image format is the one requested.
197 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
198 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000199
bungeman@google.com242bb892011-06-22 20:42:34 +0000200 //Write the pixels into the frame.
201 if (SUCCEEDED(hr)) {
msarett36c38cb2016-08-15 18:52:17 -0700202 hr = piBitmapFrameEncode->WritePixels(height,
203 (UINT) rowBytes,
204 (UINT) rowBytes * height,
205 reinterpret_cast<BYTE*>(pixels));
bungeman@google.com242bb892011-06-22 20:42:34 +0000206 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000207
bungeman@google.com242bb892011-06-22 20:42:34 +0000208 if (SUCCEEDED(hr)) {
209 hr = piBitmapFrameEncode->Commit();
210 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000211
bungeman@google.com242bb892011-06-22 20:42:34 +0000212 if (SUCCEEDED(hr)) {
213 hr = piEncoder->Commit();
214 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000215
bungeman@google.com242bb892011-06-22 20:42:34 +0000216 return SUCCEEDED(hr);
217}
218
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000219///////////////////////////////////////////////////////////////////////////////
220
msarett36931c22016-08-16 15:11:24 -0700221#ifdef SK_USE_WIC_ENCODER
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000222static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) {
bungeman@google.com242bb892011-06-22 20:42:34 +0000223 switch (t) {
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000224 case SkImageEncoder::kPNG_Type:
msarett36c38cb2016-08-15 18:52:17 -0700225 case SkImageEncoder::kJPEG_Type:
bungeman@google.com242bb892011-06-22 20:42:34 +0000226 break;
227 default:
halcanary96fcdcc2015-08-27 07:41:13 -0700228 return nullptr;
bungeman@google.com242bb892011-06-22 20:42:34 +0000229 }
halcanary385fe4d2015-08-26 13:07:48 -0700230 return new SkImageEncoder_WIC(t);
bungeman@google.com242bb892011-06-22 20:42:34 +0000231}
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000232
scroggo@google.comb5571b32013-09-25 21:34:24 +0000233static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_wic_factory);
msarett36931c22016-08-16 15:11:24 -0700234#endif
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000235
msarett36931c22016-08-16 15:11:24 -0700236SkImageEncoder* CreateImageEncoder_WIC(SkImageEncoder::Type type) {
237 return new SkImageEncoder_WIC(type);
238}
msarett39b54952016-03-23 12:26:29 -0700239
mtklein1ee76512015-11-02 10:20:27 -0800240#endif // defined(SK_BUILD_FOR_WIN32)