[dali_2.3.20] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / image-view / image-view-impl.cpp
1 /*
2  * Copyright (c) 2023 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 "image-view-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/scripting/scripting.h>
23 #include <dali/public-api/math/math-utils.h>
24 #include <dali/public-api/object/type-registry-helper.h>
25 #include <dali/public-api/object/type-registry.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/devel-api/controls/control-devel.h>
29 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
30 #include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
31 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
32 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
33 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
34 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
35 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
36 #include <dali-toolkit/public-api/visuals/visual-properties.h>
37
38 namespace Dali
39 {
40 namespace Toolkit
41 {
42 namespace Internal
43 {
44 namespace
45 {
46 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
47
48 constexpr float FULL_OPACITY = 1.0f;
49 constexpr float LOW_OPACITY  = 0.2f;
50 constexpr float TRANSITION_EFFECT_SPEED = 0.3f;
51
52 constexpr int PLACEHOLDER_DEPTH_INDEX = -2;
53 constexpr int PREVIOUS_VISUAL_DEPTH_INDEX  = -1;
54 constexpr int CURRENT_VISUAL_DEPTH_INDEX = 0;
55
56 BaseHandle Create()
57 {
58   return Toolkit::ImageView::New();
59 }
60
61 // Setup properties, signals and actions using the type-registry.
62 DALI_TYPE_REGISTRATION_BEGIN(Toolkit::ImageView, Toolkit::Control, Create);
63 DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "image", MAP, IMAGE)
64 DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "preMultipliedAlpha", BOOLEAN, PRE_MULTIPLIED_ALPHA)
65 DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "placeholderImage", STRING, PLACEHOLDER_IMAGE)
66 DALI_PROPERTY_REGISTRATION(Toolkit, ImageView, "enableTransitionEffect", BOOLEAN, ENABLE_TRANSITION_EFFECT)
67 DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT(Toolkit, ImageView, "pixelArea", Vector4(0.f, 0.f, 1.f, 1.f), PIXEL_AREA)
68 DALI_TYPE_REGISTRATION_END()
69
70 } // anonymous namespace
71
72 using namespace Dali;
73
74 ImageView::ImageView(ControlBehaviour additionalBehaviour)
75 : Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT | additionalBehaviour)),
76   mImageSize(),
77   mTransitionTargetAlpha(FULL_OPACITY),
78   mImageVisualPaddingSetByTransform(false),
79   mImageViewPixelAreaSetByFittingMode(false),
80   mTransitionEffect(false),
81   mNeedLazyFittingMode(false),
82   mImageReplaced(false)
83 {
84 }
85
86 ImageView::~ImageView()
87 {
88 }
89
90 Toolkit::ImageView ImageView::New(ControlBehaviour additionalBehaviour)
91 {
92   ImageView* impl = new ImageView(additionalBehaviour);
93
94   Toolkit::ImageView handle = Toolkit::ImageView(*impl);
95
96   // Second-phase init of the implementation
97   // This can only be done after the CustomActor connection has been made...
98   impl->Initialize();
99
100   return handle;
101 }
102
103 /////////////////////////////////////////////////////////////
104
105 void ImageView::OnInitialize()
106 {
107   // ImageView can relayout in the OnImageReady, alternative to a signal would be to have a upcall from the Control to ImageView
108   Dali::Toolkit::Control handle(GetOwner());
109   handle.ResourceReadySignal().Connect(this, &ImageView::OnResourceReady);
110
111   Self().SetProperty(DevelControl::Property::ACCESSIBILITY_ROLE, Dali::Accessibility::Role::IMAGE);
112 }
113
114 void ImageView::SetImage(const Property::Map& map)
115 {
116   if(mTransitionEffect && mVisual)
117   {
118     // Clear previous transition effect if it is playing
119     if(mPreviousVisual)
120     {
121       if(mTransitionAnimation)
122       {
123         if(mTransitionAnimation.GetState() == Animation::PLAYING)
124         {
125           // Hide placeholder
126           HidePlaceholderImage();
127           ClearTransitionAnimation();
128         }
129       }
130     }
131
132     // Enable transition effect for previous visual.
133     // This previous visual will be deleted when transition effect is done.
134     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this);
135     controlDataImpl.EnableReadyTransitionOverriden(mVisual, true);
136     mPreviousVisual = mVisual;
137   }
138
139   // Comparing a property map is too expensive so just creating a new visual
140   mPropertyMap = map;
141   mUrl.clear();
142
143   mImageReplaced = true;
144
145   // keep alpha for transition effect
146   if(mTransitionEffect)
147   {
148     float            alpha      = FULL_OPACITY;
149     Property::Value* alphaValue = map.Find(Toolkit::Visual::Property::OPACITY);
150     if(alphaValue && alphaValue->Get(alpha))
151     {
152       mTransitionTargetAlpha = alpha;
153     }
154   }
155
156   if(!mVisual)
157   {
158     ShowPlaceholderImage();
159   }
160
161   Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(mPropertyMap);
162   if(visual)
163   {
164     // Don't set mVisual until it is ready and shown. Getters will still use current visual.
165     if(!mVisual)
166     {
167       mVisual = visual;
168     }
169
170     if(!mShaderMap.Empty())
171     {
172       Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
173       visualImpl.SetCustomShader(mShaderMap);
174     }
175
176     DevelControl::RegisterVisual(*this, Toolkit::ImageView::Property::IMAGE, visual);
177   }
178   else
179   {
180     // Unregister the exsiting visual
181     DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::IMAGE);
182
183     // Trigger a size negotiation request that may be needed when unregistering a visual.
184     RelayoutRequest();
185   }
186   // Signal that a Relayout may be needed
187 }
188
189 void ImageView::SetImage(const std::string& url, ImageDimensions size)
190 {
191   if(mTransitionEffect && mVisual)
192   {
193     // Clear previous transition effect if it is playing
194     if(mPreviousVisual)
195     {
196       if(mTransitionAnimation)
197       {
198         if(mTransitionAnimation.GetState() == Animation::PLAYING)
199         {
200           HidePlaceholderImage();
201           ClearTransitionAnimation();
202         }
203       }
204     }
205
206     // Enable transition effect for previous visual.
207     // This previous visual will be deleted when transition effect is done.
208     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this);
209     controlDataImpl.EnableReadyTransitionOverriden(mVisual, true);
210     mPreviousVisual = mVisual;
211   }
212
213   
214
215   // Don't bother comparing if we had a visual previously, just drop old visual and create new one
216   mUrl       = url;
217   mImageSize = size;
218   mPropertyMap.Clear();
219
220   mImageReplaced = true;
221
222   if(!mVisual)
223   {
224     ShowPlaceholderImage();
225   }
226
227   // Don't set mVisual until it is ready and shown. Getters will still use current visual.
228   Toolkit::Visual::Base visual = Toolkit::VisualFactory::Get().CreateVisual(url, size);
229   if(visual)
230   {
231     if(!mVisual)
232     {
233       mVisual = visual;
234     }
235
236     if(!mShaderMap.Empty())
237     {
238       Internal::Visual::Base& visualImpl = Toolkit::GetImplementation(visual);
239       visualImpl.SetCustomShader(mShaderMap);
240     }
241
242     DevelControl::RegisterVisual(*this, Toolkit::ImageView::Property::IMAGE, visual);
243   }
244   else
245   {
246     // Unregister the exsiting visual
247     DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::IMAGE);
248
249     // Trigger a size negotiation request that may be needed when unregistering a visual.
250     RelayoutRequest();
251   }
252   // Signal that a Relayout may be needed
253 }
254
255 void ImageView::ClearImageVisual()
256 {
257   // Clear cached properties
258   mPropertyMap.Clear();
259   mUrl.clear();
260   mVisual.Reset();
261
262   // Unregister the exsiting visual
263   DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::IMAGE);
264
265   // Trigger a size negotiation request that may be needed when unregistering a visual.
266   RelayoutRequest();
267 }
268
269 void ImageView::EnablePreMultipliedAlpha(bool preMultipled)
270 {
271   if(mVisual)
272   {
273     Toolkit::GetImplementation(mVisual).EnablePreMultipliedAlpha(preMultipled);
274   }
275 }
276
277 bool ImageView::IsPreMultipliedAlphaEnabled() const
278 {
279   if(mVisual)
280   {
281     return Toolkit::GetImplementation(mVisual).IsPreMultipliedAlphaEnabled();
282   }
283   return false;
284 }
285
286 void ImageView::SetDepthIndex(int depthIndex)
287 {
288   if(mVisual)
289   {
290     mVisual.SetDepthIndex(depthIndex);
291   }
292 }
293
294 void ImageView::SetPlaceholderUrl(const std::string& url)
295 {
296   mPlaceholderUrl = url;
297   if(!url.empty())
298   {
299     mPlaceholderVisual.Reset();
300     CreatePlaceholderImage();
301   }
302   else
303   {
304     // Clear current placeholder image
305     Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE);
306     if(visual)
307     {
308       DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE);
309     }
310
311     mPlaceholderVisual.Reset();
312     mPlaceholderUrl = url;
313   }
314 }
315
316 std::string ImageView::GetPlaceholderUrl() const
317 {
318   return mPlaceholderUrl;
319 }
320
321 void ImageView::EnableTransitionEffect(bool effectEnable)
322 {
323   mTransitionEffect = effectEnable;
324 }
325
326 bool ImageView::IsTransitionEffectEnabled() const
327 {
328   return mTransitionEffect;
329 }
330
331 Vector3 ImageView::GetNaturalSize()
332 {
333   if(mVisual)
334   {
335     Vector2 rendererNaturalSize;
336     mVisual.GetNaturalSize(rendererNaturalSize);
337
338     Extents padding;
339     padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
340
341     rendererNaturalSize.width += (padding.start + padding.end);
342     rendererNaturalSize.height += (padding.top + padding.bottom);
343     return Vector3(rendererNaturalSize);
344   }
345
346   // if no visual then use Control's natural size
347   return Control::GetNaturalSize();
348 }
349
350 float ImageView::GetHeightForWidth(float width)
351 {
352   Extents padding;
353   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
354
355   if(mVisual)
356   {
357     return mVisual.GetHeightForWidth(width) + padding.top + padding.bottom;
358   }
359   else
360   {
361     return Control::GetHeightForWidth(width) + padding.top + padding.bottom;
362   }
363 }
364
365 float ImageView::GetWidthForHeight(float height)
366 {
367   Extents padding;
368   padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
369
370   if(mVisual)
371   {
372     return mVisual.GetWidthForHeight(height) + padding.start + padding.end;
373   }
374   else
375   {
376     return Control::GetWidthForHeight(height) + padding.start + padding.end;
377   }
378 }
379
380 void ImageView::OnRelayout(const Vector2& size, RelayoutContainer& container)
381 {
382   Control::OnRelayout(size, container);
383   if(mVisual)
384   {
385     // If Resource is not ready, fittingMode is not working well.
386     // in this case, imageview set the flag for working applyFittingMode again when the resource is ready
387     if(!IsResourceReady())
388     {
389       mNeedLazyFittingMode = true;
390     }
391
392     // Apply FittingMode using actor's size
393     ApplyFittingMode(size);
394
395     // mVisual is not updated util the resource is ready in the case of visual replacement.
396     // in this case, the Property Map must be initialized so that the previous value is not reused.
397     // after mVisual is updated, the correct value will be reset.
398     Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE);
399     if(visual && visual != mVisual)
400     {
401       visual.SetTransformAndSize(Property::Map(), size);
402     }
403
404     if(!mTransitionEffect)
405     {
406       // we don't need placeholder anymore because visual is replaced. so hide placeholder.
407       HidePlaceholderImage();
408     }
409   }
410 }
411
412 void ImageView::OnCreateTransitions(std::vector<std::pair<Dali::Property::Index, Dali::Property::Map>>& sourceProperties,
413                                     std::vector<std::pair<Dali::Property::Index, Dali::Property::Map>>& destinationProperties,
414                                     Dali::Toolkit::Control                                              source,
415                                     Dali::Toolkit::Control                                              destination)
416 {
417   // Retrieves image properties to be transitioned.
418   Dali::Property::Map imageSourcePropertyMap, imageDestinationPropertyMap;
419   MakeVisualTransition(imageSourcePropertyMap, imageDestinationPropertyMap, source, destination, Toolkit::ImageView::Property::IMAGE);
420   if(imageSourcePropertyMap.Count() > 0)
421   {
422     sourceProperties.push_back(std::pair<Dali::Property::Index, Dali::Property::Map>(Toolkit::ImageView::Property::IMAGE, imageSourcePropertyMap));
423     destinationProperties.push_back(std::pair<Dali::Property::Index, Dali::Property::Map>(Toolkit::ImageView::Property::IMAGE, imageDestinationPropertyMap));
424   }
425 }
426
427 void ImageView::OnUpdateVisualProperties(const std::vector<std::pair<Dali::Property::Index, Dali::Property::Map>>& properties)
428 {
429   Toolkit::Visual::Base visual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE);
430   if(visual)
431   {
432     Dali::Toolkit::Control handle(GetOwner());
433
434     for(auto&& data : properties)
435     {
436       if(data.first == Toolkit::ImageView::Property::IMAGE)
437       {
438         DevelControl::DoAction(handle, Toolkit::ImageView::Property::IMAGE, DevelVisual::Action::UPDATE_PROPERTY, data.second);
439         break;
440       }
441     }
442   }
443 }
444
445 void ImageView::OnResourceReady(Toolkit::Control control)
446 {
447   // In case of placeholder, we need to skip this call.
448   // TODO: In case of placeholder, it needs to be modified not to call OnResourceReady()
449   if(control.GetVisualResourceStatus(Toolkit::ImageView::Property::IMAGE) != Toolkit::Visual::ResourceStatus::READY)
450   {
451     return;
452   }
453
454   // Do transition effect if need.
455   if(mTransitionEffect)
456   {
457     // TODO: Consider about placeholder image is loaded failed
458     Toolkit::Visual::Base placeholderVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE);
459     if(!placeholderVisual || control.GetVisualResourceStatus(Toolkit::ImageView::Property::PLACEHOLDER_IMAGE) == Toolkit::Visual::ResourceStatus::READY)
460     {
461       // when placeholder is disabled or ready placeholder and image, we need to transition effect
462       TransitionImageWithEffect();
463     }
464   }
465
466   // Visual ready so update visual attached to this ImageView, following call to RelayoutRequest will use this visual.
467   mVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE);
468
469   // Applying FittingMode again if it is not working well on OnRelayout().
470   if(mNeedLazyFittingMode)
471   {
472     const Vector2& size = Self().GetProperty(Dali::Actor::Property::SIZE).Get<Vector2>();
473     ApplyFittingMode(size);
474     mNeedLazyFittingMode = false;
475   }
476
477   // Signal that a Relayout may be needed
478 }
479
480 void ImageView::SetTransformMapForFittingMode(Vector2 finalSize, Vector2 naturalSize, Vector2 finalOffset, Visual::FittingMode fittingMode, Property::Map& transformMap)
481 {
482   switch(fittingMode)
483   {
484     case Visual::FittingMode::FIT_KEEP_ASPECT_RATIO:
485     {
486       auto availableVisualSize = finalSize;
487
488       // scale to fit the padded area
489       finalSize = naturalSize * std::min((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0),
490                                          (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0));
491
492       // calculate final offset within the padded area
493       finalOffset += (availableVisualSize - finalSize) * .5f;
494
495       // populate the transform map
496       transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
497         .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
498       break;
499     }
500     case Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO:
501     {
502       mImageViewPixelAreaSetByFittingMode = true;
503       auto availableVisualSize            = finalSize;
504       finalSize                           = naturalSize * std::max((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0.0f),
505                                          (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0.0f));
506
507       auto originalOffset = finalOffset;
508       finalOffset += (availableVisualSize - finalSize) * .5f;
509
510       float   x           = abs((availableVisualSize.width - finalSize.width) / finalSize.width) * .5f;
511       float   y           = abs((availableVisualSize.height - finalSize.height) / finalSize.height) * .5f;
512       float   widthRatio  = 1.f - abs((availableVisualSize.width - finalSize.width) / finalSize.width);
513       float   heightRatio = 1.f - abs((availableVisualSize.height - finalSize.height) / finalSize.height);
514       Vector4 pixelArea   = Vector4(x, y, widthRatio, heightRatio);
515       Self().SetProperty(Toolkit::ImageView::Property::PIXEL_AREA, pixelArea);
516
517       // populate the transform map
518       transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, originalOffset)
519         .Add(Toolkit::Visual::Transform::Property::SIZE, availableVisualSize);
520       break;
521     }
522     case Visual::FittingMode::CENTER:
523     {
524       auto availableVisualSize = finalSize;
525       if(availableVisualSize.width > naturalSize.width && availableVisualSize.height > naturalSize.height)
526       {
527         finalSize = naturalSize;
528       }
529       else
530       {
531         finalSize = naturalSize * std::min((!Dali::EqualsZero(naturalSize.width) ? (availableVisualSize.width / naturalSize.width) : 0.0f),
532                                            (!Dali::EqualsZero(naturalSize.height) ? (availableVisualSize.height / naturalSize.height) : 0.0f));
533       }
534
535       finalOffset += (availableVisualSize - finalSize) * .5f;
536
537       // populate the transform map
538       transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
539         .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
540       break;
541     }
542     case Visual::FittingMode::FILL:
543     {
544       transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, finalOffset)
545         .Add(Toolkit::Visual::Transform::Property::SIZE, finalSize);
546       break;
547     }
548     case Visual::FittingMode::FIT_WIDTH:
549     case Visual::FittingMode::FIT_HEIGHT:
550     {
551       // This FittingMode already converted
552       break;
553     }
554   }
555 }
556
557 void ImageView::ApplyFittingMode(const Vector2& size)
558 {
559   Property::Map transformMap = Property::Map();
560
561   Extents padding = Self().GetProperty<Extents>(Toolkit::Control::Property::PADDING);
562
563   bool zeroPadding = (padding == Extents());
564
565   Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>(
566   Self().GetProperty(Dali::Actor::Property::LAYOUT_DIRECTION).Get<int>());
567   if(Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection)
568   {
569     std::swap(padding.start, padding.end);
570   }
571
572   // remove padding from the size to know how much is left for the visual
573   Vector2 finalSize   = size - Vector2(padding.start + padding.end, padding.top + padding.bottom);
574   Vector2 finalOffset = Vector2(padding.start, padding.top);
575
576   Visual::FittingMode fittingMode = Toolkit::GetImplementation(mVisual).GetFittingMode();
577
578   // Reset PIXEL_AREA after using OVER_FIT_KEEP_ASPECT_RATIO
579   if(mImageViewPixelAreaSetByFittingMode)
580   {
581     Self().SetProperty(Toolkit::ImageView::Property::PIXEL_AREA, FULL_TEXTURE_RECT);
582     mImageViewPixelAreaSetByFittingMode = false;
583   }
584
585   if((!zeroPadding) || // If padding is not zero
586      (fittingMode != Visual::FittingMode::FILL))
587   {
588     mImageVisualPaddingSetByTransform = true;
589
590     Vector2 naturalSize;
591     // NaturalSize will not be used for FILL fitting mode, which is default.
592     // Skip GetNaturalSize
593     if(fittingMode != Visual::FittingMode::FILL)
594     {
595       mVisual.GetNaturalSize(naturalSize);
596     }
597
598     // If FittingMode use FIT_WIDTH or FIT_HEIGTH, it need to change proper fittingMode
599     if(fittingMode == Visual::FittingMode::FIT_WIDTH)
600     {
601       fittingMode = (finalSize.height / naturalSize.height) < (finalSize.width / naturalSize.width) ? Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::FIT_KEEP_ASPECT_RATIO;
602     }
603     else if(fittingMode == Visual::FittingMode::FIT_HEIGHT)
604     {
605       fittingMode = (finalSize.height / naturalSize.height) < (finalSize.width / naturalSize.width) ? Visual::FittingMode::FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO;
606     }
607
608     SetTransformMapForFittingMode(finalSize, naturalSize, finalOffset, fittingMode, transformMap);
609
610     // Set extra value for applying transformMap
611     transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY,
612                      Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE))
613       .Add(Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN)
614       .Add(Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN)
615       .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY,
616            Vector2(Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE));
617   }
618   else if(mImageVisualPaddingSetByTransform && zeroPadding) // Reset offset to zero only if padding applied previously
619   {
620     mImageVisualPaddingSetByTransform = false;
621     // Reset the transform map
622     transformMap.Add(Toolkit::Visual::Transform::Property::OFFSET, Vector2::ZERO)
623       .Add(Toolkit::Visual::Transform::Property::OFFSET_POLICY,
624            Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE))
625       .Add(Toolkit::Visual::Transform::Property::SIZE, Vector2::ONE)
626       .Add(Toolkit::Visual::Transform::Property::SIZE_POLICY,
627            Vector2(Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE));
628   }
629
630   mVisual.SetTransformAndSize(transformMap, size);
631 }
632
633 void ImageView::CreatePlaceholderImage()
634 {
635   Property::Map propertyMap;
636   propertyMap.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
637   propertyMap.Insert(Toolkit::ImageVisual::Property::URL, mPlaceholderUrl);
638   //propertyMap.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, Toolkit::ImageVisual::LoadPolicy::IMMEDIATE); // TODO: need to enable this property
639   propertyMap.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, Toolkit::ImageVisual::ReleasePolicy::DESTROYED);
640   propertyMap.Insert(Toolkit::DevelImageVisual::Property::ENABLE_BROKEN_IMAGE, false);
641   mPlaceholderVisual = Toolkit::VisualFactory::Get().CreateVisual(propertyMap);
642   if(mPlaceholderVisual)
643   {
644     mPlaceholderVisual.SetName("placeholder");
645     mPlaceholderVisual.SetDepthIndex(mPlaceholderVisual.GetDepthIndex() + PLACEHOLDER_DEPTH_INDEX);
646   }
647   else
648   {
649     DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE);
650     mPlaceholderVisual.Reset();
651   }
652 }
653
654 void ImageView::ShowPlaceholderImage()
655 {
656   if(mPlaceholderVisual)
657   {
658     DevelControl::RegisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE, mPlaceholderVisual, false);
659     Actor self = Self();
660     Toolkit::GetImplementation(mPlaceholderVisual).SetOnScene(self);
661   }
662 }
663
664 void ImageView::HidePlaceholderImage()
665 {
666   if(mPlaceholderVisual)
667   {
668     DevelControl::UnregisterVisual(*this, Toolkit::ImageView::Property::PLACEHOLDER_IMAGE);
669
670     // Hide placeholder
671     Actor self = Self();
672     Toolkit::GetImplementation(mPlaceholderVisual).SetOffScene(self);
673   }
674 }
675
676 void ImageView::TransitionImageWithEffect()
677 {
678   Toolkit::ImageView handle = Toolkit::ImageView(GetOwner());
679
680   if(handle)
681   {
682     if(!mImageReplaced)
683     {
684       // If the image is not replaced, the transition effect is not required.
685       return;
686     }
687
688     if(mTransitionAnimation)
689     {
690       ClearTransitionAnimation();
691     }
692
693     // Control visual's depth for transition effect
694     if(mPreviousVisual)
695     {
696       mPreviousVisual.SetDepthIndex(mPreviousVisual.GetDepthIndex() + PREVIOUS_VISUAL_DEPTH_INDEX);
697     }
698
699     mTransitionAnimation = Animation::New(TRANSITION_EFFECT_SPEED);
700     mTransitionAnimation.SetEndAction(Animation::EndAction::DISCARD);
701     float destinationAlpha = (mTransitionTargetAlpha > LOW_OPACITY) ? mTransitionTargetAlpha : LOW_OPACITY;
702
703     // Transition current image
704     Toolkit::Visual::Base imageVisual = DevelControl::GetVisual(*this, Toolkit::ImageView::Property::IMAGE);
705     if(imageVisual)
706     {
707       Dali::KeyFrames fadeinKeyFrames = Dali::KeyFrames::New();
708       fadeinKeyFrames.Add(0.0f, LOW_OPACITY);
709       fadeinKeyFrames.Add(1.0f, destinationAlpha);
710       mTransitionAnimation.AnimateBetween(DevelControl::GetVisualProperty(handle, Toolkit::ImageView::Property::IMAGE, Toolkit::Visual::Property::OPACITY), fadeinKeyFrames,  AlphaFunction::EASE_IN_OUT);
711       imageVisual.SetDepthIndex(imageVisual.GetDepthIndex() + CURRENT_VISUAL_DEPTH_INDEX);
712     }
713
714     // Play transition animation
715     mTransitionAnimation.FinishedSignal().Connect(this, &ImageView::OnTransitionAnimationFinishedCallback);
716     mTransitionAnimation.Play();
717   }
718 }
719
720 void ImageView::ClearTransitionAnimation()
721 {
722   // Clear PreviousVisual
723   if(mPreviousVisual)
724   {
725     Actor                    self            = Self();
726     Internal::Control::Impl& controlDataImpl = Internal::Control::Impl::Get(*this);
727     controlDataImpl.EnableReadyTransitionOverriden(mVisual, false);
728     Toolkit::GetImplementation(mPreviousVisual).SetOffScene(self);
729     mPreviousVisual.Reset();
730   }
731
732   if(mTransitionAnimation)
733   {
734     if(mTransitionAnimation.GetState() == Animation::PLAYING)
735     {
736       mTransitionAnimation.Stop();
737     }
738     mTransitionAnimation.FinishedSignal().Disconnect(this, &ImageView::OnTransitionAnimationFinishedCallback);
739     mTransitionAnimation.Clear();
740     mTransitionAnimation.Reset();
741
742     // After transition effect is cleared, we don't need transition effect until image is replaced.
743     mImageReplaced = false;
744   }
745 }
746
747 ///////////////////////////////////////////////////////////
748 //
749 // Properties
750 //
751
752 void ImageView::SetProperty(BaseObject* object, Property::Index index, const Property::Value& value)
753 {
754   Toolkit::ImageView imageView = Toolkit::ImageView::DownCast(Dali::BaseHandle(object));
755
756   if(imageView)
757   {
758     ImageView& impl = GetImpl(imageView);
759     switch(index)
760     {
761       case Toolkit::ImageView::Property::IMAGE:
762       {
763         std::string          imageUrl;
764         const Property::Map* map;
765         if(value.Get(imageUrl))
766         {
767           impl.SetImage(imageUrl, ImageDimensions());
768         }
769         // if its not a string then get a Property::Map from the property if possible.
770         else
771         {
772           map = value.GetMap();
773           if(DALI_LIKELY(map))
774           {
775             // the property map is emtpy map. Unregister visual.
776             if(DALI_UNLIKELY(map->Count() == 0u))
777             {
778               impl.ClearImageVisual();
779             }
780             else
781             {
782               Property::Value* shaderValue = map->Find(Toolkit::Visual::Property::SHADER, CUSTOM_SHADER);
783               // set image only if property map contains image information other than custom shader
784               if(map->Count() > 1u || !shaderValue)
785               {
786                 impl.SetImage(*map);
787               }
788               // the property map contains only the custom shader
789               else if((map->Count() == 1u) && (shaderValue))
790               {
791                 Property::Map* shaderMap = shaderValue->GetMap();
792                 if(shaderMap)
793                 {
794                   impl.mShaderMap = *shaderMap;
795
796                   if(!impl.mUrl.empty())
797                   {
798                     impl.SetImage(impl.mUrl, impl.mImageSize);
799                   }
800                   else if(!impl.mPropertyMap.Empty())
801                   {
802                     impl.SetImage(impl.mPropertyMap);
803                   }
804                 }
805               }
806             }
807           }
808           else
809           {
810             // invalid property value comes. Unregister visual.
811             impl.ClearImageVisual();
812           }
813         }
814         break;
815       }
816
817       case Toolkit::ImageView::Property::PRE_MULTIPLIED_ALPHA:
818       {
819         bool isPre;
820         if(value.Get(isPre))
821         {
822           impl.EnablePreMultipliedAlpha(isPre);
823         }
824         break;
825       }
826
827       case Toolkit::ImageView::Property::PLACEHOLDER_IMAGE:
828       {
829         std::string placeholderUrl;
830         if(value.Get(placeholderUrl))
831         {
832           impl.SetPlaceholderUrl(placeholderUrl);
833         }
834         break;
835       }
836
837       case Toolkit::ImageView::Property::ENABLE_TRANSITION_EFFECT:
838       {
839         bool transitionEffect;
840         if(value.Get(transitionEffect))
841         {
842           impl.EnableTransitionEffect(transitionEffect);
843         }
844         break;
845       }
846     }
847   }
848 }
849
850 Property::Value ImageView::GetProperty(BaseObject* object, Property::Index propertyIndex)
851 {
852   Property::Value value;
853
854   Toolkit::ImageView imageview = Toolkit::ImageView::DownCast(Dali::BaseHandle(object));
855
856   if(imageview)
857   {
858     ImageView& impl = GetImpl(imageview);
859     switch(propertyIndex)
860     {
861       case Toolkit::ImageView::Property::IMAGE:
862       {
863         if(!impl.mUrl.empty())
864         {
865           value = impl.mUrl;
866         }
867         else
868         {
869           Property::Map         map;
870           Toolkit::Visual::Base visual = DevelControl::GetVisual(impl, Toolkit::ImageView::Property::IMAGE);
871           if(visual)
872           {
873             visual.CreatePropertyMap(map);
874           }
875           value = map;
876         }
877         break;
878       }
879
880       case Toolkit::ImageView::Property::PRE_MULTIPLIED_ALPHA:
881       {
882         value = impl.IsPreMultipliedAlphaEnabled();
883         break;
884       }
885
886       case Toolkit::ImageView::Property::PLACEHOLDER_IMAGE:
887       {
888         value = impl.GetPlaceholderUrl();
889         break;
890       }
891
892       case Toolkit::ImageView::Property::ENABLE_TRANSITION_EFFECT:
893       {
894         value = impl.IsTransitionEffectEnabled();
895         break;
896       }
897     }
898   }
899
900   return value;
901 }
902
903 void ImageView::OnTransitionAnimationFinishedCallback(Animation& animation)
904 {
905   // Hide placeholder
906   HidePlaceholderImage();
907   ClearTransitionAnimation();
908 }
909
910 } // namespace Internal
911 } // namespace Toolkit
912 } // namespace Dali