494 lines
15 KiB
C++
494 lines
15 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
// Modify the following defines if you have to target a platform prior to the ones specified below.
|
|
// Refer to MSDN for the latest info on corresponding values for different platforms.
|
|
|
|
#ifndef WINVER // Allow use of features specific to Windows XP or later.
|
|
#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows.
|
|
#endif
|
|
|
|
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
|
|
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
|
|
#endif
|
|
|
|
#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
|
|
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
|
|
#endif
|
|
|
|
#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later.
|
|
#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE.
|
|
#endif
|
|
|
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
|
|
|
#include <windows.h>
|
|
#include <wincodec.h>
|
|
#include <commdlg.h>
|
|
#include <d2d1.h>
|
|
#include <d2d1helper.h>
|
|
#include "WICViewerD2D.h"
|
|
|
|
|
|
template <typename T>
|
|
inline void SafeRelease(T *&p)
|
|
{
|
|
if (NULL != p)
|
|
{
|
|
p->Release();
|
|
p = NULL;
|
|
}
|
|
}
|
|
|
|
/******************************************************************
|
|
* Application entrypoint *
|
|
******************************************************************/
|
|
|
|
int WINAPI wWinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPWSTR pszCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
|
UNREFERENCED_PARAMETER(pszCmdLine);
|
|
UNREFERENCED_PARAMETER(nCmdShow);
|
|
|
|
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
|
|
|
|
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
{
|
|
DemoApp app;
|
|
hr = app.Initialize(hInstance);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BOOL fRet;
|
|
MSG msg;
|
|
|
|
// Main message loop:
|
|
while ((fRet = GetMessage(&msg, NULL, 0, 0)) != 0)
|
|
{
|
|
if (fRet == -1)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CoUninitialize();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Initialize member data *
|
|
******************************************************************/
|
|
|
|
DemoApp::DemoApp()
|
|
:
|
|
m_pD2DBitmap(NULL),
|
|
m_pConvertedSourceBitmap(NULL),
|
|
m_pIWICFactory(NULL),
|
|
m_pD2DFactory(NULL),
|
|
m_pRT(NULL)
|
|
{
|
|
}
|
|
|
|
/******************************************************************
|
|
* Tear down resources *
|
|
******************************************************************/
|
|
|
|
DemoApp::~DemoApp()
|
|
{
|
|
SafeRelease(m_pD2DBitmap);
|
|
SafeRelease(m_pConvertedSourceBitmap);
|
|
SafeRelease(m_pIWICFactory);
|
|
SafeRelease(m_pD2DFactory);
|
|
SafeRelease(m_pRT);
|
|
}
|
|
|
|
/******************************************************************
|
|
* Create application window and resources *
|
|
******************************************************************/
|
|
|
|
HRESULT DemoApp::Initialize(HINSTANCE hInstance)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Create WIC factory
|
|
hr = CoCreateInstance(
|
|
CLSID_WICImagingFactory,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS(&m_pIWICFactory)
|
|
);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Create D2D factory
|
|
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WNDCLASSEX wcex;
|
|
|
|
// Register window class
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = DemoApp::s_WndProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = sizeof(LONG_PTR);
|
|
wcex.hInstance = hInstance;
|
|
wcex.hIcon = NULL;
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = NULL;
|
|
wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MAINMENU);
|
|
wcex.lpszClassName = L"WICViewerD2D";
|
|
wcex.hIconSm = NULL;
|
|
|
|
m_hInst = hInstance;
|
|
|
|
hr = RegisterClassEx(&wcex) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Create window
|
|
HWND hWnd = CreateWindow(
|
|
L"WICViewerD2D",
|
|
L"WIC Viewer D2D Sample",
|
|
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
640,
|
|
480,
|
|
NULL,
|
|
NULL,
|
|
hInstance,
|
|
this
|
|
);
|
|
|
|
hr = hWnd ? S_OK : E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Load an image file and create an D2DBitmap *
|
|
******************************************************************/
|
|
|
|
HRESULT DemoApp::CreateD2DBitmapFromFile(HWND hWnd)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
WCHAR szFileName[MAX_PATH];
|
|
|
|
// Step 1: Create the open dialog box and locate the image file
|
|
if (LocateImageFile(hWnd, szFileName, ARRAYSIZE(szFileName)))
|
|
{
|
|
// Step 2: Decode the source image
|
|
|
|
// Create a decoder
|
|
IWICBitmapDecoder *pDecoder = NULL;
|
|
|
|
hr = m_pIWICFactory->CreateDecoderFromFilename(
|
|
szFileName, // Image to be decoded
|
|
NULL, // Do not prefer a particular vendor
|
|
GENERIC_READ, // Desired read access to the file
|
|
WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
|
|
&pDecoder // Pointer to the decoder
|
|
);
|
|
|
|
// Retrieve the first frame of the image from the decoder
|
|
IWICBitmapFrameDecode *pFrame = NULL;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pDecoder->GetFrame(0, &pFrame);
|
|
}
|
|
|
|
//Step 3: Format convert the frame to 32bppPBGRA
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SafeRelease(m_pConvertedSourceBitmap);
|
|
hr = m_pIWICFactory->CreateFormatConverter(&m_pConvertedSourceBitmap);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pConvertedSourceBitmap->Initialize(
|
|
pFrame, // Input bitmap to convert
|
|
GUID_WICPixelFormat32bppPBGRA, // Destination pixel format
|
|
WICBitmapDitherTypeNone, // Specified dither pattern
|
|
NULL, // Specify a particular palette
|
|
0.f, // Alpha threshold
|
|
WICBitmapPaletteTypeCustom // Palette translation type
|
|
);
|
|
}
|
|
|
|
//Step 4: Create render target and D2D bitmap from IWICBitmapSource
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CreateDeviceResources(hWnd);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Need to release the previous D2DBitmap if there is one
|
|
SafeRelease(m_pD2DBitmap);
|
|
hr = m_pRT->CreateBitmapFromWicBitmap(m_pConvertedSourceBitmap, NULL, &m_pD2DBitmap);
|
|
}
|
|
|
|
SafeRelease(pDecoder);
|
|
SafeRelease(pFrame);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Creates an open file dialog box and locate the image to decode. *
|
|
******************************************************************/
|
|
|
|
BOOL DemoApp::LocateImageFile(HWND hWnd, LPWSTR pszFileName, DWORD cchFileName)
|
|
{
|
|
pszFileName[0] = L'\0';
|
|
|
|
OPENFILENAME ofn;
|
|
ZeroMemory(&ofn, sizeof(ofn));
|
|
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hWnd;
|
|
ofn.lpstrFilter = L"All Image Files\0" L"*.bmp;*.dib;*.wdp;*.mdp;*.hdp;*.gif;*.png;*.jpg;*.jpeg;*.tif;*.ico\0"
|
|
L"Windows Bitmap\0" L"*.bmp;*.dib\0"
|
|
L"High Definition Photo\0" L"*.wdp;*.mdp;*.hdp\0"
|
|
L"Graphics Interchange Format\0" L"*.gif\0"
|
|
L"Portable Network Graphics\0" L"*.png\0"
|
|
L"JPEG File Interchange Format\0" L"*.jpg;*.jpeg\0"
|
|
L"Tiff File\0" L"*.tif\0"
|
|
L"Icon\0" L"*.ico\0"
|
|
L"All Files\0" L"*.*\0"
|
|
L"\0";
|
|
ofn.lpstrFile = pszFileName;
|
|
ofn.nMaxFile = cchFileName;
|
|
ofn.lpstrTitle = L"Open Image";
|
|
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
|
|
|
|
// Display the Open dialog box.
|
|
return GetOpenFileName(&ofn);
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* This method creates resources which are bound to a particular *
|
|
* D2D device. It's all centralized here, in case the resources *
|
|
* need to be recreated in the event of D2D device loss *
|
|
* (e.g. display change, remoting, removal of video card, etc). *
|
|
******************************************************************/
|
|
|
|
HRESULT DemoApp::CreateDeviceResources(HWND hWnd)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!m_pRT)
|
|
{
|
|
RECT rc;
|
|
hr = GetClientRect(hWnd, &rc) ? S_OK : E_FAIL;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Create a D2D render target properties
|
|
D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties = D2D1::RenderTargetProperties();
|
|
|
|
// Set the DPI to be the default system DPI to allow direct mapping
|
|
// between image pixels and desktop pixels in different system DPI settings
|
|
renderTargetProperties.dpiX = DEFAULT_DPI;
|
|
renderTargetProperties.dpiY = DEFAULT_DPI;
|
|
|
|
// Create a D2D render target
|
|
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
|
|
|
|
hr = m_pD2DFactory->CreateHwndRenderTarget(
|
|
renderTargetProperties,
|
|
D2D1::HwndRenderTargetProperties(hWnd, size),
|
|
&m_pRT
|
|
);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Regsitered Window message handler *
|
|
******************************************************************/
|
|
|
|
LRESULT CALLBACK DemoApp::s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DemoApp *pThis;
|
|
LRESULT lRet = 0;
|
|
|
|
if (uMsg == WM_NCCREATE)
|
|
{
|
|
LPCREATESTRUCT pcs = reinterpret_cast<LPCREATESTRUCT> (lParam);
|
|
pThis = reinterpret_cast<DemoApp *> (pcs->lpCreateParams);
|
|
|
|
SetWindowLongPtr(hWnd, GWLP_USERDATA, PtrToUlong(pThis));
|
|
lRet = DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
pThis = reinterpret_cast<DemoApp *> (GetWindowLongPtr(hWnd, GWLP_USERDATA));
|
|
if (pThis)
|
|
{
|
|
lRet = pThis->WndProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
else
|
|
{
|
|
lRet = DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
}
|
|
return lRet;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Internal Window message handler *
|
|
******************************************************************/
|
|
|
|
LRESULT DemoApp::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_COMMAND:
|
|
{
|
|
// Parse the menu selections:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDM_FILE:
|
|
{
|
|
if (SUCCEEDED(CreateD2DBitmapFromFile(hWnd)))
|
|
{
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
}
|
|
else
|
|
{
|
|
MessageBox(hWnd, L"Failed to load image, select a new one.", L"Application Error", MB_ICONEXCLAMATION | MB_OK);
|
|
}
|
|
break;
|
|
}
|
|
case IDM_EXIT:
|
|
{
|
|
PostMessage(hWnd, WM_CLOSE, 0, 0);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case WM_SIZE:
|
|
{
|
|
D2D1_SIZE_U size = D2D1::SizeU(LOWORD(lParam), HIWORD(lParam));
|
|
|
|
if (m_pRT)
|
|
{
|
|
// If we couldn't resize, release the device and we'll recreate it
|
|
// during the next render pass.
|
|
if (FAILED(m_pRT->Resize(size)))
|
|
{
|
|
SafeRelease(m_pRT);
|
|
SafeRelease(m_pD2DBitmap);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case WM_PAINT:
|
|
{
|
|
return OnPaint(hWnd);
|
|
}
|
|
case WM_DESTROY:
|
|
{
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
default:
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************
|
|
* Rendering routine using D2D *
|
|
******************************************************************/
|
|
LRESULT DemoApp::OnPaint(HWND hWnd)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PAINTSTRUCT ps;
|
|
|
|
if (BeginPaint(hWnd, &ps))
|
|
{
|
|
// Create render target if not yet created
|
|
hr = CreateDeviceResources(hWnd);
|
|
|
|
if (SUCCEEDED(hr) && !(m_pRT->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))
|
|
{
|
|
m_pRT->BeginDraw();
|
|
|
|
m_pRT->SetTransform(D2D1::Matrix3x2F::Identity());
|
|
|
|
// Clear the background
|
|
m_pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
|
|
|
|
D2D1_SIZE_F rtSize = m_pRT->GetSize();
|
|
|
|
// Create a rectangle same size of current window
|
|
D2D1_RECT_F rectangle = D2D1::RectF(0.0f, 0.0f, rtSize.width, rtSize.height);
|
|
|
|
// D2DBitmap may have been released due to device loss.
|
|
// If so, re-create it from the source bitmap
|
|
if (m_pConvertedSourceBitmap && !m_pD2DBitmap)
|
|
{
|
|
m_pRT->CreateBitmapFromWicBitmap(m_pConvertedSourceBitmap, NULL, &m_pD2DBitmap);
|
|
}
|
|
|
|
// Draws an image and scales it to the current window size
|
|
if (m_pD2DBitmap)
|
|
{
|
|
m_pRT->DrawBitmap(m_pD2DBitmap, rectangle);
|
|
}
|
|
|
|
hr = m_pRT->EndDraw();
|
|
|
|
// In case of device loss, discard D2D render target and D2DBitmap
|
|
// They will be re-create in the next rendering pass
|
|
if (hr == D2DERR_RECREATE_TARGET)
|
|
{
|
|
SafeRelease(m_pD2DBitmap);
|
|
SafeRelease(m_pRT);
|
|
// Force a re-render
|
|
hr = InvalidateRect(hWnd, NULL, TRUE)? S_OK : E_FAIL;
|
|
}
|
|
}
|
|
|
|
EndPaint(hWnd, &ps);
|
|
}
|
|
|
|
return SUCCEEDED(hr) ? 0 : 1;
|
|
}
|