1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2011 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_WIN)
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h"
13cb93a386Sopenharmony_ci#include "include/core/SkImageEncoder.h"
14cb93a386Sopenharmony_ci#include "include/core/SkStream.h"
15cb93a386Sopenharmony_ci#include "include/core/SkUnPreMultiply.h"
16cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h"
17cb93a386Sopenharmony_ci#include "src/core/SkAutoMalloc.h"
18cb93a386Sopenharmony_ci#include "src/images/SkImageEncoderPriv.h"
19cb93a386Sopenharmony_ci#include "src/utils/win/SkAutoCoInitialize.h"
20cb93a386Sopenharmony_ci#include "src/utils/win/SkIStream.h"
21cb93a386Sopenharmony_ci#include "src/utils/win/SkTScopedComPtr.h"
22cb93a386Sopenharmony_ci#include <wincodec.h>
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ci//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
25cb93a386Sopenharmony_ci//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
26cb93a386Sopenharmony_ci//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
27cb93a386Sopenharmony_ci//Undo this #define if it has been done so that we link against the symbols
28cb93a386Sopenharmony_ci//we intended to link against on all SDKs.
29cb93a386Sopenharmony_ci#if defined(CLSID_WICImagingFactory)
30cb93a386Sopenharmony_ci#undef CLSID_WICImagingFactory
31cb93a386Sopenharmony_ci#endif
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_cibool SkEncodeImageWithWIC(SkWStream* stream, const SkPixmap& pixmap,
34cb93a386Sopenharmony_ci                          SkEncodedImageFormat format, int quality) {
35cb93a386Sopenharmony_ci    GUID type;
36cb93a386Sopenharmony_ci    switch (format) {
37cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kJPEG:
38cb93a386Sopenharmony_ci            type = GUID_ContainerFormatJpeg;
39cb93a386Sopenharmony_ci            break;
40cb93a386Sopenharmony_ci        case SkEncodedImageFormat::kPNG:
41cb93a386Sopenharmony_ci            type = GUID_ContainerFormatPng;
42cb93a386Sopenharmony_ci            break;
43cb93a386Sopenharmony_ci        default:
44cb93a386Sopenharmony_ci            return false;
45cb93a386Sopenharmony_ci    }
46cb93a386Sopenharmony_ci    SkBitmap bitmapOrig;
47cb93a386Sopenharmony_ci    if (!bitmapOrig.installPixels(pixmap)) {
48cb93a386Sopenharmony_ci        return false;
49cb93a386Sopenharmony_ci    }
50cb93a386Sopenharmony_ci    bitmapOrig.setImmutable();
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci    // First convert to BGRA if necessary.
53cb93a386Sopenharmony_ci    SkBitmap bitmap;
54cb93a386Sopenharmony_ci    if (!bitmap.tryAllocPixels(bitmapOrig.info().makeColorType(kBGRA_8888_SkColorType)) ||
55cb93a386Sopenharmony_ci        !bitmapOrig.readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0))
56cb93a386Sopenharmony_ci    {
57cb93a386Sopenharmony_ci        return false;
58cb93a386Sopenharmony_ci    }
59cb93a386Sopenharmony_ci
60cb93a386Sopenharmony_ci    // WIC expects unpremultiplied pixels.  Unpremultiply if necessary.
61cb93a386Sopenharmony_ci    if (kPremul_SkAlphaType == bitmap.alphaType()) {
62cb93a386Sopenharmony_ci        uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
63cb93a386Sopenharmony_ci        for (int y = 0; y < bitmap.height(); ++y) {
64cb93a386Sopenharmony_ci            for (int x = 0; x < bitmap.width(); ++x) {
65cb93a386Sopenharmony_ci                uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel();
66cb93a386Sopenharmony_ci                SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
67cb93a386Sopenharmony_ci                SkColor* dst = reinterpret_cast<SkColor*>(bytes);
68cb93a386Sopenharmony_ci                *dst = SkUnPreMultiply::PMColorToColor(*src);
69cb93a386Sopenharmony_ci            }
70cb93a386Sopenharmony_ci        }
71cb93a386Sopenharmony_ci    }
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ci    // Finally, if we are performing a jpeg encode, we must convert to BGR.
74cb93a386Sopenharmony_ci    void* pixels = bitmap.getPixels();
75cb93a386Sopenharmony_ci    size_t rowBytes = bitmap.rowBytes();
76cb93a386Sopenharmony_ci    SkAutoMalloc pixelStorage;
77cb93a386Sopenharmony_ci    WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
78cb93a386Sopenharmony_ci    if (SkEncodedImageFormat::kJPEG == format) {
79cb93a386Sopenharmony_ci        formatDesired = GUID_WICPixelFormat24bppBGR;
80cb93a386Sopenharmony_ci        rowBytes = SkAlign4(bitmap.width() * 3);
81cb93a386Sopenharmony_ci        pixelStorage.reset(rowBytes * bitmap.height());
82cb93a386Sopenharmony_ci        for (int y = 0; y < bitmap.height(); y++) {
83cb93a386Sopenharmony_ci            uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes);
84cb93a386Sopenharmony_ci            for (int x = 0; x < bitmap.width(); x++) {
85cb93a386Sopenharmony_ci                uint32_t bgra = *bitmap.getAddr32(x, y);
86cb93a386Sopenharmony_ci                dstRow[0] = (uint8_t) ((bgra >>  0) & 0xFF);
87cb93a386Sopenharmony_ci                dstRow[1] = (uint8_t) ((bgra >>  8) & 0xFF);
88cb93a386Sopenharmony_ci                dstRow[2] = (uint8_t) ((bgra >> 16) & 0xFF);
89cb93a386Sopenharmony_ci                dstRow += 3;
90cb93a386Sopenharmony_ci            }
91cb93a386Sopenharmony_ci        }
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_ci        pixels = pixelStorage.get();
94cb93a386Sopenharmony_ci    }
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ci
97cb93a386Sopenharmony_ci    //Initialize COM.
98cb93a386Sopenharmony_ci    SkAutoCoInitialize scopedCo;
99cb93a386Sopenharmony_ci    if (!scopedCo.succeeded()) {
100cb93a386Sopenharmony_ci        return false;
101cb93a386Sopenharmony_ci    }
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci    HRESULT hr = S_OK;
104cb93a386Sopenharmony_ci
105cb93a386Sopenharmony_ci    //Create Windows Imaging Component ImagingFactory.
106cb93a386Sopenharmony_ci    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
107cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
108cb93a386Sopenharmony_ci        hr = CoCreateInstance(
109cb93a386Sopenharmony_ci            CLSID_WICImagingFactory
110cb93a386Sopenharmony_ci            , nullptr
111cb93a386Sopenharmony_ci            , CLSCTX_INPROC_SERVER
112cb93a386Sopenharmony_ci            , IID_PPV_ARGS(&piImagingFactory)
113cb93a386Sopenharmony_ci        );
114cb93a386Sopenharmony_ci    }
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    //Convert the SkWStream to an IStream.
117cb93a386Sopenharmony_ci    SkTScopedComPtr<IStream> piStream;
118cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
119cb93a386Sopenharmony_ci        hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
120cb93a386Sopenharmony_ci    }
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci    //Create an encode of the appropriate type.
123cb93a386Sopenharmony_ci    SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
124cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
125cb93a386Sopenharmony_ci        hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
126cb93a386Sopenharmony_ci    }
127cb93a386Sopenharmony_ci
128cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
129cb93a386Sopenharmony_ci        hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
130cb93a386Sopenharmony_ci    }
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci    //Create a the frame.
133cb93a386Sopenharmony_ci    SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
134cb93a386Sopenharmony_ci    SkTScopedComPtr<IPropertyBag2> piPropertybag;
135cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
136cb93a386Sopenharmony_ci        hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
137cb93a386Sopenharmony_ci    }
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
140cb93a386Sopenharmony_ci        PROPBAG2 name;
141cb93a386Sopenharmony_ci        memset(&name, 0, sizeof(name));
142cb93a386Sopenharmony_ci        name.dwType = PROPBAG2_TYPE_DATA;
143cb93a386Sopenharmony_ci        name.vt = VT_R4;
144cb93a386Sopenharmony_ci        name.pstrName = const_cast<LPOLESTR>(L"ImageQuality");
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ci        VARIANT value;
147cb93a386Sopenharmony_ci        VariantInit(&value);
148cb93a386Sopenharmony_ci        value.vt = VT_R4;
149cb93a386Sopenharmony_ci        value.fltVal = (FLOAT)(quality / 100.0);
150cb93a386Sopenharmony_ci
151cb93a386Sopenharmony_ci        //Ignore result code.
152cb93a386Sopenharmony_ci        //  This returns E_FAIL if the named property is not in the bag.
153cb93a386Sopenharmony_ci        //TODO(bungeman) enumerate the properties,
154cb93a386Sopenharmony_ci        //  write and set hr iff property exists.
155cb93a386Sopenharmony_ci        piPropertybag->Write(1, &name, &value);
156cb93a386Sopenharmony_ci    }
157cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
158cb93a386Sopenharmony_ci        hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
159cb93a386Sopenharmony_ci    }
160cb93a386Sopenharmony_ci
161cb93a386Sopenharmony_ci    //Set the size of the frame.
162cb93a386Sopenharmony_ci    const UINT width = bitmap.width();
163cb93a386Sopenharmony_ci    const UINT height = bitmap.height();
164cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
165cb93a386Sopenharmony_ci        hr = piBitmapFrameEncode->SetSize(width, height);
166cb93a386Sopenharmony_ci    }
167cb93a386Sopenharmony_ci
168cb93a386Sopenharmony_ci    //Set the pixel format of the frame.  If native encoded format cannot match BGRA,
169cb93a386Sopenharmony_ci    //it will choose the closest pixel format that it supports.
170cb93a386Sopenharmony_ci    WICPixelFormatGUID formatGUID = formatDesired;
171cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
172cb93a386Sopenharmony_ci        hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
173cb93a386Sopenharmony_ci    }
174cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
175cb93a386Sopenharmony_ci        //Be sure the image format is the one requested.
176cb93a386Sopenharmony_ci        hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
177cb93a386Sopenharmony_ci    }
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ci    //Write the pixels into the frame.
180cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
181cb93a386Sopenharmony_ci        hr = piBitmapFrameEncode->WritePixels(height,
182cb93a386Sopenharmony_ci                                              (UINT) rowBytes,
183cb93a386Sopenharmony_ci                                              (UINT) rowBytes * height,
184cb93a386Sopenharmony_ci                                              reinterpret_cast<BYTE*>(pixels));
185cb93a386Sopenharmony_ci    }
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
188cb93a386Sopenharmony_ci        hr = piBitmapFrameEncode->Commit();
189cb93a386Sopenharmony_ci    }
190cb93a386Sopenharmony_ci
191cb93a386Sopenharmony_ci    if (SUCCEEDED(hr)) {
192cb93a386Sopenharmony_ci        hr = piEncoder->Commit();
193cb93a386Sopenharmony_ci    }
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_ci    return SUCCEEDED(hr);
196cb93a386Sopenharmony_ci}
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ci#endif // defined(SK_BUILD_FOR_WIN)
199