예제 #1
0
//----------------------------------------------------------------------------
bool WICFileIO::SaveToPNG(std::string const& filename, Texture2* texture)
{
    if (!texture || !texture->GetData())
    {
        LogError("The texture and its data must exist.");
        return false;
    }

    // Select the WIC format that matches the input texture format.
    WICPixelFormatGUID wicSourceGUID = GUID_WICPixelFormatUndefined;
    for (int i = 0; i < NUM_SAVE_FORMATS; ++i)
    {
        if (msSaveFormatMap[i].gtFormat == texture->GetFormat())
        {
            wicSourceGUID = *msSaveFormatMap[i].wicOutputGUID;
            break;
        }
    }
    if (IsEqualGUID(wicSourceGUID, GUID_WICPixelFormatUndefined))
    {
        LogError("Format " +
            DataFormat::GetName(texture->GetFormat()) +
            "cannot be saved to PNG.");
        return false;
    }

    // Start COM and create WIC.
    ComInitializer comInitializer;
    if (!comInitializer.IsInitialized())
    {
        LogError("Unable to initialize COM for WIC.");
        return false;
    }

    // Create a WIC imaging factory.
    ComObject<IWICImagingFactory> wicFactory;
    HRESULT hr = ::CoCreateInstance(CLSID_WICImagingFactory, nullptr,
        CLSCTX_INPROC_SERVER, IID_IWICImagingFactory,
        reinterpret_cast<LPVOID*>(&wicFactory));
    if (FAILED(hr))
    {
        LogError("Unable to create WIC imaging factory.");
        return false;
    }

    // Create a WIC stream for output.
    ComObject<IWICStream> wicStream;
    hr = wicFactory->CreateStream(&wicStream);
    if (FAILED(hr))
    {
        LogError("wicFactory->CreateStream failed.");
        return false;
    }

    std::wstring wfilename = Environment::Convert(filename);
    hr = wicStream->InitializeFromFilename(wfilename.c_str(), GENERIC_WRITE);
    if (FAILED(hr))
    {
        LogError("wicStream->InitializeFromFilename failed (" +
            filename + ").");
        return false;
    }

    // Create a WIC PNG encoder.
    ComObject<IWICBitmapEncoder> wicEncoder;
    hr = wicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr,
        &wicEncoder);
    if (FAILED(hr))
    {
        LogError("wicFactory->CreateEncoder failed.");
        return false;
    }

    hr = wicEncoder->Initialize(wicStream, WICBitmapEncoderNoCache);
    if (FAILED(hr))
    {
        LogError("wicEncoder->Initialize failed.");
        return false;
    }

    // Create a new frame and a property bag for encoder options.
    ComObject<IWICBitmapFrameEncode> wicFrameEncode;
    ComObject<IPropertyBag2> wicPropertyBag;
    hr = wicEncoder->CreateNewFrame(&wicFrameEncode, &wicPropertyBag);
    if (FAILED(hr))
    {
        LogError("wicEncoder->CreateNewFrame failed.");
        return false;
    }

    // Set the options for the PNG encoder.
    PROPBAG2 option = { 0 };
    VARIANT varValue;

    // Disable interlacing.
    option.pstrName = L"InterlaceOption";
    VariantInit(&varValue);
    varValue.vt = VT_BOOL;
    varValue.boolVal = FALSE;
    hr = wicPropertyBag->Write(1, &option, &varValue);
    if (FAILED(hr))
    {
        LogError("wicPropertyBag->Write failed for InterlaceOption.");
        return false;
    }

    // Disable filtering.
    option.pstrName = L"FilterOption";
    VariantInit(&varValue);
    varValue.vt = VT_UI1;
    varValue.bVal = WICPngFilterNone;
    hr = wicPropertyBag->Write(1, &option, &varValue);
    if (FAILED(hr))
    {
        LogError("wicPropertyBag->Write failed for FilterOption.");
        return false;
    }

    // Initialize the encoder.
    hr = wicFrameEncode->Initialize(wicPropertyBag);
    if (FAILED(hr))
    {
        LogError("wicFrameEncode->Initialize failed.");
        return false;
    }

    // Set the image size.
    UINT width = texture->GetWidth();
    UINT height = texture->GetHeight();
    hr = wicFrameEncode->SetSize(width, height);
    if (FAILED(hr))
    {
        LogError("wicFrameEncode->SetSize failed.");
        return false;
    }

    // Set the image format.
    WICPixelFormatGUID wicTargetGUID = wicSourceGUID;
    hr = wicFrameEncode->SetPixelFormat(&wicTargetGUID);
    if (FAILED(hr))
    {
        LogError("wicFrameEncode->SetPixelFormat failed.");
        return false;
    }

    // Compute the stride and image size.
    UINT const stride = width * texture->GetElementSize();
    UINT const imageSize = stride * height;

    // Create a WIC bitmap to wrap the texture image data.
    ComObject<IWICBitmap> wicTextureBitmap;
    hr = wicFactory->CreateBitmapFromMemory(width, height,
        wicSourceGUID, stride, imageSize, texture->Get<BYTE>(),
        &wicTextureBitmap);
    if (FAILED(hr))
    {
        LogError("wicFactory->CreateBitmapFromMemory failed.");
        return false;
    }

    // The wicTextureBitmap value is used for no conversion.  If the encoder
    // does not support the format in the texture, then a conversion is
    // required.
    IWICBitmapSource* wicBitmapSource = wicTextureBitmap;
    ComObject<IWICFormatConverter> wicFormatConverter;
    if (!IsEqualGUID(wicSourceGUID, wicTargetGUID))
    {
        // Create a WIC format converter.
        hr = wicFactory->CreateFormatConverter(&wicFormatConverter);
        if (FAILED(hr))
        {
            LogError("wicFactory->CreateFormatConverter failed.");
            return false;
        }

        // Initialize the format converter to convert to the nearest format
        // supported by the encoder.
        hr = wicFormatConverter->Initialize(wicTextureBitmap, wicTargetGUID,
            WICBitmapDitherTypeNone, nullptr, 0.0,
            WICBitmapPaletteTypeCustom);
        if (FAILED(hr))
        {
            LogError("wicFormatConverter->Initialize failed.");
            return false;
        }

        // Use the format converter.
        wicBitmapSource = wicFormatConverter;
    }

    // Send the pixels to the encoder.
    hr = wicFrameEncode->WriteSource(wicBitmapSource, nullptr);
    if (FAILED(hr))
    {
        LogError("wicFrameEncode->WriteSource failed.");
        return false;
    }

    // Commit the frame.
    hr = wicFrameEncode->Commit();
    if (FAILED(hr))
    {
        LogError("wicFrameEncode->Commit failed.");
        return false;
    }

    // Commit the encoder.
    hr = wicEncoder->Commit();
    if (FAILED(hr))
    {
        LogError("wicEncoder->Commit failed.");
        return false;
    }

    return true;
}