Search code examples
c++graphicsdrawinggdi+

How to increase width of rounded rectangle path in GDI+?


I have a few dozen rounded rectangles which I draw but frequently need to change the width of. I'm currently using the following (heavily simplified) class:

class RRPath
{
    std::unique_ptr<GraphicsPath> pth;
    bool newWidth{};
    float xPos{};
    PathData pd;        
public:

    RRPath()
    {
        GraphicsPath path;
        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.GetPathData(&pd);
    }

    void setWidth(float tabW) { tabWidth = tabW; newWidth = true; }

    GraphicsPath *getPath(float x)  // return a GraphicsPath*, adjusted for starting position `x`
    {
        if (newWidth) mkNewPath();

        Matrix m{ 1, 0, 0, 1, x - xPos, 0 };
        pth->Transform(&m);
        xPos = x;

        return pth.get();
    }
private:
    void mkNewPath()
    {
        for (int i = 8; i < 19; i++) pd.Points[i].X += tabWidth;
        pth = std::make_unique<GraphicsPath>(pd.Points, pd.Types, pd.Count);
        for (int i = 8; i < 19; i++) pd.Points[i].X -= tabWidth;
        
        xPos = 0.f;
    }
};

It creates the PathData in the contructor and creates a new path every time the width changes (which is frequently) Now, this works fine but it bugs me that it needs to delete and create a new path (on the heap) every time. The number of points in the path never changes so I'm thinking there should be a way to use only one path, created in the contructor, and adjust it as needed to increase the width without having to create a new path every time. The only thing done with the path is FillPath.


Solution

  • Ok, seems like there's not much interest in this area, or at least not in this question. But I have been able to resolve it so I will post the answer here in case anyone is curious. I wasn't able to find a way to modify a GraphicsPath however I was able to achieve the main result that being getting rid of the heap allocations.

    The code below enables me to create a single rounded rectangle path and use it for numerous paths with different widths and horizontal positioning. It also runs around 20% faster.

    class RRPath
    {
        GraphicsPath pth;
        float prevW{}, prevX{};
    protected:
        GraphicsPath pS, pE;
    public:
        RRPath() 
        {
            pS.AddArc(0.f, 12.f, 24.f, 24.f, 90, 90);
            pS.AddArc(0.f, 0.f, 24.f, 24.f, 180, 90);
            pE.AddArc(0.f, 0.f, 24.f, 24.f, 270, 90);
            pE.AddArc(0.f, 12.f, 24.f, 24.f, 0, 90);
        };
    
        void setWidth(float w)
        {
            pth.Reset();
            pth.AddPath(&pS, false);
            Matrix m{ 1, 0, 0, 1, w - prevW, 0 };
            pE.Transform(&m);
            pth.AddPath(&pE, true);
            prevX = 0.f;
            prevW = w;
        };
        GraphicsPath *getPath(float offsetX)
        {
            Matrix m{ 1, 0, 0, 1, offsetX - prevX, 0 };
            pth.Transform(&m);
            prevX = offsetX;
            return &pth;
        };
    };