2025-11-28 00:35:46 +09:00

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