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