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