一个更复杂的程序——透明带阴影的按钮+旋转的企鹅照片和炫彩字体
二进制程序:
效果图:
源代码:
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <math.h>
#include <d3d10_1.h>
#include <d2d1.h>
#include <dwrite.h>
#include <wincodec.h>
#include <directxmath.h>
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dwrite.lib")
#pragma comment(lib, "d3d10_1.lib")
HINSTANCE hInst;
HWND hMainWnd;
BOOL realtime = TRUE; // 实时绘图标志
LRESULT __stdcall WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Win32错误处理,这个程序暂时没用到
void ShowSysError(DWORD errcode)
{
wchar_t *lpMsgBuf = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errcode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
MessageBox(NULL, lpMsgBuf, L"错误", MB_ICONERROR);
LocalFree(lpMsgBuf);
}
// 入口点
int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
wchar_t *szCmdLine, int nShowCmd)
{
HRESULT hr = CoInitialize(NULL); // WIC图片解码器要求初始化COM
// 注册窗口类别
WNDCLASS wc;
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); // 注意!防闪烁:NULL_BRUSH
wc.lpszMenuName = NULL;
wc.lpszClassName = L"MainWndClass";
if (!RegisterClass(&wc))
return 0;
// 创建窗口,可以指定WS_CLIPCHILDREN以支持Win32控件
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, FALSE);
hMainWnd = CreateWindow(L"MainWndClass", L"Main Window", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
if (!hMainWnd)
return 0;
// 显示并第一次绘制窗口
ShowWindow(hMainWnd, nShowCmd);
UpdateWindow(hMainWnd);
// 消息循环
MSG msg;
memset(&msg, 0, sizeof msg);
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// 进行实时绘图或等待下一个消息
if (realtime)
SendMessage(hMainWnd, WM_TIMER, 222, 0);
else
WaitMessage();
}
}
CoUninitialize(); // 卸载COM
return (int)msg.wParam;
}
// 窗口消息处理函数
LRESULT __stdcall WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// 静态对象
static ID2D1Factory *d2dfac;
static IDWriteFactory *dwfac;
static IWICImagingFactory *wicfac;
static ID3D10Device1 *d3ddev;
static IDXGISwapChain *swpch;
static ID3D10RenderTargetView *d3drtv;
static ID2D1RenderTarget *rt;
static ID2D1Bitmap *bmpPenguins;
static ID3D10Texture2D *tx2dPic;
static ID3D10ShaderResourceView *tx2dPicSRV;
static ID2D1RenderTarget *rtPic;
static ID3D10Effect *eff;
static ID3D10InputLayout *inlayoutPic;
static ID3D10Buffer *vbPic;
// 每次进入都要用到的对象
HRESULT hr;
RECT rc;
GetClientRect(hWnd, &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
// 自绘按钮状态
static RECT rcbtn = { 10, 10, 100, 50 };
static RECT rcbtn2 = { 10 + 5, 10 + 5, 100 + 5, 50 + 5 };
static int trbtn = 0;
// D3D效果文件
static char efftxt[] = R"EFFECTS(
float4x4 matWorld;
float4x4 matViewAndProj;
float timerad;
Texture2D tx2dPic;
SamplerState tx2dPicSampStat
{
Texture = tx2dPic;
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
void PicVS(float4 pos0 : POSITION, float2 tex0 : TEXCOORD,
out float4 pos1 : SV_POSITION, out float2 tex1 : TEXCOORD)
{
pos1 = mul(mul(pos0, matWorld), matViewAndProj);
tex1 = tex0;
return;
}
float4 PicPS(float4 pos : SV_Position,
float2 tex0 : TEXCOORD) : SV_Target
{
return tx2dPic.Sample(tx2dPicSampStat, tex0);
}
RasterizerState rasstate
{
CULLMODE = 1;
};
technique10 Pic {
pass P0 {
SetVertexShader(CompileShader(vs_4_0, PicVS()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, PicPS()));
SetRasterizerState(rasstate);
}
}
)EFFECTS";
// 窗口创建
if (msg == WM_CREATE) {
// 创建D2D、DWrite、WIC根工厂
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dfac);
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), (IUnknown**)&dwfac);
// 注意!一定要用CLSID_WICImagingFactory1,不要用默认的CLSID_WICImagingFactory(2),否则需要安装IE11
hr = CoCreateInstance(CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, __uuidof(wicfac), (void**)&wicfac);
// 创建D3D设备和DXGI交换链,包括后台缓冲区
DXGI_SWAP_CHAIN_DESC swapDesc;
memset(&swapDesc, 0, sizeof swapDesc);
swapDesc.BufferDesc.Width = width;
swapDesc.BufferDesc.Height = height;
swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // 注意!D2D兼容选项:BGRA
swapDesc.BufferDesc.RefreshRate.Numerator = 60;
swapDesc.BufferDesc.RefreshRate.Denominator = 1;
swapDesc.SampleDesc.Count = 1;
swapDesc.SampleDesc.Quality = 0;
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapDesc.BufferCount = 1;
swapDesc.OutputWindow = hWnd;
swapDesc.Windowed = TRUE;
swapDesc.Flags = 0;
hr = D3D10CreateDeviceAndSwapChain1(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL,
D3D10_CREATE_DEVICE_BGRA_SUPPORT, // 重要!D2D兼容选项:BGRA
D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &swapDesc, &swpch, &d3ddev);
if (FAILED(hr))
{
hr = D3D10CreateDeviceAndSwapChain1(NULL, D3D10_DRIVER_TYPE_WARP, NULL,
D3D10_CREATE_DEVICE_BGRA_SUPPORT, // 重要!D2D兼容选项:BGRA
D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &swapDesc, &swpch, &d3ddev);
}
// 禁止Alt-Enter
IDXGIFactory *dxgifac;
hr = swpch->GetParent(__uuidof(dxgifac), (void**)&dxgifac);
hr = dxgifac->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES);
dxgifac->Release();
// 创建渲染对象
SendMessage(hWnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(width, height));
// 解码图片
IWICBitmapDecoder *wicdec;
hr = wicfac->CreateDecoderFromFilename(L"Penguins.jpg", NULL, GENERIC_READ,
WICDecodeMetadataCacheOnDemand, &wicdec);
if (SUCCEEDED(hr))
{
// 获取第一帧
IWICBitmapFrameDecode *wicorgfmt;
hr = wicdec->GetFrame(0, &wicorgfmt);
// 转换为D2D可用的PBGRA格式
IWICFormatConverter *wicd2dfmt;
hr = wicfac->CreateFormatConverter(&wicd2dfmt);
hr = wicd2dfmt->Initialize(wicorgfmt, GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom);
// 创建D2D位图
hr = rt->CreateBitmapFromWicBitmap(wicd2dfmt, &bmpPenguins);
// 释放临时对象
wicdec->Release();
wicorgfmt->Release();
wicd2dfmt->Release();
}
else
{
MessageBox(hWnd, L"找不到Penguins.jpg", L"警告", MB_ICONWARNING);
}
// 创建独立纹理以及D3D资源对象
D3D10_TEXTURE2D_DESC tx2dPicDesc;
tx2dPicDesc.Width = 320;
tx2dPicDesc.Height = 240;
tx2dPicDesc.MipLevels = 1;
tx2dPicDesc.ArraySize = 1;
tx2dPicDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
tx2dPicDesc.SampleDesc.Count = 1;
tx2dPicDesc.SampleDesc.Quality = 0;
tx2dPicDesc.Usage = D3D10_USAGE_DEFAULT;
tx2dPicDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
tx2dPicDesc.CPUAccessFlags = 0;
tx2dPicDesc.MiscFlags = 0;
hr = d3ddev->CreateTexture2D(&tx2dPicDesc, NULL, &tx2dPic);
hr = d3ddev->CreateShaderResourceView(tx2dPic, NULL, &tx2dPicSRV);
// 创建对于纹理的渲染对象
IDXGISurface *surfPic;
hr = tx2dPic->QueryInterface(__uuidof(surfPic), (void**)&surfPic);
hr = d2dfac->CreateDxgiSurfaceRenderTarget(surfPic,
D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
96,
96,
D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT
),
&rtPic);
surfPic->Release();
// 创建效果
ID3D10Blob *effcode = NULL, *errmsg = NULL;
hr = D3D10CompileEffectFromMemory(efftxt, strlen(efftxt), "main.cpp:efftxtPic",
NULL, NULL, 0, 0, &effcode, &errmsg);
if (FAILED(hr) && errmsg)
{
OutputDebugStringA((char*)errmsg->GetBufferPointer());
#ifdef _M_IX86
__asm int 3
#else
DebugBreak();
#endif
}
hr = D3D10CreateEffectFromMemory(effcode->GetBufferPointer(), effcode->GetBufferSize(), 0, d3ddev, NULL, &eff);
ID3D10EffectPass *pass = eff->GetTechniqueByName("Pic")->GetPassByName("P0");
D3D10_PASS_DESC passdesc;
hr = pass->GetDesc(&passdesc);
// 创建输入格式
D3D10_INPUT_ELEMENT_DESC inputlayout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
hr = d3ddev->CreateInputLayout(inputlayout, 2, passdesc.pIAInputSignature,
passdesc.IAInputSignatureSize, &inlayoutPic);
// 创建顶点缓冲区
float buf[] = {
-2.0f, 1.5f, 0.0f, 0.0f, 0.0f,
2.0f, 1.5f, 0.0f, 1.0f, 0.0f,
2.0f, -1.5f, 0.0f, 1.0f, 1.0f,
-2.0f, 1.5f, 0.0f, 0.0f, 0.0f,
2.0f, -1.5f, 0.0f, 1.0f, 1.0f,
-2.0f, -1.5f, 0.0f, 0.0f, 1.0f,
};
D3D10_BUFFER_DESC bufdesc;
bufdesc.ByteWidth = sizeof buf;
bufdesc.Usage = D3D10_USAGE_DEFAULT;
bufdesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bufdesc.CPUAccessFlags = 0;
bufdesc.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA subresdata;
subresdata.pSysMem = buf;
subresdata.SysMemPitch = 0;
subresdata.SysMemSlicePitch = 0;
hr = d3ddev->CreateBuffer(&bufdesc, &subresdata, &vbPic);
return 0;
}
// 窗口大小变化
if (msg == WM_SIZE) {
// 释放旧对象
if (rt) {
rt->Release();
rt = NULL;
}
if (d3drtv) {
d3drtv->Release();
d3drtv = NULL;
}
if (swpch && d2dfac && d3ddev) {
// 调整后台缓冲区
hr = swpch->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0);
// 获取后台缓冲区
ID3D10Texture2D *backbuffer;
hr = swpch->GetBuffer(0, __uuidof(backbuffer), (void**)&backbuffer);
IDXGISurface *surf;
hr = backbuffer->QueryInterface(__uuidof(surf), (void**)&surf);
// 创建D2D渲染对象
float dpix, dpiy;
d2dfac->GetDesktopDpi(&dpix, &dpiy);
hr = d2dfac->CreateDxgiSurfaceRenderTarget(surf,
D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
dpix,
dpiy,
D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT
),
&rt);
// 创建D3D渲染对象
hr = d3ddev->CreateRenderTargetView(backbuffer, NULL, &d3drtv);
// 释放临时对象
backbuffer->Release();
surf->Release();
}
return 0;
}
// 鼠标左键按下
if (msg == WM_LBUTTONDOWN) {
POINT pt;
SetCapture(hWnd);
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
if (PtInRect(&rcbtn, pt))
trbtn = 5;
if (PtInRect(&rcbtn2, pt))
trbtn = 5;
InvalidateRect(hWnd, NULL, TRUE);
return 0;
}
// 鼠标左键弹起
if (msg == WM_LBUTTONUP) {
trbtn = 0;
ReleaseCapture();
InvalidateRect(hWnd, NULL, TRUE);
return 0;
}
// 需要绘图(WM_PAINT)或实时绘图(WM_TIMER,222)见入口点内部的消息循环
if (msg == WM_PAINT || (msg == WM_TIMER && wParam == 222)) {
// 移除WM_PAINT消息
if (msg == WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
// 防崩溃
if (!rt) return 0;
if (!swpch) return 0;
// 创建临时资源
ID2D1SolidColorBrush *brtransblue;
ID2D1SolidColorBrush *brtransblack;
ID2D1SolidColorBrush *brwhite;
IDWriteTextFormat *tfsegoeui;
hr = rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Blue, 0.5f), &brtransblue);
hr = rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gray, 0.5f), &brtransblack);
hr = rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &brwhite);
hr = dwfac->CreateTextFormat(L"Segoe UI", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 12, L"", &tfsegoeui);
hr = tfsegoeui->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
hr = tfsegoeui->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
// 开始底部绘制
rt->BeginDraw();
rt->Clear(D2D1::ColorF(D2D1::ColorF::Wheat));
// 绘制按钮
rt->FillRoundedRectangle(D2D1::RoundedRect(
D2D1::Rect(rcbtn2.left, rcbtn2.top, rcbtn2.right, rcbtn2.bottom),
10, 10),
brtransblack);
rt->FillRoundedRectangle(D2D1::RoundedRect(
D2D1::Rect(rcbtn.left + trbtn, rcbtn.top + trbtn, rcbtn.right + trbtn, rcbtn.bottom + trbtn),
10, 10),
brtransblue);
#define str L"按钮1"
rt->DrawText(str, lstrlen(str), tfsegoeui,
D2D1::Rect(rcbtn.left + trbtn, rcbtn.top + trbtn, rcbtn.right + trbtn, rcbtn.bottom + trbtn),
brwhite);
#undef str
// 结束底部绘制
hr = rt->EndDraw();
// 释放临时资源
if (tfsegoeui) tfsegoeui->Release();
if (brwhite) brwhite->Release();
if (brtransblack) brtransblack->Release();
if (brtransblue) brtransblue->Release();
if (d3ddev && rtPic)
{
using namespace DirectX;
float timerad = GetTickCount() % 2000 / 2000.0f * 2 * XM_PI;
// 创建临时资源
ID2D1SolidColorBrush *brred;
IDWriteTextFormat *tfsegoeuibig;
hr = rtPic->CreateSolidColorBrush(
D2D1::ColorF(
pow(sin(timerad), 2),
pow(cos(timerad + XM_PI / 6), 2),
pow(sin(timerad + XM_PI / 3), 2)),
&brred);
hr = dwfac->CreateTextFormat(L"Segoe UI", NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 24, L"", &tfsegoeuibig);
hr = tfsegoeuibig->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
hr = tfsegoeuibig->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
rtPic->BeginDraw();
// 向纹理绘制图片
if (bmpPenguins)
rtPic->DrawBitmap(bmpPenguins, &D2D1::RectF(0, 0, 320, 240));
else
rtPic->Clear(D2D1::ColorF(D2D1::ColorF::Blue));
#define str L"画布里的企鹅"
rtPic->DrawText(str, lstrlen(str), tfsegoeuibig,
D2D1::Rect(0, 0, 320, 240),
brred);
#undef str
// 结束纹理绘制
hr = rtPic->EndDraw();
// 释放临时资源
brred->Release();
tfsegoeuibig->Release();
// 设置必要的D3D状态
// 设置渲染目标
d3ddev->OMSetRenderTargets(1, &d3drtv, NULL);
// 设置视口参数
D3D10_VIEWPORT vp;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
vp.Width = width;
vp.Height = height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
d3ddev->RSSetViewports(1, &vp);
// 生成3D参数
XMMATRIX matWorld = XMMatrixRotationY(timerad);
XMMATRIX matView = XMMatrixLookAtLH(
XMVectorSet(0.0f, 3.0f, -8.0f, 0.0f),
XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f),
XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f));
XMMATRIX matProj = XMMatrixPerspectiveFovLH(
XM_PI / 4, width / (float)height, 1.0f, 100.0f);
XMMATRIX matViewAndProj = matView * matProj;
// 上传参数
eff->GetVariableByName("timerad")->AsScalar()->SetFloat(timerad);
eff->GetVariableByName("matWorld")->AsMatrix()->SetMatrix((float*)&matWorld);
eff->GetVariableByName("matViewAndProj")->AsMatrix()->SetMatrix((float*)&matViewAndProj);
eff->GetVariableByName("tx2dPic")->AsShaderResource()->SetResource(tx2dPicSRV);
// 设置输入状态
d3ddev->IASetInputLayout(inlayoutPic);
UINT stride = 4 * 5, offset = 0;
d3ddev->IASetVertexBuffers(0, 1, &vbPic, &stride, &offset);
d3ddev->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 应用效果
eff->GetTechniqueByName("Pic")->GetPassByName("P0")->Apply(0);
// 绘制图形
d3ddev->Draw(6, 0);
}
// D3D上屏
hr = swpch->Present(0, 0);
// 释放临时资源
return 0;
}
// 窗口结束
if (msg == WM_DESTROY) {
// 释放静态对象
if (vbPic) {
vbPic->Release();
vbPic = NULL;
}
if (inlayoutPic) {
inlayoutPic->Release();
inlayoutPic = NULL;
}
if (eff) {
eff->Release();
eff = NULL;
}
if (rtPic) {
rtPic->Release();
rtPic = NULL;
}
if (tx2dPicSRV) {
tx2dPicSRV->Release();
tx2dPicSRV = NULL;
}
if (tx2dPic) {
tx2dPic->Release();
tx2dPic = NULL;
}
if (bmpPenguins) {
bmpPenguins->Release();
bmpPenguins = NULL;
}
if (rt) {
rt->Release();
rt = NULL;
}
if (d3drtv) {
d3drtv->Release();
d3drtv = NULL;
}
if (swpch) {
swpch->Release();
swpch = NULL;
}
if (d3ddev) {
d3ddev->Release();
d3ddev = NULL;
}
if (wicfac) {
wicfac->Release();
wicfac = NULL;
}
if (dwfac) {
dwfac->Release();
dwfac = NULL;
}
if (d2dfac) {
d2dfac->Release();
d2dfac = NULL;
}
PostQuitMessage(0);
return 0;
}
// 其它消息交给系统处理
return DefWindowProc(hWnd, msg, wParam, lParam);
}
</directxmath.h></wincodec.h></dwrite.h></d2d1.h></d3d10_1.h></math.h></windows.h>