Home | History | Annotate | Download | only in ports
      1 /*
      2  * 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.
      6  */
      7 
      8 #include "SkTypes.h"
      9 
     10 #if defined(SK_BUILD_FOR_WIN)
     11 
     12 #include "SkAutoCoInitialize.h"
     13 #include "SkAutoMalloc.h"
     14 #include "SkBitmap.h"
     15 #include "SkImageEncoderPriv.h"
     16 #include "SkIStream.h"
     17 #include "SkImageEncoder.h"
     18 #include "SkStream.h"
     19 #include "SkTScopedComPtr.h"
     20 #include "SkTemplates.h"
     21 #include "SkUnPreMultiply.h"
     22 #include <wincodec.h>
     23 
     24 //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 
     33 bool SkEncodeImageWithWIC(SkWStream* stream, const SkPixmap& pixmap,
     34                           SkEncodedImageFormat format, int quality) {
     35     GUID type;
     36     switch (format) {
     37         case SkEncodedImageFormat::kJPEG:
     38             type = GUID_ContainerFormatJpeg;
     39             break;
     40         case SkEncodedImageFormat::kPNG:
     41             type = GUID_ContainerFormatPng;
     42             break;
     43         default:
     44             return false;
     45     }
     46     SkBitmap bitmapOrig;
     47     if (!bitmapOrig.installPixels(pixmap)) {
     48         return false;
     49     }
     50     bitmapOrig.setImmutable();
     51 
     52     // First convert to BGRA if necessary.
     53     SkBitmap bitmap;
     54     if (!bitmap.tryAllocPixels(bitmapOrig.info().makeColorType(kBGRA_8888_SkColorType)) ||
     55         !bitmapOrig.readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0))
     56     {
     57         return false;
     58     }
     59 
     60     // 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();
     66                 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
     67                 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
     68                 *dst = SkUnPreMultiply::PMColorToColor(*src);
     69             }
     70         }
     71     }
     72 
     73     // 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;
     78     if (SkEncodedImageFormat::kJPEG == format) {
     79         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);
     86                 dstRow[0] = (uint8_t) (bgra >>  0);
     87                 dstRow[1] = (uint8_t) (bgra >>  8);
     88                 dstRow[2] = (uint8_t) (bgra >> 16);
     89                 dstRow += 3;
     90             }
     91         }
     92 
     93         pixels = pixelStorage.get();
     94     }
     95 
     96 
     97     //Initialize COM.
     98     SkAutoCoInitialize scopedCo;
     99     if (!scopedCo.succeeded()) {
    100         return false;
    101     }
    102 
    103     HRESULT hr = S_OK;
    104 
    105     //Create Windows Imaging Component ImagingFactory.
    106     SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
    107     if (SUCCEEDED(hr)) {
    108         hr = CoCreateInstance(
    109             CLSID_WICImagingFactory
    110             , nullptr
    111             , CLSCTX_INPROC_SERVER
    112             , IID_PPV_ARGS(&piImagingFactory)
    113         );
    114     }
    115 
    116     //Convert the SkWStream to an IStream.
    117     SkTScopedComPtr<IStream> piStream;
    118     if (SUCCEEDED(hr)) {
    119         hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
    120     }
    121 
    122     //Create an encode of the appropriate type.
    123     SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
    124     if (SUCCEEDED(hr)) {
    125         hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
    126     }
    127 
    128     if (SUCCEEDED(hr)) {
    129         hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
    130     }
    131 
    132     //Create a the frame.
    133     SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
    134     SkTScopedComPtr<IPropertyBag2> piPropertybag;
    135     if (SUCCEEDED(hr)) {
    136         hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
    137     }
    138 
    139     if (SUCCEEDED(hr)) {
    140         PROPBAG2 name;
    141         memset(&name, 0, sizeof(name));
    142         name.dwType = PROPBAG2_TYPE_DATA;
    143         name.vt = VT_R4;
    144         name.pstrName = const_cast<LPOLESTR>(L"ImageQuality");
    145 
    146         VARIANT value;
    147         VariantInit(&value);
    148         value.vt = VT_R4;
    149         value.fltVal = (FLOAT)(quality / 100.0);
    150 
    151         //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     }
    160 
    161     //Set the size of the frame.
    162     const UINT width = bitmap.width();
    163     const UINT height = bitmap.height();
    164     if (SUCCEEDED(hr)) {
    165         hr = piBitmapFrameEncode->SetSize(width, height);
    166     }
    167 
    168     //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.
    170     WICPixelFormatGUID formatGUID = formatDesired;
    171     if (SUCCEEDED(hr)) {
    172         hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
    173     }
    174     if (SUCCEEDED(hr)) {
    175         //Be sure the image format is the one requested.
    176         hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
    177     }
    178 
    179     //Write the pixels into the frame.
    180     if (SUCCEEDED(hr)) {
    181         hr = piBitmapFrameEncode->WritePixels(height,
    182                                               (UINT) rowBytes,
    183                                               (UINT) rowBytes * height,
    184                                               reinterpret_cast<BYTE*>(pixels));
    185     }
    186 
    187     if (SUCCEEDED(hr)) {
    188         hr = piBitmapFrameEncode->Commit();
    189     }
    190 
    191     if (SUCCEEDED(hr)) {
    192         hr = piEncoder->Commit();
    193     }
    194 
    195     return SUCCEEDED(hr);
    196 }
    197 
    198 #endif // defined(SK_BUILD_FOR_WIN)
    199