2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "gaussian-blur-view-impl.h"
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>
37 #include <dali-toolkit/public-api/visuals/visual-properties.h>
40 // pixel format / size - set from JSON
41 // 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
42 // default near clip value
43 // Manager object - re-use render targets if there are multiple GaussianBlurViews created
46 /////////////////////////////////////////////////////////
47 // IMPLEMENTATION NOTES
49 // As the GaussianBlurView actor changes size, the amount of pixels we need to blur changes. Therefore we need some way of doing this. However:-
50 // OnSetSize() does not get called when GaussianBlurView object size is modified using a Constraint.
51 // 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
52 // OnSizeAnimation() to alter render target sizes.
53 // 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
54 // 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
55 // by using constraints relative to the GaussianBlurView actor size.
59 // 1st mode, this control has a tree of actors (use Add() to add children) that are rendered and blurred.
60 // mRenderChildrenTask renders children to FB mRenderTargetForRenderingChildren
61 // mHorizBlurTask renders mHorizBlurActor Actor showing FB mRenderTargetForRenderingChildren into FB mRenderTarget2
62 // mVertBlurTask renders mVertBlurActor Actor showing FB mRenderTarget2 into FB mRenderTarget1
63 // mCompositeTask renders mCompositingActor Actor showing FB mRenderTarget1 into FB mRenderTargetForRenderingChildren
65 // 2nd mode, an image is blurred and rendered to a supplied target framebuffer
66 // mHorizBlurTask renders mHorizBlurActor Actor showing mUserInputImage into FB mRenderTarget2
67 // mVertBlurTask renders mVertBlurActor Actor showing mRenderTarget2 into FB mUserOutputRenderTarget
69 // Only this 2nd mode handles ActivateOnce
87 return Toolkit::GaussianBlurView::New();
90 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::GaussianBlurView, Toolkit::Control, Create )
91 DALI_TYPE_REGISTRATION_END()
93 const unsigned int GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES = 5;
94 const float GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH = 1.5f;
95 const Pixel::Format GAUSSIAN_BLUR_VIEW_DEFAULT_RENDER_TARGET_PIXEL_FORMAT = Pixel::RGBA8888;
96 const float GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH = 1.0f; // default, fully blurred
97 const char* const GAUSSIAN_BLUR_VIEW_STRENGTH_PROPERTY_NAME = "GaussianBlurStrengthPropertyName";
98 const float GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE = 0.5f;
99 const float GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE = 0.5f;
101 const float ARBITRARY_FIELD_OF_VIEW = Math::PI / 4.0f;
103 #define DALI_COMPOSE_SHADER(STR) #STR
105 const char * const BASIC_VERTEX_SOURCE = DALI_COMPOSE_SHADER(
106 precision mediump float;\n
107 attribute mediump vec2 aPosition;\n
108 attribute mediump vec2 aTexture;\n
109 varying mediump vec2 vTexCoord;\n
110 uniform mediump mat4 uMvpMatrix;\n
111 uniform mediump vec3 uSize;\n
115 mediump vec4 vertexPosition = vec4(aPosition * uSize.xy, 0.0, 1.0);\n
116 vTexCoord = aTexture;\n
117 gl_Position = uMvpMatrix * vertexPosition;\n
121 const char * const BASIC_FRAGMENT_SOURCE = DALI_COMPOSE_SHADER(
122 precision mediump float;\n
123 varying mediump vec2 vTexCoord;\n
124 uniform sampler2D sTexture;\n
125 uniform vec4 uColor;\n
129 gl_FragColor = texture2D(sTexture, vTexCoord);\n
130 gl_FragColor *= uColor;
134 const char* const GAUSSIAN_BLUR_FRAGMENT_SOURCE = DALI_COMPOSE_SHADER(
135 varying mediump vec2 vTexCoord;\n
136 uniform sampler2D sTexture;\n
137 uniform lowp vec4 uColor;\n
138 uniform mediump vec2 uSampleOffsets[NUM_SAMPLES];\n
139 uniform mediump float uSampleWeights[NUM_SAMPLES];\n
143 mediump vec4 col = texture2D(sTexture, vTexCoord + uSampleOffsets[0]) * uSampleWeights[0];\n
144 for (int i=1; i<NUM_SAMPLES; ++i)\n
146 col += texture2D(sTexture, vTexCoord + uSampleOffsets[i]) * uSampleWeights[i];\n
148 gl_FragColor = col;\n
152 Renderer CreateRenderer( const char* vertexSrc, const char* fragmentSrc )
154 Shader shader = Shader::New( vertexSrc, fragmentSrc );
156 Geometry texturedQuadGeometry = Geometry::New();
158 struct VertexPosition { Vector2 position; };
159 struct VertexTexture { Vector2 texture; };
161 VertexPosition positionArray[] =
163 { Vector2( -0.5f, -0.5f ) },
164 { Vector2( 0.5f, -0.5f ) },
165 { Vector2( -0.5f, 0.5f ) },
166 { Vector2( 0.5f, 0.5f ) }
168 uint32_t numberOfVertices = sizeof(positionArray)/sizeof(VertexPosition);
170 VertexTexture uvArray[] =
172 { Vector2( 0.0f, 0.0f ) },
173 { Vector2( 1.0f, 0.0f ) },
174 { Vector2( 0.0f, 1.0f ) },
175 { Vector2( 1.0f, 1.0f ) }
178 Property::Map positionVertexFormat;
179 positionVertexFormat["aPosition"] = Property::VECTOR2;
180 PropertyBuffer positionVertices = PropertyBuffer::New( positionVertexFormat );
181 positionVertices.SetData( positionArray, numberOfVertices );
182 texturedQuadGeometry.AddVertexBuffer( positionVertices );
184 Property::Map textureVertexFormat;
185 textureVertexFormat["aTexture"] = Property::VECTOR2;
186 PropertyBuffer textureVertices = PropertyBuffer::New( textureVertexFormat );
187 textureVertices.SetData( uvArray, numberOfVertices );
188 texturedQuadGeometry.AddVertexBuffer( textureVertices );
190 const uint16_t indices[] = { 0, 3, 1, 0, 2, 3 };
191 texturedQuadGeometry.SetIndexBuffer ( &indices[0], sizeof( indices )/ sizeof( indices[0] ) );
193 Renderer renderer = Renderer::New( texturedQuadGeometry, shader );
195 TextureSet textureSet = TextureSet::New();
196 renderer.SetTextures( textureSet );
201 void SetTexture( Actor actor, Texture texture )
203 if( Renderer renderer = actor.GetRendererAt(0) )
205 TextureSet textureSet = renderer.GetTextures();
206 textureSet.SetTexture( 0u, texture );
210 void SetTexture( Actor actor, FrameBuffer frameBuffer )
214 SetTexture( actor, frameBuffer.GetColorTexture() );
221 GaussianBlurView::GaussianBlurView()
222 : Control( ControlBehaviour( DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS ) ),
223 mNumSamples(GAUSSIAN_BLUR_VIEW_DEFAULT_NUM_SAMPLES),
224 mBlurBellCurveWidth( 0.001f ),
225 mPixelFormat(GAUSSIAN_BLUR_VIEW_DEFAULT_RENDER_TARGET_PIXEL_FORMAT),
226 mDownsampleWidthScale(GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_WIDTH_SCALE),
227 mDownsampleHeightScale(GAUSSIAN_BLUR_VIEW_DEFAULT_DOWNSAMPLE_HEIGHT_SCALE),
228 mDownsampledWidth( 0.0f ),
229 mDownsampledHeight( 0.0f ),
230 mBlurUserImage( false ),
231 mRenderOnce( false ),
232 mBackgroundColor( Color::BLACK ),
233 mTargetSize(Vector2::ZERO),
234 mLastSize(Vector2::ZERO),
235 mChildrenRoot(Actor::New()),
236 mInternalRoot(Actor::New()),
237 mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
240 SetBlurBellCurveWidth(GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_BELL_CURVE_WIDTH);
243 GaussianBlurView::GaussianBlurView( const unsigned int numSamples,
244 const float blurBellCurveWidth,
245 const Pixel::Format renderTargetPixelFormat,
246 const float downsampleWidthScale,
247 const float downsampleHeightScale,
249 : Control( ControlBehaviour( DISABLE_SIZE_NEGOTIATION | DISABLE_STYLE_CHANGE_SIGNALS ) ),
250 mNumSamples(numSamples),
251 mBlurBellCurveWidth( 0.001f ),
252 mPixelFormat(renderTargetPixelFormat),
253 mDownsampleWidthScale(downsampleWidthScale),
254 mDownsampleHeightScale(downsampleHeightScale),
255 mDownsampledWidth( 0.0f ),
256 mDownsampledHeight( 0.0f ),
257 mBlurUserImage( blurUserImage ),
258 mRenderOnce( false ),
259 mBackgroundColor( Color::BLACK ),
260 mTargetSize(Vector2::ZERO),
261 mLastSize(Vector2::ZERO),
262 mChildrenRoot(Actor::New()),
263 mInternalRoot(Actor::New()),
264 mBlurStrengthPropertyIndex(Property::INVALID_INDEX),
267 SetBlurBellCurveWidth(blurBellCurveWidth);
270 GaussianBlurView::~GaussianBlurView()
275 Toolkit::GaussianBlurView GaussianBlurView::New()
277 GaussianBlurView* impl = new GaussianBlurView();
279 Dali::Toolkit::GaussianBlurView handle = Dali::Toolkit::GaussianBlurView( *impl );
281 // Second-phase init of the implementation
282 // This can only be done after the CustomActor connection has been made...
288 Toolkit::GaussianBlurView GaussianBlurView::New(const unsigned int numSamples, const float blurBellCurveWidth, const Pixel::Format renderTargetPixelFormat,
289 const float downsampleWidthScale, const float downsampleHeightScale,
292 GaussianBlurView* impl = new GaussianBlurView( numSamples, blurBellCurveWidth, renderTargetPixelFormat,
293 downsampleWidthScale, downsampleHeightScale,
296 Dali::Toolkit::GaussianBlurView handle = Dali::Toolkit::GaussianBlurView( *impl );
298 // Second-phase init of the implementation
299 // This can only be done after the CustomActor connection has been made...
305 /////////////////////////////////////////////////////////////
306 // 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
307 // 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.
308 void GaussianBlurView::Add(Actor child)
310 mChildrenRoot.Add(child);
313 void GaussianBlurView::Remove(Actor child)
315 mChildrenRoot.Remove(child);
318 void GaussianBlurView::SetUserImageAndOutputRenderTarget(Texture inputImage, FrameBuffer outputRenderTarget)
320 // can only do this if the GaussianBlurView object was created with this parameter set
321 DALI_ASSERT_ALWAYS(mBlurUserImage);
323 mUserInputImage = inputImage;
325 SetTexture( mHorizBlurActor, inputImage );
327 mUserOutputRenderTarget = outputRenderTarget;
330 FrameBuffer GaussianBlurView::GetBlurredRenderTarget() const
332 if(!mUserOutputRenderTarget)
334 return mRenderTargetForRenderingChildren;
337 return mUserOutputRenderTarget;
340 void GaussianBlurView::SetBackgroundColor( const Vector4& color )
342 mBackgroundColor = color;
345 Vector4 GaussianBlurView::GetBackgroundColor() const
347 return mBackgroundColor;
350 ///////////////////////////////////////////////////////////
355 void GaussianBlurView::OnInitialize()
357 // root actor to parent all user added actors, needed to allow us to set that subtree as exclusive for our child render task
358 mChildrenRoot.SetParentOrigin(ParentOrigin::CENTER);
359 mInternalRoot.SetParentOrigin(ParentOrigin::CENTER);
361 //////////////////////////////////////////////////////
364 std::ostringstream fragmentStringStream;
365 fragmentStringStream << "#define NUM_SAMPLES " << mNumSamples << "\n";
366 fragmentStringStream << GAUSSIAN_BLUR_FRAGMENT_SOURCE;
367 std::string fragmentSource(fragmentStringStream.str());
369 //////////////////////////////////////////////////////
372 // Create an actor for performing a horizontal blur on the texture
373 mHorizBlurActor = Actor::New();
374 mHorizBlurActor.SetParentOrigin(ParentOrigin::CENTER);
375 Renderer renderer = CreateRenderer( BASIC_VERTEX_SOURCE, fragmentSource.c_str() );
376 mHorizBlurActor.AddRenderer( renderer );
378 // Create an actor for performing a vertical blur on the texture
379 mVertBlurActor = Actor::New();
380 mVertBlurActor.SetParentOrigin(ParentOrigin::CENTER);
381 renderer = CreateRenderer( BASIC_VERTEX_SOURCE, fragmentSource.c_str() );
382 mVertBlurActor.AddRenderer( renderer );
384 // Register a property that the user can control to fade the blur in / out via the GaussianBlurView object
386 mBlurStrengthPropertyIndex = self.RegisterProperty(GAUSSIAN_BLUR_VIEW_STRENGTH_PROPERTY_NAME, GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH);
388 // Create an image view for compositing the blur and the original child actors render
391 mCompositingActor = Actor::New();
392 mCompositingActor.SetParentOrigin(ParentOrigin::CENTER);
393 mCompositingActor.SetOpacity(GAUSSIAN_BLUR_VIEW_DEFAULT_BLUR_STRENGTH); // ensure alpha is enabled for this object and set default value
394 renderer = CreateRenderer( BASIC_VERTEX_SOURCE, BASIC_FRAGMENT_SOURCE );
395 mCompositingActor.AddRenderer( renderer );
397 Constraint blurStrengthConstraint = Constraint::New<float>( mCompositingActor, Actor::Property::COLOR_ALPHA, EqualToConstraint());
398 blurStrengthConstraint.AddSource( Source( self, mBlurStrengthPropertyIndex) );
399 blurStrengthConstraint.Apply();
401 // 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
402 mTargetActor = Actor::New();
403 mTargetActor.SetParentOrigin(ParentOrigin::CENTER);
404 renderer = CreateRenderer( BASIC_VERTEX_SOURCE, BASIC_FRAGMENT_SOURCE );
405 mTargetActor.AddRenderer( renderer );
407 //////////////////////////////////////////////////////
408 // Create cameras for the renders corresponding to the view size
409 mRenderFullSizeCamera = CameraActor::New();
410 mRenderFullSizeCamera.SetInvertYAxis( true );
411 mRenderFullSizeCamera.SetParentOrigin(ParentOrigin::CENTER);
413 //////////////////////////////////////////////////////
414 // Connect to actor tree
415 mInternalRoot.Add( mCompositingActor );
416 mInternalRoot.Add( mTargetActor );
417 mInternalRoot.Add( mRenderFullSizeCamera );
420 //////////////////////////////////////////////////////
421 // Create camera for the renders corresponding to the (potentially downsampled) render targets' size
422 mRenderDownsampledCamera = CameraActor::New();
423 mRenderDownsampledCamera.SetInvertYAxis( true );
424 mRenderDownsampledCamera.SetParentOrigin(ParentOrigin::CENTER);
426 //////////////////////////////////////////////////////
427 // Connect to actor tree
428 Self().Add( mChildrenRoot );
429 Self().Add( mInternalRoot );
430 mInternalRoot.Add( mHorizBlurActor );
431 mInternalRoot.Add( mVertBlurActor );
432 mInternalRoot.Add( mRenderDownsampledCamera );
436 void GaussianBlurView::OnSizeSet(const Vector3& targetSize)
438 mTargetSize = Vector2(targetSize);
440 mChildrenRoot.SetSize(targetSize);
442 if( !mBlurUserImage )
444 mCompositingActor.SetSize(targetSize);
445 mTargetActor.SetSize(targetSize);
447 // 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
448 // 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
449 // size, this is the trade off for not being able to modify render target size
450 // Change camera z position based on GaussianBlurView actor height
451 float cameraPosConstraintScale = 0.5f / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f);
452 mRenderFullSizeCamera.SetZ(mTargetSize.height * cameraPosConstraintScale);
456 // if we have already activated the blur, need to update render target sizes now to reflect the new size of this actor
463 Control::OnSizeSet( targetSize );
466 void GaussianBlurView::OnChildAdd( Actor& child )
468 if( child != mChildrenRoot && child != mInternalRoot)
470 mChildrenRoot.Add( child );
473 Control::OnChildAdd( child );
476 void GaussianBlurView::OnChildRemove( Actor& child )
478 mChildrenRoot.Remove( child );
480 Control::OnChildRemove( child );
483 void GaussianBlurView::AllocateResources()
485 // size of render targets etc is based on the size of this actor, ignoring z
486 if(mTargetSize != mLastSize)
488 mLastSize = mTargetSize;
490 // get size of downsampled render targets
491 mDownsampledWidth = mTargetSize.width * mDownsampleWidthScale;
492 mDownsampledHeight = mTargetSize.height * mDownsampleHeightScale;
494 // Create and place a camera for the renders corresponding to the (potentially downsampled) render targets' size
495 mRenderDownsampledCamera.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW);
496 // 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
497 mRenderDownsampledCamera.SetNearClippingPlane(1.0f);
498 mRenderDownsampledCamera.SetAspectRatio(mDownsampledWidth / mDownsampledHeight);
499 mRenderDownsampledCamera.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor
501 mRenderDownsampledCamera.SetPosition(0.0f, 0.0f, ((mDownsampledHeight * 0.5f) / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f)));
503 // setup for normal operation
506 // Create and place a camera for the children render, corresponding to its render target size
507 mRenderFullSizeCamera.SetFieldOfView(ARBITRARY_FIELD_OF_VIEW);
508 // 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
509 mRenderFullSizeCamera.SetNearClippingPlane(1.0f);
510 mRenderFullSizeCamera.SetAspectRatio(mTargetSize.width / mTargetSize.height);
511 mRenderFullSizeCamera.SetType(Dali::Camera::FREE_LOOK); // camera orientation based solely on actor
513 float cameraPosConstraintScale = 0.5f / tanf(ARBITRARY_FIELD_OF_VIEW * 0.5f);
514 mRenderFullSizeCamera.SetPosition(0.0f, 0.0f, mTargetSize.height * cameraPosConstraintScale);
516 // create offscreen buffer of new size to render our child actors to
517 mRenderTargetForRenderingChildren = FrameBuffer::New( mTargetSize.width, mTargetSize.height, FrameBuffer::Attachment::NONE );
518 Texture texture = Texture::New( TextureType::TEXTURE_2D, mPixelFormat, unsigned(mTargetSize.width), unsigned(mTargetSize.height) );
519 mRenderTargetForRenderingChildren.AttachColorTexture( texture );
521 // Set actor for performing a horizontal blur
522 SetTexture( mHorizBlurActor, mRenderTargetForRenderingChildren );
524 // Create offscreen buffer for vert blur pass
525 mRenderTarget1 = FrameBuffer::New( mDownsampledWidth, mDownsampledHeight, FrameBuffer::Attachment::NONE );
526 texture = Texture::New(TextureType::TEXTURE_2D, mPixelFormat, unsigned(mDownsampledWidth), unsigned(mDownsampledHeight));
527 mRenderTarget1.AttachColorTexture( texture );
529 // use the completed blur in the first buffer and composite with the original child actors render
530 SetTexture( mCompositingActor, mRenderTarget1 );
532 // set up target actor for rendering result, i.e. the blurred image
533 SetTexture( mTargetActor, mRenderTargetForRenderingChildren );
536 // Create offscreen buffer for horiz blur pass
537 mRenderTarget2 = FrameBuffer::New( mDownsampledWidth, mDownsampledHeight, FrameBuffer::Attachment::NONE );
538 Texture texture = Texture::New(TextureType::TEXTURE_2D, mPixelFormat, unsigned(mDownsampledWidth), unsigned(mDownsampledHeight));
539 mRenderTarget2.AttachColorTexture( texture );
541 // size needs to match render target
542 mHorizBlurActor.SetSize(mDownsampledWidth, mDownsampledHeight);
544 // size needs to match render target
545 mVertBlurActor.SetSize(mDownsampledWidth, mDownsampledHeight);
546 SetTexture( mVertBlurActor, mRenderTarget2 );
548 // set gaussian blur up for new sized render targets
549 SetShaderConstants();
553 void GaussianBlurView::CreateRenderTasks()
555 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
559 // create render task to render our child actors to offscreen buffer
560 mRenderChildrenTask = taskList.CreateTask();
561 mRenderChildrenTask.SetSourceActor( mChildrenRoot );
562 mRenderChildrenTask.SetExclusive(true);
563 mRenderChildrenTask.SetInputEnabled( false );
564 mRenderChildrenTask.SetClearEnabled( true );
565 mRenderChildrenTask.SetClearColor( mBackgroundColor );
567 mRenderChildrenTask.SetCameraActor(mRenderFullSizeCamera);
568 mRenderChildrenTask.SetFrameBuffer( mRenderTargetForRenderingChildren );
571 // perform a horizontal blur targeting the second buffer
572 mHorizBlurTask = taskList.CreateTask();
573 mHorizBlurTask.SetSourceActor( mHorizBlurActor );
574 mHorizBlurTask.SetExclusive(true);
575 mHorizBlurTask.SetInputEnabled( false );
576 mHorizBlurTask.SetClearEnabled( true );
577 mHorizBlurTask.SetClearColor( mBackgroundColor );
578 mHorizBlurTask.SetCameraActor(mRenderDownsampledCamera);
579 mHorizBlurTask.SetFrameBuffer( mRenderTarget2 );
580 if( mRenderOnce && mBlurUserImage )
582 mHorizBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
585 // use the second buffer and perform a horizontal blur targeting the first buffer
586 mVertBlurTask = taskList.CreateTask();
587 mVertBlurTask.SetSourceActor( mVertBlurActor );
588 mVertBlurTask.SetExclusive(true);
589 mVertBlurTask.SetInputEnabled( false );
590 mVertBlurTask.SetClearEnabled( true );
591 mVertBlurTask.SetClearColor( mBackgroundColor );
592 mVertBlurTask.SetCameraActor(mRenderDownsampledCamera);
593 if(mUserOutputRenderTarget)
595 mVertBlurTask.SetFrameBuffer( mUserOutputRenderTarget );
599 mVertBlurTask.SetFrameBuffer( mRenderTarget1 );
601 if( mRenderOnce && mBlurUserImage )
603 mVertBlurTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
604 mVertBlurTask.FinishedSignal().Connect( this, &GaussianBlurView::OnRenderTaskFinished );
607 // use the completed blur in the first buffer and composite with the original child actors render
610 mCompositeTask = taskList.CreateTask();
611 mCompositeTask.SetSourceActor( mCompositingActor );
612 mCompositeTask.SetExclusive(true);
613 mCompositeTask.SetInputEnabled( false );
615 mCompositeTask.SetCameraActor(mRenderFullSizeCamera);
616 mCompositeTask.SetFrameBuffer( mRenderTargetForRenderingChildren );
620 void GaussianBlurView::RemoveRenderTasks()
622 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
624 taskList.RemoveTask(mRenderChildrenTask);
625 taskList.RemoveTask(mHorizBlurTask);
626 taskList.RemoveTask(mVertBlurTask);
627 taskList.RemoveTask(mCompositeTask);
630 void GaussianBlurView::Activate()
632 // make sure resources are allocated and start the render tasks processing
638 void GaussianBlurView::ActivateOnce()
640 DALI_ASSERT_ALWAYS(mBlurUserImage); // Only works with blurring image mode.
645 void GaussianBlurView::Deactivate()
647 // stop render tasks processing
648 // Note: render target resources are automatically freed since we set the Image::Unused flag
650 mRenderTargetForRenderingChildren.Reset();
651 mRenderTarget1.Reset();
652 mRenderTarget2.Reset();
657 void GaussianBlurView::SetBlurBellCurveWidth(float blurBellCurveWidth)
659 // a value of zero leads to undefined Gaussian weights, do not allow user to do this
660 mBlurBellCurveWidth = std::max( blurBellCurveWidth, 0.001f );
663 float GaussianBlurView::CalcGaussianWeight(float x)
665 return (1.0f / sqrt(2.0f * Math::PI * mBlurBellCurveWidth)) * exp(-(x * x) / (2.0f * mBlurBellCurveWidth * mBlurBellCurveWidth));
668 void GaussianBlurView::SetShaderConstants()
673 float w, totalWeights;
676 uvOffsets = new Vector2[mNumSamples + 1];
677 weights = new float[mNumSamples + 1];
679 totalWeights = weights[0] = CalcGaussianWeight(0);
680 uvOffsets[0].x = 0.0f;
681 uvOffsets[0].y = 0.0f;
683 for(i=0; i<mNumSamples >> 1; i++)
685 w = CalcGaussianWeight((float)(i + 1));
686 weights[(i << 1) + 1] = w;
687 weights[(i << 1) + 2] = w;
688 totalWeights += w * 2.0f;
690 // offset texture lookup to between texels, that way the bilinear filter in the texture hardware will average two samples with one lookup
691 ofs = ((float)(i << 1)) + 1.5f;
693 // get offsets from units of pixels into uv coordinates in [0..1]
694 float ofsX = ofs / mDownsampledWidth;
695 float ofsY = ofs / mDownsampledHeight;
696 uvOffsets[(i << 1) + 1].x = ofsX;
697 uvOffsets[(i << 1) + 1].y = ofsY;
699 uvOffsets[(i << 1) + 2].x = -ofsX;
700 uvOffsets[(i << 1) + 2].y = -ofsY;
703 for(i=0; i<mNumSamples; i++)
705 weights[i] /= totalWeights;
708 // set shader constants
709 Vector2 xAxis(1.0f, 0.0f);
710 Vector2 yAxis(0.0f, 1.0f);
711 for (i = 0; i < mNumSamples; ++i )
713 mHorizBlurActor.RegisterProperty( GetSampleOffsetsPropertyName( i ), uvOffsets[ i ] * xAxis );
714 mHorizBlurActor.RegisterProperty( GetSampleWeightsPropertyName( i ), weights[ i ] );
716 mVertBlurActor.RegisterProperty( GetSampleOffsetsPropertyName( i ), uvOffsets[ i ] * yAxis );
717 mVertBlurActor.RegisterProperty( GetSampleWeightsPropertyName( i ), weights[ i ] );
724 std::string GaussianBlurView::GetSampleOffsetsPropertyName( unsigned int index ) const
726 DALI_ASSERT_ALWAYS( index < mNumSamples );
728 std::ostringstream oss;
729 oss << "uSampleOffsets[" << index << "]";
733 std::string GaussianBlurView::GetSampleWeightsPropertyName( unsigned int index ) const
735 DALI_ASSERT_ALWAYS( index < mNumSamples );
737 std::ostringstream oss;
738 oss << "uSampleWeights[" << index << "]";
742 Dali::Toolkit::GaussianBlurView::GaussianBlurViewSignal& GaussianBlurView::FinishedSignal()
744 return mFinishedSignal;
747 void GaussianBlurView::OnRenderTaskFinished(Dali::RenderTask& renderTask)
749 Toolkit::GaussianBlurView handle( GetOwner() );
750 mFinishedSignal.Emit( handle );
753 } // namespace Internal
754 } // namespace Toolkit