blob: 6d355c1d47c6398c437e654a774695fbd199e6dc [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.com9df621d2011-06-23 21:43:52 +000031#include "SkAutoCoInitialize.h"
Hal Canary95e3c052017-01-11 12:44:43 -050032#include "SkAutoMalloc.h"
msarettd442d322016-03-25 11:08:14 -070033#include "SkBitmap.h"
Hal Canarydb683012016-11-23 08:55:18 -070034#include "SkImageEncoderPriv.h"
bungeman@google.com9df621d2011-06-23 21:43:52 +000035#include "SkIStream.h"
Hal Canary95e3c052017-01-11 12:44:43 -050036#include "SkImageEncoder.h"
bungeman@google.com242bb892011-06-22 20:42:34 +000037#include "SkStream.h"
bungeman@google.com9df621d2011-06-23 21:43:52 +000038#include "SkTScopedComPtr.h"
Ben Wagner4d1955c2017-03-10 13:08:15 -050039#include "SkTemplates.h"
robertphillips@google.com59bfb122012-11-08 15:06:52 +000040#include "SkUnPreMultiply.h"
Hal Canary95e3c052017-01-11 12:44:43 -050041#include <wincodec.h>
bungeman@google.com242bb892011-06-22 20:42:34 +000042
bungeman@google.comc18143e2013-01-11 20:02:32 +000043//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
Hal Canary1fcc4042016-11-30 17:07:59 -050052bool SkEncodeImageWithWIC(SkWStream* stream, const SkPixmap& pixmap,
53 SkEncodedImageFormat format, int quality) {
bungeman@google.com242bb892011-06-22 20:42:34 +000054 GUID type;
Hal Canary1fcc4042016-11-30 17:07:59 -050055 switch (format) {
Hal Canarydb683012016-11-23 08:55:18 -070056 case SkEncodedImageFormat::kJPEG:
bungeman@google.com242bb892011-06-22 20:42:34 +000057 type = GUID_ContainerFormatJpeg;
58 break;
Hal Canarydb683012016-11-23 08:55:18 -070059 case SkEncodedImageFormat::kPNG:
bungeman@google.com242bb892011-06-22 20:42:34 +000060 type = GUID_ContainerFormatPng;
61 break;
62 default:
63 return false;
64 }
Hal Canary1fcc4042016-11-30 17:07:59 -050065 SkBitmap bitmapOrig;
66 if (!bitmapOrig.installPixels(pixmap)) {
67 return false;
68 }
69 bitmapOrig.setImmutable();
bungeman@google.com242bb892011-06-22 20:42:34 +000070
msarett36c38cb2016-08-15 18:52:17 -070071 // First convert to BGRA if necessary.
72 SkBitmap bitmap;
73 if (!bitmapOrig.copyTo(&bitmap, kBGRA_8888_SkColorType)) {
74 return false;
bungeman@google.com22b49502011-08-01 19:37:43 +000075 }
76
msarett36c38cb2016-08-15 18:52:17 -070077 // 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();
robertphillips@google.com59bfb122012-11-08 15:06:52 +000083 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
84 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
robertphillips@google.com59bfb122012-11-08 15:06:52 +000085 *dst = SkUnPreMultiply::PMColorToColor(*src);
86 }
87 }
88 }
89
msarett36c38cb2016-08-15 18:52:17 -070090 // 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;
Hal Canary1fcc4042016-11-30 17:07:59 -050095 if (SkEncodedImageFormat::kJPEG == format) {
msarett36c38cb2016-08-15 18:52:17 -070096 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
bungeman@google.com242bb892011-06-22 20:42:34 +0000114 //Initialize COM.
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000115 SkAutoCoInitialize scopedCo;
116 if (!scopedCo.succeeded()) {
117 return false;
118 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000119
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000120 HRESULT hr = S_OK;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000121
bungeman@google.com242bb892011-06-22 20:42:34 +0000122 //Create Windows Imaging Component ImagingFactory.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000123 SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
bungeman@google.com242bb892011-06-22 20:42:34 +0000124 if (SUCCEEDED(hr)) {
125 hr = CoCreateInstance(
126 CLSID_WICImagingFactory
halcanary96fcdcc2015-08-27 07:41:13 -0700127 , nullptr
bungeman@google.com242bb892011-06-22 20:42:34 +0000128 , CLSCTX_INPROC_SERVER
129 , IID_PPV_ARGS(&piImagingFactory)
130 );
131 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000132
bungeman@google.com9df621d2011-06-23 21:43:52 +0000133 //Convert the SkWStream to an IStream.
134 SkTScopedComPtr<IStream> piStream;
bungeman@google.com242bb892011-06-22 20:42:34 +0000135 if (SUCCEEDED(hr)) {
bungeman@google.com9df621d2011-06-23 21:43:52 +0000136 hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
bungeman@google.com242bb892011-06-22 20:42:34 +0000137 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000138
bungeman@google.com242bb892011-06-22 20:42:34 +0000139 //Create an encode of the appropriate type.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000140 SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
bungeman@google.com242bb892011-06-22 20:42:34 +0000141 if (SUCCEEDED(hr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700142 hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
bungeman@google.com242bb892011-06-22 20:42:34 +0000143 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000144
bungeman@google.com242bb892011-06-22 20:42:34 +0000145 if (SUCCEEDED(hr)) {
146 hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
147 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000148
bungeman@google.com242bb892011-06-22 20:42:34 +0000149 //Create a the frame.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000150 SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
151 SkTScopedComPtr<IPropertyBag2> piPropertybag;
bungeman@google.com242bb892011-06-22 20:42:34 +0000152 if (SUCCEEDED(hr)) {
153 hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
154 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000155
bungeman@google.com242bb892011-06-22 20:42:34 +0000156 if (SUCCEEDED(hr)) {
157 PROPBAG2 name = { 0 };
158 name.dwType = PROPBAG2_TYPE_DATA;
159 name.vt = VT_R4;
160 name.pstrName = L"ImageQuality";
rmistry@google.comd6176b02012-08-23 18:14:13 +0000161
bungeman@google.com242bb892011-06-22 20:42:34 +0000162 VARIANT value;
163 VariantInit(&value);
164 value.vt = VT_R4;
165 value.fltVal = (FLOAT)(quality / 100.0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000166
bungeman@google.com242bb892011-06-22 20:42:34 +0000167 //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 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000176
bungeman@google.com242bb892011-06-22 20:42:34 +0000177 //Set the size of the frame.
msarett36c38cb2016-08-15 18:52:17 -0700178 const UINT width = bitmap.width();
179 const UINT height = bitmap.height();
bungeman@google.com242bb892011-06-22 20:42:34 +0000180 if (SUCCEEDED(hr)) {
181 hr = piBitmapFrameEncode->SetSize(width, height);
182 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000183
msarette820dfe2016-03-18 12:13:47 -0700184 //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.
bungeman@google.com242bb892011-06-22 20:42:34 +0000186 WICPixelFormatGUID formatGUID = formatDesired;
187 if (SUCCEEDED(hr)) {
188 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
189 }
msarett60316a92016-03-21 13:35:43 -0700190 if (SUCCEEDED(hr)) {
191 //Be sure the image format is the one requested.
192 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
193 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000194
bungeman@google.com242bb892011-06-22 20:42:34 +0000195 //Write the pixels into the frame.
196 if (SUCCEEDED(hr)) {
msarett36c38cb2016-08-15 18:52:17 -0700197 hr = piBitmapFrameEncode->WritePixels(height,
198 (UINT) rowBytes,
199 (UINT) rowBytes * height,
200 reinterpret_cast<BYTE*>(pixels));
bungeman@google.com242bb892011-06-22 20:42:34 +0000201 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000202
bungeman@google.com242bb892011-06-22 20:42:34 +0000203 if (SUCCEEDED(hr)) {
204 hr = piBitmapFrameEncode->Commit();
205 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000206
bungeman@google.com242bb892011-06-22 20:42:34 +0000207 if (SUCCEEDED(hr)) {
208 hr = piEncoder->Commit();
209 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000210
bungeman@google.com242bb892011-06-22 20:42:34 +0000211 return SUCCEEDED(hr);
212}
213
mtklein1ee76512015-11-02 10:20:27 -0800214#endif // defined(SK_BUILD_FOR_WIN32)