// 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 #include 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 static std::array boxesForGauss (double sigma) { std::array boxes; double ideal = std::sqrt ((12 * sigma * sigma / numBoxes) + 1); uint16_t l = static_cast (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 (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 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 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