Files
2026-08-16 18:24:52 +07:00

252 lines
8.1 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 "cshadowviewcontainer.h"
#include "coffscreencontext.h"
#include "cbitmapfilter.h"
#include "cframe.h"
#include "cbitmap.h"
#include <cassert>
#include <array>
namespace VSTGUI {
//-----------------------------------------------------------------------------
CShadowViewContainer::CShadowViewContainer (const CRect& size)
: CViewContainer (size)
, dontDrawBackground (false)
, shadowIntensity (0.3f)
, shadowBlurSize (4)
, scaleFactorUsed (0.)
{
registerViewContainerListener (this);
}
//-----------------------------------------------------------------------------
CShadowViewContainer::CShadowViewContainer (const CShadowViewContainer& copy)
: CViewContainer (copy)
, dontDrawBackground (false)
, shadowIntensity (copy.shadowIntensity)
, shadowBlurSize (copy.shadowBlurSize)
, scaleFactorUsed (0.)
{
registerViewContainerListener (this);
}
//------------------------------------------------------------------------
CShadowViewContainer::~CShadowViewContainer () noexcept = default;
//------------------------------------------------------------------------
void CShadowViewContainer::beforeDelete ()
{
unregisterViewContainerListener (this);
CViewContainer::beforeDelete ();
}
//-----------------------------------------------------------------------------
bool CShadowViewContainer::removed (CView* parent)
{
getFrame ()->unregisterScaleFactorChangedListener (this);
setBackground (nullptr);
return CViewContainer::removed (parent);
}
//-----------------------------------------------------------------------------
bool CShadowViewContainer::attached (CView* parent)
{
if (CViewContainer::attached (parent))
{
invalidateShadow ();
getFrame ()->registerScaleFactorChangedListener (this);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::onScaleFactorChanged (CFrame* frame, double newScaleFactor)
{
invalidateShadow ();
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::setShadowOffset (const CPoint& offset)
{
if (shadowOffset != offset)
{
shadowOffset = offset;
invalidateShadow ();
}
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::setShadowIntensity (float intensity)
{
if (shadowIntensity != intensity)
{
shadowIntensity = intensity;
invalid ();
}
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::setShadowBlurSize (double size)
{
if (shadowBlurSize != size)
{
shadowBlurSize = size;
invalidateShadow ();
}
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::invalidateShadow ()
{
scaleFactorUsed = 0.;
invalid ();
}
//-----------------------------------------------------------------------------
CMessageResult CShadowViewContainer::notify (CBaseObject* sender, IdStringPtr message)
{
if (message == kMsgViewSizeChanged)
invalidateShadow ();
return CViewContainer::notify(sender, message);
}
//-----------------------------------------------------------------------------
template <size_t numBoxes>
static std::array<int32_t, numBoxes> boxesForGauss (double sigma)
{
std::array<int32_t, numBoxes> boxes;
double ideal = std::sqrt ((12 * sigma * sigma / numBoxes) + 1);
uint16_t l = static_cast<uint16_t> (std::floor (ideal));
if (l % 2 == 0)
l--;
int32_t u = l + 2;
ideal = ((12. * sigma * sigma) - (numBoxes * l * l) - (4. * numBoxes * l) - (3. * numBoxes)) / ((-4. * l) - 4.);
int32_t m = static_cast<int32_t> (std::floor (ideal));
for (int32_t i = 0; i < numBoxes; ++i)
boxes[i] = (i < m ? l : u);
return boxes;
}
//-----------------------------------------------------------------------------
static bool isUniformScaled (const CGraphicsTransform& matrix)
{
return matrix.m11 == matrix.m22;
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::drawRect (CDrawContext* pContext, const CRect& updateRect)
{
double scaleFactor = pContext->getScaleFactor ();
CGraphicsTransform matrix = pContext->getCurrentTransform ();
if (isUniformScaled (matrix))
{
double matrixScale = std::floor (matrix.m11 + 0.5);
if (matrixScale != 0.)
scaleFactor *= matrixScale;
}
if (scaleFactor != scaleFactorUsed && getWidth () > 0. && getHeight () > 0.)
{
scaleFactorUsed = scaleFactor;
CCoord width = getWidth ();
CCoord height = getHeight ();
if (auto offscreenContext = COffscreenContext::create ({width, height}, scaleFactor))
{
offscreenContext->beginDraw ();
CDrawContext::Transform transform (*offscreenContext, CGraphicsTransform ().translate (-getViewSize ().left - shadowOffset.x, -getViewSize ().top - shadowOffset.y));
dontDrawBackground = true;
CViewContainer::draw (offscreenContext);
dontDrawBackground = false;
offscreenContext->endDraw ();
CBitmap* bitmap = offscreenContext->getBitmap ();
if (bitmap)
{
setBackground (bitmap);
SharedPointer<BitmapFilter::IFilter> setColorFilter = owned (BitmapFilter::Factory::getInstance ().createFilter (BitmapFilter::Standard::kSetColor));
if (setColorFilter)
{
setColorFilter->setProperty (BitmapFilter::Standard::Property::kInputBitmap, bitmap);
setColorFilter->setProperty (BitmapFilter::Standard::Property::kInputColor, kBlackCColor);
setColorFilter->setProperty (BitmapFilter::Standard::Property::kIgnoreAlphaColorValue, (int32_t)1);
if (setColorFilter->run (true))
{
SharedPointer<BitmapFilter::IFilter> boxBlurFilter = owned (BitmapFilter::Factory::getInstance ().createFilter (BitmapFilter::Standard::kBoxBlur));
if (boxBlurFilter)
{
auto boxSizes = boxesForGauss<3> (shadowBlurSize);
boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kInputBitmap, bitmap);
boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kRadius, boxSizes[0]);
boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kAlphaChannelOnly, 1);
if (boxBlurFilter->run (true))
{
boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kRadius, boxSizes[1]);
boxBlurFilter->run (true);
boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kRadius, boxSizes[2]);
boxBlurFilter->run (true);
}
}
}
}
CViewContainer::drawRect (pContext, updateRect);
}
}
}
else
{
CViewContainer::drawRect (pContext, updateRect);
}
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::drawBackgroundRect (CDrawContext* pContext, const CRect& _updateRect)
{
if (!dontDrawBackground)
{
float tmp = pContext->getGlobalAlpha ();
pContext->setGlobalAlpha (tmp * shadowIntensity);
CViewContainer::drawBackgroundRect (pContext, _updateRect);
pContext->setGlobalAlpha (tmp);
}
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::setViewSize (const CRect& rect, bool invalid)
{
if (getViewSize () != rect)
{
bool diffSize = (getWidth () != rect.getWidth () || getHeight () != rect.getHeight ());
CViewContainer::setViewSize (rect, invalid);
if (diffSize)
invalidateShadow ();
}
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::viewContainerViewAdded (CViewContainer* container, CView* view)
{
vstgui_assert (container == this);
invalidateShadow ();
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::viewContainerViewRemoved (CViewContainer* container, CView* view)
{
vstgui_assert (container == this);
invalidateShadow ();
}
//-----------------------------------------------------------------------------
void CShadowViewContainer::viewContainerViewZOrderChanged (CViewContainer* container, CView* view)
{
vstgui_assert (container == this);
invalidateShadow ();
}
} // VSTGUI