1 // This file is part of VSTGUI. It is subject to the license terms
2 // in the LICENSE file found in the top-level directory of this
3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
4 
5 #include "cshadowviewcontainer.h"
6 #include "coffscreencontext.h"
7 #include "cbitmapfilter.h"
8 #include "cframe.h"
9 #include "cbitmap.h"
10 #include <cassert>
11 #include <array>
12 
13 namespace VSTGUI {
14 
15 //-----------------------------------------------------------------------------
CShadowViewContainer(const CRect & size)16 CShadowViewContainer::CShadowViewContainer (const CRect& size)
17 : CViewContainer (size)
18 , dontDrawBackground (false)
19 , shadowIntensity (0.3f)
20 , shadowBlurSize (4)
21 , scaleFactorUsed (0.)
22 {
23 	registerViewContainerListener (this);
24 }
25 
26 //-----------------------------------------------------------------------------
CShadowViewContainer(const CShadowViewContainer & copy)27 CShadowViewContainer::CShadowViewContainer (const CShadowViewContainer& copy)
28 : CViewContainer (copy)
29 , dontDrawBackground (false)
30 , shadowIntensity (copy.shadowIntensity)
31 , shadowBlurSize (copy.shadowBlurSize)
32 , scaleFactorUsed (0.)
33 {
34 	registerViewContainerListener (this);
35 }
36 
37 //------------------------------------------------------------------------
38 CShadowViewContainer::~CShadowViewContainer () noexcept = default;
39 
40 //------------------------------------------------------------------------
beforeDelete()41 void CShadowViewContainer::beforeDelete ()
42 {
43 	unregisterViewContainerListener (this);
44 	CViewContainer::beforeDelete ();
45 }
46 
47 //-----------------------------------------------------------------------------
removed(CView * parent)48 bool CShadowViewContainer::removed (CView* parent)
49 {
50 	getFrame ()->unregisterScaleFactorChangedListeneer (this);
51 	setBackground (nullptr);
52 	return CViewContainer::removed (parent);
53 }
54 
55 //-----------------------------------------------------------------------------
attached(CView * parent)56 bool CShadowViewContainer::attached (CView* parent)
57 {
58 	if (CViewContainer::attached (parent))
59 	{
60 		invalidateShadow ();
61 		getFrame ()->registerScaleFactorChangedListeneer (this);
62 		return true;
63 	}
64 	return false;
65 }
66 
67 //-----------------------------------------------------------------------------
onScaleFactorChanged(CFrame * frame,double newScaleFactor)68 void CShadowViewContainer::onScaleFactorChanged (CFrame* frame, double newScaleFactor)
69 {
70 	invalidateShadow ();
71 }
72 
73 //-----------------------------------------------------------------------------
setShadowOffset(const CPoint & offset)74 void CShadowViewContainer::setShadowOffset (const CPoint& offset)
75 {
76 	if (shadowOffset != offset)
77 	{
78 		shadowOffset = offset;
79 		invalidateShadow ();
80 	}
81 }
82 
83 //-----------------------------------------------------------------------------
setShadowIntensity(float intensity)84 void CShadowViewContainer::setShadowIntensity (float intensity)
85 {
86 	if (shadowIntensity != intensity)
87 	{
88 		shadowIntensity = intensity;
89 		invalid ();
90 	}
91 }
92 
93 //-----------------------------------------------------------------------------
setShadowBlurSize(double size)94 void CShadowViewContainer::setShadowBlurSize (double size)
95 {
96 	if (shadowBlurSize != size)
97 	{
98 		shadowBlurSize = size;
99 		invalidateShadow ();
100 	}
101 }
102 
103 //-----------------------------------------------------------------------------
invalidateShadow()104 void CShadowViewContainer::invalidateShadow ()
105 {
106 	scaleFactorUsed = 0.;
107 	invalid ();
108 }
109 
110 //-----------------------------------------------------------------------------
notify(CBaseObject * sender,IdStringPtr message)111 CMessageResult CShadowViewContainer::notify (CBaseObject* sender, IdStringPtr message)
112 {
113 	if (message == kMsgViewSizeChanged)
114 		invalidateShadow ();
115 	return CViewContainer::notify(sender, message);
116 }
117 
118 //-----------------------------------------------------------------------------
119 template <size_t numBoxes>
boxesForGauss(double sigma)120 static std::array<int32_t, numBoxes> boxesForGauss (double sigma)
121 {
122 	std::array<int32_t, numBoxes> boxes;
123 	double ideal = std::sqrt ((12 * sigma * sigma / numBoxes) + 1);
124 	uint16_t l = static_cast<uint16_t> (std::floor (ideal));
125 	if (l % 2 == 0)
126 		l--;
127 	int32_t u = l + 2;
128 	ideal = ((12. * sigma * sigma) - (numBoxes * l * l) - (4. * numBoxes * l) - (3. * numBoxes)) / ((-4. * l) - 4.);
129 	int32_t m = static_cast<int32_t> (std::floor (ideal));
130 	for (int32_t i = 0; i < numBoxes; ++i)
131 		boxes[i] = (i < m ? l : u);
132 	return boxes;
133 }
134 
135 //-----------------------------------------------------------------------------
isUniformScaled(const CGraphicsTransform & matrix)136 static bool isUniformScaled (const CGraphicsTransform& matrix)
137 {
138 	return matrix.m11 == matrix.m22;
139 }
140 
141 //-----------------------------------------------------------------------------
drawRect(CDrawContext * pContext,const CRect & updateRect)142 void CShadowViewContainer::drawRect (CDrawContext* pContext, const CRect& updateRect)
143 {
144 	double scaleFactor = pContext->getScaleFactor ();
145 	CGraphicsTransform matrix = pContext->getCurrentTransform ();
146 	if (isUniformScaled (matrix))
147 	{
148 		double matrixScale = std::floor (matrix.m11 + 0.5);
149 		if (matrixScale != 0.)
150 			scaleFactor *= matrixScale;
151 	}
152 	if (scaleFactor != scaleFactorUsed && getWidth () > 0. && getHeight () > 0.)
153 	{
154 		scaleFactorUsed = scaleFactor;
155 		CCoord width = getWidth ();
156 		CCoord height = getHeight ();
157 
158 		if (auto offscreenContext = COffscreenContext::create (getFrame (), width, height, scaleFactor))
159 		{
160 			offscreenContext->beginDraw ();
161 			CDrawContext::Transform transform (*offscreenContext, CGraphicsTransform ().translate (-getViewSize ().left - shadowOffset.x, -getViewSize ().top - shadowOffset.y));
162 			dontDrawBackground = true;
163 			CViewContainer::draw (offscreenContext);
164 			dontDrawBackground = false;
165 			offscreenContext->endDraw ();
166 			CBitmap* bitmap = offscreenContext->getBitmap ();
167 			if (bitmap)
168 			{
169 				setBackground (bitmap);
170 				SharedPointer<BitmapFilter::IFilter> setColorFilter = owned (BitmapFilter::Factory::getInstance ().createFilter (BitmapFilter::Standard::kSetColor));
171 				if (setColorFilter)
172 				{
173 					setColorFilter->setProperty (BitmapFilter::Standard::Property::kInputBitmap, bitmap);
174 					setColorFilter->setProperty (BitmapFilter::Standard::Property::kInputColor, kBlackCColor);
175 					setColorFilter->setProperty (BitmapFilter::Standard::Property::kIgnoreAlphaColorValue, (int32_t)1);
176 					if (setColorFilter->run (true))
177 					{
178 						SharedPointer<BitmapFilter::IFilter> boxBlurFilter = owned (BitmapFilter::Factory::getInstance ().createFilter (BitmapFilter::Standard::kBoxBlur));
179 						if (boxBlurFilter)
180 						{
181 							auto boxSizes = boxesForGauss<3> (shadowBlurSize);
182 							boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kInputBitmap, bitmap);
183 							boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kRadius, boxSizes[0]);
184 							boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kAlphaChannelOnly, 1);
185 							if (boxBlurFilter->run (true))
186 							{
187 								boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kRadius, boxSizes[1]);
188 								boxBlurFilter->run (true);
189 								boxBlurFilter->setProperty (BitmapFilter::Standard::Property::kRadius, boxSizes[2]);
190 								boxBlurFilter->run (true);
191 							}
192 						}
193 					}
194 				}
195 
196 				CViewContainer::drawRect (pContext, updateRect);
197 			}
198 		}
199 	}
200 	else
201 	{
202 		CViewContainer::drawRect (pContext, updateRect);
203 	}
204 }
205 
206 //-----------------------------------------------------------------------------
drawBackgroundRect(CDrawContext * pContext,const CRect & _updateRect)207 void CShadowViewContainer::drawBackgroundRect (CDrawContext* pContext, const CRect& _updateRect)
208 {
209 	if (!dontDrawBackground)
210 	{
211 		float tmp = pContext->getGlobalAlpha ();
212 		pContext->setGlobalAlpha (tmp * shadowIntensity);
213 		CViewContainer::drawBackgroundRect (pContext, _updateRect);
214 		pContext->setGlobalAlpha (tmp);
215 	}
216 }
217 
218 //-----------------------------------------------------------------------------
setViewSize(const CRect & rect,bool invalid)219 void CShadowViewContainer::setViewSize (const CRect& rect, bool invalid)
220 {
221 	if (getViewSize () != rect)
222 	{
223 		bool diffSize = (getWidth () != rect.getWidth () || getHeight () != rect.getHeight ());
224 		CViewContainer::setViewSize (rect, invalid);
225 		if (diffSize)
226 			invalidateShadow ();
227 	}
228 }
229 
230 //-----------------------------------------------------------------------------
viewContainerViewAdded(CViewContainer * container,CView * view)231 void CShadowViewContainer::viewContainerViewAdded (CViewContainer* container, CView* view)
232 {
233 	vstgui_assert (container == this);
234 	invalidateShadow ();
235 }
236 
237 //-----------------------------------------------------------------------------
viewContainerViewRemoved(CViewContainer * container,CView * view)238 void CShadowViewContainer::viewContainerViewRemoved (CViewContainer* container, CView* view)
239 {
240 	vstgui_assert (container == this);
241 	invalidateShadow ();
242 }
243 
244 //-----------------------------------------------------------------------------
viewContainerViewZOrderChanged(CViewContainer * container,CView * view)245 void CShadowViewContainer::viewContainerViewZOrderChanged (CViewContainer* container, CView* view)
246 {
247 	vstgui_assert (container == this);
248 	invalidateShadow ();
249 }
250 
251 } // namespace
252