如何在GDI+中无需重建路径即可调整圆角矩形路径宽度?
优化圆角矩形路径类,避免重复堆分配
你的核心思路完全正确——既然路径点数固定,完全可以复用同一个GraphicsPath对象,通过直接修改路径点的坐标来调整宽度,无需每次创建新对象。以下是优化后的实现,解决了堆分配问题,同时修复了原代码的内存泄漏隐患:
#include <gdiplus.h> #include <cstring> #include <objbase.h> // 用于CoTaskMemFree/Alloc using namespace Gdiplus; class RRPath { GraphicsPath path; PathData basePD; // 保存构造时的原始路径数据,作为修改模板 float currentWidth = 0.f; float currentX = 0.f; public: RRPath() { // 创建初始圆角矩形模板(与原逻辑一致) path.AddArc(0.f, 12.f, 24.f, 24.f, 90, 90); path.AddArc(0.f, 0.f, 24.f, 24.f, 180, 90); path.AddArc(0.f, 0.f, 24.f, 24.f, 270, 90); path.AddArc(0.f, 12.f, 24.f, 24.f, 0, 90); path.CloseFigure(); // 确保路径闭合,填充更可靠 // 获取原始路径数据,后续以此为模板修改 path.GetPathData(&basePD); } ~RRPath() { // 释放GDI+分配的路径数据内存,避免泄漏 if (basePD.Points != nullptr) { CoTaskMemFree(basePD.Points); basePD.Points = nullptr; } if (basePD.Types != nullptr) { CoTaskMemFree(basePD.Types); basePD.Types = nullptr; } } // 禁止拷贝和移动,避免路径数据的内存管理问题 RRPath(const RRPath&) = delete; RRPath& operator=(const RRPath&) = delete; RRPath(RRPath&&) = delete; RRPath& operator=(RRPath&&) = delete; void setWidth(float tabW) { if (tabW == currentWidth) return; // 宽度未变化,无需操作 // 复制原始路径数据,基于模板修改宽度 PathData pd; pd.Count = basePD.Count; // 分配内存存储点和类型数据 pd.Points = static_cast<PointF*>(CoTaskMemAlloc(pd.Count * sizeof(PointF))); pd.Types = static_cast<BYTE*>(CoTaskMemAlloc(pd.Count)); if (!pd.Points || !pd.Types) return; // 内存分配失败,直接返回 // 拷贝原始数据 memcpy(pd.Points, basePD.Points, pd.Count * sizeof(PointF)); memcpy(pd.Types, basePD.Types, pd.Count); // 修改对应点的X坐标,调整宽度(与原逻辑一致) for (int i = 8; i < 19; ++i) { pd.Points[i].X += tabW; } // 更新现有路径,无需创建新对象 path.SetPathData(&pd); // 释放临时分配的内存 CoTaskMemFree(pd.Points); CoTaskMemFree(pd.Types); currentWidth = tabW; currentX = 0.f; // 重置位置,路径回到X=0基准点 } GraphicsPath* getPath(float x) { if (x != currentX) { // 计算平移差值,仅调整位置变化量 const float deltaX = x - currentX; Matrix transform{1, 0, 0, 1, deltaX, 0}; path.Transform(&transform); currentX = x; } return &path; } };
关键优化说明
- 复用单个GraphicsPath对象:不再每次宽度变化时创建新的
GraphicsPath,直接修改现有对象的路径数据,彻底避免堆上的分配与释放操作。 - 保存原始路径模板:构造时保存
basePD作为原始路径数据,每次宽度调整时基于该模板复制修改,无需像原代码那样临时修改后再还原。 - 修复内存泄漏:原代码中未释放
PathData的Points和Types数组(GDI+通过CoTaskMemAlloc分配),优化后的代码在析构函数和临时数据处理中正确释放内存。 - 避免不必要操作:增加
currentWidth和currentX缓存,仅在宽度或位置真正变化时执行修改,减少冗余计算。 - 路径闭合保障:添加
CloseFigure()确保路径闭合,避免FillPath时出现异常填充。
内容的提问来源于stack exchange,提问作者poby
相关产品推荐
相关产品推荐

