57b13b8a7887b0e2403088ffe9fcb470156368c7
[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 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   return !mRenderTask; // RenderTask is discarded during Pause()
265 }
266
267 void MaskedImageView::SetEditMode( Dali::Toolkit::MaskedImageView::EditMode editMode )
268 {
269   Actor self = Self();
270
271   mEditMode = editMode;
272
273   if ( Dali::Toolkit::MaskedImageView::EDIT_DISABLED == editMode )
274   {
275     if ( mPanGestureDetector )
276     {
277       mPanGestureDetector.DetachAll();
278       mPanGestureDetector.Reset();
279     }
280
281     if ( mPinchDetector )
282     {
283       mPinchDetector.DetachAll();
284       mPinchDetector.Reset();
285     }
286   }
287   else
288   {
289     if ( !mPanGestureDetector )
290     {
291       mPanGestureDetector = PanGestureDetector::New();
292       mPanGestureDetector.Attach( self );
293       mPanGestureDetector.DetectedSignal().Connect(this, &MaskedImageView::OnPan);
294     }
295
296     if ( !mPinchDetector )
297     {
298       mPinchDetector = PinchGestureDetector::New();
299       mPinchDetector.Attach( self );
300       mPinchDetector.DetectedSignal().Connect(this, &MaskedImageView::OnPinch);
301     }
302
303     if( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == editMode )
304     {
305       // Re-clamp values to preserve image aspect-ratio etc.
306       ClampSourceSizeAndOffset();
307     }
308   }
309 }
310
311 Dali::Toolkit::MaskedImageView::EditMode MaskedImageView::GetEditMode() const
312 {
313   return mEditMode;
314 }
315
316 void MaskedImageView::OnPropertySet( Property::Index index, Property::Value propertyValue )
317 {
318   // Ignore OnPropertySet if MaskedImageView is setting the properties
319   if( !mSelfPropertySetting )
320   {
321     // Synchronize with user-supplied property values...
322     if( mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ] == index )
323     {
324       // Note that clamping will take effect when edit-mode is used later
325       mSourcePosition.mStartPinchSize   = propertyValue.Get<Vector2>();
326       mSourcePosition.mCurrentPinchSize = propertyValue.Get<Vector2>();
327     }
328     else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ] == index )
329     {
330       // Note that clamping will take effect when edit-mode is used later
331       mSourcePosition.mPanOffset = propertyValue.Get<Vector2>();
332     }
333     else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ] == index )
334     {
335       mMaskPosition.mStartPinchSize   = propertyValue.Get<Vector2>();
336       mMaskPosition.mCurrentPinchSize = propertyValue.Get<Vector2>();
337     }
338     else if( mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ] == index )
339     {
340       mMaskPosition.mPanOffset = propertyValue.Get<Vector2>();
341     }
342     // else it's fine to do nothing here
343   }
344 }
345
346 void MaskedImageView::OnPan(Actor source, PanGesture gesture)
347 {
348   // Used to flag whether edit mode is setting properties
349   mSelfPropertySetting = true;
350
351   Actor self = Self();
352
353   if ( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == mEditMode )
354   {
355     mSourcePosition.mPanOffset += gesture.displacement;
356     mSourcePosition.mPanOffset = ClampSourceOffset( mSourcePosition.mPanOffset, mTargetSize, mSourcePosition.mCurrentPinchSize );
357
358     self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ), mSourcePosition.mPanOffset );
359   }
360   else // Edit mask
361   {
362     mMaskPosition.mPanOffset += gesture.displacement;
363
364     self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::MASK_OFFSET ), mMaskPosition.mPanOffset );
365   }
366
367   // Used to flag whether edit mode is setting properties
368   mSelfPropertySetting = false;
369 }
370
371 void MaskedImageView::OnPinch(Actor actor, PinchGesture pinch)
372 {
373   // Used to flag whether edit mode is setting properties
374   mSelfPropertySetting = true;
375
376   Actor self = Self();
377
378   if ( Dali::Toolkit::MaskedImageView::EDIT_SOURCE == mEditMode )
379   {
380     if ( pinch.state == Gesture::Started )
381     {
382       mSourcePosition.mStartPinchSize = mSourcePosition.mCurrentPinchSize;
383     }
384
385     mSourcePosition.mCurrentPinchSize = mSourcePosition.mStartPinchSize * pinch.scale;
386
387     ClampSourceSizeAndOffset();
388   }
389   else // Edit mask
390   {
391     if ( pinch.state == Gesture::Started )
392     {
393       mMaskPosition.mStartPinchSize = mMaskPosition.mCurrentPinchSize;
394     }
395
396     mMaskPosition.mCurrentPinchSize = mMaskPosition.mStartPinchSize * pinch.scale;
397
398     self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::MASK_SIZE ), mMaskPosition.mCurrentPinchSize );
399   }
400
401   // Used to flag whether edit mode is setting properties
402   mSelfPropertySetting = false;
403 }
404
405 void MaskedImageView::SetSourceAspectRatio( float widthOverHeight )
406 {
407   Actor self = Self();
408
409   if ( widthOverHeight > 0.0f )
410   {
411     mWidthOverHeight = widthOverHeight;
412
413     ClampSourceSizeAndOffset();
414   }
415   else
416   {
417     mWidthOverHeight = 0.0f; // ignore aspect-ratio
418   }
419 }
420
421 float MaskedImageView::GetSourceAspectRatio() const
422 {
423   return mWidthOverHeight;
424 }
425
426 void MaskedImageView::SetMaximumSourceScale( float scale )
427 {
428   mMaximumSourceScale = scale;
429 }
430
431 float MaskedImageView::GetMaximumSourceScale() const
432 {
433   return mMaximumSourceScale;
434 }
435
436 void MaskedImageView::SetSourceRotation( MaskedImageView::ImageRotation newRotation )
437 {
438   if( mSourceRotation != newRotation )
439   {
440     bool oldLandscape( Dali::Toolkit::MaskedImageView::ROTATE_90 == mSourceRotation || Dali::Toolkit::MaskedImageView::ROTATE_270 == mSourceRotation );
441     bool newLandscape( Dali::Toolkit::MaskedImageView::ROTATE_90 == newRotation     || Dali::Toolkit::MaskedImageView::ROTATE_270 == newRotation     );
442
443     if ( oldLandscape != newLandscape )
444     {
445       // Changing between landscape & portraint, swap width & height
446       float temp = mSourcePosition.mCurrentPinchSize.width;
447       mSourcePosition.mCurrentPinchSize.width  = mSourcePosition.mCurrentPinchSize.height;
448       mSourcePosition.mCurrentPinchSize.height = temp;
449     }
450
451     mSourceRotation = newRotation;
452
453     ApplyMaskedImageShader( newRotation );
454
455     ClampSourceSizeAndOffset();
456   }
457 }
458
459 MaskedImageView::ImageRotation MaskedImageView::GetSourceRotation() const
460 {
461   return mSourceRotation;
462 }
463
464 Dali::Toolkit::MaskedImageView::MaskedImageViewSignal& MaskedImageView::MaskFinishedSignal()
465 {
466   return mMaskFinishedSignal;
467 }
468
469 MaskedImageView::MaskedImageView()
470 : ControlImpl(true),
471   mEditMode( Dali::Toolkit::MaskedImageView::EDIT_DISABLED ),
472   mSelfPropertySetting( false ),
473   mSourceRotation( Dali::Toolkit::MaskedImageView::ROTATE_0 ),
474   mWidthOverHeight( 0.0f ),
475   mMaximumSourceScale( Dali::Toolkit::MaskedImageView::DEFAULT_MAXIMUM_SOURCE_SCALE )
476 {
477 }
478
479 void MaskedImageView::Initialize( unsigned int targetWidth,
480                                   unsigned int targetHeight,
481                                   Image sourceImage,
482                                   Image maskImage )
483 {
484   Actor self = Self();
485
486   // Register custom properties
487
488   mTargetSize = Vector2( static_cast<float>(targetWidth), static_cast<float>(targetHeight) );
489
490   mCustomProperties[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ]
491     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ], Color::BLACK  );
492
493   mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ]
494     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ], mTargetSize );
495
496   mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ]
497     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ], Vector2::ZERO );
498
499   mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ]
500     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::MASK_SIZE ], mTargetSize );
501
502   mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ]
503     = self.RegisterProperty( CUSTOM_PROPERTY_NAMES[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ], Vector2::ZERO );
504
505   // Create destination image (FBO)
506   mDestinationImage = FrameBufferImage::New( targetWidth, targetHeight, Pixel::RGBA8888 );
507
508   // Create source actor for off-screen image processing
509   mSourceImageActor = ImageActor::New( sourceImage );
510   self.Add( mSourceImageActor );
511   mSourceImageActor.SetParentOrigin( ParentOrigin::CENTER );
512   mSourceImageActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
513   mSourceImageActor.SetInheritRotation( false );
514   mSourceImageActor.SetInheritScale( false );
515   mSourceImageActor.SetColorMode( USE_OWN_COLOR );
516   mSourceImageActor.SetSize( Vector3::ONE );
517
518   // Apply masking effect to source actor
519   mMaskImage = maskImage;
520   ApplyMaskedImageShader( Dali::Toolkit::MaskedImageView::ROTATE_0 );
521
522   // Create actor to display result of off-screen rendering
523   mDestinationImageActor = ImageActor::New( mDestinationImage );
524   self.Add( mDestinationImageActor );
525   mDestinationImageActor.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) );
526   mDestinationImageActor.SetPositionInheritanceMode( Dali::USE_PARENT_POSITION );
527
528   // Start the masking operation
529   mRenderTask = Stage::GetCurrent().GetRenderTaskList().CreateTask();
530   mRenderTask.SetSourceActor( mSourceImageActor );
531   mRenderTask.SetTargetFrameBuffer( mDestinationImage );
532   mRenderTask.SetInputEnabled( false );
533   mRenderTask.SetExclusive( true );
534   mRenderTask.SetClearEnabled( true );
535   mRenderTask.ApplyConstraint( Constraint::New<Vector4>( RenderTask::CLEAR_COLOR,
536                                                          Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::BACKGROUND_COLOR ] ),
537                                                          EqualToConstraint() ) );
538   mRenderTask.FinishedSignal().Connect( this, &MaskedImageView::OnRenderTaskFinished );
539
540   // Edit mode initialization
541   mSourcePosition.mCurrentPinchSize = Vector2( targetWidth, targetHeight );
542   mMaskPosition.mCurrentPinchSize   = mSourcePosition.mCurrentPinchSize;
543 }
544
545 void MaskedImageView::ApplyMaskedImageShader( ImageRotation rotation )
546 {
547   Actor self = Self();
548
549   // Vertex shader has different postfix for each rotation
550   std::stringstream vertexSource;
551   vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE;
552   if( Dali::Toolkit::MaskedImageView::ROTATE_90 == rotation )
553   {
554     vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE90;
555   }
556   else if( Dali::Toolkit::MaskedImageView::ROTATE_180 == rotation )
557   {
558     vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE180;
559   }
560   else if( Dali::Toolkit::MaskedImageView::ROTATE_270 == rotation )
561   {
562     vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE270;
563   }
564   else // Default to Dali::Toolkit::MaskedImageView::ROTATE_0
565   {
566     vertexSource << MASKED_IMAGE_VIEW_VERTEX_SOURCE_ROTATE0;
567   }
568
569   ShaderEffect shader = ShaderEffect::New( vertexSource.str(),
570                                            MASKED_IMAGE_VIEW_FRAGMENT_SOURCE,
571                                            GeometryType( GEOMETRY_TYPE_IMAGE ),
572                                            ShaderEffect::GeometryHints( ShaderEffect::HINT_BLENDING ) );
573
574   shader.SetUniform( "uTargetSize", mTargetSize );
575   shader.SetUniform( "uSourceSize", mTargetSize );
576   shader.ApplyConstraint( Constraint::New<Vector2>( shader.GetPropertyIndex( "uSourceSize" ),
577                                                     Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_SIZE ] ),
578                                                     EqualToConstraintVector2 ) );
579   shader.SetUniform( "uSourceOffset", Vector2::ZERO );
580   shader.ApplyConstraint( Constraint::New<Vector2>( shader.GetPropertyIndex( "uSourceOffset" ),
581                                                     Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ] ),
582                                                     EqualToConstraintVector2 ) );
583   shader.SetUniform( "uMaskSize", mTargetSize );
584   shader.ApplyConstraint( Constraint::New<Vector2>( shader.GetPropertyIndex( "uMaskSize" ),
585                                                     Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_SIZE ] ),
586                                                     EqualToConstraintVector2 ) );
587   shader.SetUniform( "uMaskOffset", mTargetSize );
588   shader.ApplyConstraint( Constraint::New<Vector2>( shader.GetPropertyIndex( "uMaskOffset" ),
589                                                     Source( self, mCustomProperties[ Dali::Toolkit::MaskedImageView::MASK_OFFSET ] ),
590                                                     EqualToConstraintVector2 ) );
591
592   shader.SetEffectImage( mMaskImage );
593   mSourceImageActor.SetShaderEffect( shader );
594 }
595
596 void MaskedImageView::ClampSourceSizeAndOffset()
597 {
598   float rotatedAspectRatio( mWidthOverHeight );
599   if( mWidthOverHeight > 0.0f &&
600       ( Dali::Toolkit::MaskedImageView::ROTATE_90  == mSourceRotation ||
601         Dali::Toolkit::MaskedImageView::ROTATE_270 == mSourceRotation ) )
602   {
603     rotatedAspectRatio = 1.0f / mWidthOverHeight;
604   }
605
606   Actor self = Self();
607
608   mSourcePosition.mCurrentPinchSize = ClampSourceSize( mSourcePosition.mCurrentPinchSize, mTargetSize, rotatedAspectRatio, mMaximumSourceScale );
609   self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_SIZE ), mSourcePosition.mCurrentPinchSize );
610
611   mSourcePosition.mPanOffset = ClampSourceOffset( mSourcePosition.mPanOffset, mTargetSize, mSourcePosition.mCurrentPinchSize );
612   self.SetProperty( GetPropertyIndex( Dali::Toolkit::MaskedImageView::SOURCE_OFFSET ), mSourcePosition.mPanOffset );
613 }
614
615 MaskedImageView::~MaskedImageView()
616 {
617   // Guard to allow handle destruction after Core has been destroyed
618   if( Stage::IsInstalled() )
619   {
620     Stage::GetCurrent().GetRenderTaskList().RemoveTask( mRenderTask );
621   }
622 }
623
624 void MaskedImageView::OnRenderTaskFinished( Dali::RenderTask& renderTask )
625 {
626   Toolkit::MaskedImageView handle( GetOwner() );
627   mMaskFinishedSignal.Emit( handle );
628 }
629
630 } // namespace Internal
631
632 } // namespace Toolkit
633
634 } // namespace Dali