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 // 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 
     31 #include "SkAutoCoInitialize.h"
     32 #include "SkAutoMalloc.h"
     33 #include "SkBitmap.h"
     34 #include "SkImageEncoderPriv.h"
     35 #include "SkIStream.h"
     36 #include "SkImageEncoder.h"
     37 #include "SkStream.h"
     38 #include "SkTScopedComPtr.h"
     39 #include "SkTemplates.h"
     40 #include "SkUnPreMultiply.h"
     41 #include <wincodec.h>
     42 
     43 //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 
     52 bool SkEncodeImageWithWIC(SkWStream* stream, const SkPixmap& pixmap,
     53                           SkEncodedImageFormat format, int quality) {
     54     GUID type;
     55     switch (format) {
     56         case SkEncodedImageFormat::kJPEG:
     57             type = GUID_ContainerFormatJpeg;
     58             break;
     59         case SkEncodedImageFormat::kPNG:
     60             type = GUID_ContainerFormatPng;
     61             break;
     62         default:
     63             return false;
     64     }
     65     SkBitmap bitmapOrig;
     66     if (!bitmapOrig.installPixels(pixmap)) {
     67         return false;
     68     }
     69     bitmapOrig.setImmutable();
     70 
     71     // First convert to BGRA if necessary.
     72     SkBitmap bitmap;
     73     if (!bitmap.tryAllocPixels(bitmapOrig.info().makeColorType(kBGRA_8888_SkColorType)) ||
     74         !bitmapOrig.readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0))
     75     {
     76         return false;
     77     }
     78 
     79     // WIC expects unpremultiplied pixels.  Unpremultiply if necessary.
     80     if (kPremul_SkAlphaType == bitmap.alphaType()) {
     81         uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
     82         for (int y = 0; y < bitmap.height(); ++y) {
     83             for (int x = 0; x < bitmap.width(); ++x) {
     84                 uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel();
     85                 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
     86                 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
     87                 *dst = SkUnPreMultiply::PMColorToColor(*src);
     88             }
     89         }
     90     }
     91 
     92     // Finally, if we are performing a jpeg encode, we must convert to BGR.
     93     void* pixels = bitmap.getPixels();
     94     size_t rowBytes = bitmap.rowBytes();
     95     SkAutoMalloc pixelStorage;
     96     WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
     97     if (SkEncodedImageFormat::kJPEG == format) {
     98         formatDesired = GUID_WICPixelFormat24bppBGR;
     99         rowBytes = SkAlign4(bitmap.width() * 3);
    100         pixelStorage.reset(rowBytes * bitmap.height());
    101         for (int y = 0; y < bitmap.height(); y++) {
    102             uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes);
    103             for (int x = 0; x < bitmap.width(); x++) {
    104                 uint32_t bgra = *bitmap.getAddr32(x, y);
    105                 dstRow[0] = (uint8_t) (bgra >>  0);
    106                 dstRow[1] = (uint8_t) (bgra >>  8);
    107                 dstRow[2] = (uint8_t) (bgra >> 16);
    108                 dstRow += 3;
    109             }
    110         }
    111 
    112         pixels = pixelStorage.get();
    113     }
    114 
    115 
    116     //Initialize COM.
    117     SkAutoCoInitialize scopedCo;
    118     if (!scopedCo.succeeded()) {
    119         return false;
    120     }
    121 
    122     HRESULT hr = S_OK;
    123 
    124     //Create Windows Imaging Component ImagingFactory.
    125     SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
    126     if (SUCCEEDED(hr)) {
    127         hr = CoCreateInstance(
    128             CLSID_WICImagingFactory
    129             , nullptr
    130             , CLSCTX_INPROC_SERVER
    131             , IID_PPV_ARGS(&piImagingFactory)
    132         );
    133     }
    134 
    135     //Convert the SkWStream to an IStream.
    136     SkTScopedComPtr<IStream> piStream;
    137     if (SUCCEEDED(hr)) {
    138         hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
    139     }
    140 
    141     //Create an encode of the appropriate type.
    142     SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
    143     if (SUCCEEDED(hr)) {
    144         hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
    145     }
    146 
    147     if (SUCCEEDED(hr)) {
    148         hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
    149     }
    150 
    151     //Create a the frame.
    152     SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
    153     SkTScopedComPtr<IPropertyBag2> piPropertybag;
    154     if (SUCCEEDED(hr)) {
    155         hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
    156     }
    157 
    158     if (SUCCEEDED(hr)) {
    159         PROPBAG2 name;
    160         memset(&name, 0, sizeof(name));
    161         name.dwType = PROPBAG2_TYPE_DATA;
    162         name.vt = VT_R4;
    163         name.pstrName = const_cast<LPOLESTR>(L"ImageQuality");
    164 
    165         VARIANT value;
    166         VariantInit(&value);
    167         value.vt = VT_R4;
    168         value.fltVal = (FLOAT)(quality / 100.0);
    169 
    170         //Ignore result code.
    171         //  This returns E_FAIL if the named property is not in the bag.
    172         //TODO(bungeman) enumerate the properties,
    173         //  write and set hr iff property exists.
    174         piPropertybag->Write(1, &name, &value);
    175     }
    176     if (SUCCEEDED(hr)) {
    177         hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
    178     }
    179 
    180     //Set the size of the frame.
    181     const UINT width = bitmap.width();
    182     const UINT height = bitmap.height();
    183     if (SUCCEEDED(hr)) {
    184         hr = piBitmapFrameEncode->SetSize(width, height);
    185     }
    186 
    187     //Set the pixel format of the frame.  If native encoded format cannot match BGRA,
    188     //it will choose the closest pixel format that it supports.
    189     WICPixelFormatGUID formatGUID = formatDesired;
    190     if (SUCCEEDED(hr)) {
    191         hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
    192     }
    193     if (SUCCEEDED(hr)) {
    194         //Be sure the image format is the one requested.
    195         hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
    196     }
    197 
    198     //Write the pixels into the frame.
    199     if (SUCCEEDED(hr)) {
    200         hr = piBitmapFrameEncode->WritePixels(height,
    201                                               (UINT) rowBytes,
    202                                               (UINT) rowBytes * height,
    203                                               reinterpret_cast<BYTE*>(pixels));
    204     }
    205 
    206     if (SUCCEEDED(hr)) {
    207         hr = piBitmapFrameEncode->Commit();
    208     }
    209 
    210     if (SUCCEEDED(hr)) {
    211         hr = piEncoder->Commit();
    212     }
    213 
    214     return SUCCEEDED(hr);
    215 }
    216 
    217 #endif // defined(SK_BUILD_FOR_WIN)
    218