//---------------------------------------------------------------------------- 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; }