Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,779 @@
// 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 "cgraphicspath.h"
#include "cgradient.h"
#include "cbitmap.h"
#include "cstring.h"
#include "platform/iplatformgraphicsdevice.h"
#include "platform/iplatformfont.h"
#include "platform/iplatformgraphicspath.h"
#include "platform/iplatformgradient.h"
#include "platform/platformfactory.h"
#include <cassert>
#include <stack>
namespace VSTGUI {
//-----------------------------------------------------------------------------
CDrawContext::Transform::Transform (CDrawContext& context, const CGraphicsTransform& transformation)
: context (context)
, transformation (transformation)
{
if (transformation.isInvariant () == false)
context.pushTransform (transformation);
}
//-----------------------------------------------------------------------------
CDrawContext::Transform::~Transform () noexcept
{
if (transformation.isInvariant () == false)
context.popTransform ();
}
//-----------------------------------------------------------------------------
struct CDrawContext::Impl
{
//-----------------------------------------------------------------------------
struct State
{
SharedPointer<CFontDesc> font;
CColor frameColor {kTransparentCColor};
CColor fillColor {kTransparentCColor};
CColor fontColor {kTransparentCColor};
CCoord frameWidth {0.};
CPoint penLoc {};
CRect clipRect {};
CLineStyle lineStyle {kLineOnOffDash};
CDrawMode drawMode {kAntiAliasing};
float globalAlpha {1.f};
BitmapInterpolationQuality bitmapQuality {BitmapInterpolationQuality::kDefault};
State () = default;
State (const State& state);
State& operator= (const State& state) = default;
State (State&& state) noexcept;
State& operator= (State&& state) noexcept;
};
UTF8String* drawStringHelper {nullptr};
CRect surfaceRect;
double scaleFactor {1.};
State currentState;
std::stack<State> globalStatesStack;
std::stack<CGraphicsTransform> transformStack;
PlatformGraphicsDeviceContextPtr device;
};
//-----------------------------------------------------------------------------
CDrawContext::Impl::State::State (const State& state) { *this = state; }
//-----------------------------------------------------------------------------
CDrawContext::Impl::State::State (State&& state) noexcept { *this = std::move (state); }
//-----------------------------------------------------------------------------
CDrawContext::Impl::State&
CDrawContext::Impl::State::operator= (CDrawContext::Impl::State&& state) noexcept
{
font = std::move (state.font);
frameColor = std::move (state.frameColor);
fillColor = std::move (state.fillColor);
fontColor = std::move (state.fontColor);
frameWidth = std::move (state.frameWidth);
penLoc = std::move (state.penLoc);
clipRect = std::move (state.clipRect);
lineStyle = std::move (state.lineStyle);
drawMode = std::move (state.drawMode);
globalAlpha = std::move (state.globalAlpha);
return *this;
}
//-----------------------------------------------------------------------------
CDrawContext::CDrawContext (const CRect& surfaceRect)
{
impl = std::make_unique<Impl> ();
impl->surfaceRect = surfaceRect;
impl->transformStack.push (CGraphicsTransform ());
}
//------------------------------------------------------------------------
CDrawContext::CDrawContext (const PlatformGraphicsDeviceContextPtr device, const CRect& surfaceRect,
double scaleFactor)
: CDrawContext (surfaceRect)
{
impl->device = device;
impl->scaleFactor = scaleFactor;
setClipRect (surfaceRect);
}
//-----------------------------------------------------------------------------
CDrawContext::~CDrawContext () noexcept
{
#if DEBUG
if (!impl->globalStatesStack.empty ())
DebugPrint ("Global state stack not empty. Save and restore global state must be called in sequence !\n");
#endif
if (impl->drawStringHelper)
delete impl->drawStringHelper;
}
//------------------------------------------------------------------------
const PlatformGraphicsDeviceContextPtr& CDrawContext::getPlatformDeviceContext () const
{
return impl->device;
}
//-----------------------------------------------------------------------------
const CRect& CDrawContext::getSurfaceRect () const { return impl->surfaceRect; }
//------------------------------------------------------------------------
double CDrawContext::getScaleFactor () const { return impl->scaleFactor; }
//-----------------------------------------------------------------------------
void CDrawContext::init ()
{
// set the default values
setFrameColor (kWhiteCColor);
setLineStyle (kLineSolid);
setLineWidth (1);
setFillColor (kBlackCColor);
setFontColor (kWhiteCColor);
setFont (kSystemFont);
setDrawMode (kAliasing);
setClipRect (impl->surfaceRect);
}
//-----------------------------------------------------------------------------
void CDrawContext::saveGlobalState ()
{
impl->globalStatesStack.push (impl->currentState);
if (impl->device)
impl->device->saveGlobalState ();
}
//-----------------------------------------------------------------------------
void CDrawContext::restoreGlobalState ()
{
if (impl->device)
impl->device->restoreGlobalState ();
if (!impl->globalStatesStack.empty ())
{
impl->currentState = std::move (impl->globalStatesStack.top ());
impl->globalStatesStack.pop ();
}
else
{
#if DEBUG
DebugPrint ("No saved global state in draw context !!!\n");
#endif
}
}
//-----------------------------------------------------------------------------
void CDrawContext::setBitmapInterpolationQuality (BitmapInterpolationQuality quality)
{
impl->currentState.bitmapQuality = quality;
}
//-----------------------------------------------------------------------------
BitmapInterpolationQuality CDrawContext::getBitmapInterpolationQuality () const
{
return impl->currentState.bitmapQuality;
}
//-----------------------------------------------------------------------------
void CDrawContext::setLineStyle (const CLineStyle& style)
{
if (impl->device)
impl->device->setLineStyle (style);
impl->currentState.lineStyle = style;
}
//-----------------------------------------------------------------------------
const CLineStyle& CDrawContext::getLineStyle () const { return impl->currentState.lineStyle; }
//-----------------------------------------------------------------------------
void CDrawContext::setLineWidth (CCoord width)
{
if (impl->device)
impl->device->setLineWidth (width);
impl->currentState.frameWidth = width;
}
//-----------------------------------------------------------------------------
CCoord CDrawContext::getLineWidth () const { return impl->currentState.frameWidth; }
//-----------------------------------------------------------------------------
void CDrawContext::setDrawMode (CDrawMode mode)
{
if (impl->device)
impl->device->setDrawMode (mode);
impl->currentState.drawMode = mode;
}
//-----------------------------------------------------------------------------
CDrawMode CDrawContext::getDrawMode () const { return impl->currentState.drawMode; }
//-----------------------------------------------------------------------------
CRect& CDrawContext::getClipRect (CRect &clip) const
{
clip = impl->currentState.clipRect;
getCurrentTransform ().inverse ().transform (clip);
clip.normalize ();
return clip;
}
//-----------------------------------------------------------------------------
const CRect& CDrawContext::getAbsoluteClipRect () const { return impl->currentState.clipRect; }
//-----------------------------------------------------------------------------
void CDrawContext::setClipRect (const CRect &clip)
{
impl->currentState.clipRect = clip;
getCurrentTransform ().transform (impl->currentState.clipRect);
impl->currentState.clipRect.normalize ();
if (impl->device)
impl->device->setClipRect (impl->currentState.clipRect);
}
//-----------------------------------------------------------------------------
void CDrawContext::resetClipRect ()
{
if (impl->device)
impl->device->setClipRect (getSurfaceRect ());
impl->currentState.clipRect = getSurfaceRect ();
}
//-----------------------------------------------------------------------------
void CDrawContext::setFillColor (const CColor& color)
{
if (impl->device)
impl->device->setFillColor (color);
impl->currentState.fillColor = color;
}
//-----------------------------------------------------------------------------
CColor CDrawContext::getFillColor () const { return impl->currentState.fillColor; }
//-----------------------------------------------------------------------------
void CDrawContext::setFrameColor (const CColor& color)
{
if (impl->device)
impl->device->setFrameColor (color);
impl->currentState.frameColor = color;
}
//-----------------------------------------------------------------------------
CColor CDrawContext::getFrameColor () const { return impl->currentState.frameColor; }
//-----------------------------------------------------------------------------
void CDrawContext::setFontColor (const CColor& color) { impl->currentState.fontColor = color; }
//-----------------------------------------------------------------------------
CColor CDrawContext::getFontColor () const { return impl->currentState.fontColor; }
//-----------------------------------------------------------------------------
void CDrawContext::setFont (const CFontRef newFont, const CCoord& size, const int32_t& style)
{
if (newFont == nullptr)
return;
if ((size > 0 && newFont->getSize () != size) || (style != -1 && newFont->getStyle () != style))
{
impl->currentState.font = makeOwned<CFontDesc> (*newFont);
if (size > 0)
impl->currentState.font->setSize (size);
if (style != -1)
impl->currentState.font->setStyle (style);
}
else
{
impl->currentState.font = newFont;
}
}
//-----------------------------------------------------------------------------
const CFontRef CDrawContext::getFont () const { return impl->currentState.font; }
//-----------------------------------------------------------------------------
void CDrawContext::setGlobalAlpha (float newAlpha)
{
if (impl->device)
impl->device->setGlobalAlpha (newAlpha);
impl->currentState.globalAlpha = newAlpha;
}
//-----------------------------------------------------------------------------
float CDrawContext::getGlobalAlpha () const { return impl->currentState.globalAlpha; }
//-----------------------------------------------------------------------------
const UTF8String& CDrawContext::getDrawString (UTF8StringPtr string)
{
if (impl->drawStringHelper == nullptr)
impl->drawStringHelper = new UTF8String (string);
else
impl->drawStringHelper->assign (string);
return *impl->drawStringHelper;
}
//-----------------------------------------------------------------------------
void CDrawContext::clearDrawString ()
{
if (impl->drawStringHelper)
impl->drawStringHelper->clear ();
}
//------------------------------------------------------------------------
CCoord CDrawContext::getStringWidth (IPlatformString* string)
{
CCoord result = -1;
if (impl->currentState.font == nullptr || string == nullptr)
return result;
if (auto painter = impl->currentState.font->getFontPainter ())
result = painter->getStringWidth (impl->device, string, true);
return result;
}
//------------------------------------------------------------------------
void CDrawContext::drawString (IPlatformString* string, const CRect& _rect, const CHoriTxtAlign hAlign, bool antialias)
{
if (!string || impl->currentState.font == nullptr)
return;
auto painter = impl->currentState.font->getFontPainter ();
if (painter == nullptr)
return;
CRect rect (_rect);
double capHeight = -1;
auto platformFont = impl->currentState.font->getPlatformFont ();
if (platformFont)
capHeight = platformFont->getCapHeight ();
if (capHeight > 0.)
rect.bottom -= (rect.getHeight () / 2. - capHeight / 2.);
else
rect.bottom -= (rect.getHeight () / 2. - impl->currentState.font->getSize () / 2.) + 1.;
if (hAlign != kLeftText)
{
CCoord stringWidth = painter->getStringWidth (impl->device, string, antialias);
if (hAlign == kRightText)
rect.left = rect.right - stringWidth;
else
rect.left = rect.left + (rect.getWidth () / 2.) - (stringWidth / 2.);
}
painter->drawString (impl->device, string, CPoint (rect.left, rect.bottom),
impl->currentState.fontColor, antialias);
}
//------------------------------------------------------------------------
void CDrawContext::drawString (IPlatformString* string, const CPoint& point, bool antialias)
{
if (string == nullptr || impl->currentState.font == nullptr)
return;
if (auto painter = impl->currentState.font->getFontPainter ())
painter->drawString (impl->device, string, point, impl->currentState.fontColor, antialias);
}
//-----------------------------------------------------------------------------
CCoord CDrawContext::getStringWidth (UTF8StringPtr string)
{
return getStringWidth (getDrawString (string).getPlatformString ());
}
//-----------------------------------------------------------------------------
void CDrawContext::drawString (UTF8StringPtr string, const CPoint& point, bool antialias)
{
drawString (getDrawString (string).getPlatformString (), point, antialias);
clearDrawString ();
}
//-----------------------------------------------------------------------------
void CDrawContext::drawString (UTF8StringPtr string, const CRect& rect, const CHoriTxtAlign hAlign, bool antialias)
{
drawString (getDrawString (string).getPlatformString (), rect, hAlign, antialias);
clearDrawString ();
}
//-----------------------------------------------------------------------------
void CDrawContext::fillRectWithBitmap (CBitmap* bitmap, const CRect& srcRect, const CRect& dstRect, float alpha)
{
if (srcRect.isEmpty () || dstRect.isEmpty ())
return;
if (srcRect.getWidth () == dstRect.getWidth () && srcRect.getHeight () == dstRect.getHeight ())
{
drawBitmap (bitmap, dstRect, srcRect.getTopLeft (), alpha);
return;
}
if (impl->device)
{
if (auto deviceBitmapExt = impl->device->asBitmapExt ())
{
double transformedScaleFactor = getScaleFactor ();
CGraphicsTransform t = getCurrentTransform ();
if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0)
transformedScaleFactor *= t.m11;
if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor))
{
if (deviceBitmapExt->fillRectWithBitmap (*pb, srcRect, dstRect, alpha,
getBitmapInterpolationQuality ()))
{
return;
}
}
}
}
CRect bitmapPartRect;
CPoint sourceOffset (srcRect.left, srcRect.top);
for (auto top = dstRect.top; top < dstRect.bottom; top += srcRect.getHeight ())
{
bitmapPartRect.top = top;
bitmapPartRect.bottom = top + srcRect.getHeight ();
if (bitmapPartRect.bottom > dstRect.bottom)
bitmapPartRect.bottom = dstRect.bottom;
// The following should never be true, I guess
if (bitmapPartRect.getHeight () > srcRect.getHeight ())
bitmapPartRect.setHeight (srcRect.getHeight ());
for (auto left = dstRect.left; left < dstRect.right; left += srcRect.getWidth ())
{
bitmapPartRect.left = left;
bitmapPartRect.right = left + srcRect.getWidth ();
if (bitmapPartRect.right > dstRect.right)
bitmapPartRect.right = dstRect.right;
// The following should never be true, I guess
if (bitmapPartRect.getWidth () > srcRect.getWidth ())
bitmapPartRect.setWidth (srcRect.getWidth ());
drawBitmap (bitmap, bitmapPartRect, sourceOffset, alpha);
}
}
}
//-----------------------------------------------------------------------------
void CDrawContext::drawBitmapNinePartTiled (CBitmap* bitmap, const CRect& dest, const CNinePartTiledDescription& desc, float alpha)
{
if (impl->device)
{
if (auto deviceBitmapExt = impl->device->asBitmapExt ())
{
double transformedScaleFactor = getScaleFactor ();
CGraphicsTransform t = getCurrentTransform ();
if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0)
transformedScaleFactor *= t.m11;
if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor))
{
if (deviceBitmapExt->drawBitmapNinePartTiled (*pb, dest, desc, alpha,
getBitmapInterpolationQuality ()))
{
return;
}
}
}
}
CRect myBitmapBounds (0, 0, bitmap->getWidth (), bitmap->getHeight ());
CRect mySourceRect [CNinePartTiledDescription::kPartCount];
CRect myDestRect [CNinePartTiledDescription::kPartCount];
desc.calcRects (myBitmapBounds, mySourceRect);
desc.calcRects (dest, myDestRect);
for (size_t i = 0; i < CNinePartTiledDescription::kPartCount; i++)
fillRectWithBitmap (bitmap, mySourceRect[i], myDestRect[i], alpha);
}
//-----------------------------------------------------------------------------
CGraphicsPath* CDrawContext::createRoundRectGraphicsPath (const CRect& size, CCoord radius)
{
if (auto path = createGraphicsPath ())
{
path->addRoundRect (size, radius);
return path;
}
return {};
}
//-----------------------------------------------------------------------------
void CDrawContext::pushTransform (const CGraphicsTransform& transformation)
{
vstgui_assert (!impl->transformStack.empty ());
const CGraphicsTransform& currentTransform = impl->transformStack.top ();
CGraphicsTransform newTransform = currentTransform * transformation;
impl->transformStack.push (newTransform);
if (impl->device)
impl->device->setTransformMatrix (newTransform);
}
//-----------------------------------------------------------------------------
void CDrawContext::popTransform ()
{
vstgui_assert (impl->transformStack.size () > 1);
impl->transformStack.pop ();
if (impl->device)
impl->device->setTransformMatrix (impl->transformStack.top ());
}
//-----------------------------------------------------------------------------
const CGraphicsTransform& CDrawContext::getCurrentTransform () const
{
return impl->transformStack.top ();
}
//------------------------------------------------------------------------
CCoord CDrawContext::getHairlineSize () const
{
return 1. / (getScaleFactor () * getCurrentTransform ().m11);
}
//------------------------------------------------------------------------
static PlatformGraphicsDrawStyle convert (CDrawStyle s)
{
switch (s)
{
case CDrawStyle::kDrawFilled:
return PlatformGraphicsDrawStyle::Filled;
case CDrawStyle::kDrawStroked:
return PlatformGraphicsDrawStyle::Stroked;
case CDrawStyle::kDrawFilledAndStroked:
return PlatformGraphicsDrawStyle::FilledAndStroked;
default:
assert (false);
}
return {};
}
//------------------------------------------------------------------------
void CDrawContext::drawLine (const LinePair& line)
{
if (impl->device)
impl->device->drawLine (line);
}
//------------------------------------------------------------------------
void CDrawContext::drawLines (const LineList& lines)
{
if (impl->device)
impl->device->drawLines (lines);
}
//------------------------------------------------------------------------
void CDrawContext::drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle)
{
if (impl->device)
impl->device->drawPolygon (polygonPointList, convert (drawStyle));
}
//------------------------------------------------------------------------
void CDrawContext::drawRect (const CRect& rect, const CDrawStyle drawStyle)
{
if (impl->device)
impl->device->drawRect (rect, convert (drawStyle));
}
//------------------------------------------------------------------------
void CDrawContext::drawArc (const CRect& rect, const float startAngle1, const float endAngle2,
const CDrawStyle drawStyle)
{
if (impl->device)
impl->device->drawArc (rect, startAngle1, endAngle2, convert (drawStyle));
}
//------------------------------------------------------------------------
void CDrawContext::drawEllipse (const CRect& rect, const CDrawStyle drawStyle)
{
if (impl->device)
impl->device->drawEllipse (rect, convert (drawStyle));
}
//------------------------------------------------------------------------
void CDrawContext::drawPoint (const CPoint& point, const CColor& color)
{
if (impl->device && impl->device->drawPoint (point, color))
return;
// if the platform does not support drawing points, emulate it somehow
saveGlobalState ();
CRect r (point.x, point.y, point.x, point.y);
r.inset (-0.5, -0.5);
setDrawMode (kAliasing);
setFillColor (color);
drawRect (r, kDrawFilled);
restoreGlobalState ();
}
//------------------------------------------------------------------------
void CDrawContext::drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset,
float alpha)
{
if (impl->device)
{
double transformedScaleFactor = getScaleFactor ();
CGraphicsTransform t = getCurrentTransform ();
if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0)
transformedScaleFactor *= t.m11;
if (auto pb = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor))
impl->device->drawBitmap (*pb, dest, offset, alpha, getBitmapInterpolationQuality ());
}
}
//------------------------------------------------------------------------
void CDrawContext::clearRect (const CRect& rect)
{
if (impl->device)
impl->device->clearRect (rect);
}
//------------------------------------------------------------------------
static PlatformGraphicsPathDrawMode convert (CDrawContext::PathDrawMode mode)
{
switch (mode)
{
case CDrawContext::PathDrawMode::kPathFilled:
return PlatformGraphicsPathDrawMode::Filled;
case CDrawContext::PathDrawMode::kPathStroked:
return PlatformGraphicsPathDrawMode::Stroked;
case CDrawContext::PathDrawMode::kPathFilledEvenOdd:
return PlatformGraphicsPathDrawMode::FilledEvenOdd;
default:
assert (false);
}
return {};
}
//------------------------------------------------------------------------
void CDrawContext::drawGraphicsPath (CGraphicsPath* path, PathDrawMode mode,
CGraphicsTransform* transformation)
{
if (impl->device)
{
if (auto& pp = path->getPlatformPath (mode == kPathFilledEvenOdd
? PlatformGraphicsPathFillMode::Alternate
: PlatformGraphicsPathFillMode::Winding))
impl->device->drawGraphicsPath (*pp.get (), convert (mode), transformation);
}
}
//------------------------------------------------------------------------
void CDrawContext::fillLinearGradient (CGraphicsPath* path, const CGradient& gradient,
const CPoint& startPoint, const CPoint& endPoint,
bool evenOdd, CGraphicsTransform* transformation)
{
if (impl->device)
{
if (auto& platformGradient = gradient.getPlatformGradient ())
{
if (auto& pp = path->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate
: PlatformGraphicsPathFillMode::Winding))
{
impl->device->fillLinearGradient (*pp.get (), *platformGradient, startPoint,
endPoint, evenOdd, transformation);
}
}
}
}
//------------------------------------------------------------------------
void CDrawContext::fillRadialGradient (CGraphicsPath* path, const CGradient& gradient,
const CPoint& center, CCoord radius,
const CPoint& originOffset, bool evenOdd,
CGraphicsTransform* transformation)
{
if (impl->device)
{
if (auto& platformGradient = gradient.getPlatformGradient ())
{
if (auto& pp = path->getPlatformPath (evenOdd ? PlatformGraphicsPathFillMode::Alternate
: PlatformGraphicsPathFillMode::Winding))
{
impl->device->fillRadialGradient (*pp.get (), *platformGradient, center, radius,
originOffset, evenOdd, transformation);
}
}
}
}
//------------------------------------------------------------------------
bool CDrawContext::drawLinearGradientLine (const DrawLinearGradientLineCallback& cb)
{
if (auto ext =
std::dynamic_pointer_cast<IPlatformGraphicsDeviceContextGradientExt> (impl->device))
{
struct Tmp : IDrawLinearGradientLine
{
std::shared_ptr<IPlatformGraphicsDeviceContextGradientExt> obj;
bool draw (const PointList& line, const CGradient& gradient, CCoord lineWidth,
CLineStyle::LineCap cap, CLineStyle::LineJoin join) const override
{
const auto& platformGradient = gradient.getPlatformGradient ();
if (!platformGradient)
return false;
return obj->drawLinearGradientLine (line, *gradient.getPlatformGradient ().get (),
lineWidth, static_cast<LineCap> (cap),
static_cast<LineJoin> (join));
}
};
Tmp t;
t.obj = ext;
cb (t);
return true;
}
return false;
}
//------------------------------------------------------------------------
CGraphicsPath* CDrawContext::createGraphicsPath ()
{
if (impl->device)
return new CGraphicsPath (impl->device->getGraphicsPathFactory (), nullptr);
return nullptr;
}
//------------------------------------------------------------------------
CGraphicsPath* CDrawContext::createTextPath (const CFontRef font, UTF8StringPtr text)
{
if (impl->device)
{
auto platformFont = font->getPlatformFont ();
auto pathFactory = impl->device->getGraphicsPathFactory ();
if (platformFont && pathFactory)
{
if (auto textPath = pathFactory->createTextPath (platformFont, text))
return new CGraphicsPath (pathFactory, std::move (textPath));
}
}
return nullptr;
}
//------------------------------------------------------------------------
void CDrawContext::beginDraw ()
{
if (impl->device)
impl->device->beginDraw ();
}
//------------------------------------------------------------------------
void CDrawContext::endDraw ()
{
if (impl->device)
impl->device->endDraw ();
}
} // VSTGUI