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