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