Revert "License conversion from Flora to Apache 2.0"
[platform/core/uifw/dali-toolkit.git] / optional / dali-toolkit / internal / controls / image-view / masked-image-view-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // EXTERNAL INCLUDES
18 #include <sstream>
19
20 // INTERNAL INCLUDES
21 #include <dali-toolkit/internal/controls/image-view/masked-image-view-impl.h>
22
23 namespace Dali
24 {
25
26 namespace Toolkit
27 {
28
29 namespace Internal
30 {
31
32 namespace // unnamed namespace
33 {
34
35 const char* CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::CUSTOM_PROPERTY_COUNT ] =
36 {
37   "background-color",
38   "source-size",
39   "source-offset",
40   "mask-size",
41   "mask-offset"
42 };
43
44 const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE =
45   "precision mediump float;                                                                                      \n"
46   "uniform vec2 uTargetSize;                                                                                     \n"
47   "uniform vec2 uSourceSize;                                                                                     \n"
48   "uniform vec2 uSourceOffset;                                                                                   \n"
49   "uniform vec2 uMaskSize;                                                                                       \n"
50   "uniform vec2 uMaskOffset;                                                                                     \n"
51   "varying vec2 vMaskTexCoord;                                                                                   \n"
52   "void main()                                                                                                   \n"
53   "{                                                                                                             \n"
54   "  float x = uSourceSize.x*aPosition.x + uSourceOffset.x;                                                      \n"
55   "  float y = uSourceSize.y*aPosition.y + uSourceOffset.y;                                                      \n"
56   "                                                                                                              \n"
57   "  gl_Position = vec4( x/(uTargetSize.x*0.5), y/(uTargetSize.y*0.5), 0.0, 1.0 );                               \n"
58   "                                                                                                              \n"
59   "  vMaskTexCoord.x = (uMaskSize.x*0.5 + x - uMaskOffset.x) / uMaskSize.x;                                      \n"
60   "  vMaskTexCoord.y = (uMaskSize.y*0.5 + y - uMaskOffset.y) / uMaskSize.y;                                      \n";
61
62 const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE0 =
63   "                                                                                                              \n"
64   "  vTexCoord = aTexCoord;                                                                                      \n"
65   "}";
66
67 const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE90 =
68   "                                                                                                              \n"
69   "  vTexCoord.x = aTexCoord.y;                                                                                  \n"
70   "  vTexCoord.y = 1.0 - aTexCoord.x;                                                                            \n"
71   "}";
72
73 const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE180 =
74   "                                                                                                              \n"
75   "  vTexCoord.x = 1.0 - aTexCoord.x;                                                                            \n"
76   "  vTexCoord.y = 1.0 - aTexCoord.y;                                                                            \n"
77   "}";
78
79 const char* const MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE270 =
80   "                                                                                                              \n"
81   "  vTexCoord.x = 1.0 - aTexCoord.y;                                                                            \n"
82   "  vTexCoord.y = aTexCoord.x;                                                                                  \n"
83   "}";
84
85 const char* const MASKED_IMAGE_VIEW_FRAGMENT_SOURCE =
86   "precision mediump float;                                                                                      \n"
87   "varying vec2 vMaskTexCoord;                                                                                   \n"
88   "void main()                                                                                                   \n"
89   "{                                                                                                             \n"
90   "  highp vec4 mask = texture2D(sEffect, vMaskTexCoord);                                                        \n"
91   "  gl_FragColor = texture2D(sTexture, vTexCoord) * vec4(1,1,1,mask.a);                                         \n"
92   "}";
93
94 Vector2 EqualToConstraintVector2( const Vector2& current, const PropertyInput& property )
95 {
96   return property.GetVector2();
97 }
98
99 Vector2 GetSizeForAspectRatio( const Vector2& targetSize, float aspectRatio )
100 {
101   Vector2 sizeToKeepAspectRatio( targetSize );
102
103   float targetAspectRatio( targetSize.width / targetSize.height );
104
105   if( aspectRatio > targetAspectRatio )
106   {
107     sizeToKeepAspectRatio.width = sizeToKeepAspectRatio.height * aspectRatio;
108   }
109   else if ( aspectRatio < targetAspectRatio )
110   {
111     sizeToKeepAspectRatio.height = sizeToKeepAspectRatio.width / aspectRatio;
112   }
113
114   return sizeToKeepAspectRatio;
115 }
116
117 Vector2 ClampSourceSize( const Vector2& sourceSize, const Vector2& targetSize, float widthOverHeight, float maxSourceScale )
118 {
119   Vector2 clampedSize( sourceSize );
120
121   Vector2 minSize( targetSize );
122   if ( widthOverHeight > 0.0f )
123   {
124     minSize = GetSizeForAspectRatio( targetSize, widthOverHeight );
125   }
126
127   if ( clampedSize.width  < minSize.width ||
128        clampedSize.height < minSize.height )
129   {
130     clampedSize = minSize;
131   }
132   else if ( clampedSize.width  > minSize.width *maxSourceScale ||
133             clampedSize.height > minSize.height*maxSourceScale )
134   {
135     clampedSize = minSize * maxSourceScale;
136   }
137
138   return clampedSize;
139 }
140
141 Vector2 ClampSourceOffset( const Vector2& sourceOffset, const Vector2& targetSize, const Vector2& sourceSize )
142 {
143   Vector2 min, max;
144
145   if ( sourceSize.width > targetSize.width )
146   {
147     float offset = (sourceSize.width - targetSize.width) * 0.5f;
148     min.x = -offset;
149     max.x =  offset;
150   }
151
152   if ( sourceSize.height > targetSize.height )
153   {
154     float offset = (sourceSize.height - targetSize.height) * 0.5f;
155     min.y = -offset;
156     max.y =  offset;
157   }
158
159   return Vector2( Clamp(sourceOffset.x, min.x, max.x), Clamp(sourceOffset.y, min.y, max.y) );
160 }
161
162 } // unnamed namespace
163
164 Dali::Toolkit::MaskedImageView MaskedImageView::New( unsigned int targetWidth,
165                                                      unsigned int targetHeight,
166                                                      Image sourceImage,
167                                                      Image maskImage )
168 {
169   // Create the implementation
170   MaskedImageView* maskedImageView = new MaskedImageView();
171
172   // Pass ownership to CustomActor via derived handle
173   Dali::Toolkit::MaskedImageView handle(*maskedImageView);
174
175   // Second-phase init of the implementation
176   // This can only be done after the CustomActor connection has been made...
177   maskedImageView->Initialize( targetWidth, targetHeight, sourceImage, maskImage );
178
179   return handle;
180 }
181
182 void MaskedImageView::SetSourceImage( Image sourceImage )
183 {
184   mSourceImageActor.SetImage( sourceImage );
185 }
186
187 Image MaskedImageView::GetSourceImage()
188 {
189   return mSourceImageActor.GetImage();
190 }
191
192 void MaskedImageView::SetMaskImage( Image maskImage )
193 {
194   mMaskImage = maskImage;
195   mSourceImageActor.GetShaderEffect().SetEffectImage( maskImage );
196 }
197
198 Image MaskedImageView::GetMaskImage()
199 {
200   return mMaskImage;
201 }
202
203 Property::Index MaskedImageView::GetPropertyIndex( Dali::Toolkit::MaskedImageView::CustomProperty customProperty ) const
204 {
205   Property::Index index = Property::INVALID_INDEX;
206
207   switch ( customProperty )
208   {
209     case Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR:
210     {
211       index = mCustomProperties[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ];
212       break;
213     }
214
215     case Dali::Toolkit::MaskedImageView::SOURCE_SIZE:
216     {
217       index = mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ];
218       break;
219     }
220
221     case Dali::Toolkit::MaskedImageView::SOURCE_OFFSET:
222     {
223       index = mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ];
224       break;
225     }
226
227     case Dali::Toolkit::MaskedImageView::MASK_SIZE:
228     {
229       index = mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ];
230       break;
231     }
232
233     case Dali::Toolkit::MaskedImageView::MASK_OFFSET:
234     {
235       index = mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ];
236       break;
237     }
238
239     default:
240       break;
241   }
242
243   return index;
244 }
245
246 void MaskedImageView::Pause()
247 {
248   if ( mRenderTask )
249   {
250     mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE );
251   }
252 }
253
254 void MaskedImageView::Resume()
255 {
256   if ( mRenderTask )
257   {
258     mRenderTask.SetRefreshRate( RenderTask::REFRESH_ALWAYS );
259   }
260 }
261
262 bool MaskedImageView::IsPaused() const
263 {
264   if( mRenderTask.GetRefreshRate() ) // REFRESH_ALWAYS
265   {
266     return false;
267   }
268   else // REFRESH_ONCE
269   {
270     return true;
271   }
272 }
273
274 void MaskedImageView::SetEditMode( Dali::Toolkit::MaskedImageView::EditMode editMode )
275 {
276   Actor self = Self();
277
278   mEditMode = editMode;
279
280   if ( Dali::Toolkit::MaskedImageView::EDIT_DISABLED == editMode )
281   {
282     if ( mPanGestureDetector )
283     {
284       mPanGestureDetector.DetachAll();
285       mPanGestureDetector.Reset();
286     }
287
288     if ( mPinchDetector )
289     {
290       mPinchDetector.DetachAll();
291       mPinchDetector.Reset();
292     }
293   }
294   else
295   {
296     if ( !mPanGestureDetector )
297     {
298       mPanGestureDetector = PanGestureDetector::New();
299       mPanGestureDetector.Attach( self );
300       mPanGestureDetector.DetectedSignal().Connect(this, &MaskedImageView::OnPan);
301     }
302
303     if ( !mPinchDetector )
304     {
305       mPinchDetector = PinchGestureDetector::New();
306       mPinchDetector.Attach( self );
307       mPinchDetector.DetectedSignal().Connect(this, &MaskedImageView::OnPinch);
308     }
309
310     if( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == editMode )
311     {
312       // Re-clamp values to preserve image aspect-ratio etc.
313       ClampSourceSizeAndOffset();
314     }
315   }
316 }
317
318 Dali::Toolkit::MaskedImageView::EditMode MaskedImageView::GetEditMode() const
319 {
320   return mEditMode;
321 }
322
323 void MaskedImageView::OnPropertySet( Property::Index index, Property::Value propertyValue )
324 {
325   // Ignore OnPropertySet if MaskedImageView is setting the properties
326   if( !mSelfPropertySetting )
327   {
328     // Synchronize with user-supplied property values...
329     if( mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ] == index )
330     {
331       // Note that clamping will take effect when edit-mode is used later
332       mSourcePosition.mStartPinchSize   = propertyValue.Get<Vector2>();
333       mSourcePosition.mCurrentPinchSize = propertyValue.Get<Vector2>();
334     }
335     else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ] == index )
336     {
337       // Note that clamping will take effect when edit-mode is used later
338       mSourcePosition.mPanOffset = propertyValue.Get<Vector2>();
339     }
340     else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ] == index )
341     {
342       mMaskPosition.mStartPinchSize   = propertyValue.Get<Vector2>();
343       mMaskPosition.mCurrentPinchSize = propertyValue.Get<Vector2>();
344     }
345     else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ] == index )
346     {
347       mMaskPosition.mPanOffset = propertyValue.Get<Vector2>();
348     }
349     // else it's fine to do nothing here
350   }
351 }
352
353 void MaskedImageView::OnPan(Actor source, PanGesture gesture)
354 {
355   // Used to flag whether edit mode is setting properties
356   mSelfPropertySetting = true;
357
358   Actor self = Self();
359
360   if ( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == mEditMode )
361   {
362     mSourcePosition.mPanOffset += gesture.displacement;
363     mSourcePosition.mPanOffset = ClampSourceOffset( mSourcePosition.mPanOffset, mTargetSize, mSourcePosition.mCurrentPinchSize );
364
365     self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ), mSourcePosition.mPanOffset );
366   }
367   else // Edit mask
368   {
369     mMaskPosition.mPanOffset += gesture.displacement;
370
371     self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::MASK_OFFSET ), mMaskPosition.mPanOffset );
372   }
373
374   // Used to flag whether edit mode is setting properties
375   mSelfPropertySetting = false;
376 }
377
378 void MaskedImageView::OnPinch(Actor actor, PinchGesture pinch)
379 {
380   // Used to flag whether edit mode is setting properties
381   mSelfPropertySetting = true;
382
383   Actor self = Self();
384
385   if ( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == mEditMode )
386   {
387     if ( pinch.state == Gesture::Started )
388     {
389       mSourcePosition.mStartPinchSize = mSourcePosition.mCurrentPinchSize;
390     }
391
392     mSourcePosition.mCurrentPinchSize = mSourcePosition.mStartPinchSize * pinch.scale;
393
394     ClampSourceSizeAndOffset();
395   }
396   else // Edit mask
397   {
398     if ( pinch.state == Gesture::Started )
399     {
400       mMaskPosition.mStartPinchSize = mMaskPosition.mCurrentPinchSize;
401     }
402
403     mMaskPosition.mCurrentPinchSize = mMaskPosition.mStartPinchSize * pinch.scale;
404
405     self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::MASK_SIZE ), mMaskPosition.mCurrentPinchSize );
406   }
407
408   // Used to flag whether edit mode is setting properties
409   mSelfPropertySetting = false;
410 }
411
412 void MaskedImageView::SetSourceAspectRatio( float widthOverHeight )
413 {
414   Actor self = Self();
415
416   if ( widthOverHeight > 0.0f )
417   {
418     mWidthOverHeight = widthOverHeight;
419
420     ClampSourceSizeAndOffset();
421   }
422   else
423   {
424     mWidthOverHeight = 0.0f; // ignore aspect-ratio
425   }
426 }
427
428 float MaskedImageView::GetSourceAspectRatio() const
429 {
430   return mWidthOverHeight;
431 }
432
433 void MaskedImageView::SetMaximumSourceScale( float scale )
434 {
435   mMaximumSourceScale = scale;
436 }
437
438 float MaskedImageView::GetMaximumSourceScale() const
439 {
440   return mMaximumSourceScale;
441 }
442
443 void MaskedImageView::SetSourceRotation( MaskedImageView::ImageRotation newRotation )
444 {
445   if( mSourceRotation != newRotation )
446   {
447     bool oldLandscape( Dali::Toolkit::MaskedImageView::ROTATE_90 == mSourceRotation || Dali::Toolkit::MaskedImageView::ROTATE_270 == mSourceRotation );
448     bool newLandscape( Dali::Toolkit::MaskedImageView::ROTATE_90 == newRotation     || Dali::Toolkit::MaskedImageView::ROTATE_270 == newRotation     );
449
450     if ( oldLandscape != newLandscape )
451     {
452       // Changing between landscape & portraint, swap width & height
453       float temp = mSourcePosition.mCurrentPinchSize.width;
454       mSourcePosition.mCurrentPinchSize.width  = mSourcePosition.mCurrentPinchSize.height;
455       mSourcePosition.mCurrentPinchSize.height = temp;
456     }
457
458     mSourceRotation = newRotation;
459
460     ApplyMaskedImageShader( newRotation );
461
462     ClampSourceSizeAndOffset();
463   }
464 }
465
466 MaskedImageView::ImageRotation MaskedImageView::GetSourceRotation() const
467 {
468   return mSourceRotation;
469 }
470
471 Dali::Toolkit::MaskedImageView::MaskedImageViewSignal& MaskedImageView::MaskFinishedSignal()
472 {
473   return mMaskFinishedSignal;
474 }
475
476 MaskedImageView::MaskedImageView()
477 : ControlImpl(true),
478   mEditMode( Dali::Toolkit::MaskedImageView::EDIT_DISABLED ),
479   mSelfPropertySetting( false ),
480   mSourceRotation( Dali::Toolkit::MaskedImageView::ROTATE_0 ),
481   mWidthOverHeight( 0.0f ),
482   mMaximumSourceScale( Dali::Toolkit::MaskedImageView::DEFAULT_MAXIMUM_SOURCE_SCALE )
483 {
484 }
485
486 void MaskedImageView::Initialize( unsigned int targetWidth,
487                                   unsigned int targetHeight,
488                                   Image sourceImage,
489                                   Image maskImage )
490 {
491   Actor self = Self();
492
493   // Register custom properties
494
495   mTargetSize = Vector2( static_cast<float>(targetWidth), static_cast<float>(targetHeight) );
496
497   mCustomProperties[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ]
498     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ], Color::BLACK  );
499
500   mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ]
501     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ], mTargetSize );
502
503   mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ]
504     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ], Vector2::ZERO );
505
506   mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ]
507     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::MASK_SIZE ], mTargetSize );
508
509   mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ]
510     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ], Vector2::ZERO );
511
512   // Create destination image (FBO)
513   mDestinationImage = FrameBufferImage::New( targetWidth, targetHeight, Pixel::RGBA8888 );
514
515   // Create source actor for off-screen image processing
516   mSourceImageActor = ImageActor::New( sourceImage );
517   self.Add( mSourceImageActor );
518   mSourceImageActor.SetParentOrigin( ParentOrigin::CENTER );
519   mSourceImageActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
520   mSourceImageActor.SetInheritRotation( false );
521   mSourceImageActor.SetInheritScale( false );
522   mSourceImageActor.SetColorMode( USE_OWN_COLOR );
523   mSourceImageActor.SetSize( Vector3::ONE );
524
525   // Apply masking effect to source actor
526   mMaskImage = maskImage;
527   ApplyMaskedImageShader( Dali::Toolkit::MaskedImageView::ROTATE_0 );
528
529   // Create actor to display result of off-screen rendering
530   mDestinationImageActor = ImageActor::New( mDestinationImage );
531   self.Add( mDestinationImageActor );
532   mDestinationImageActor.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) );
533   mDestinationImageActor.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
534
535   // Start the masking operation
536   mRenderTask = Stage::GetCurrent().GetRenderTaskList().CreateTask();
537   mRenderTask.SetSourceActor( mSourceImageActor );
538   mRenderTask.SetTargetFrameBuffer( mDestinationImage );
539   mRenderTask.SetInputEnabled( false );
540   mRenderTask.SetExclusive( true );
541   mRenderTask.SetClearEnabled( true );
542   mRenderTask.ApplyConstraint( Constraint::New<Vector4>( RenderTask::CLEAR_COLOR,
543                                                          Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ] ),
544                                                          EqualToConstraint() ) );
545   mRenderTask.FinishedSignal().Connect( this, &MaskedImageView::OnRenderTaskFinished );
546
547   // Edit mode initialization
548   mSourcePosition.mCurrentPinchSize = Vector2( targetWidth, targetHeight );
549   mMaskPosition.mCurrentPinchSize   = mSourcePosition.mCurrentPinchSize;
550 }
551
552 void MaskedImageView::ApplyMaskedImageShader( ImageRotation rotation )
553 {
554   Actor self = Self();
555
556   // Vertex shader has different postfix for each rotation
557   std::stringstream vertexSource;
558   vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE;
559   if( Dali::Toolkit::MaskedImageView::ROTATE_90 == rotation )
560   {
561     vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE90;
562   }
563   else if( Dali::Toolkit::MaskedImageView::ROTATE_180 == rotation )
564   {
565     vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE180;
566   }
567   else if( Dali::Toolkit::MaskedImageView::ROTATE_270 == rotation )
568   {
569     vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE270;
570   }
571   else // Default to Dali::Toolkit::MaskedImageView::ROTATE_0
572   {
573     vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE0;
574   }
575
576   ShaderEffect shader = ShaderEffect::New( vertexSource.str(),
577                                            MASKED_IMAGE_VIEW_FRAGMENT_SOURCE,
578                                            GeometryType( GEOMETRY_TYPE_IMAGE ),
579                                            ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) );
580
581   shader.SetUniform( "uTargetSize", mTargetSize );
582   shader.SetUniform( "uSourceSize", mTargetSize );
583   shader.ApplyConstraint( Constraint::New<Vector2>( shader.GetPropertyIndex( "uSourceSize" ),
584                                                     Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ] ),
585                                                     EqualToConstraintVector2 ) );
586   shader.SetUniform( "uSourceOffset", Vector2::ZERO );
587   shader.ApplyConstraint( Constraint::New<Vector2>( shader.GetPropertyIndex( "uSourceOffset" ),
588                                                     Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ] ),
589                                                     EqualToConstraintVector2 ) );
590   shader.SetUniform( "uMaskSize", mTargetSize );
591   shader.ApplyConstraint( Constraint::New<Vector2>( shader.GetPropertyIndex( "uMaskSize" ),
592                                                     Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ] ),
593                                                     EqualToConstraintVector2 ) );
594   shader.SetUniform( "uMaskOffset", mTargetSize );
595   shader.ApplyConstraint( Constraint::New<Vector2>( shader.GetPropertyIndex( "uMaskOffset" ),
596                                                     Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ] ),
597                                                     EqualToConstraintVector2 ) );
598
599   shader.SetEffectImage( mMaskImage );
600   mSourceImageActor.SetShaderEffect( shader );
601 }
602
603 void MaskedImageView::ClampSourceSizeAndOffset()
604 {
605   float rotatedAspectRatio( mWidthOverHeight );
606   if( mWidthOverHeight > 0.0f &&
607       ( Dali::Toolkit::MaskedImageView::ROTATE_90  == mSourceRotation ||
608         Dali::Toolkit::MaskedImageView::ROTATE_270 == mSourceRotation ) )
609   {
610     rotatedAspectRatio = 1.0f / mWidthOverHeight;
611   }
612
613   Actor self = Self();
614
615   mSourcePosition.mCurrentPinchSize = ClampSourceSize( mSourcePosition.mCurrentPinchSize, mTargetSize, rotatedAspectRatio, mMaximumSourceScale );
616   self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_SIZE ), mSourcePosition.mCurrentPinchSize );
617
618   mSourcePosition.mPanOffset = ClampSourceOffset( mSourcePosition.mPanOffset, mTargetSize, mSourcePosition.mCurrentPinchSize );
619   self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ), mSourcePosition.mPanOffset );
620 }
621
622 MaskedImageView::~MaskedImageView()
623 {
624   // Guard to allow handle destruction after Core has been destroyed
625   if( Stage::IsInstalled() )
626   {
627     Stage::GetCurrent().GetRenderTaskList().RemoveTask( mRenderTask );
628   }
629 }
630
631 void MaskedImageView::OnRenderTaskFinished( Dali::RenderTask& renderTask )
632 {
633   Toolkit::MaskedImageView handle( GetOwner() );
634   mMaskFinishedSignal.Emit( handle );
635 }
636
637 } // namespace Internal
638
639 } // namespace Toolkit
640
641 } // namespace Dali