blob: 9be95727cad2b0cd16d82da44ae46def4cbc6092 [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
msarette8597a42016-03-24 10:41:47 -070053 // DO NOT USE this constructor. This exists only so SkForceLinking can
54 // link the WIC image encoder.
55 SkImageEncoder_WIC() {}
bungeman@google.com242bb892011-06-22 20:42:34 +000056
57protected:
58 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
59
60private:
61 Type fType;
62};
63
64bool SkImageEncoder_WIC::onEncode(SkWStream* stream
bungeman@google.com22b49502011-08-01 19:37:43 +000065 , const SkBitmap& bitmapOrig
bungeman@google.com242bb892011-06-22 20:42:34 +000066 , int quality)
67{
68 GUID type;
69 switch (fType) {
scroggo@google.com4c6adf92013-04-17 21:07:55 +000070 case kBMP_Type:
71 type = GUID_ContainerFormatBmp;
72 break;
scroggo@google.com4c6adf92013-04-17 21:07:55 +000073 case kICO_Type:
74 type = GUID_ContainerFormatIco;
75 break;
bungeman@google.com242bb892011-06-22 20:42:34 +000076 case kJPEG_Type:
77 type = GUID_ContainerFormatJpeg;
78 break;
79 case kPNG_Type:
80 type = GUID_ContainerFormatPng;
81 break;
82 default:
83 return false;
84 }
85
msarett36c38cb2016-08-15 18:52:17 -070086 // First convert to BGRA if necessary.
87 SkBitmap bitmap;
88 if (!bitmapOrig.copyTo(&bitmap, kBGRA_8888_SkColorType)) {
89 return false;
bungeman@google.com22b49502011-08-01 19:37:43 +000090 }
91
msarett36c38cb2016-08-15 18:52:17 -070092 // WIC expects unpremultiplied pixels. Unpremultiply if necessary.
93 if (kPremul_SkAlphaType == bitmap.alphaType()) {
94 uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
95 for (int y = 0; y < bitmap.height(); ++y) {
96 for (int x = 0; x < bitmap.width(); ++x) {
97 uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel();
robertphillips@google.com59bfb122012-11-08 15:06:52 +000098 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
99 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
robertphillips@google.com59bfb122012-11-08 15:06:52 +0000100 *dst = SkUnPreMultiply::PMColorToColor(*src);
101 }
102 }
103 }
104
msarett36c38cb2016-08-15 18:52:17 -0700105 // Finally, if we are performing a jpeg encode, we must convert to BGR.
106 void* pixels = bitmap.getPixels();
107 size_t rowBytes = bitmap.rowBytes();
108 SkAutoMalloc pixelStorage;
109 WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
110 if (kJPEG_Type == fType) {
111 formatDesired = GUID_WICPixelFormat24bppBGR;
112 rowBytes = SkAlign4(bitmap.width() * 3);
113 pixelStorage.reset(rowBytes * bitmap.height());
114 for (int y = 0; y < bitmap.height(); y++) {
115 uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes);
116 for (int x = 0; x < bitmap.width(); x++) {
117 uint32_t bgra = *bitmap.getAddr32(x, y);
118 dstRow[0] = (uint8_t) (bgra >> 0);
119 dstRow[1] = (uint8_t) (bgra >> 8);
120 dstRow[2] = (uint8_t) (bgra >> 16);
121 dstRow += 3;
122 }
123 }
124
125 pixels = pixelStorage.get();
126 }
127
128
bungeman@google.com242bb892011-06-22 20:42:34 +0000129 //Initialize COM.
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000130 SkAutoCoInitialize scopedCo;
131 if (!scopedCo.succeeded()) {
132 return false;
133 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000134
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000135 HRESULT hr = S_OK;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000136
bungeman@google.com242bb892011-06-22 20:42:34 +0000137 //Create Windows Imaging Component ImagingFactory.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000138 SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
bungeman@google.com242bb892011-06-22 20:42:34 +0000139 if (SUCCEEDED(hr)) {
140 hr = CoCreateInstance(
141 CLSID_WICImagingFactory
halcanary96fcdcc2015-08-27 07:41:13 -0700142 , nullptr
bungeman@google.com242bb892011-06-22 20:42:34 +0000143 , CLSCTX_INPROC_SERVER
144 , IID_PPV_ARGS(&piImagingFactory)
145 );
146 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000147
bungeman@google.com9df621d2011-06-23 21:43:52 +0000148 //Convert the SkWStream to an IStream.
149 SkTScopedComPtr<IStream> piStream;
bungeman@google.com242bb892011-06-22 20:42:34 +0000150 if (SUCCEEDED(hr)) {
bungeman@google.com9df621d2011-06-23 21:43:52 +0000151 hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
bungeman@google.com242bb892011-06-22 20:42:34 +0000152 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000153
bungeman@google.com242bb892011-06-22 20:42:34 +0000154 //Create an encode of the appropriate type.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000155 SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
bungeman@google.com242bb892011-06-22 20:42:34 +0000156 if (SUCCEEDED(hr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700157 hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
bungeman@google.com242bb892011-06-22 20:42:34 +0000158 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000159
bungeman@google.com242bb892011-06-22 20:42:34 +0000160 if (SUCCEEDED(hr)) {
161 hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
162 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000163
bungeman@google.com242bb892011-06-22 20:42:34 +0000164 //Create a the frame.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000165 SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
166 SkTScopedComPtr<IPropertyBag2> piPropertybag;
bungeman@google.com242bb892011-06-22 20:42:34 +0000167 if (SUCCEEDED(hr)) {
168 hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
169 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000170
bungeman@google.com242bb892011-06-22 20:42:34 +0000171 if (SUCCEEDED(hr)) {
172 PROPBAG2 name = { 0 };
173 name.dwType = PROPBAG2_TYPE_DATA;
174 name.vt = VT_R4;
175 name.pstrName = L"ImageQuality";
rmistry@google.comd6176b02012-08-23 18:14:13 +0000176
bungeman@google.com242bb892011-06-22 20:42:34 +0000177 VARIANT value;
178 VariantInit(&value);
179 value.vt = VT_R4;
180 value.fltVal = (FLOAT)(quality / 100.0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000181
bungeman@google.com242bb892011-06-22 20:42:34 +0000182 //Ignore result code.
183 // This returns E_FAIL if the named property is not in the bag.
184 //TODO(bungeman) enumerate the properties,
185 // write and set hr iff property exists.
186 piPropertybag->Write(1, &name, &value);
187 }
188 if (SUCCEEDED(hr)) {
189 hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
190 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000191
bungeman@google.com242bb892011-06-22 20:42:34 +0000192 //Set the size of the frame.
msarett36c38cb2016-08-15 18:52:17 -0700193 const UINT width = bitmap.width();
194 const UINT height = bitmap.height();
bungeman@google.com242bb892011-06-22 20:42:34 +0000195 if (SUCCEEDED(hr)) {
196 hr = piBitmapFrameEncode->SetSize(width, height);
197 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000198
msarette820dfe2016-03-18 12:13:47 -0700199 //Set the pixel format of the frame. If native encoded format cannot match BGRA,
200 //it will choose the closest pixel format that it supports.
bungeman@google.com242bb892011-06-22 20:42:34 +0000201 WICPixelFormatGUID formatGUID = formatDesired;
202 if (SUCCEEDED(hr)) {
203 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
204 }
msarett60316a92016-03-21 13:35:43 -0700205 if (SUCCEEDED(hr)) {
206 //Be sure the image format is the one requested.
207 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
208 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000209
bungeman@google.com242bb892011-06-22 20:42:34 +0000210 //Write the pixels into the frame.
211 if (SUCCEEDED(hr)) {
msarett36c38cb2016-08-15 18:52:17 -0700212 hr = piBitmapFrameEncode->WritePixels(height,
213 (UINT) rowBytes,
214 (UINT) rowBytes * height,
215 reinterpret_cast<BYTE*>(pixels));
bungeman@google.com242bb892011-06-22 20:42:34 +0000216 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000217
bungeman@google.com242bb892011-06-22 20:42:34 +0000218 if (SUCCEEDED(hr)) {
219 hr = piBitmapFrameEncode->Commit();
220 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000221
bungeman@google.com242bb892011-06-22 20:42:34 +0000222 if (SUCCEEDED(hr)) {
223 hr = piEncoder->Commit();
224 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000225
bungeman@google.com242bb892011-06-22 20:42:34 +0000226 return SUCCEEDED(hr);
227}
228
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000229///////////////////////////////////////////////////////////////////////////////
230
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000231static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) {
bungeman@google.com242bb892011-06-22 20:42:34 +0000232 switch (t) {
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000233 case SkImageEncoder::kBMP_Type:
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000234 case SkImageEncoder::kICO_Type:
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000235 case SkImageEncoder::kPNG_Type:
msarett36c38cb2016-08-15 18:52:17 -0700236 case SkImageEncoder::kJPEG_Type:
bungeman@google.com242bb892011-06-22 20:42:34 +0000237 break;
238 default:
halcanary96fcdcc2015-08-27 07:41:13 -0700239 return nullptr;
bungeman@google.com242bb892011-06-22 20:42:34 +0000240 }
halcanary385fe4d2015-08-26 13:07:48 -0700241 return new SkImageEncoder_WIC(t);
bungeman@google.com242bb892011-06-22 20:42:34 +0000242}
scroggo@google.com4c6adf92013-04-17 21:07:55 +0000243
scroggo@google.comb5571b32013-09-25 21:34:24 +0000244static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_wic_factory);
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000245
msarette8597a42016-03-24 10:41:47 -0700246DEFINE_ENCODER_CREATOR(ImageEncoder_WIC);
msarett39b54952016-03-23 12:26:29 -0700247
mtklein1ee76512015-11-02 10:20:27 -0800248#endif // defined(SK_BUILD_FOR_WIN32)