29d36b1e9bb21d88998aef37e9f07315c8cc8d35
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / gaussian-blur-view / gaussian-blur-view-impl.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "gaussian-blur-view-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/actors/actor-devel.h>
23 #include <dali/devel-api/common/stage.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/animation/constraint.h>
26 #include <dali/public-api/animation/constraints.h>
27 #include <dali/public-api/object/type-registry-helper.h>
28 #include <dali/public-api/object/type-registry.h>
29 #include <dali/public-api/render-tasks/render-task-list.h>
30 #include <dali/public-api/rendering/geometry.h>
31 #include <dali/public-api/rendering/renderer.h>
32 #include <dali/public-api/rendering/shader.h>
33 #include <iomanip>
34 #include <sstream>
35
36 // INTERNAL INCLUDES
37 #include <dali-toolkit/devel-api/controls/control-devel.h>
38 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
39 #include <dali-toolkit/internal/controls/control/control-renderers.h>
40 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
41 #include <dali-toolkit/public-api/visuals/visual-properties.h>
42
43 // TODO:
44 // pixel format / size - set from JSON
45 // aspect ratio property needs to be able to be constrained also for cameras, not possible currently. Therefore changing aspect ratio of GaussianBlurView won't currently work
46 // default near clip value
47 // Manager object - re-use render targets if there are multiple GaussianBlurViews created
48
49 /////////////////////////////////////////////////////////
50 // IMPLEMENTATION NOTES
51
52 // As the GaussianBlurView actor changes size, the amount of pixels we need to blur changes. Therefore we need some way of doing this. However:-
53 // OnSetSize() does not get called when GaussianBlurView object size is modified using a Constraint.
54 // OnSizeAnimation() only gets called once per AnimateTo/By() and if an Animation has N such calls then only the final one will end up being used. Therefore we can't use
55 // OnSizeAnimation() to alter render target sizes.
56 // To get around the above problems, we use fixed sized render targets, from the last SetSize() call (which calls OnSetSize()), then we adjust the internal cameras / actors
57 // to take account of the changed GaussianBlurView object size, projecting to the unchanged render target sizes. This is done relative to the fixed render target / actor sizes
58 // by using constraints relative to the GaussianBlurView actor size.
59
60 // 2 modes:
61 // 1st mode, this control has a tree of actors (use Add() to add children) that are rendered and blurred.
62 // mRenderChildrenTask renders children to FB mRenderTargetForRenderingChildren
63 // mHorizBlurTask renders mHorizBlurActor Actor showing FB mRenderTargetForRenderingChildren into FB mRenderTarget2
64 // mVertBlurTask renders mVertBlurActor Actor showing FB mRenderTarget2 into FB mRenderTarget1
65 // mCompositeTask renders mCompositingActor Actor showing FB mRenderTarget1 into FB mRenderTargetForRenderingChildren
66 //
67 // 2nd mode, an image is blurred and rendered to a supplied target framebuffer
68 // mHorizBlurTask renders mHorizBlurActor Actor showing mUserInputImage into FB mRenderTarget2
69 // mVertBlurTask renders mVertBlurActor Actor showing mRenderTarget2 into FB mUserOutputRenderTarget
70 //
71 // Only this 2nd mode handles ActivateOnce
72
73 namespace Dali
74 {
75 namespace Toolkit
76 {
77 namespace Internal
78 {
79 namespace
80 {
81 using namespace Dali;
82
83 BaseHandle Create()
84 {
85   return Toolkit::GaussianBlurView::New();
86 }
87
88 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::GaussianBlurView, Toolkit::Control, Create)
89 DALI_TYPE_REGISTRATION_END()
90
91 const unsigned int  GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES                = 5;
92 const float         GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH      = 1.5f;
93 const Pixel::Format GAUSSIAN_BLUR_VIEW_DEFAULT_RENDER_TARGET_PIXEL_FORMAT = Pixel::RGBA8888;
94 const float         GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH              = 1.0f; // default, fully blurred
95 const char* const   GAUSSIAN_BLUR_VIEW_STRENGTH_PROPERTY_NAME             = "GaussianBlurStrengthPropertyName";
96 const float         GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE     = 0.5f;
97 const float         GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE    = 0.5f;
98
99 const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f;
100
101 } // namespace
102
103 GaussianBlurView::GaussianBlurView()
104 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
105   mNumSamples(GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES),
106   mBlurBellCurveWidth(0.001f),
107   mPixelFormat(GAUSSIAN_BLUR_VIEW_DEFAULT_RENDER_TARGET_PIXEL_FORMAT),
108   mDownsampleWidthScale(GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE),
109   mDownsampleHeightScale(GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE),
110   mDownsampledWidth(0.0f),
111   mDownsampledHeight(0.0f),
112   mBlurUserImage(false),
113   mRenderOnce(false),
114   mBackgroundColor(Color::BLACK),
115   mTargetSize(Vector2::ZERO),
116   mLastSize(Vector2::ZERO),
117   mChildrenRoot(Actor::New()),
118   mInternalRoot(Actor::New()),
119   mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
120   mActivated(false)
121 {
122   SetBlurBellCurveWidth(GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH);
123 }
124
125 GaussianBlurView::GaussianBlurView(const unsigned int  numSamples,
126                                    const float         blurBellCurveWidth,
127                                    const Pixel::Format renderTargetPixelFormat,
128                                    const float         downsampleWidthScale,
129                                    const float         downsampleHeightScale,
130                                    bool                blurUserImage)
131 : Control(ControlBehaviour(DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS)),
132   mNumSamples(numSamples),
133   mBlurBellCurveWidth(0.001f),
134   mPixelFormat(renderTargetPixelFormat),
135   mDownsampleWidthScale(downsampleWidthScale),
136   mDownsampleHeightScale(downsampleHeightScale),
137   mDownsampledWidth(0.0f),
138   mDownsampledHeight(0.0f),
139   mBlurUserImage(blurUserImage),
140   mRenderOnce(false),
141   mBackgroundColor(Color::BLACK),
142   mTargetSize(Vector2::ZERO),
143   mLastSize(Vector2::ZERO),
144   mChildrenRoot(Actor::New()),
145   mInternalRoot(Actor::New()),
146   mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
147   mActivated(false)
148 {
149   SetBlurBellCurveWidth(blurBellCurveWidth);
150 }
151
152 GaussianBlurView::~GaussianBlurView()
153 {
154 }
155
156 Toolkit::GaussianBlurView GaussianBlurView::New()
157 {
158   GaussianBlurView* impl = new GaussianBlurView();
159
160   Dali::Toolkit::GaussianBlurView handle = Dali::Toolkit::GaussianBlurView(*impl);
161
162   // Second-phase init of the implementation
163   // This can only be done after the CustomActor connection has been made...
164   impl->Initialize();
165
166   return handle;
167 }
168
169 Toolkit::GaussianBlurView GaussianBlurView::New(const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat, const float downsampleWidthScale, const float downsampleHeightScale, bool blurUserImage)
170 {
171   GaussianBlurView* impl = new GaussianBlurView(numSamples, blurBellCurveWidth, renderTargetPixelFormat, downsampleWidthScale, downsampleHeightScale, blurUserImage);
172
173   Dali::Toolkit::GaussianBlurView handle = Dali::Toolkit::GaussianBlurView(*impl);
174
175   // Second-phase init of the implementation
176   // This can only be done after the CustomActor connection has been made...
177   impl->Initialize();
178
179   return handle;
180 }
181
182 void GaussianBlurView::SetUserImageAndOutputRenderTarget(Texture inputImage, FrameBuffer outputRenderTarget)
183 {
184   // can only do this if the GaussianBlurView object was created with this parameter set
185   DALI_ASSERT_ALWAYS(mBlurUserImage);
186
187   mUserInputImage = inputImage;
188
189   SetRendererTexture(mHorizBlurActor.GetRendererAt(0), inputImage);
190
191   mUserOutputRenderTarget = outputRenderTarget;
192 }
193
194 FrameBuffer GaussianBlurView::GetBlurredRenderTarget() const
195 {
196   if(!mUserOutputRenderTarget)
197   {
198     return mRenderTargetForRenderingChildren;
199   }
200
201   return mUserOutputRenderTarget;
202 }
203
204 void GaussianBlurView::SetBackgroundColor(const Vector4& color)
205 {
206   mBackgroundColor = color;
207 }
208
209 Vector4 GaussianBlurView::GetBackgroundColor() const
210 {
211   return mBackgroundColor;
212 }
213
214 ///////////////////////////////////////////////////////////
215 //
216 // Private methods
217 //
218
219 void GaussianBlurView::OnInitialize()
220 {
221   // root actor to parent all user added actors, needed to allow us to set that subtree as exclusive for our child render task
222   mChildrenRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
223   mInternalRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
224
225   //////////////////////////////////////////////////////
226   // Create shaders
227
228   std::ostringstream fragmentStringStream;
229   fragmentStringStream << "#define NUM_SAMPLES " << mNumSamples << "\n";
230   fragmentStringStream << SHADER_GAUSSIAN_BLUR_VIEW_FRAG;
231   std::string fragmentSource(fragmentStringStream.str());
232
233   //////////////////////////////////////////////////////
234   // Create actors
235
236   // Create an actor for performing a horizontal blur on the texture
237   mHorizBlurActor = Actor::New();
238   mHorizBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
239
240   Renderer renderer = CreateRenderer(BASIC_VERTEX_SOURCE, fragmentSource.c_str());
241   mHorizBlurActor.AddRenderer(renderer);
242
243   // Create an actor for performing a vertical blur on the texture
244   mVertBlurActor = Actor::New();
245   mVertBlurActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
246   renderer = CreateRenderer(BASIC_VERTEX_SOURCE, fragmentSource.c_str());
247   mVertBlurActor.AddRenderer(renderer);
248
249   // Register a property that the user can control to fade the blur in / out via the GaussianBlurView object
250   Actor self                 = Self();
251   mBlurStrengthPropertyIndex = self.RegisterProperty(GAUSSIAN_BLUR_VIEW_STRENGTH_PROPERTY_NAME, GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH);
252
253   // Create an image view for compositing the blur and the original child actors render
254   if(!mBlurUserImage)
255   {
256     mCompositingActor = Actor::New();
257     mCompositingActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
258     mCompositingActor.SetProperty(Actor::Property::OPACITY, GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH); // ensure alpha is enabled for this object and set default value
259     renderer = CreateRenderer(BASIC_VERTEX_SOURCE, BASIC_FRAGMENT_SOURCE);
260     mCompositingActor.AddRenderer(renderer);
261
262     Constraint blurStrengthConstraint = Constraint::New<float>(mCompositingActor, Actor::Property::COLOR_ALPHA, EqualToConstraint());
263     blurStrengthConstraint.AddSource(Source(self, mBlurStrengthPropertyIndex));
264     blurStrengthConstraint.Apply();
265
266     // Create an image view for holding final result, i.e. the blurred image. This will get rendered to screen later, via default / user render task
267     mTargetActor = Actor::New();
268     mTargetActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
269     renderer = CreateRenderer(BASIC_VERTEX_SOURCE, BASIC_FRAGMENT_SOURCE);
270     mTargetActor.AddRenderer(renderer);
271
272     //////////////////////////////////////////////////////
273     // Create cameras for the renders corresponding to the view size
274     mRenderFullSizeCamera = CameraActor::New();
275     mRenderFullSizeCamera.SetInvertYAxis(true);
276     mRenderFullSizeCamera.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
277
278     //////////////////////////////////////////////////////
279     // Connect to actor tree
280     mInternalRoot.Add(mCompositingActor);
281     mInternalRoot.Add(mTargetActor);
282     mInternalRoot.Add(mRenderFullSizeCamera);
283   }
284
285   //////////////////////////////////////////////////////
286   // Create camera for the renders corresponding to the (potentially downsampled) render targets' size
287   mRenderDownsampledCamera = CameraActor::New();
288   mRenderDownsampledCamera.SetInvertYAxis(true);
289   mRenderDownsampledCamera.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
290
291   //////////////////////////////////////////////////////
292   // Connect to actor tree
293   Self().Add(mChildrenRoot);
294   mInternalRoot.Add(mHorizBlurActor);
295   mInternalRoot.Add(mVertBlurActor);
296   mInternalRoot.Add(mRenderDownsampledCamera);
297
298   DevelControl::SetAccessibilityConstructor(Self(), [](Dali::Actor actor) {
299     return std::unique_ptr<Dali::Accessibility::Accessible>(
300       new DevelControl::AccessibleImpl(actor, Dali::Accessibility::Role::FILLER));
301   });
302 }
303
304 void GaussianBlurView::OnSizeSet(const Vector3& targetSize)
305 {
306   mTargetSize = Vector2(targetSize);
307
308   mChildrenRoot.SetProperty(Actor::Property::SIZE, targetSize);
309
310   if(!mBlurUserImage)
311   {
312     mCompositingActor.SetProperty(Actor::Property::SIZE, targetSize);
313     mTargetActor.SetProperty(Actor::Property::SIZE, targetSize);
314
315     // Children render camera must move when GaussianBlurView object is resized. This is since we cannot change render target size - so we need to remap the child actors' rendering
316     // accordingly so they still exactly fill the render target. Note that this means the effective resolution of the child render changes as the GaussianBlurView object changes
317     // size, this is the trade off for not being able to modify render target size
318     // Change camera z position based on GaussianBlurView actor height
319     float cameraPosConstraintScale = 0.5f / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f);
320     mRenderFullSizeCamera.SetProperty(Actor::Property::POSITION_Z, mTargetSize.height * cameraPosConstraintScale);
321   }
322
323   // if we have already activated the blur, need to update render target sizes now to reflect the new size of this actor
324   if(mActivated)
325   {
326     Deactivate();
327     Activate();
328   }
329
330   Control::OnSizeSet(targetSize);
331 }
332
333 void GaussianBlurView::OnChildAdd(Actor& child)
334 {
335   if(child != mChildrenRoot && child != mInternalRoot)
336   {
337     mChildrenRoot.Add(child);
338   }
339
340   Control::OnChildAdd(child);
341 }
342
343 void GaussianBlurView::OnChildRemove(Actor& child)
344 {
345   mChildrenRoot.Remove(child);
346
347   Control::OnChildRemove(child);
348 }
349
350 void GaussianBlurView::AllocateResources()
351 {
352   mLastSize = mTargetSize;
353
354   // get size of downsampled render targets
355   mDownsampledWidth  = mTargetSize.width * mDownsampleWidthScale;
356   mDownsampledHeight = mTargetSize.height * mDownsampleHeightScale;
357
358   // Create and place a camera for the renders corresponding to the (potentially downsampled) render targets' size
359   mRenderDownsampledCamera.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW);
360   // TODO: how do we pick a reasonable value for near clip? Needs to relate to normal camera the user renders with, but we don't have a handle on it
361   mRenderDownsampledCamera.SetNearClippingPlane(1.0f);
362   mRenderDownsampledCamera.SetAspectRatio(mDownsampledWidth / mDownsampledHeight);
363   mRenderDownsampledCamera.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor
364
365   mRenderDownsampledCamera.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, ((mDownsampledHeight * 0.5f) / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f))));
366
367   // setup for normal operation
368   if(!mBlurUserImage)
369   {
370     // Create and place a camera for the children render, corresponding to its render target size
371     mRenderFullSizeCamera.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW);
372     // TODO: how do we pick a reasonable value for near clip? Needs to relate to normal camera the user renders with, but we don't have a handle on it
373     mRenderFullSizeCamera.SetNearClippingPlane(1.0f);
374     mRenderFullSizeCamera.SetAspectRatio(mTargetSize.width / mTargetSize.height);
375     mRenderFullSizeCamera.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor
376
377     float cameraPosConstraintScale = 0.5f / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f);
378     mRenderFullSizeCamera.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, mTargetSize.height * cameraPosConstraintScale));
379
380     // create offscreen buffer of new size to render our child actors to
381     mRenderTargetForRenderingChildren = FrameBuffer::New(mTargetSize.width, mTargetSize.height, FrameBuffer::Attachment::NONE);
382     Texture texture                   = Texture::New(TextureType::TEXTURE_2D, mPixelFormat, unsigned(mTargetSize.width), unsigned(mTargetSize.height));
383     mRenderTargetForRenderingChildren.AttachColorTexture(texture);
384
385     // Set actor for performing a horizontal blur
386     SetRendererTexture(mHorizBlurActor.GetRendererAt(0), mRenderTargetForRenderingChildren);
387
388     // Create offscreen buffer for vert blur pass
389     mRenderTarget1 = FrameBuffer::New(mDownsampledWidth, mDownsampledHeight, FrameBuffer::Attachment::NONE);
390     texture        = Texture::New(TextureType::TEXTURE_2D, mPixelFormat, unsigned(mDownsampledWidth), unsigned(mDownsampledHeight));
391     mRenderTarget1.AttachColorTexture(texture);
392
393     // use the completed blur in the first buffer and composite with the original child actors render
394     SetRendererTexture(mCompositingActor.GetRendererAt(0), mRenderTarget1);
395
396     // set up target actor for rendering result, i.e. the blurred image
397     SetRendererTexture(mTargetActor.GetRendererAt(0), mRenderTargetForRenderingChildren);
398   }
399
400   // Create offscreen buffer for horiz blur pass
401   mRenderTarget2  = FrameBuffer::New(mDownsampledWidth, mDownsampledHeight, FrameBuffer::Attachment::NONE);
402   Texture texture = Texture::New(TextureType::TEXTURE_2D, mPixelFormat, unsigned(mDownsampledWidth), unsigned(mDownsampledHeight));
403   mRenderTarget2.AttachColorTexture(texture);
404
405   // size needs to match render target
406   mHorizBlurActor.SetProperty(Actor::Property::SIZE, Vector2(mDownsampledWidth, mDownsampledHeight));
407
408   // size needs to match render target
409   mVertBlurActor.SetProperty(Actor::Property::SIZE, Vector2(mDownsampledWidth, mDownsampledHeight));
410   SetRendererTexture(mVertBlurActor.GetRendererAt(0), mRenderTarget2);
411
412   // set gaussian blur up for new sized render targets
413   SetShaderConstants();
414 }
415
416 void GaussianBlurView::CreateRenderTasks()
417 {
418   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
419
420   if(!mBlurUserImage)
421   {
422     // create render task to render our child actors to offscreen buffer
423     mRenderChildrenTask = taskList.CreateTask();
424     mRenderChildrenTask.SetSourceActor(mChildrenRoot);
425     mRenderChildrenTask.SetExclusive(true);
426     mRenderChildrenTask.SetInputEnabled(false);
427     mRenderChildrenTask.SetClearEnabled(true);
428     mRenderChildrenTask.SetClearColor(mBackgroundColor);
429
430     mRenderChildrenTask.SetCameraActor(mRenderFullSizeCamera);
431     mRenderChildrenTask.SetFrameBuffer(mRenderTargetForRenderingChildren);
432
433     if(mRenderOnce)
434     {
435       mRenderChildrenTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
436     }
437   }
438
439   // perform a horizontal blur targeting the second buffer
440   mHorizBlurTask = taskList.CreateTask();
441   mHorizBlurTask.SetSourceActor(mHorizBlurActor);
442   mHorizBlurTask.SetExclusive(true);
443   mHorizBlurTask.SetInputEnabled(false);
444   mHorizBlurTask.SetClearEnabled(true);
445   mHorizBlurTask.SetClearColor(mBackgroundColor);
446   mHorizBlurTask.SetCameraActor(mRenderDownsampledCamera);
447   mHorizBlurTask.SetFrameBuffer(mRenderTarget2);
448   if(mRenderOnce || (mRenderOnce && mBlurUserImage))
449   {
450     mHorizBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
451   }
452
453   // use the second buffer and perform a horizontal blur targeting the first buffer
454   mVertBlurTask = taskList.CreateTask();
455   mVertBlurTask.SetSourceActor(mVertBlurActor);
456   mVertBlurTask.SetExclusive(true);
457   mVertBlurTask.SetInputEnabled(false);
458   mVertBlurTask.SetClearEnabled(true);
459   mVertBlurTask.SetClearColor(mBackgroundColor);
460   mVertBlurTask.SetCameraActor(mRenderDownsampledCamera);
461   if(mUserOutputRenderTarget)
462   {
463     mVertBlurTask.SetFrameBuffer(mUserOutputRenderTarget);
464   }
465   else
466   {
467     mVertBlurTask.SetFrameBuffer(mRenderTarget1);
468   }
469   if(mRenderOnce || (mRenderOnce && mBlurUserImage))
470   {
471     mVertBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
472     mVertBlurTask.FinishedSignal().Connect(this, &GaussianBlurView::OnRenderTaskFinished);
473   }
474
475   // use the completed blur in the first buffer and composite with the original child actors render
476   if(!mBlurUserImage)
477   {
478     mCompositeTask = taskList.CreateTask();
479     mCompositeTask.SetSourceActor(mCompositingActor);
480     mCompositeTask.SetExclusive(true);
481     mCompositeTask.SetInputEnabled(false);
482
483     mCompositeTask.SetCameraActor(mRenderFullSizeCamera);
484     mCompositeTask.SetFrameBuffer(mRenderTargetForRenderingChildren);
485
486     if(mRenderOnce)
487     {
488       mCompositeTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
489     }
490   }
491 }
492
493 void GaussianBlurView::RemoveRenderTasks()
494 {
495   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
496
497   taskList.RemoveTask(mRenderChildrenTask);
498   taskList.RemoveTask(mHorizBlurTask);
499   taskList.RemoveTask(mVertBlurTask);
500   taskList.RemoveTask(mCompositeTask);
501 }
502
503 void GaussianBlurView::Activate()
504 {
505   if(!mActivated)
506   {
507     // make sure resources are allocated and start the render tasks processing
508     Self().Add(mInternalRoot);
509     AllocateResources();
510     CreateRenderTasks();
511     mActivated = true;
512   }
513 }
514
515 void GaussianBlurView::ActivateOnce()
516 {
517   Deactivate();
518   mRenderOnce = true;
519   Activate();
520 }
521
522 void GaussianBlurView::Deactivate()
523 {
524   if(mActivated)
525   {
526     // stop render tasks processing
527     // Note: render target resources are automatically freed since we set the Image::Unused flag
528     mInternalRoot.Unparent();
529     mRenderTargetForRenderingChildren.Reset();
530     mRenderTarget1.Reset();
531     mRenderTarget2.Reset();
532     RemoveRenderTasks();
533     mRenderOnce = false;
534     mActivated  = false;
535   }
536 }
537
538 void GaussianBlurView::SetBlurBellCurveWidth(float blurBellCurveWidth)
539 {
540   // a value of zero leads to undefined Gaussian weights, do not allow user to do this
541   mBlurBellCurveWidth = std::max(blurBellCurveWidth, 0.001f);
542 }
543
544 float GaussianBlurView::CalcGaussianWeight(float x)
545 {
546   return (1.0f / sqrt(2.0f * Math::PI * mBlurBellCurveWidth)) * exp(-(x * x) / (2.0f * mBlurBellCurveWidth * mBlurBellCurveWidth));
547 }
548
549 void GaussianBlurView::SetShaderConstants()
550 {
551   Vector2*     uvOffsets;
552   float        ofs;
553   float*       weights;
554   float        w, totalWeights;
555   unsigned int i;
556
557   uvOffsets = new Vector2[mNumSamples + 1];
558   weights   = new float[mNumSamples + 1];
559
560   totalWeights = weights[0] = CalcGaussianWeight(0);
561   uvOffsets[0].x            = 0.0f;
562   uvOffsets[0].y            = 0.0f;
563
564   for(i = 0; i<mNumSamples >> 1; i++)
565   {
566     w                     = CalcGaussianWeight((float)(i + 1));
567     weights[(i << 1) + 1] = w;
568     weights[(i << 1) + 2] = w;
569     totalWeights += w * 2.0f;
570
571     // offset texture lookup to between texels, that way the bilinear filter in the texture hardware will average two samples with one lookup
572     ofs = ((float)(i << 1)) + 1.5f;
573
574     // get offsets from units of pixels into uv coordinates in [0..1]
575     float ofsX                = ofs / mDownsampledWidth;
576     float ofsY                = ofs / mDownsampledHeight;
577     uvOffsets[(i << 1) + 1].x = ofsX;
578     uvOffsets[(i << 1) + 1].y = ofsY;
579
580     uvOffsets[(i << 1) + 2].x = -ofsX;
581     uvOffsets[(i << 1) + 2].y = -ofsY;
582   }
583
584   for(i = 0; i < mNumSamples; i++)
585   {
586     weights[i] /= totalWeights;
587   }
588
589   // set shader constants
590   Vector2 xAxis(1.0f, 0.0f);
591   Vector2 yAxis(0.0f, 1.0f);
592   for(i = 0; i < mNumSamples; ++i)
593   {
594     mHorizBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), uvOffsets[i] * xAxis);
595     mHorizBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
596
597     mVertBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), uvOffsets[i] * yAxis);
598     mVertBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
599   }
600
601   delete[] uvOffsets;
602   delete[] weights;
603 }
604
605 std::string GaussianBlurView::GetSampleOffsetsPropertyName(unsigned int index) const
606 {
607   DALI_ASSERT_ALWAYS(index < mNumSamples);
608
609   std::ostringstream oss;
610   oss << "uSampleOffsets[" << index << "]";
611   return oss.str();
612 }
613
614 std::string GaussianBlurView::GetSampleWeightsPropertyName(unsigned int index) const
615 {
616   DALI_ASSERT_ALWAYS(index < mNumSamples);
617
618   std::ostringstream oss;
619   oss << "uSampleWeights[" << index << "]";
620   return oss.str();
621 }
622
623 Dali::Toolkit::GaussianBlurView::GaussianBlurViewSignal& GaussianBlurView::FinishedSignal()
624 {
625   return mFinishedSignal;
626 }
627
628 void GaussianBlurView::OnRenderTaskFinished(Dali::RenderTask& renderTask)
629 {
630   Toolkit::GaussianBlurView handle(GetOwner());
631   mFinishedSignal.Emit(handle);
632 }
633
634 } // namespace Internal
635 } // namespace Toolkit
636 } // namespace Dali