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