You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;
    }
};

关键优化说明

  1. 复用单个GraphicsPath对象:不再每次宽度变化时创建新的GraphicsPath,直接修改现有对象的路径数据,彻底避免堆上的分配与释放操作。
  2. 保存原始路径模板:构造时保存basePD作为原始路径数据,每次宽度调整时基于该模板复制修改,无需像原代码那样临时修改后再还原。
  3. 修复内存泄漏:原代码中未释放PathData的Points和Types数组(GDI+通过CoTaskMemAlloc分配),优化后的代码在析构函数和临时数据处理中正确释放内存。
  4. 避免不必要操作:增加currentWidth和currentX缓存,仅在宽度或位置真正变化时执行修改,减少冗余计算。
  5. 路径闭合保障:添加CloseFigure()确保路径闭合,避免FillPath时出现异常填充。

内容的提问来源于stack exchange,提问作者poby

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 20:42:34