blob: 29f6c5957aa08606e05e5f525799664ad2a70764 [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
Mike Klein8f11d4d2018-01-24 12:42:55 -050010#if defined(SK_BUILD_FOR_WIN)
mtklein1ee76512015-11-02 10:20:27 -080011
bungeman@google.com9df621d2011-06-23 21:43:52 +000012#include "SkAutoCoInitialize.h"
Hal Canary95e3c052017-01-11 12:44:43 -050013#include "SkAutoMalloc.h"
msarettd442d322016-03-25 11:08:14 -070014#include "SkBitmap.h"
Hal Canarydb683012016-11-23 08:55:18 -070015#include "SkImageEncoderPriv.h"
bungeman@google.com9df621d2011-06-23 21:43:52 +000016#include "SkIStream.h"
Hal Canary95e3c052017-01-11 12:44:43 -050017#include "SkImageEncoder.h"
bungeman@google.com242bb892011-06-22 20:42:34 +000018#include "SkStream.h"
bungeman@google.com9df621d2011-06-23 21:43:52 +000019#include "SkTScopedComPtr.h"
Ben Wagner4d1955c2017-03-10 13:08:15 -050020#include "SkTemplates.h"
robertphillips@google.com59bfb122012-11-08 15:06:52 +000021#include "SkUnPreMultiply.h"
Hal Canary95e3c052017-01-11 12:44:43 -050022#include <wincodec.h>
bungeman@google.com242bb892011-06-22 20:42:34 +000023
bungeman@google.comc18143e2013-01-11 20:02:32 +000024//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
25//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
26//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
27//Undo this #define if it has been done so that we link against the symbols
28//we intended to link against on all SDKs.
29#if defined(CLSID_WICImagingFactory)
30#undef CLSID_WICImagingFactory
31#endif
32
Hal Canary1fcc4042016-11-30 17:07:59 -050033bool SkEncodeImageWithWIC(SkWStream* stream, const SkPixmap& pixmap,
34 SkEncodedImageFormat format, int quality) {
bungeman@google.com242bb892011-06-22 20:42:34 +000035 GUID type;
Hal Canary1fcc4042016-11-30 17:07:59 -050036 switch (format) {
Hal Canarydb683012016-11-23 08:55:18 -070037 case SkEncodedImageFormat::kJPEG:
bungeman@google.com242bb892011-06-22 20:42:34 +000038 type = GUID_ContainerFormatJpeg;
39 break;
Hal Canarydb683012016-11-23 08:55:18 -070040 case SkEncodedImageFormat::kPNG:
bungeman@google.com242bb892011-06-22 20:42:34 +000041 type = GUID_ContainerFormatPng;
42 break;
43 default:
44 return false;
45 }
Hal Canary1fcc4042016-11-30 17:07:59 -050046 SkBitmap bitmapOrig;
47 if (!bitmapOrig.installPixels(pixmap)) {
48 return false;
49 }
50 bitmapOrig.setImmutable();
bungeman@google.com242bb892011-06-22 20:42:34 +000051
msarett36c38cb2016-08-15 18:52:17 -070052 // First convert to BGRA if necessary.
53 SkBitmap bitmap;
Matt Sarett68b8e3d2017-04-28 11:15:22 -040054 if (!bitmap.tryAllocPixels(bitmapOrig.info().makeColorType(kBGRA_8888_SkColorType)) ||
55 !bitmapOrig.readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0))
56 {
msarett36c38cb2016-08-15 18:52:17 -070057 return false;
bungeman@google.com22b49502011-08-01 19:37:43 +000058 }
59
msarett36c38cb2016-08-15 18:52:17 -070060 // WIC expects unpremultiplied pixels. Unpremultiply if necessary.
61 if (kPremul_SkAlphaType == bitmap.alphaType()) {
62 uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
63 for (int y = 0; y < bitmap.height(); ++y) {
64 for (int x = 0; x < bitmap.width(); ++x) {
65 uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel();
robertphillips@google.com59bfb122012-11-08 15:06:52 +000066 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
67 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
robertphillips@google.com59bfb122012-11-08 15:06:52 +000068 *dst = SkUnPreMultiply::PMColorToColor(*src);
69 }
70 }
71 }
72
msarett36c38cb2016-08-15 18:52:17 -070073 // Finally, if we are performing a jpeg encode, we must convert to BGR.
74 void* pixels = bitmap.getPixels();
75 size_t rowBytes = bitmap.rowBytes();
76 SkAutoMalloc pixelStorage;
77 WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
Hal Canary1fcc4042016-11-30 17:07:59 -050078 if (SkEncodedImageFormat::kJPEG == format) {
msarett36c38cb2016-08-15 18:52:17 -070079 formatDesired = GUID_WICPixelFormat24bppBGR;
80 rowBytes = SkAlign4(bitmap.width() * 3);
81 pixelStorage.reset(rowBytes * bitmap.height());
82 for (int y = 0; y < bitmap.height(); y++) {
83 uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes);
84 for (int x = 0; x < bitmap.width(); x++) {
85 uint32_t bgra = *bitmap.getAddr32(x, y);
Brian Osman50ea3c02019-02-04 10:01:53 -050086 dstRow[0] = (uint8_t) ((bgra >> 0) & 0xFF);
87 dstRow[1] = (uint8_t) ((bgra >> 8) & 0xFF);
88 dstRow[2] = (uint8_t) ((bgra >> 16) & 0xFF);
msarett36c38cb2016-08-15 18:52:17 -070089 dstRow += 3;
90 }
91 }
92
93 pixels = pixelStorage.get();
94 }
95
96
bungeman@google.com242bb892011-06-22 20:42:34 +000097 //Initialize COM.
bungeman@google.com2e2f3f52011-09-16 15:37:20 +000098 SkAutoCoInitialize scopedCo;
99 if (!scopedCo.succeeded()) {
100 return false;
101 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000102
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000103 HRESULT hr = S_OK;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000104
bungeman@google.com242bb892011-06-22 20:42:34 +0000105 //Create Windows Imaging Component ImagingFactory.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000106 SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
bungeman@google.com242bb892011-06-22 20:42:34 +0000107 if (SUCCEEDED(hr)) {
108 hr = CoCreateInstance(
109 CLSID_WICImagingFactory
halcanary96fcdcc2015-08-27 07:41:13 -0700110 , nullptr
bungeman@google.com242bb892011-06-22 20:42:34 +0000111 , CLSCTX_INPROC_SERVER
112 , IID_PPV_ARGS(&piImagingFactory)
113 );
114 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000115
bungeman@google.com9df621d2011-06-23 21:43:52 +0000116 //Convert the SkWStream to an IStream.
117 SkTScopedComPtr<IStream> piStream;
bungeman@google.com242bb892011-06-22 20:42:34 +0000118 if (SUCCEEDED(hr)) {
bungeman@google.com9df621d2011-06-23 21:43:52 +0000119 hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
bungeman@google.com242bb892011-06-22 20:42:34 +0000120 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000121
bungeman@google.com242bb892011-06-22 20:42:34 +0000122 //Create an encode of the appropriate type.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000123 SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
bungeman@google.com242bb892011-06-22 20:42:34 +0000124 if (SUCCEEDED(hr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700125 hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
bungeman@google.com242bb892011-06-22 20:42:34 +0000126 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000127
bungeman@google.com242bb892011-06-22 20:42:34 +0000128 if (SUCCEEDED(hr)) {
129 hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
130 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000131
bungeman@google.com242bb892011-06-22 20:42:34 +0000132 //Create a the frame.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000133 SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
134 SkTScopedComPtr<IPropertyBag2> piPropertybag;
bungeman@google.com242bb892011-06-22 20:42:34 +0000135 if (SUCCEEDED(hr)) {
136 hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
137 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000138
bungeman@google.com242bb892011-06-22 20:42:34 +0000139 if (SUCCEEDED(hr)) {
Chris Dalton1ef80942017-12-04 12:01:30 -0700140 PROPBAG2 name;
141 memset(&name, 0, sizeof(name));
bungeman@google.com242bb892011-06-22 20:42:34 +0000142 name.dwType = PROPBAG2_TYPE_DATA;
143 name.vt = VT_R4;
Mike Kleinc722f792017-07-31 11:57:21 -0400144 name.pstrName = const_cast<LPOLESTR>(L"ImageQuality");
rmistry@google.comd6176b02012-08-23 18:14:13 +0000145
bungeman@google.com242bb892011-06-22 20:42:34 +0000146 VARIANT value;
147 VariantInit(&value);
148 value.vt = VT_R4;
149 value.fltVal = (FLOAT)(quality / 100.0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000150
bungeman@google.com242bb892011-06-22 20:42:34 +0000151 //Ignore result code.
152 // This returns E_FAIL if the named property is not in the bag.
153 //TODO(bungeman) enumerate the properties,
154 // write and set hr iff property exists.
155 piPropertybag->Write(1, &name, &value);
156 }
157 if (SUCCEEDED(hr)) {
158 hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
159 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000160
bungeman@google.com242bb892011-06-22 20:42:34 +0000161 //Set the size of the frame.
msarett36c38cb2016-08-15 18:52:17 -0700162 const UINT width = bitmap.width();
163 const UINT height = bitmap.height();
bungeman@google.com242bb892011-06-22 20:42:34 +0000164 if (SUCCEEDED(hr)) {
165 hr = piBitmapFrameEncode->SetSize(width, height);
166 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000167
msarette820dfe2016-03-18 12:13:47 -0700168 //Set the pixel format of the frame. If native encoded format cannot match BGRA,
169 //it will choose the closest pixel format that it supports.
bungeman@google.com242bb892011-06-22 20:42:34 +0000170 WICPixelFormatGUID formatGUID = formatDesired;
171 if (SUCCEEDED(hr)) {
172 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
173 }
msarett60316a92016-03-21 13:35:43 -0700174 if (SUCCEEDED(hr)) {
175 //Be sure the image format is the one requested.
176 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
177 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000178
bungeman@google.com242bb892011-06-22 20:42:34 +0000179 //Write the pixels into the frame.
180 if (SUCCEEDED(hr)) {
msarett36c38cb2016-08-15 18:52:17 -0700181 hr = piBitmapFrameEncode->WritePixels(height,
182 (UINT) rowBytes,
183 (UINT) rowBytes * height,
184 reinterpret_cast<BYTE*>(pixels));
bungeman@google.com242bb892011-06-22 20:42:34 +0000185 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000186
bungeman@google.com242bb892011-06-22 20:42:34 +0000187 if (SUCCEEDED(hr)) {
188 hr = piBitmapFrameEncode->Commit();
189 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000190
bungeman@google.com242bb892011-06-22 20:42:34 +0000191 if (SUCCEEDED(hr)) {
192 hr = piEncoder->Commit();
193 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000194
bungeman@google.com242bb892011-06-22 20:42:34 +0000195 return SUCCEEDED(hr);
196}
197
Mike Klein8f11d4d2018-01-24 12:42:55 -0500198#endif // defined(SK_BUILD_FOR_WIN)