Home | History | Annotate | Download | only in ports
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #define WIN32_LEAN_AND_MEAN
     11 #include <Windows.h>
     12 #include <wincodec.h>
     13 #include "SkAutoCoInitialize.h"
     14 #include "SkImageDecoder.h"
     15 #include "SkImageEncoder.h"
     16 #include "SkIStream.h"
     17 #include "SkMovie.h"
     18 #include "SkStream.h"
     19 #include "SkTScopedComPtr.h"
     20 #include "SkUnPreMultiply.h"
     21 
     22 //All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
     23 //In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
     24 //but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
     25 //Undo this #define if it has been done so that we link against the symbols
     26 //we intended to link against on all SDKs.
     27 #if defined(CLSID_WICImagingFactory)
     28 #undef CLSID_WICImagingFactory
     29 #endif
     30 
     31 class SkImageDecoder_WIC : public SkImageDecoder {
     32 protected:
     33     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
     34 };
     35 
     36 bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     37     //Initialize COM.
     38     SkAutoCoInitialize scopedCo;
     39     if (!scopedCo.succeeded()) {
     40         return false;
     41     }
     42 
     43     HRESULT hr = S_OK;
     44 
     45     //Create Windows Imaging Component ImagingFactory.
     46     SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
     47     if (SUCCEEDED(hr)) {
     48         hr = CoCreateInstance(
     49             CLSID_WICImagingFactory
     50             , NULL
     51             , CLSCTX_INPROC_SERVER
     52             , IID_PPV_ARGS(&piImagingFactory)
     53         );
     54     }
     55 
     56     //Convert SkStream to IStream.
     57     SkTScopedComPtr<IStream> piStream;
     58     if (SUCCEEDED(hr)) {
     59         hr = SkIStream::CreateFromSkStream(stream, false, &piStream);
     60     }
     61 
     62     //Make sure we're at the beginning of the stream.
     63     if (SUCCEEDED(hr)) {
     64         LARGE_INTEGER liBeginning = { 0 };
     65         hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL);
     66     }
     67 
     68     //Create the decoder from the stream content.
     69     SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder;
     70     if (SUCCEEDED(hr)) {
     71         hr = piImagingFactory->CreateDecoderFromStream(
     72             piStream.get()                    //Image to be decoded
     73             , NULL                            //No particular vendor
     74             , WICDecodeMetadataCacheOnDemand  //Cache metadata when needed
     75             , &piBitmapDecoder                //Pointer to the decoder
     76         );
     77     }
     78 
     79     //Get the first frame from the decoder.
     80     SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode;
     81     if (SUCCEEDED(hr)) {
     82         hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode);
     83     }
     84 
     85     //Get the BitmapSource interface of the frame.
     86     SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal;
     87     if (SUCCEEDED(hr)) {
     88         hr = piBitmapFrameDecode->QueryInterface(
     89             IID_PPV_ARGS(&piBitmapSourceOriginal)
     90         );
     91     }
     92 
     93     //Get the size of the bitmap.
     94     UINT width;
     95     UINT height;
     96     if (SUCCEEDED(hr)) {
     97         hr = piBitmapSourceOriginal->GetSize(&width, &height);
     98     }
     99 
    100     //Exit early if we're only looking for the bitmap bounds.
    101     if (SUCCEEDED(hr)) {
    102         bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
    103         if (SkImageDecoder::kDecodeBounds_Mode == mode) {
    104             return true;
    105         }
    106         if (!this->allocPixelRef(bm, NULL)) {
    107             return false;
    108         }
    109     }
    110 
    111     //Create a format converter.
    112     SkTScopedComPtr<IWICFormatConverter> piFormatConverter;
    113     if (SUCCEEDED(hr)) {
    114         hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
    115     }
    116 
    117     if (SUCCEEDED(hr)) {
    118         hr = piFormatConverter->Initialize(
    119             piBitmapSourceOriginal.get()      //Input bitmap to convert
    120             , GUID_WICPixelFormat32bppPBGRA   //Destination pixel format
    121             , WICBitmapDitherTypeNone         //Specified dither patterm
    122             , NULL                            //Specify a particular palette
    123             , 0.f                             //Alpha threshold
    124             , WICBitmapPaletteTypeCustom      //Palette translation type
    125         );
    126     }
    127 
    128     //Get the BitmapSource interface of the format converter.
    129     SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted;
    130     if (SUCCEEDED(hr)) {
    131         hr = piFormatConverter->QueryInterface(
    132             IID_PPV_ARGS(&piBitmapSourceConverted)
    133         );
    134     }
    135 
    136     //Copy the pixels into the bitmap.
    137     if (SUCCEEDED(hr)) {
    138         SkAutoLockPixels alp(*bm);
    139         bm->eraseColor(SK_ColorTRANSPARENT);
    140         const int stride = bm->rowBytes();
    141         hr = piBitmapSourceConverted->CopyPixels(
    142             NULL,                             //Get all the pixels
    143             stride,
    144             stride * height,
    145             reinterpret_cast<BYTE *>(bm->getPixels())
    146         );
    147 
    148         // Note: we don't need to premultiply here since we specified PBGRA
    149         bm->computeAndSetOpaquePredicate();
    150     }
    151 
    152     return SUCCEEDED(hr);
    153 }
    154 
    155 /////////////////////////////////////////////////////////////////////////
    156 
    157 SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
    158     return SkNEW(SkImageDecoder_WIC);
    159 }
    160 
    161 /////////////////////////////////////////////////////////////////////////
    162 
    163 SkMovie* SkMovie::DecodeStream(SkStream* stream) {
    164     return NULL;
    165 }
    166 
    167 /////////////////////////////////////////////////////////////////////////
    168 
    169 class SkImageEncoder_WIC : public SkImageEncoder {
    170 public:
    171     SkImageEncoder_WIC(Type t) : fType(t) {}
    172 
    173 protected:
    174     virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
    175 
    176 private:
    177     Type fType;
    178 };
    179 
    180 bool SkImageEncoder_WIC::onEncode(SkWStream* stream
    181                                 , const SkBitmap& bitmapOrig
    182                                 , int quality)
    183 {
    184     GUID type;
    185     switch (fType) {
    186         case kJPEG_Type:
    187             type = GUID_ContainerFormatJpeg;
    188             break;
    189         case kPNG_Type:
    190             type = GUID_ContainerFormatPng;
    191             break;
    192         default:
    193             return false;
    194     }
    195 
    196     //Convert to 8888 if needed.
    197     const SkBitmap* bitmap;
    198     SkBitmap bitmapCopy;
    199     if (SkBitmap::kARGB_8888_Config == bitmapOrig.config() && bitmapOrig.isOpaque()) {
    200         bitmap = &bitmapOrig;
    201     } else {
    202         if (!bitmapOrig.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config)) {
    203             return false;
    204         }
    205         bitmap = &bitmapCopy;
    206     }
    207 
    208     // We cannot use PBGRA so we need to unpremultiply ourselves
    209     if (!bitmap->isOpaque()) {
    210         SkAutoLockPixels alp(*bitmap);
    211 
    212         uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap->getPixels());
    213         for (int y = 0; y < bitmap->height(); ++y) {
    214             for (int x = 0; x < bitmap->width(); ++x) {
    215                 uint8_t* bytes = pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel();
    216 
    217                 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
    218                 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
    219 
    220                 *dst = SkUnPreMultiply::PMColorToColor(*src);
    221             }
    222         }
    223     }
    224 
    225     //Initialize COM.
    226     SkAutoCoInitialize scopedCo;
    227     if (!scopedCo.succeeded()) {
    228         return false;
    229     }
    230 
    231     HRESULT hr = S_OK;
    232 
    233     //Create Windows Imaging Component ImagingFactory.
    234     SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
    235     if (SUCCEEDED(hr)) {
    236         hr = CoCreateInstance(
    237             CLSID_WICImagingFactory
    238             , NULL
    239             , CLSCTX_INPROC_SERVER
    240             , IID_PPV_ARGS(&piImagingFactory)
    241         );
    242     }
    243 
    244     //Convert the SkWStream to an IStream.
    245     SkTScopedComPtr<IStream> piStream;
    246     if (SUCCEEDED(hr)) {
    247         hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
    248     }
    249 
    250     //Create an encode of the appropriate type.
    251     SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
    252     if (SUCCEEDED(hr)) {
    253         hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder);
    254     }
    255 
    256     if (SUCCEEDED(hr)) {
    257         hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
    258     }
    259 
    260     //Create a the frame.
    261     SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
    262     SkTScopedComPtr<IPropertyBag2> piPropertybag;
    263     if (SUCCEEDED(hr)) {
    264         hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
    265     }
    266 
    267     if (SUCCEEDED(hr)) {
    268         PROPBAG2 name = { 0 };
    269         name.dwType = PROPBAG2_TYPE_DATA;
    270         name.vt = VT_R4;
    271         name.pstrName = L"ImageQuality";
    272 
    273         VARIANT value;
    274         VariantInit(&value);
    275         value.vt = VT_R4;
    276         value.fltVal = (FLOAT)(quality / 100.0);
    277 
    278         //Ignore result code.
    279         //  This returns E_FAIL if the named property is not in the bag.
    280         //TODO(bungeman) enumerate the properties,
    281         //  write and set hr iff property exists.
    282         piPropertybag->Write(1, &name, &value);
    283     }
    284     if (SUCCEEDED(hr)) {
    285         hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
    286     }
    287 
    288     //Set the size of the frame.
    289     const UINT width = bitmap->width();
    290     const UINT height = bitmap->height();
    291     if (SUCCEEDED(hr)) {
    292         hr = piBitmapFrameEncode->SetSize(width, height);
    293     }
    294 
    295     //Set the pixel format of the frame.
    296     const WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
    297     WICPixelFormatGUID formatGUID = formatDesired;
    298     if (SUCCEEDED(hr)) {
    299         hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
    300     }
    301     if (SUCCEEDED(hr)) {
    302         //Be sure the image format is the one requested.
    303         hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
    304     }
    305 
    306     //Write the pixels into the frame.
    307     if (SUCCEEDED(hr)) {
    308         SkAutoLockPixels alp(*bitmap);
    309         hr = piBitmapFrameEncode->WritePixels(
    310             height
    311             , bitmap->rowBytes()
    312             , bitmap->rowBytes()*height
    313             , reinterpret_cast<BYTE*>(bitmap->getPixels()));
    314     }
    315 
    316     if (SUCCEEDED(hr)) {
    317         hr = piBitmapFrameEncode->Commit();
    318     }
    319 
    320     if (SUCCEEDED(hr)) {
    321         hr = piEncoder->Commit();
    322     }
    323 
    324     return SUCCEEDED(hr);
    325 }
    326 
    327 SkImageEncoder* SkImageEncoder::Create(Type t) {
    328     switch (t) {
    329         case kJPEG_Type:
    330         case kPNG_Type:
    331             break;
    332         default:
    333             return NULL;
    334     }
    335     return SkNEW_ARGS(SkImageEncoder_WIC, (t));
    336 }
    337