LinearGradientBrush需避开坐标陷阱:默认按设备像素工作,应映射客户区逻辑坐标或统一Transform缩放;渐变方向向量须归一化;缓存Brush并用SetLinearColors更新;注意CompositingMode与TextRenderingHint设置;Graphics生命周期须手动管理;高DPI下需显式设PageUnit、PageScale及缩放字体与矩形。

GDI+绘制渐变色:LinearGradientBrush坐标陷阱必须避开
直接用LinearGradientBrush画渐变,最常翻车的是颜色“跑偏”或“拉不出方向”。根本原因不是参数写错,而是它默认按设备像素坐标工作,而你传的起点终点如果没对齐窗口客户区逻辑坐标(尤其高DPI下),渐变轴就歪了。
实操建议:
- 始终用
Graphics::GetDC()拿到的HDC做坐标映射,或直接在Graphics对象上用Transform统一缩放——别靠肉眼估算(0,0)到(200,100) - 渐变方向向量要归一化再乘长度,否则斜率大时颜色带会压缩变形;例如从左上到右下,用
Point(0,0)和Point(1,1)比Point(0,0)和Point(100,100)更可控 - 避免在
WM_PAINT里反复new LinearGradientBrush,构造开销大;应缓存为成员变量,重绘前调用SetLinearColors()更新颜色数组即可 - 若背景是位图或需要透明叠加,务必确认
Graphics::SetCompositingMode(CompositingModeSourceOver),否则渐变可能被半透明图层吃掉Alpha
抗锯齿文本:SmoothingMode vs TextRenderingHint别混用
Graphics::SetSmoothingMode(SmoothingModeAntiAlias)对线条有效,但对文本几乎没用——GDI+把文本渲染单独拎出来管,走的是另一套子系统。
真正起效的是Graphics::SetTextRenderingHint(),而且不同值效果差异极大:
立即学习“C++免费学习笔记(深入)”;
-
TextRenderingHintClearTypeGridFit:Windows默认,依赖ClearType子像素渲染,在LCD屏上最锐利,但转到OLED或远程桌面可能发虚 -
TextRenderingHintAntiAliasGridFit:关闭子像素,只做灰度抗锯齿,跨设备一致性更好,适合导出图片或打印预览 -
TextRenderingHintSingleBitPerPixelGridFit:纯二值,无抗锯齿,但极快,适合状态栏小字号或性能敏感场景 - 切记:这个设置只影响后续
DrawString(),不影响DrawLine()或FillRectangle()
混合绘制时资源泄漏:Graphics对象生命周期必须手动管
很多人在MFC或Win32中用CPaintDC dc(this)后直接Graphics g(dc.m_hDC),以为自动析构。错——Graphics不持有HDC所有权,CPaintDC析构时HDC就被释放,但Graphics内部缓存可能还在用,下次绘图就崩。
安全做法:
- 在
OnPaint()或WM_PAINT处理函数内,用BeginPaint()/EndPaint()获取HDC,构造Graphics,用完立刻销毁(C++栈对象最稳) - 不要把
Graphics*存为类成员;如需复用,改用Bitmap+Graphics::FromImage()离屏绘制,再BitBlt上屏 - 所有
Pen、Brush、Font对象都应在同一作用域内创建并销毁;GDI+不会自动回收,漏掉一个DeleteObject()等价于内存泄漏
高DPI下渐变与文本错位:DPI感知必须显式开启
即使设置了SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2),GDI+仍可能按96 DPI算坐标,导致渐变条宽度不对、文字位置偏移几个像素。
关键补丁:
- 在创建
Graphics后,立即调用g.SetPageUnit(UnitPixel)和g.SetPageScale(1.0f),防止它偷偷换单位 - 用
GetDpiForWindow()查当前屏幕DPI,把字体大小乘以scale = dpi / 96.0f再传给Font构造函数 - 渐变刷的矩形区域(
RectF)坐标也得用同样scale缩放,否则渐变停在半路就断了 - MFC项目记得在
OnInitDialog()或OnCreate()里调用AfxEnableControlContainer(),否则某些控件会覆盖DPI设置
LinearGradientBrush的起点终点和TextRenderingHint的选择,差一个参数,成品观感就从专业掉到简陋。


















