//---------------------------------------------------------------------------- Texture2* WICFileIO::Load(std::string const& filename, bool wantMipmaps) { // Start COM and create WIC. ComInitializer comInitializer; if (!comInitializer.IsInitialized()) { LogError("Unable to initialize COM for WIC."); return nullptr; } // 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 nullptr; } // Create a decoder based on the file name. std::wstring wfilename = Environment::Convert(filename); ComObject<IWICBitmapDecoder> wicDecoder; hr = wicFactory->CreateDecoderFromFilename(wfilename.c_str(), nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &wicDecoder); if (FAILED(hr)) { LogError("wicFactory->CreateDecoderFromFilename failed (" + filename + ")."); return nullptr; } // Create a WIC decoder. ComObject<IWICBitmapFrameDecode> wicFrameDecode; hr = wicDecoder->GetFrame(0, &wicFrameDecode); if (FAILED(hr)) { LogError("wicDecoder->GetFrame failed."); return nullptr; } // Get the pixel format of the image. WICPixelFormatGUID wicSourceGUID; hr = wicFrameDecode->GetPixelFormat(&wicSourceGUID); if (FAILED(hr)) { LogError("wicFrameDecode->GetPixelFormat failed."); return nullptr; } // Find the supported WIC input pixel format that matches a Texture2 // format. If a matching format is not found, the returned texture // is an R8G8B8A8 format with texels converted from the source format. WICPixelFormatGUID wicConvertGUID = GUID_WICPixelFormat32bppRGBA; DFType gtformat = DF_R8G8B8A8_UNORM; for (int i = 0; i < NUM_LOAD_FORMATS; ++i) { if (IsEqualGUID(wicSourceGUID, *msLoadFormatMap[i].wicInputGUID)) { // Determine whether there is a conversion format. if (msLoadFormatMap[i].wicConvertGUID) { wicConvertGUID = *msLoadFormatMap[i].wicConvertGUID; } else { wicConvertGUID = *msLoadFormatMap[i].wicInputGUID; } gtformat = msLoadFormatMap[i].gtFormat; break; } } // The wicFrameDecode value is used for no conversion. If the decoder // does not support the format in the texture, then a conversion is // required. IWICBitmapSource* wicBitmapSource = wicFrameDecode; ComObject<IWICFormatConverter> wicFormatConverter; if (!IsEqualGUID(wicSourceGUID, wicConvertGUID)) { // Create a WIC format converter. hr = wicFactory->CreateFormatConverter(&wicFormatConverter); if (FAILED(hr)) { LogError("wicFactory->CreateFormatConverter failed."); return false; } // Initialize format converter to convert the input texture format // to the nearest format supported by the decoder. hr = wicFormatConverter->Initialize(wicFrameDecode, wicConvertGUID, WICBitmapDitherTypeNone, nullptr, 0.0, WICBitmapPaletteTypeCustom); if (FAILED(hr)) { LogError("wicFormatConverter->Initialize failed."); return false; } // Use the format converter. wicBitmapSource = wicFormatConverter; } // Get the image dimensions. UINT width, height; hr = wicBitmapSource->GetSize(&width, &height); if (FAILED(hr)) { LogError("wicBitmapSource->GetSize failed."); return nullptr; } // Create the 2D texture and compute the stride and image size. std::unique_ptr<Texture2> texture(new Texture2(gtformat, width, height, wantMipmaps)); UINT const stride = width * texture->GetElementSize(); UINT const imageSize = stride * height; // Copy the pixels from the decoder to the texture. hr = wicBitmapSource->CopyPixels(nullptr, stride, imageSize, texture->Get<BYTE>()); if (FAILED(hr)) { LogError("wicBitmapSource->CopyPixels failed."); return nullptr; } return texture.release(); }
//---------------------------------------------------------------------------- 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; }