534 lines
16 KiB
C++
534 lines
16 KiB
C++
|
|
/************************************************************************
|
|
*
|
|
* File: GdiInterop.cpp
|
|
*
|
|
* Description:
|
|
*
|
|
*
|
|
* This file is part of the Microsoft Windows SDK Code Samples.
|
|
*
|
|
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
*
|
|
* This source code is intended only as a supplement to Microsoft
|
|
* Development Tools and/or on-line documentation. See these other
|
|
* materials for detailed information regarding Microsoft code samples.
|
|
*
|
|
* THIS CODE AND INFORMATION ARE 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.
|
|
*
|
|
************************************************************************/
|
|
|
|
#include "GdiInterop.h"
|
|
|
|
#define MAX_LOADSTRING 100
|
|
|
|
// Global Variables:
|
|
HINSTANCE hInst; // current instance
|
|
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
|
|
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
|
|
|
|
// DirectWrite global variables.
|
|
IDWriteFactory* g_pDWriteFactory = NULL;
|
|
IDWriteTextFormat* g_pTextFormat = NULL;
|
|
IDWriteGdiInterop* g_pGdiInterop = NULL;
|
|
IDWriteTextLayout* g_pTextLayout = NULL;
|
|
IDWriteBitmapRenderTarget* g_pBitmapRenderTarget = NULL;
|
|
IDWriteRenderingParams* g_pRenderingParams = NULL;
|
|
|
|
// For our custom renderer class.
|
|
IDWriteTextRenderer* g_pGdiTextRenderer = NULL;
|
|
|
|
// Forward declarations of functions included in this code module:
|
|
ATOM MyRegisterClass(HINSTANCE hInstance);
|
|
BOOL InitInstance(HINSTANCE, int);
|
|
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
void GdiOnPaint(HDC hdc, wchar_t *text, HFONT hfont);
|
|
HRESULT DWriteOnPaint(HDC hdc, wchar_t *text, HFONT hfont);
|
|
HRESULT DWriteCreateResources(HDC hdc, wchar_t *text, HFONT hfont);
|
|
|
|
/******************************************************************
|
|
* *
|
|
* OnPaint *
|
|
* *
|
|
* Creates a handle to a LOGFONT and a string and then calls the *
|
|
* GDI and DirectWrite OnPaint functions to display actual text. *
|
|
* *
|
|
******************************************************************/
|
|
|
|
void OnPaint(HDC hdc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
HFONT hfont = NULL;
|
|
long height = 0;
|
|
HDC memoryHdc = NULL;
|
|
SIZE size = {};
|
|
|
|
// Set the height, equivalent to 50 em for DirectWrite.
|
|
height = -MulDiv(50, GetDeviceCaps(hdc, LOGPIXELSY), 96);
|
|
|
|
hfont = CreateFontW(height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Times New Roman");
|
|
|
|
wchar_t dwriteText[ ] = L"Sample text using DirectWrite.";
|
|
wchar_t gdiText[ ] = L"Sample text using GDI TextOut.";
|
|
|
|
// The DirectWrite objects need only be created once and can be reused each time the window paints
|
|
hr = DWriteCreateResources(hdc, dwriteText, hfont);
|
|
|
|
// Get the memory device context, the drawing is done offscreen.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
memoryHdc = g_pBitmapRenderTarget->GetMemoryDC();
|
|
SetBoundsRect(memoryHdc, NULL, DCB_ENABLE|DCB_RESET);
|
|
}
|
|
|
|
// Get the size of the target.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pBitmapRenderTarget->GetSize(&size);
|
|
}
|
|
|
|
// Clear the background
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SetDCBrushColor(memoryHdc, 0xFFFFFF);
|
|
SelectObject(memoryHdc, GetStockObject(NULL_PEN));
|
|
SelectObject(memoryHdc, GetStockObject(DC_BRUSH));
|
|
Rectangle(memoryHdc, 0, 0, size.cx + 1, size.cy + 1);
|
|
}
|
|
|
|
// Draw the string to the memory HDC using GDI.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
GdiOnPaint(memoryHdc, gdiText, hfont);
|
|
}
|
|
|
|
// Draw the same string below the first to the memory HDC using DirectWrite.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = DWriteOnPaint(memoryHdc, dwriteText, hfont);
|
|
}
|
|
|
|
// Transfer from DWrite's rendering target to the window.
|
|
BitBlt(
|
|
hdc,
|
|
0, 0,
|
|
size.cx + 1, size.cy + 1,
|
|
memoryHdc,
|
|
0, 0,
|
|
SRCCOPY
|
|
);
|
|
|
|
// If the DirectWrite drawing failed, exit and display an error.
|
|
if (FAILED(hr))
|
|
{
|
|
wchar_t error[255];
|
|
|
|
swprintf_s(error, 254, L"HRESULT = %x", hr);
|
|
|
|
MessageBox(0, error, L"Error, exiting.", 0);
|
|
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/******************************************************************
|
|
* *
|
|
* GdiOnPaint *
|
|
* *
|
|
* Draw the text using GDI and the TextOut function. *
|
|
* *
|
|
* *
|
|
******************************************************************/
|
|
|
|
void GdiOnPaint(HDC hdc, wchar_t *text, HFONT hfont)
|
|
{
|
|
HFONT hf;
|
|
UINT32 textLength;
|
|
|
|
textLength = UINT32(wcslen(text));
|
|
|
|
hf = (HFONT) SelectObject(hdc, hfont);
|
|
|
|
SetTextColor(hdc, RGB(0, 200, 255));
|
|
|
|
TextOut(hdc, 0, 0, text, textLength);
|
|
|
|
SelectObject(hdc, hf);
|
|
}
|
|
|
|
/******************************************************************
|
|
* *
|
|
* DWriteOnPaint *
|
|
* *
|
|
* Clears the memory DC of the bitmap render target, renders to *
|
|
* it using a custom text renderer, and then copies it to the *
|
|
* window device context using the BitBlt function. *
|
|
* *
|
|
******************************************************************/
|
|
|
|
HRESULT DWriteOnPaint(HDC hdc, wchar_t *text, HFONT hfont)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Draw the text below the GDI text
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pTextLayout->Draw(NULL, g_pGdiTextRenderer, 0, 65);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************
|
|
* *
|
|
* DWriteCreateResources *
|
|
* *
|
|
* Creates the global DirectWrite objects needed for rendering, *
|
|
* including the IDWriteGdiInterop and IDWriteBitmapRenderTarget *
|
|
* interfaces. *
|
|
* *
|
|
* It also converts the GDI LOGFONT structure to a IDWriteFont *
|
|
* object and creates a IDWriteTextLayout object using this *
|
|
* information. *
|
|
* *
|
|
******************************************************************/
|
|
|
|
HRESULT DWriteCreateResources(HDC hdc, wchar_t *text, HFONT hfont)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// If the DirectWrite factory doesn't exist, create the resources,
|
|
// only create these resources once.
|
|
if (!g_pDWriteFactory)
|
|
{
|
|
HWND hwnd;
|
|
RECT r;
|
|
|
|
// DirectWrite variables.
|
|
IDWriteFontFamily* pFontFamily = NULL;
|
|
IDWriteFont* pFont = NULL;
|
|
IDWriteLocalizedStrings* pFamilyNames = NULL;
|
|
|
|
// Logical (GDI) font.
|
|
LOGFONT lf = {};
|
|
|
|
UINT32 length = 0;
|
|
UINT32 index = 0;
|
|
float fontSize = 0;
|
|
|
|
// length of the string
|
|
UINT32 textLength = 0;
|
|
|
|
wchar_t *name = NULL;
|
|
|
|
// Get a handle to the DC and the window rect.
|
|
hwnd = WindowFromDC(hdc);
|
|
GetClientRect(hwnd, &r);
|
|
|
|
// Calculate the string length.
|
|
textLength = UINT32(wcslen(text));
|
|
|
|
// Create the DirectWrite factory.
|
|
hr = DWriteCreateFactory(
|
|
DWRITE_FACTORY_TYPE_SHARED,
|
|
__uuidof(IDWriteFactory),
|
|
reinterpret_cast<IUnknown**>(&g_pDWriteFactory)
|
|
);
|
|
|
|
// Create a GDI interop interface.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pDWriteFactory->GetGdiInterop(&g_pGdiInterop);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Get a logical font from the font handle.
|
|
GetObject(hfont, sizeof(LOGFONT), &lf);
|
|
}
|
|
|
|
// Convert to a DirectWrite font.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pGdiInterop->CreateFontFromLOGFONT(&lf, &pFont);
|
|
}
|
|
|
|
// Get the font family.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pFont->GetFontFamily(&pFontFamily);
|
|
}
|
|
|
|
// Get a list of localized family names.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pFontFamily->GetFamilyNames(&pFamilyNames);
|
|
}
|
|
|
|
// Select the first locale. This is OK, because we are not displaying the family name.
|
|
index = 0;
|
|
|
|
// Get the length of the family name.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pFamilyNames->GetStringLength(index, &length);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Allocate a new string.
|
|
name = new (std::nothrow) wchar_t[length+1];
|
|
if (name == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
// Get the actual family name.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pFamilyNames->GetString(index, name, length+1);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Calculate the font size.
|
|
fontSize = (float) -MulDiv(lf.lfHeight, 96, GetDeviceCaps(hdc, LOGPIXELSY));
|
|
}
|
|
|
|
// Create a text format using the converted font information.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pDWriteFactory->CreateTextFormat(
|
|
name, // Font family name.
|
|
NULL,
|
|
pFont->GetWeight(),
|
|
pFont->GetStyle(),
|
|
pFont->GetStretch(),
|
|
fontSize,
|
|
L"en-us",
|
|
&g_pTextFormat
|
|
);
|
|
}
|
|
|
|
// Create a text layout.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pDWriteFactory->CreateTextLayout(
|
|
text,
|
|
textLength,
|
|
g_pTextFormat,
|
|
1024.0f,
|
|
480.0f,
|
|
&g_pTextLayout
|
|
);
|
|
}
|
|
|
|
// Underline and strikethrough are part of a LOGFONT structure, but are not
|
|
// part of a DWrite font object so we must set them using the text layout.
|
|
if(lf.lfUnderline)
|
|
{
|
|
DWRITE_TEXT_RANGE textRange = {0, textLength};
|
|
g_pTextLayout->SetUnderline(true, textRange);
|
|
}
|
|
|
|
if(lf.lfStrikeOut)
|
|
{
|
|
DWRITE_TEXT_RANGE textRange = {0, textLength};
|
|
g_pTextLayout->SetStrikethrough(true, textRange);
|
|
}
|
|
|
|
// Create a bitmap render target for our custom renderer.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pGdiInterop->CreateBitmapRenderTarget(hdc, r.right, r.bottom, &g_pBitmapRenderTarget);
|
|
}
|
|
|
|
// Create default rendering params for our custom renderer.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = g_pDWriteFactory->CreateRenderingParams(&g_pRenderingParams);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Initialize the custom renderer class.
|
|
g_pGdiTextRenderer = new (std::nothrow) GdiTextRenderer(g_pBitmapRenderTarget, g_pRenderingParams);
|
|
}
|
|
|
|
// Clean up local interfaces.
|
|
SafeRelease(&pFontFamily);
|
|
SafeRelease(&pFont);
|
|
SafeRelease(&pFamilyNames);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
int APIENTRY wWinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPWSTR lpCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
|
UNREFERENCED_PARAMETER(lpCmdLine);
|
|
|
|
// TODO: Place code here.
|
|
MSG msg;
|
|
|
|
// Initialize global strings
|
|
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
|
|
LoadString(hInstance, IDC_GDIINTEROP, szWindowClass, MAX_LOADSTRING);
|
|
MyRegisterClass(hInstance);
|
|
|
|
// Perform application initialization:
|
|
if (!InitInstance (hInstance, nCmdShow))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Main message loop:
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return (int) msg.wParam;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: MyRegisterClass()
|
|
//
|
|
// PURPOSE: Registers the window class.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function and its usage are only necessary if you want this code
|
|
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
|
|
// function that was added to Windows 95. It is important to call this function
|
|
// so that the application will get 'well formed' small icons associated
|
|
// with it.
|
|
//
|
|
ATOM MyRegisterClass(HINSTANCE hInstance)
|
|
{
|
|
WNDCLASSEX wcex;
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
wcex.lpfnWndProc = WndProc;
|
|
wcex.cbClsExtra = 0;
|
|
wcex.cbWndExtra = 0;
|
|
wcex.hInstance = hInstance;
|
|
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_GDIINTEROP));
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wcex.hbrBackground = NULL;
|
|
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_GDIINTEROP);
|
|
wcex.lpszClassName = szWindowClass;
|
|
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
|
|
|
|
return RegisterClassEx(&wcex);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: InitInstance(HINSTANCE, int)
|
|
//
|
|
// PURPOSE: Saves instance handle and creates main window
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// In this function, we save the instance handle in a global variable and
|
|
// create and display the main program window.
|
|
//
|
|
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
|
|
{
|
|
HWND hWnd;
|
|
|
|
hInst = hInstance; // Store instance handle in our global variable
|
|
|
|
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
|
|
|
|
if (!hWnd)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ShowWindow(hWnd, nCmdShow);
|
|
UpdateWindow(hWnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
|
|
//
|
|
// PURPOSE: Processes messages for the main window.
|
|
//
|
|
// WM_COMMAND - process the application menu
|
|
// WM_PAINT - Paint the main window
|
|
// WM_DESTROY - post a quit message and return
|
|
//
|
|
//
|
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int wmId, wmEvent;
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
|
|
switch (message)
|
|
{
|
|
case WM_COMMAND:
|
|
wmId = LOWORD(wParam);
|
|
wmEvent = HIWORD(wParam);
|
|
// Parse the menu selections:
|
|
switch (wmId)
|
|
{
|
|
case IDM_EXIT:
|
|
DestroyWindow(hWnd);
|
|
break;
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
break;
|
|
case WM_PAINT:
|
|
hdc = BeginPaint(hWnd, &ps);
|
|
OnPaint(hdc);
|
|
EndPaint(hWnd, &ps);
|
|
break;
|
|
case WM_SIZE:
|
|
if (g_pBitmapRenderTarget)
|
|
{
|
|
int width = LOWORD(lParam);
|
|
int height = HIWORD(lParam);
|
|
g_pBitmapRenderTarget->Resize(width, height);
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
// The window is closing, release the DirectWrite variables we created for drawing.
|
|
SafeRelease(&g_pDWriteFactory);
|
|
SafeRelease(&g_pTextFormat);
|
|
SafeRelease(&g_pGdiInterop);
|
|
SafeRelease(&g_pTextLayout);
|
|
SafeRelease(&g_pBitmapRenderTarget);
|
|
SafeRelease(&g_pRenderingParams);
|
|
SafeRelease(&g_pGdiTextRenderer);
|
|
|
|
PostQuitMessage(0);
|
|
break;
|
|
default:
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
}
|
|
return 0;
|
|
}
|
|
|