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