Files
hot-step-cpp-ROCm/engine/vendor/vst3sdk/vstgui4/vstgui/lib/cgraphicspath.cpp
T
2026-08-16 18:24:52 +07:00

331 lines
9.0 KiB
C++

// This file is part of VSTGUI. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
#include "cdrawcontext.h"
#include "cgradient.h"
#include "cgraphicspath.h"
#include "cgraphicstransform.h"
#include "platform/iplatformgraphicspath.h"
namespace VSTGUI {
//-----------------------------------------------------------------------------
void CGraphicsPath::addRoundRect (const CRect& size, CCoord radius)
{
if (radius <= 0.)
{
addRect (size);
return;
}
CRect rect2 (size);
rect2.normalize ();
const CCoord left = rect2.left;
const CCoord right = rect2.right;
const CCoord top = rect2.top;
const CCoord bottom = rect2.bottom;
beginSubpath (CPoint (right - radius, top));
addArc (CRect (right - 2.0 * radius, top, right, top + 2.0 * radius), 270., 360., true);
addArc (CRect (right - 2.0 * radius, bottom - 2.0 * radius, right, bottom), 0., 90., true);
addArc (CRect (left, bottom - 2.0 * radius, left + 2.0 * radius, bottom), 90., 180., true);
addArc (CRect (left, top, left + 2.0 * radius, top + 2.0 * radius), 180., 270., true);
closeSubpath ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addPath (const CGraphicsPath& inPath, CGraphicsTransform* transformation)
{
for (auto e : inPath.elements)
{
if (transformation)
{
switch (e.type)
{
case Element::kArc:
{
transformation->transform (
e.instruction.arc.rect.left, e.instruction.arc.rect.right,
e.instruction.arc.rect.top, e.instruction.arc.rect.bottom);
break;
}
case Element::kEllipse:
case Element::kRect:
{
transformation->transform (e.instruction.rect.left, e.instruction.rect.right,
e.instruction.rect.top, e.instruction.rect.bottom);
break;
}
case Element::kBeginSubpath:
case Element::kLine:
{
transformation->transform (e.instruction.point.x, e.instruction.point.y);
break;
}
case Element::kBezierCurve:
{
transformation->transform (e.instruction.curve.control1.x,
e.instruction.curve.control1.y);
transformation->transform (e.instruction.curve.control2.x,
e.instruction.curve.control2.y);
transformation->transform (e.instruction.curve.end.x,
e.instruction.curve.end.y);
break;
}
case Element::kCloseSubpath:
{
break;
}
}
}
elements.emplace_back (e);
}
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addArc (const CRect& rect, double startAngle, double endAngle, bool clockwise)
{
Element e;
e.type = Element::kArc;
CRect2Rect (rect, e.instruction.arc.rect);
e.instruction.arc.startAngle = startAngle;
e.instruction.arc.endAngle = endAngle;
e.instruction.arc.clockwise = clockwise;
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addEllipse (const CRect& rect)
{
Element e;
e.type = Element::kEllipse;
CRect2Rect (rect, e.instruction.rect);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addRect (const CRect& rect)
{
Element e;
e.type = Element::kRect;
CRect2Rect (rect, e.instruction.rect);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addLine (const CPoint& to)
{
Element e;
e.type = Element::kLine;
CPoint2Point (to, e.instruction.point);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::addBezierCurve (const CPoint& control1, const CPoint& control2,
const CPoint& end)
{
Element e;
e.type = Element::kBezierCurve;
CPoint2Point (control1, e.instruction.curve.control1);
CPoint2Point (control2, e.instruction.curve.control2);
CPoint2Point (end, e.instruction.curve.end);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::beginSubpath (const CPoint& start)
{
Element e;
e.type = Element::kBeginSubpath;
CPoint2Point (start, e.instruction.point);
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
void CGraphicsPath::closeSubpath ()
{
Element e;
e.type = Element::kCloseSubpath;
elements.emplace_back (e);
dirty ();
}
//-----------------------------------------------------------------------------
CGraphicsPath::CGraphicsPath (const PlatformGraphicsPathFactoryPtr& factory,
PlatformGraphicsPathPtr&& path)
: factory (factory), path (std::move (path))
{
}
//------------------------------------------------------------------------
CGraphicsPath::CGraphicsPath (const CGraphicsPath& p) : elements (p.elements), factory (p.factory)
{
}
//-----------------------------------------------------------------------------
CGraphicsPath::~CGraphicsPath () noexcept {}
//-----------------------------------------------------------------------------
CGradient* CGraphicsPath::createGradient (double color1Start, double color2Start,
const CColor& color1, const CColor& color2)
{
return CGradient::create (color1Start, color2Start, color1, color2);
}
//-----------------------------------------------------------------------------
void CGraphicsPath::makePlatformGraphicsPath (PlatformGraphicsPathFillMode fillMode)
{
if (!factory)
return;
path = factory->createPath (fillMode);
if (!path)
return;
for (const auto& e : elements)
{
switch (e.type)
{
case Element::kArc:
{
path->addArc (rect2CRect (e.instruction.arc.rect), e.instruction.arc.startAngle,
e.instruction.arc.endAngle, e.instruction.arc.clockwise);
break;
}
case Element::kEllipse:
{
path->addEllipse (rect2CRect (e.instruction.rect));
break;
}
case Element::kRect:
{
path->addRect (rect2CRect (e.instruction.rect));
break;
}
case Element::kLine:
{
path->addLine (point2CPoint (e.instruction.point));
break;
}
case Element::kBezierCurve:
{
path->addBezierCurve (point2CPoint (e.instruction.curve.control1),
point2CPoint (e.instruction.curve.control2),
point2CPoint (e.instruction.curve.end));
break;
}
case Element::kBeginSubpath:
{
path->beginSubpath (point2CPoint (e.instruction.point));
break;
}
case Element::kCloseSubpath:
{
path->closeSubpath ();
break;
}
}
}
path->finishBuilding ();
}
//-----------------------------------------------------------------------------
bool CGraphicsPath::ensurePlatformGraphicsPathValid (PlatformGraphicsPathFillMode fillMode)
{
if (path == nullptr || (path->getFillMode () != PlatformGraphicsPathFillMode::Ignored &&
path->getFillMode () != fillMode))
{
makePlatformGraphicsPath (fillMode);
}
return path != nullptr;
}
//-----------------------------------------------------------------------------
void CGraphicsPath::dirty ()
{
path = nullptr;
}
//-----------------------------------------------------------------------------
bool CGraphicsPath::hitTest (const CPoint& p, bool evenOddFilled, CGraphicsTransform* transform)
{
ensurePlatformGraphicsPathValid (evenOddFilled ? PlatformGraphicsPathFillMode::Alternate
: PlatformGraphicsPathFillMode::Winding);
return path ? path->hitTest (p, evenOddFilled, transform) : false;
}
//-----------------------------------------------------------------------------
CPoint CGraphicsPath::getCurrentPosition ()
{
CPoint res;
if (!elements.empty ())
{
const auto& e = elements.back ();
switch (e.type)
{
case Element::kBeginSubpath:
{
res = point2CPoint (e.instruction.point);
break;
}
case Element::kCloseSubpath:
{
// TODO: find opening point
break;
}
case Element::kArc:
{
// TODO: calculate end point
break;
}
case Element::kEllipse:
{
res = {e.instruction.rect.left +
(e.instruction.rect.right - e.instruction.rect.left) / 2.,
e.instruction.rect.bottom};
break;
}
case Element::kRect:
{
res = rect2CRect (e.instruction.rect).getTopLeft ();
break;
}
case Element::kLine:
{
res = point2CPoint (e.instruction.point);
break;
}
case Element::kBezierCurve:
{
res = point2CPoint (e.instruction.curve.end);
break;
}
}
}
return res;
}
//-----------------------------------------------------------------------------
CRect CGraphicsPath::getBoundingBox ()
{
ensurePlatformGraphicsPathValid (path ? path->getFillMode ()
: PlatformGraphicsPathFillMode::Winding);
return path ? path->getBoundingBox () : CRect ();
}
//-----------------------------------------------------------------------------
const PlatformGraphicsPathPtr&
CGraphicsPath::getPlatformPath (PlatformGraphicsPathFillMode fillMode)
{
ensurePlatformGraphicsPathValid (fillMode);
return path;
}
} // VSTGUI