[ATL/WTL]_[Gdiplus]_[关于混用GDI和GDI+(GDIPlus)的注意点]

场景

  1. 有一次开发WTL窗口时,使用GDICMemoryDC来绘制中文文字显示不出来,但是文字改为英文就绘制出来的,确认字符集,字体,坐标都没问题,那是什么情况?

说明

  1. 在开发WTL程序时, 我们有时候会容易混合使用GDIGDI+,比如使用GDICDC的方法RoundRect绘制圆角矩形,而GDI+没有类似的方法。同样,使用GDI+轻松绘制png图片,而GDI绘制需要繁琐的转换。但是,实际上,微软MSDN文档说明GDIGDI+是不能混用的,上边的绘制不出来中文就是混用导致的结果之一。

  2. 微软的文档说明 GDI 和 GDI + 之间的互操作性 介绍了4种需要互操作性的情况,我们最常用的就是第三种情况在 GDI HDC 上使用 GDI +. 也就是说在创建Gdiplus::Graphics(HDC)之后如果需要使用HDC,那么有两种方式:

    2.1. 请先销毁Graphics再使用HDC.

    2.2. 使用Graphics.GetHDC()来创建新的HDC,之后直到调用Graphics.ReleaseHDC()之前不要使用Graphics对象.

您可以通过使用采用 HDC 作为参数的图形构造函数来协助在 HDC 上使用 GDI +。 可以使用此方法在 HDC 上绘制 Graphics 类的绘图成员。 将 Graphics 对象附加到 HDC 后,不应在 HDC 上执行任何 GDI 操作,直到 Graphics 对象损坏或超出范围。 如果在 HDC 上需要 GDI 输出,请先销毁 Graphics 对象,然后再使用原始 HDC 或使用 Graphics::GetHDC() 获取新的 hdc,然后按照本文前面所述的规则进行互操作,同时对 GDI + 对象使用 gdi。

例子

  1. 以下例子说明如何使用GDIGDI+混用的方式,它是微软官方说明的第三种情况,也有3种用法。

View.h

// View.h : interface of the CView class
//
/////////////////////////////////////////////////////////////////////////////

#pragma once

#include 
#include 

class CView : public CWindowImpl<CView>
{
public:
	DECLARE_WND_CLASS(NULL)

	BOOL PreTranslateMessage(MSG* pMsg);

	BEGIN_MSG_MAP_EX(CView)
		MSG_WM_CREATE(OnCreate)
		MSG_WM_DESTROY(OnDestroy)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
	END_MSG_MAP()


	LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
	int OnCreate(LPCREATESTRUCT lpCreateStruct);
	void PaintImage(CDC& cdc,CRect& rect);
	void OnDestroy();

private:
	Gdiplus::Font* font_;
	HFONT font_normal_;
	Gdiplus::Bitmap* bitmap_;
};

View.cpp

// View.cpp : implementation of the CView class
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "resource.h"
#include 
#include 
#include 
#include "View.h"

using namespace std;
using namespace Gdiplus;

static HFONT GetHFONT(int em,int charset = DEFAULT_CHARSET,
		bool bold = false,const wchar_t* fontName = L"Arial")
{
	LOGFONT lf; 
	memset(&lf, 0, sizeof(LOGFONT)); // zero out structure 
	lf.lfHeight = em; // request a 8-pixel-height font
	lf.lfCharSet = charset;
	lstrcpy(lf.lfFaceName,fontName); // request a face name "Arial"
	if(bold)
		lf.lfWeight = FW_BOLD;
	else
		lf.lfWeight = FW_NORMAL;
	HFONT font = ::CreateFontIndirect(&lf);
	return font;
}

static Gdiplus::Font* GetGDIFont(HDC hdc,int em,int charset = DEFAULT_CHARSET,
		bool bold = false,const wchar_t* fontName = L"Arial")
{
	LOGFONT lf; 
	memset(&lf, 0, sizeof(LOGFONT)); // zero out structure 
	lf.lfHeight = em; // request a 8-pixel-height font
	lf.lfCharSet = charset;
	lstrcpy(lf.lfFaceName,fontName); // request a face name "Arial"
	if(bold)
		lf.lfWeight = FW_BOLD;
	else
		lf.lfWeight = FW_NORMAL;
	return new Gdiplus::Font(hdc,&lf);
}

int CView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	auto one = GetDC();
	font_ = GetGDIFont(one,16);
	ReleaseDC(one);
	font_normal_= GetHFONT(16);
	bitmap_ = new Bitmap(L"res\\toolbar.bmp");
	return 0;
}

BOOL CView::PreTranslateMessage(MSG* pMsg)
{
	pMsg;
	return FALSE;
}

void CView::PaintImage(CDC& cdc,CRect& rect)
{
	static std::wstring title(L"学院课程: C++17语言特性和标准库-第一部");
	static std::wstring url(L"https://edu.csdn.net/course/detail/30136");
	CSize size;
	cdc.GetTextExtent(title.c_str(),title.size(),&size);
	CRect rectText(CPoint(100,130),size);

	cdc.DrawText(title.c_str(),title.size(),rectText,DT_LEFT);

	// 2. 可以用大括号进行包括graphics进行绘制,当语句块结束时,graphics会销毁.
	// 接着再使用原HDC.
	{
		Gdiplus::Graphics graphics(cdc);
		graphics.DrawImage(bitmap_,100,180);

		// 混淆GDI+和GDI方式3
		// 如果需要使用HDC,通过GetHDC()方法来锁定graphics,之后再通过方法ReleaseHDC释放锁定graphics.
		auto hdc = graphics.GetHDC();
		CDCHandle dc(hdc);
		rectText.MoveToY(200);
		dc.DrawText(title.c_str(),title.size(),rectText,DT_LEFT);
		graphics.ReleaseHDC(hdc);
		graphics.DrawImage(bitmap_,100,220);
	}

	cdc.GetTextExtent(url.c_str(),url.size(),&size);
	rectText = CRect(CPoint(100,160),size);
	cdc.DrawText(url.c_str(),url.size(),rectText,DT_LEFT);
}

void CView::OnDestroy()
{
	delete font_;
	DeleteObject(font_normal_);
}

// 第三种情况. hdc作为Graphics的参数传入,在Graphics销毁之前不要使用hdc.
LRESULT CView::OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	CPaintDC dc(m_hWnd);
	CMemoryDC mdc(dc,dc.m_ps.rcPaint);
	mdc.SelectFont(font_normal_);

	CRect rect;
	GetClientRect(&rect);
	mdc.FillSolidRect(rect,RGB(255,255,255));
	static std::wstring temp(L"https://blog.csdn.net/infoworld");

	// 混淆GDI+和GDI方式1.
	PaintImage(mdc,rect);

	// 混淆GDI+和GDI方式2,放到最后再使用graphics绘制.
	Graphics graphics(mdc);
	RectF rectf_text;
	rectf_text.X = 100;
	rectf_text.Y = 100;
	graphics.MeasureString(temp.c_str(),temp.size(),font_,rectf_text,&rectf_text);
	auto sf = StringFormat::GenericDefault();
	Color color;
	color.SetFromCOLORREF(RGB(0,0,0));
	SolidBrush sb(color);
	graphics.DrawString(temp.c_str(),temp.size(),font_,rectf_text,sf,&sb);


	return 0;
}

截图

[ATL/WTL]_[Gdiplus]_[关于混用GDI和GDI+(GDIPlus)的注意点]_第1张图片

下载地址

项目代码

参考

  1. 中文有些翻译的不正确,可以结合英文看。比如这句
如果在 HDC 上需要 GDI 输出,请先销毁 Graphics 对象,然后再使用原始 HDC 或使用 Graphics::GetHDC() 获取新的 hdc...

原英文是:

 If GDI output is required on the HDC, either destroy the Graphics object before using the original HDC or use Graphics::GetHDC() to get a new HDC...

应该是:

如果在 HDC 上需要 GDI 输出,请先销毁 Graphics 对象,然后再使用原始 HDC; 或者使用 Graphics::GetHDC() 获取新的 hdc...

GDI 和 GDI + 之间的互操作性

Interoperability between GDI and GDI+

你可能感兴趣的:(GDI+编程日积月累,ATL/WTL界面开发)