This tutorial shows how to use the Windows Imaging Component (WIC) to save image files. After completing the tutorial, register any or all of the LEAD WIC-Enabled Codecs. You will then be able to save any of these image formats without recompiling the demo.
Start with the tutorial that you created in WIC-Enabled Codecs C++ Tutorial: Loading an Image File
First, you will create a Save dialog by hooking into the standard Windows Save Common Dialog.
IDD_DIALOG_SAVE_TEMPLATE DIALOGEX 0, 0, 423, 77 STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_SYSMENU FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN LTEXT "&Pixel Format:",IDC_STATIC,67,2,53,8 COMBOBOX IDC_COMBO_PIXEL_FORMAT,130,0,147,213,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP END
#include <vector> // To maintain a dynamic array with information about each encoder #include <dlgs.h> // Contains constants for accessing Save Dialog items
#include "resource.h"
Next, customize the standard Windows Common Save dialog. The Save as type static text box will programmatically be changed to display Encoders. In this combo box, all the available WIC-Enabled Encoders on your system will be displayed. Also, a Pixel Format combo box is added. This will display the available pixel formats for the selected encoder.
// SaveDlg.cpp #include "stdafx.h" extern HINSTANCE hInst; extern IWICBitmapSource *gpiBitmapSource; IWICImagingFactory *gpiImagingFactory = NULL; class CEncoderData { public: CEncoderData(IWICBitmapEncoderInfo *piBitmapEncoderInfo, WCHAR *pszExtensions) { m_piBitmapEncoderInfo = piBitmapEncoderInfo; m_csExtensions = pszExtensions; GetDefaultExtension(); } ~CEncoderData() { RELEASE_INTERFACE(m_piBitmapEncoderInfo); } public: IWICBitmapEncoderInfo *m_piBitmapEncoderInfo; CString m_csExtensions; CString m_csDefaultExtension; void GetDefaultExtension() { WCHAR seps[] = L","; WCHAR *psz = NULL; WCHAR *next_token = NULL; WCHAR *token = NULL; size_t uLen = CString::StringLength(m_csExtensions) + 1; if (uLen > 0) { psz = new WCHAR[uLen]; wcscpy_s(psz, uLen, m_csExtensions.GetBuffer()); token = wcstok_s(psz, seps, &next_token); while (token != NULL) { if (wcslen(token) == 4) m_csDefaultExtension = token; token = wcstok_s( NULL, seps, &next_token); } } if (psz == NULL) { delete psz; psz = NULL; } } }; std::vector <CEncoderData *> gEncoderData; HRESULT EnumerateEncoders(CString &csFilter) { HRESULT hr = S_OK; IEnumUnknown *piEnumUnknown = NULL; gEncoderData.clear(); IFS(gpiImagingFactory->CreateComponentEnumerator(WICEncoder, WICComponentEnumerateRefresh, &piEnumUnknown)); if (SUCCEEDED(hr)) { ULONG num = 0; IUnknown *piUnknown = NULL; piEnumUnknown->Reset(); while ((S_OK == piEnumUnknown->Next(1, &piUnknown, &num)) && (1 == num)) { CString csFriendlyName; IWICBitmapEncoderInfo *piBitmapEncoderInfo = NULL; IFS(piUnknown->QueryInterface(IID_IWICBitmapEncoderInfo, reinterpret_cast<void**>(&piBitmapEncoderInfo))); // Friendly name WCHAR *pszFriendlyName = NULL; UINT uActual = 0; IFS(piBitmapEncoderInfo->GetFriendlyName(0, NULL, &uActual)); if (uActual > 0) { pszFriendlyName = new WCHAR[uActual+1]; if (pszFriendlyName) { memset(pszFriendlyName, 0, sizeof(WCHAR) * (uActual + 1)); IFS(piBitmapEncoderInfo->GetFriendlyName(uActual, pszFriendlyName, &uActual)); } } // Extension WCHAR *pszExtensions = NULL; IFS(piBitmapEncoderInfo->GetFileExtensions(0, NULL, &uActual)); if (uActual>0) { pszExtensions = new WCHAR[uActual+1]; if (pszExtensions) { memset(pszExtensions, 0, sizeof(WCHAR) * (uActual + 1)); IFS(piBitmapEncoderInfo->GetFileExtensions(uActual, pszExtensions, &uActual)); } } CString cs; cs.Format(L"s(s)", pszFriendlyName, pszExtensions); csFilter = csFilter + cs + L"|" + pszExtensions + L"|"; CEncoderData *pData = new CEncoderData(piBitmapEncoderInfo, pszExtensions); gEncoderData.push_back(pData); // Cleanup DELETE_POINTER(pszFriendlyName); RELEASE_INTERFACE(piBitmapEncoderInfo); RELEASE_INTERFACE(piUnknown); } // Terminate and replace the "|" character with a NULL csFilter.Replace(L".", L"*."); csFilter = csFilter + L"|"; INT uLen = CString::StringLength(csFilter); for (INT i=0; i < uLen; i++) { if (csFilter[i] == L'|') csFilter.SetAt(i, L'\0'); if (csFilter[i] == L',') csFilter.SetAt(i, L';'); } } return hr; } //********************************************************************************** // PixelFormat Combo Box //********************************************************************************** class CPixelFormatData { public: CPixelFormatData(GUID guid) { m_guid = guid; } ~CPixelFormatData() { } GUID m_guid; }; // Cleanup for PixelFormat Combo Box void FreePixelFormatComboBox(HWND hDlg) { HWND hDlgItem = GetDlgItem(hDlg, IDC_COMBO_PIXEL_FORMAT); LRESULT nCount = SendMessage(hDlgItem, CB_GETCOUNT, 0, 0); CPixelFormatData* pData = NULL; for (LONG_PTR i=0; i < nCount; i++) { LRESULT lResult = SendMessage(hDlgItem, CB_GETITEMDATA, i, 0); if (lResult != CB_ERR) { pData = (CPixelFormatData*)lResult; if (pData) delete pData; } } SendMessage(hDlgItem, CB_RESETCONTENT, (WPARAM)0, (LPARAM)0); } CEncoderData *GetEncoderData(HWND hDlg) { CEncoderData *pData = NULL; HWND hWndParent = GetParent(hDlg); LRESULT nIndex = SendMessage(GetDlgItem(hWndParent, cmb1), CB_GETCURSEL, (WPARAM)0, (LPARAM)0); if (nIndex >=0) pData = (CEncoderData *)gEncoderData[nIndex]; return pData; } HRESULT EnumeratePixelFormat(HWND hDlg) { HRESULT hr = S_OK; FreePixelFormatComboBox(hDlg); CEncoderData *pData = GetEncoderData(hDlg); if (!pData) return hr; if (((LRESULT)pData != CB_ERR) && pData && pData->m_piBitmapEncoderInfo) { UINT uActual = 0; // Pixel Formats GUID *guids = NULL; IFS(pData->m_piBitmapEncoderInfo->GetPixelFormats(0, NULL, &uActual)); if (uActual > 0) { IWICComponentInfo *piComponentInfo = NULL; guids = new GUID[uActual]; IFS(pData->m_piBitmapEncoderInfo->GetPixelFormats(uActual, guids, &uActual)); for (UINT i = 0; i < uActual; i++) { IFS(gpiImagingFactory->CreateComponentInfo(guids[i], &piComponentInfo)); // Friendly name WCHAR *pszFriendlyName = NULL; UINT uActual = 0; IFS(piComponentInfo->GetFriendlyName(0, NULL, &uActual)); if (uActual > 0) { pszFriendlyName = new WCHAR[uActual+1]; if (pszFriendlyName) { memset(pszFriendlyName, 0, sizeof(WCHAR) * (uActual + 1)); IFS(piComponentInfo->GetFriendlyName(uActual, pszFriendlyName, &uActual)); } } LRESULT nIndex = SendMessage(GetDlgItem(hDlg, IDC_COMBO_PIXEL_FORMAT), CB_ADDSTRING, 0, (LPARAM)pszFriendlyName); CPixelFormatData *pData = new CPixelFormatData(guids[i]); SendMessage(GetDlgItem(hDlg, IDC_COMBO_PIXEL_FORMAT), CB_SETITEMDATA, nIndex, (LPARAM)pData); } DELETE_POINTER(guids); RELEASE_INTERFACE(piComponentInfo); } SendMessage(GetDlgItem(hDlg, IDC_COMBO_PIXEL_FORMAT), CB_SETCURSEL, 0, 0); } return hr; } GUID GetSelectedPixelFormat(HWND hDlg) { GUID guid = GUID_NULL; HWND hDlgItem = GetDlgItem(hDlg, IDC_COMBO_PIXEL_FORMAT); LRESULT nIndex = SendMessage(hDlgItem, CB_GETCURSEL, 0, 0); LRESULT lResult = SendMessage(hDlgItem, CB_GETITEMDATA, nIndex, 0); if (lResult == CB_ERR) return guid; CPixelFormatData *pData = (CPixelFormatData *)lResult; if (pData) { guid = pData->m_guid; } return guid; } //********************************************************************************** // Misc //********************************************************************************** class CMySaveDlgData { public: CMySaveDlgData() { m_guidContainer = GUID_NULL; m_guidPixelFormat = GUID_WICPixelFormatUndefined; m_nPixelFormatIndex = -1; } ~CMySaveDlgData() { } GUID m_guidContainer; GUID m_guidPixelFormat; // member variables for index of combo items INT m_nPixelFormatIndex; }; void SetComboBoxIndex(HWND hDlg, INT nID, INT nIndex) { if (nIndex > 0) SendMessage(GetDlgItem(hDlg, nID), CB_SETCURSEL, (WPARAM)nIndex, 0); } HRESULT InitDialog(HWND hDlg, LPARAM lParam) { HRESULT hr =S_OK; CMySaveDlgData *pData = NULL; if (!lParam) return hr; LPOPENFILENAME pOpenFileName = (LPOPENFILENAME)lParam; pData = (CMySaveDlgData*)pOpenFileName->lCustData; if (!pData) return hr; FreePixelFormatComboBox(hDlg); EnumeratePixelFormat(hDlg); SetComboBoxIndex(hDlg, IDC_COMBO_PIXEL_FORMAT, pData->m_nPixelFormatIndex); HWND hWndParent = GetParent(hDlg); SetWindowText(GetDlgItem(hWndParent, stc2), L"&Encoders:"); SetWindowText(GetDlgItem(hWndParent, IDC_STATIC_PIXEL_FORMAT), L"&Pixel Format:"); return hr; } void FreeDialog(HWND hDlg) { FreePixelFormatComboBox(hDlg); } // Append appropriate extension to file name void AppendAppropriateExtension(HWND hDlg) { HWND hWndParent = GetParent(hDlg); CEncoderData *pData = GetEncoderData(hDlg); if (!pData) return; WCHAR szFileName[MAX_PATH] = {0}; UINT uRet = GetDlgItemText(GetParent(hDlg), cmb13, szFileName, MAX_PATH); if (uRet > 0) { WCHAR drive[MAX_PATH] = {0}; WCHAR dir[MAX_PATH] = {0}; WCHAR fname[MAX_PATH] = {0}; WCHAR ext[MAX_PATH] = {0}; DWORD dwFlags = GetFileAttributes(szFileName); if (((dwFlags & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) || (dwFlags == INVALID_FILE_ATTRIBUTES)) { _wsplitpath_s(szFileName, drive, MAX_PATH, dir, MAX_PATH, fname, MAX_PATH, ext, MAX_PATH ); CString cs; cs.Format(L"ssss", drive, dir ,fname, pData->m_csDefaultExtension); SetDlgItemText(hWndParent, cmb13, cs.GetBuffer()); } } } UINT_PTR CALLBACK MySaveHookProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: InitDialog(hDlg, lParam); return (INT_PTR)TRUE; case WM_DESTROY: FreeDialog(hDlg); return 0; //case WM_SIZE: // ArrangeDialogItems(hDlg); // return 0; case WM_NOTIFY: { LPOFNOTIFYW lpOfNotify = (LPOFNOTIFY) lParam; switch (lpOfNotify->hdr.code) { case CDN_TYPECHANGE: EnumeratePixelFormat(hDlg); AppendAppropriateExtension(hDlg); break; case CDN_FILEOK: { CMySaveDlgData *pMySaveDlgData = (CMySaveDlgData *)lpOfNotify->lpOFN->lCustData; if (pMySaveDlgData) { // Get container guid pMySaveDlgData->m_guidContainer = GUID_NULL; CEncoderData *pEncoderData = (CEncoderData *)gEncoderData[lpOfNotify->lpOFN->nFilterIndex-1]; pEncoderData->m_piBitmapEncoderInfo->GetContainerFormat(&pMySaveDlgData->m_guidContainer); // Get pixel format pMySaveDlgData->m_guidPixelFormat = GetSelectedPixelFormat(hDlg); pMySaveDlgData->m_nPixelFormatIndex = (INT)SendMessage(GetDlgItem(hDlg, IDC_COMBO_PIXEL_FORMAT),CB_GETCURSEL, (WPARAM)0, (LPARAM)0); } } break; } } return 0; // return value ignored case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return 0; } HRESULT MySaveDialog(HWND hWnd) { HRESULT hr = S_OK; static OPENFILENAME ofn = {0}; CString csFilters; WCHAR szTitle[MAX_PATH] = L"Save As..."; WCHAR szFileTitle[MAX_PATH] = L"MyFileTitle"; WCHAR szDefExt[4] = {0}; WCHAR szFile[MAX_PATH] = {0}; IFS(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*) &gpiImagingFactory)); EnumerateEncoders(csFilters); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFilter = csFilters.GetBuffer();//szFilter; // Find the first 'LEAD' filter ofn.nFilterIndex = 0; ofn.lpstrFile= szFile; ofn.nMaxFile = sizeof(szFile)/ sizeof(*szFile); ofn.lpstrFileTitle = szFileTitle; ofn.nMaxFileTitle = sizeof(szFileTitle); ofn.lpstrInitialDir = (LPWSTR)NULL; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_ENABLETEMPLATE | OFN_ENABLESIZING | OFN_ENABLEHOOK; ofn.lpstrTitle = szTitle; ofn.lpstrDefExt = szDefExt; ofn.hInstance = hInst; ofn.lpTemplateName = MAKEINTRESOURCE(IDD_DIALOG_SAVE_TEMPLATE); ofn.lpfnHook = MySaveHookProc; static CMySaveDlgData mySaveDlgData; ofn.lCustData = (LPARAM)&mySaveDlgData; if (GetSaveFileName(&ofn)) { HCURSOR hCursorPrev = SetCursor(LoadCursor(NULL, IDC_WAIT)); //hr = EncoderSave(ofn.lpstrFile, &mySaveDlgData); SetCursor(hCursorPrev); } RELEASE_INTERFACE(gpiImagingFactory); return hr; }
// Forward declarations of functions included in this code module: HRESULT MySaveDialog(HWND hWnd);
case ID_FILE_SAVE: MySaveDialog(hWnd); break;
BOOL FreeGlobalBitmaps() { BOOL bRet = FALSE; // Release any existing global bitmaps if (gpGdiPlusBitmap) { delete gpGdiPlusBitmap; gpGdiPlusBitmap= NULL; bRet = TRUE; } if (gpiBitmapSource) { gpiBitmapSource->Release(); gpiBitmapSource = NULL; bRet = TRUE; } return bRet; } void SetGlobalBitmaps(IWICBitmapSource *piBitmapSource) { Bitmap *pGdiPlusBitmap = NULL; BYTE *pbGdiPlusBuffer = NULL; if (!piBitmapSource) return; BitmapSourceToGdiPlusBitmap(piBitmapSource, &pGdiPlusBitmap, &pbGdiPlusBuffer); if (pGdiPlusBitmap) { FreeGlobalBitmaps(); gpGdiPlusBitmap= pGdiPlusBitmap; gpiBitmapSource = piBitmapSource; gpiBitmapSource->AddRef(); } }
hr = EncoderSave(ofn.lpstrFile, &mySaveDlgData);
UINT PaletteColorCount(GUID guidPixelFormat) { UINT uPaletteColorCount = 0; if (GUID_WICPixelFormat1bppIndexed == guidPixelFormat) { uPaletteColorCount = 2; } else if (GUID_WICPixelFormat2bppIndexed == guidPixelFormat) { uPaletteColorCount = 4; } else if (GUID_WICPixelFormat4bppIndexed == guidPixelFormat) { uPaletteColorCount = 16; } else if (GUID_WICPixelFormat8bppIndexed == guidPixelFormat) { uPaletteColorCount = 256; } return uPaletteColorCount; } HRESULT CopyBitmapSourcePalette(IWICBitmapSource *piSource, IWICPalette **ppiPalette, UINT uPaletteColorCount) { HRESULT hr = S_OK; if (!piSource) return E_INVALIDARG; IWICPalette *piPalette = NULL; IFS(gpiImagingFactory->CreatePalette(&piPalette)); // CopyPalette my fail, if the source does not have a palette hr = piSource->CopyPalette(piPalette); if (FAILED(hr)) { // Try to create a palette hr = S_OK; IFS(piPalette->InitializeFromBitmap(piSource, uPaletteColorCount, FALSE)); } if (SUCCEEDED(hr)) { if (ppiPalette) { (*ppiPalette) = piPalette; (*ppiPalette)->AddRef(); } } RELEASE_INTERFACE(piPalette); return hr; } // Converts a BitmapSource to a BitmapFrame HRESULT BitmapSourceToBitmapFrameEncode(IWICBitmapSource *piSource, IWICBitmapFrameEncode *piBitmapFrame, GUID pixelFormat) { HRESULT hr = S_OK; UINT uWidth = 0; UINT uHeight = 0; IWICPalette *piPalette = NULL; IFS(piSource->GetSize(&uWidth, &uHeight)); IFS(piBitmapFrame->SetSize(uWidth, uHeight)); UINT uPaletteColorCount = PaletteColorCount(pixelFormat); if (uPaletteColorCount > 0) { CopyBitmapSourcePalette(piSource, &piPalette, uPaletteColorCount); if (piPalette) { piBitmapFrame->SetPalette(piPalette); } } WICRect rc = {0,0,uWidth, uHeight}; IFS(piBitmapFrame->WriteSource(piSource, &rc)); RELEASE_INTERFACE(piPalette); return hr; } HRESULT EncoderSave( LPCWSTR pszFileSave, CMySaveDlgData *pMySaveDlgData) { IWICStream *piStream = NULL; HRESULT hr = S_OK; IWICBitmapEncoder *piEncoder = NULL; IWICBitmapFrameEncode *piBitmapFrame = NULL; IPropertyBag2 *piPropertyBag = NULL; // *********************************************** // Create the appropriate encoder //************************************************ IFS(gpiImagingFactory->CreateStream(&piStream)); IFS(piStream->InitializeFromFilename(pszFileSave, GENERIC_WRITE)); IFS(gpiImagingFactory->CreateEncoder(pMySaveDlgData->m_guidContainer, NULL, &piEncoder)); IFS(piEncoder->Initialize(piStream, WICBitmapEncoderNoCache)); IFS(piEncoder->CreateNewFrame(&piBitmapFrame, &piPropertyBag)); IFS(piBitmapFrame->Initialize(piPropertyBag)); IFS(piBitmapFrame->SetPixelFormat(&pMySaveDlgData->m_guidPixelFormat)); IFS(BitmapSourceToBitmapFrameEncode(gpiBitmapSource, piBitmapFrame, pMySaveDlgData->m_guidPixelFormat)); // *********************************************** // Save the file //************************************************ IFS(piBitmapFrame->Commit()); IFS(piEncoder->Commit()); // *********************************************** // Cleanup //************************************************ RELEASE_INTERFACE(piBitmapFrame); RELEASE_INTERFACE(piEncoder); RELEASE_INTERFACE(piStream); return hr; }