[Tizen] Support asan build option
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-base-impl.cpp
1 /*
2  * Copyright (c) 2024 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 "visual-base-impl.h"
20
21 // EXTERNAL HEADER
22 #include <dali-toolkit/public-api/dali-toolkit-common.h>
23 #include <dali/devel-api/rendering/renderer-devel.h>
24 #include <dali/devel-api/scripting/enum-helper.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/public-api/rendering/decorated-visual-renderer.h>
27 #include <dali/public-api/rendering/visual-renderer.h>
28
29 //INTERNAL HEARDER
30 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
31 #include <dali-toolkit/devel-api/visuals/color-visual-properties-devel.h>
32 #include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
33 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
34 #include <dali-toolkit/internal/helpers/property-helper.h>
35 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
36 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
37 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
38 #include <dali-toolkit/public-api/visuals/primitive-visual-properties.h>
39 #include <dali-toolkit/public-api/visuals/visual-properties.h>
40
41 namespace
42 {
43 #if defined(DEBUG_ENABLED)
44 Debug::Filter* gVisualBaseLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VISUAL_BASE");
45 #endif
46
47 // visual string constants contains OFFSET_SIZE_MODE instead
48 const char* const OFFSET_POLICY("offsetPolicy");
49 const char* const SIZE_POLICY("sizePolicy");
50
51 } // namespace
52
53 namespace Dali
54 {
55 namespace Toolkit
56 {
57 namespace Internal
58 {
59 namespace
60 {
61 DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_FITTING_MODE)
62   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_KEEP_ASPECT_RATIO)
63   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FILL)
64   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, OVER_FIT_KEEP_ASPECT_RATIO)
65   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, CENTER)
66   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_HEIGHT)
67   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_WIDTH)
68 DALI_ENUM_TO_STRING_TABLE_END(VISUAL_FITTING_MODE)
69
70 /**
71  * @brief Check whether this visual type can use corner radius feature or not.
72  * @param type VisualType that want to checkup
73  * @return true if type can use corner radius feature.
74  */
75 static bool IsTypeAvailableForCornerRadius(Toolkit::Visual::Type type)
76 {
77   switch(static_cast<Toolkit::DevelVisual::Type>(type))
78   {
79     case Toolkit::Visual::Type::COLOR:
80     case Toolkit::Visual::Type::GRADIENT:
81     case Toolkit::Visual::Type::IMAGE:
82     case Toolkit::Visual::Type::SVG:
83     case Toolkit::Visual::Type::ANIMATED_IMAGE:
84     case Toolkit::DevelVisual::Type::ANIMATED_VECTOR_IMAGE:
85     {
86       return true;
87     }
88     default:
89     {
90       return false;
91     }
92   }
93 }
94
95 /**
96  * @brief Check whether this visual type can use borderline feature or not.
97  * @param type VisualType that want to checkup
98  * @return true if type can use borderline feature.
99  */
100 static bool IsTypeAvailableForBorderline(Toolkit::Visual::Type type)
101 {
102   switch(static_cast<Toolkit::DevelVisual::Type>(type))
103   {
104     case Toolkit::Visual::Type::COLOR:
105     case Toolkit::Visual::Type::GRADIENT:
106     case Toolkit::Visual::Type::IMAGE:
107     case Toolkit::Visual::Type::SVG:
108     case Toolkit::Visual::Type::ANIMATED_IMAGE:
109     case Toolkit::DevelVisual::Type::ANIMATED_VECTOR_IMAGE:
110     {
111       return true;
112     }
113     default:
114     {
115       return false;
116     }
117   }
118 }
119
120 struct StringProperty
121 {
122   const char* const name;
123   Property::Index   index;
124 };
125 StringProperty PROPERTY_NAME_INDEX_TABLE[] =
126   {
127     {CUSTOM_SHADER, Toolkit::Visual::Property::SHADER},
128     {TRANSFORM, Toolkit::Visual::Property::TRANSFORM},
129     {PREMULTIPLIED_ALPHA, Toolkit::Visual::Property::PREMULTIPLIED_ALPHA},
130     {MIX_COLOR, Toolkit::Visual::Property::MIX_COLOR},
131     {OPACITY, Toolkit::Visual::Property::OPACITY},
132     {VISUAL_FITTING_MODE, Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE},
133     {BORDERLINE_WIDTH, Toolkit::DevelVisual::Property::BORDERLINE_WIDTH},
134     {BORDERLINE_COLOR, Toolkit::DevelVisual::Property::BORDERLINE_COLOR},
135     {BORDERLINE_OFFSET, Toolkit::DevelVisual::Property::BORDERLINE_OFFSET},
136     {CORNER_RADIUS, Toolkit::DevelVisual::Property::CORNER_RADIUS},
137     {CORNER_RADIUS_POLICY, Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY},
138 };
139 const uint16_t PROPERTY_NAME_INDEX_TABLE_COUNT = sizeof(PROPERTY_NAME_INDEX_TABLE) / sizeof(PROPERTY_NAME_INDEX_TABLE[0]);
140
141 Property::Index GetVisualPropertyIndex(Property::Key key)
142 {
143   if(key.type == Property::Key::STRING)
144   {
145     for(auto tableId = 0u; tableId < PROPERTY_NAME_INDEX_TABLE_COUNT; ++tableId)
146     {
147       if(key == PROPERTY_NAME_INDEX_TABLE[tableId].name)
148       {
149         return PROPERTY_NAME_INDEX_TABLE[tableId].index;
150         break;
151       }
152     }
153   }
154   return key.indexKey;
155 }
156
157 } // namespace
158
159 Visual::Base::Base(VisualFactoryCache& factoryCache, FittingMode fittingMode, Toolkit::Visual::Type type)
160 : mImpl(new Impl(fittingMode, type)),
161   mFactoryCache(factoryCache)
162 {
163 }
164
165 Visual::Base::~Base()
166 {
167   delete mImpl;
168 }
169
170 void Visual::Base::Initialize()
171 {
172   // The Renderer should be created inside derived class here.
173   OnInitialize();
174
175   if(mImpl->mRenderer)
176   {
177     RegisterMixColor();
178     RegisterDecoration();
179
180     if(IsBorderlineRequired())
181     {
182       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
183     }
184     else if(IsRoundedCornerRequired())
185     {
186       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
187     }
188   }
189 }
190
191 void Visual::Base::SetCustomShader(const Property::Map& shaderMap)
192 {
193   if(mImpl->mCustomShader)
194   {
195     mImpl->mCustomShader->SetPropertyMap(shaderMap);
196   }
197   else
198   {
199     mImpl->mCustomShader = new Impl::CustomShader(shaderMap);
200   }
201
202   // Let derived class know
203   UpdateShader();
204 }
205
206 void Visual::Base::SetProperties(const Property::Map& propertyMap)
207 {
208   bool needUpdateShader = false;
209   for(size_t i = 0; i < propertyMap.Count(); ++i)
210   {
211     const KeyValuePair&    pair  = propertyMap.GetKeyValue(i);
212     const Property::Key&   key   = pair.first;
213     const Property::Value& value = pair.second;
214
215     switch(GetVisualPropertyIndex(key))
216     {
217       case Toolkit::Visual::Property::SHADER:
218       {
219         Property::Map shaderMap;
220         if(value.Get(shaderMap))
221         {
222           SetCustomShader(shaderMap);
223         }
224         break;
225       }
226
227       case Toolkit::Visual::Property::TRANSFORM:
228       {
229         Property::Map map;
230         if(value.Get(map))
231         {
232           mImpl->mTransform.SetPropertyMap(map);
233         }
234         break;
235       }
236
237       case Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
238       {
239         bool premultipliedAlpha = false;
240         if(value.Get(premultipliedAlpha))
241         {
242           EnablePreMultipliedAlpha(premultipliedAlpha);
243         }
244         break;
245       }
246
247       case Toolkit::Visual::Property::MIX_COLOR:
248       {
249         Vector4 mixColor;
250         if(value.Get(mixColor))
251         {
252           if(value.GetType() == Property::VECTOR4)
253           {
254             SetMixColor(mixColor);
255           }
256           else
257           {
258             Vector3 mixColor3(mixColor);
259             SetMixColor(mixColor3);
260           }
261         }
262         break;
263       }
264       case Toolkit::Visual::Property::OPACITY:
265       {
266         float opacity;
267         if(value.Get(opacity))
268         {
269           mImpl->mMixColor.a = opacity;
270           SetMixColor(mImpl->mMixColor);
271         }
272         break;
273       }
274       case Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE:
275       {
276         Scripting::GetEnumerationProperty<Visual::FittingMode>(
277           value, VISUAL_FITTING_MODE_TABLE, VISUAL_FITTING_MODE_TABLE_COUNT, mImpl->mFittingMode);
278         break;
279       }
280       case Toolkit::DevelVisual::Property::BORDERLINE_WIDTH:
281       {
282         float width;
283         if(value.Get(width) && (mImpl->mDecorationData != nullptr || !Dali::EqualsZero(width)))
284         {
285           mImpl->SetBorderlineWidth(width);
286         }
287
288         if(DALI_UNLIKELY(mImpl->mRenderer && IsTypeAvailableForBorderline(mImpl->mType)))
289         {
290           // Unusual case. SetProperty called after OnInitialize().
291           // Assume that DoAction call UPDATE_PROPERTY.
292           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterBorderlineUniform();
293           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH, mImpl->GetBorderlineWidth());
294
295           // Check whether we must update shader.
296           if(!mImpl->mAlwaysUsingBorderline && IsBorderlineRequired())
297           {
298             // Required to change shader mean, we didn't setup BORDERLINE_COLOR and BORDERLINE_OFFSET into mRenderer before. Set property now.
299             mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->GetBorderlineColor());
300             mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->GetBorderlineOffset());
301
302             // Make Blend mode ON_WITHOUT_CULL for transparent mix color.
303             mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
304
305             // Change the shader must not be occured many times. we always have to use borderline feature.
306             mImpl->mAlwaysUsingBorderline = true;
307
308             // Change shader
309             if(!mImpl->mCustomShader)
310             {
311               needUpdateShader = true;
312             }
313           }
314         }
315         break;
316       }
317       case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
318       {
319         Vector4 color;
320         if(value.Get(color) && (mImpl->mDecorationData != nullptr || color != Vector4::ZERO))
321         {
322           mImpl->SetBorderlineColor(color);
323         }
324
325         if(DALI_UNLIKELY(mImpl->mRenderer && IsTypeAvailableForBorderline(mImpl->mType)))
326         {
327           // Unusual case. SetProperty called after OnInitialize().
328           // Assume that DoAction call UPDATE_PROPERTY.
329           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->GetBorderlineColor());
330         }
331         break;
332       }
333       case Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
334       {
335         float offset;
336         if(value.Get(offset) && (mImpl->mDecorationData != nullptr || !Dali::EqualsZero(offset)))
337         {
338           mImpl->SetBorderlineOffset(offset);
339         }
340
341         if(DALI_UNLIKELY(mImpl->mRenderer && IsTypeAvailableForBorderline(mImpl->mType)))
342         {
343           // Unusual case. SetProperty called after OnInitialize().
344           // Assume that DoAction call UPDATE_PROPERTY.
345           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->GetBorderlineOffset());
346         }
347         break;
348       }
349       case Toolkit::DevelVisual::Property::CORNER_RADIUS:
350       {
351         if(value.GetType() == Property::VECTOR4)
352         {
353           // If CORNER_RADIUS Property is Vector4,
354           // Each values mean the radius of
355           // (top-left, top-right, bottom-right, bottom-left)
356           Vector4 radius;
357           if(value.Get(radius) && (mImpl->mDecorationData != nullptr || radius != Vector4::ZERO))
358           {
359             mImpl->SetCornerRadius(radius);
360           }
361         }
362         else
363         {
364           // If CORNER_RADIUS Property is float,
365           // Every corner radius have same value
366           float radius;
367           if(value.Get(radius) && (mImpl->mDecorationData != nullptr || !Dali::EqualsZero(radius)))
368           {
369             mImpl->SetCornerRadius(Vector4(radius, radius, radius, radius));
370           }
371         }
372
373         if(DALI_UNLIKELY(mImpl->mRenderer && IsTypeAvailableForCornerRadius(mImpl->mType)))
374         {
375           // Unusual case. SetProperty called after OnInitialize().
376           // Assume that DoAction call UPDATE_PROPERTY.
377           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterCornerRadiusUniform();
378           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS, mImpl->GetCornerRadius());
379
380           // Check whether we must update shader.
381           if(!mImpl->mAlwaysUsingCornerRadius && IsRoundedCornerRequired())
382           {
383             // Required to change shader mean, we didn't setup CORNER_RADIUS_POLICY into mRenderer before. Set property now.
384             mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, static_cast<float>(mImpl->GetCornerRadiusPolicy()));
385
386             // Change the shader must not be occured many times. we always have to use corner radius feature.
387             mImpl->mAlwaysUsingCornerRadius = true;
388
389             if(!IsBorderlineRequired())
390             {
391               // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
392               mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
393             }
394
395             // Change shader
396             if(!mImpl->mCustomShader)
397             {
398               needUpdateShader = true;
399             }
400           }
401         }
402
403         break;
404       }
405       case Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY:
406       {
407         int policy;
408         if(value.Get(policy))
409         {
410           switch(policy)
411           {
412             case Toolkit::Visual::Transform::Policy::RELATIVE:
413             case Toolkit::Visual::Transform::Policy::ABSOLUTE:
414             {
415               mImpl->SetCornerRadiusPolicy(policy);
416               if(DALI_UNLIKELY(mImpl->mRenderer))
417               {
418                 // Unusual case. SetProperty called after OnInitialize().
419                 // Assume that DoAction call UPDATE_PROPERTY.
420                 mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, static_cast<float>(mImpl->GetCornerRadiusPolicy()));
421               }
422               break;
423             }
424             default:
425             {
426               DALI_LOG_ERROR("Unsupported policy: %d\n", policy);
427               break;
428             }
429           }
430         }
431         break;
432       }
433     }
434   }
435
436   DoSetProperties(propertyMap);
437
438   if(DALI_UNLIKELY(needUpdateShader))
439   {
440     UpdateShader();
441   }
442 }
443
444 void Visual::Base::SetTransformAndSize(const Property::Map& transform, Size controlSize)
445 {
446   mImpl->mControlSize = controlSize;
447   mImpl->mTransform.UpdatePropertyMap(transform);
448
449 #if defined(DEBUG_ENABLED)
450   std::ostringstream oss;
451   oss << transform;
452   DALI_LOG_INFO(gVisualBaseLogFilter, Debug::General, "Visual::Base::SetTransformAndSize(%s) - [\e[1;32mtransform: %s  controlSize: (%3.1f, %3.1f)]\e[0m\n", GetName().c_str(), oss.str().c_str(), controlSize.x, controlSize.y);
453 #endif
454
455   OnSetTransform();
456 }
457
458 void Visual::Base::SetName(const std::string& name)
459 {
460   mImpl->mName = name;
461 }
462
463 const std::string& Visual::Base::GetName() const
464 {
465   return mImpl->mName;
466 }
467
468 float Visual::Base::GetHeightForWidth(float width)
469 {
470   float   aspectCorrectedHeight = 0.f;
471   Vector2 naturalSize;
472   GetNaturalSize(naturalSize);
473   if(naturalSize.width > 0.0f)
474   {
475     aspectCorrectedHeight = naturalSize.height * width / naturalSize.width;
476   }
477   return aspectCorrectedHeight;
478 }
479
480 float Visual::Base::GetWidthForHeight(float height)
481 {
482   float   aspectCorrectedWidth = 0.f;
483   Vector2 naturalSize;
484   GetNaturalSize(naturalSize);
485   if(naturalSize.height > 0.0f)
486   {
487     aspectCorrectedWidth = naturalSize.width * height / naturalSize.height;
488   }
489   return aspectCorrectedWidth;
490 }
491
492 void Visual::Base::GetNaturalSize(Vector2& naturalSize)
493 {
494   naturalSize = Vector2::ZERO;
495 }
496
497 void Visual::Base::DoAction(const Property::Index actionId, const Property::Value attributes)
498 {
499   OnDoAction(actionId, attributes);
500
501   // Check if action is valid for this visual type and perform action if possible
502   switch(actionId)
503   {
504     case DevelVisual::Action::UPDATE_PROPERTY:
505     {
506       const Property::Map* map = attributes.GetMap();
507       if(map)
508       {
509         SetProperties(*map);
510       }
511       break;
512     }
513   }
514 }
515
516 void Visual::Base::DoActionExtension(const Dali::Property::Index actionId, const Dali::Any attributes)
517 {
518   OnDoActionExtension(actionId, attributes);
519 }
520
521 void Visual::Base::SetDepthIndex(int index)
522 {
523   // Clamp input value as valid range.
524   Dali::ClampInPlace(index, static_cast<int>(Toolkit::DepthIndex::MINIMUM_DEPTH_INDEX), static_cast<int>(Toolkit::DepthIndex::MAXIMUM_DEPTH_INDEX));
525
526   if(mImpl->mDepthIndex != index)
527   {
528     mImpl->mDepthIndex = index;
529     if(mImpl->mRenderer)
530     {
531       mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
532     }
533   }
534 }
535
536 int Visual::Base::GetDepthIndex() const
537 {
538   return mImpl->mDepthIndex;
539 }
540
541 void Visual::Base::SetOnScene(Actor& actor)
542 {
543   if(!IsOnScene())
544   {
545     // To display the actor correctly, renderer should not be added to actor until all required resources are ready.
546     // Thus the calling of actor.AddRenderer() should happen inside derived class as base class does not know the exact timing.
547     DoSetOnScene(actor);
548
549     if(mImpl->mRenderer)
550     {
551       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
552       if(mImpl->mDepthIndex == static_cast<int>(DepthIndex::AUTO_INDEX))
553       {
554         mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, static_cast<int>(DepthIndex::CONTENT));
555       }
556       else
557       {
558         mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
559       }
560     }
561
562     mImpl->mFlags |= Impl::IS_ON_SCENE;
563   }
564 }
565
566 void Visual::Base::SetOffScene(Actor& actor)
567 {
568   if(IsOnScene())
569   {
570     DoSetOffScene(actor);
571     mImpl->mFlags &= ~Impl::IS_ON_SCENE;
572   }
573 }
574
575 void Visual::Base::CreatePropertyMap(Property::Map& map) const
576 {
577   if(mImpl->mRenderer)
578   {
579     // Update values from Renderer
580     mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(VisualRenderer::Property::VISUAL_MIX_COLOR);
581     mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
582
583     mImpl->mTransform.mOffset = mImpl->mRenderer.GetProperty<Vector2>(VisualRenderer::Property::TRANSFORM_OFFSET);
584     mImpl->mTransform.mSize   = mImpl->mRenderer.GetProperty<Vector2>(VisualRenderer::Property::TRANSFORM_SIZE);
585
586     if(IsRoundedCornerRequired())
587     {
588       mImpl->SetCornerRadius(mImpl->mRenderer.GetProperty<Vector4>(DecoratedVisualRenderer::Property::CORNER_RADIUS));
589     }
590     if(IsBorderlineRequired())
591     {
592       mImpl->SetBorderlineWidth(mImpl->mRenderer.GetProperty<float>(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH));
593       mImpl->SetBorderlineColor(mImpl->mRenderer.GetProperty<Vector4>(DecoratedVisualRenderer::Property::BORDERLINE_COLOR));
594       mImpl->SetBorderlineOffset(mImpl->mRenderer.GetProperty<float>(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET));
595     }
596   }
597
598   DoCreatePropertyMap(map);
599
600   if(mImpl->mCustomShader)
601   {
602     mImpl->mCustomShader->CreatePropertyMap(map);
603   }
604
605   Property::Map transform;
606   mImpl->mTransform.GetPropertyMap(transform);
607   map.Insert(Toolkit::Visual::Property::TRANSFORM, transform);
608
609   bool premultipliedAlpha(IsPreMultipliedAlphaEnabled());
610   map.Insert(Toolkit::Visual::Property::PREMULTIPLIED_ALPHA, premultipliedAlpha);
611
612   // Note, Color and Primitive will also insert their own mix color into the map
613   // which is ok, because they have a different key value range, but uses same cached value anyway.
614   map.Insert(Toolkit::Visual::Property::MIX_COLOR, mImpl->mMixColor); // vec4
615   map.Insert(Toolkit::Visual::Property::OPACITY, mImpl->mMixColor.a);
616
617   auto fittingModeString = Scripting::GetLinearEnumerationName<FittingMode>(
618     mImpl->mFittingMode, VISUAL_FITTING_MODE_TABLE, VISUAL_FITTING_MODE_TABLE_COUNT);
619   map.Insert(Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE, fittingModeString);
620
621   if(IsTypeAvailableForBorderline(mImpl->mType))
622   {
623     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_WIDTH, mImpl->GetBorderlineWidth());
624     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_COLOR, mImpl->GetBorderlineColor());
625     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, mImpl->GetBorderlineOffset());
626   }
627
628   if(IsTypeAvailableForCornerRadius(mImpl->mType))
629   {
630     map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS, mImpl->GetCornerRadius());
631     map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY, mImpl->GetCornerRadiusPolicy());
632   }
633 }
634
635 void Visual::Base::CreateInstancePropertyMap(Property::Map& map) const
636 {
637   DoCreateInstancePropertyMap(map);
638
639   if(mImpl->mCustomShader)
640   {
641     mImpl->mCustomShader->CreatePropertyMap(map);
642   }
643 }
644
645 void Visual::Base::EnablePreMultipliedAlpha(bool preMultiplied)
646 {
647   if(preMultiplied)
648   {
649     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
650   }
651   else
652   {
653     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
654   }
655
656   if(mImpl->mRenderer)
657   {
658     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultiplied);
659     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultiplied);
660   }
661 }
662
663 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
664 {
665   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
666 }
667
668 void Visual::Base::DoSetOffScene(Actor& actor)
669 {
670   actor.RemoveRenderer(mImpl->mRenderer);
671 }
672
673 bool Visual::Base::IsOnScene() const
674 {
675   return mImpl->mFlags & Impl::IS_ON_SCENE;
676 }
677
678 bool Visual::Base::IsRoundedCornerRequired() const
679 {
680   // If VisualType doesn't support rounded corner, always return false.
681   if(IsTypeAvailableForCornerRadius(mImpl->mType))
682   {
683     if(mImpl->mRenderer)
684     {
685       // Update values from Renderer
686       Property::Value value = mImpl->mRenderer.GetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS);
687
688       Vector4 retValue = Vector4::ZERO;
689       if(value.Get(retValue))
690       {
691         if(mImpl->mDecorationData != nullptr || retValue != Vector4::ZERO)
692         {
693           mImpl->SetCornerRadius(retValue);
694         }
695       }
696     }
697     return mImpl->mAlwaysUsingCornerRadius || !(mImpl->GetCornerRadius() == Vector4::ZERO);
698   }
699   return false;
700 }
701
702 bool Visual::Base::IsBorderlineRequired() const
703 {
704   // If VisualType doesn't support borderline, always return false.
705   if(IsTypeAvailableForBorderline(mImpl->mType))
706   {
707     if(mImpl->mRenderer)
708     {
709       // Update values from Renderer
710       Property::Value value = mImpl->mRenderer.GetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH);
711
712       float retValue = 0.0f;
713       if(value.Get(retValue))
714       {
715         if(mImpl->mDecorationData != nullptr || !Dali::EqualsZero(retValue))
716         {
717           mImpl->SetBorderlineWidth(retValue);
718         }
719       }
720     }
721     return mImpl->mAlwaysUsingBorderline || !EqualsZero(mImpl->GetBorderlineWidth());
722   }
723   return false;
724 }
725
726 void Visual::Base::OnDoAction(const Property::Index actionId, const Property::Value& attributes)
727 {
728   // May be overriden by derived class
729 }
730
731 void Visual::Base::OnDoActionExtension(const Property::Index actionId, const Dali::Any attributes)
732 {
733   // May be overriden by derived class
734 }
735
736 void Visual::Base::RegisterMixColor()
737 {
738   if(mImpl->mRenderer)
739   {
740     // All visual renderers now use same mix color / opacity properties.
741     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(mImpl->mMixColor));
742     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, mImpl->mMixColor.a);
743
744     float preMultipliedAlpha = 0.0f;
745     if(IsPreMultipliedAlphaEnabled())
746     {
747       preMultipliedAlpha = 1.0f;
748     }
749     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultipliedAlpha);
750   }
751 }
752
753 void Visual::Base::RegisterDecoration()
754 {
755   if(mImpl->mRenderer)
756   {
757     if(IsTypeAvailableForCornerRadius(mImpl->mType))
758     {
759       if(mImpl->mAlwaysUsingCornerRadius || !(mImpl->GetCornerRadius() == Vector4::ZERO))
760       {
761         DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterCornerRadiusUniform();
762         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS, mImpl->GetCornerRadius());
763         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, static_cast<float>(mImpl->GetCornerRadiusPolicy()));
764       }
765     }
766     if(IsTypeAvailableForBorderline(mImpl->mType))
767     {
768       if(mImpl->mAlwaysUsingBorderline || !EqualsZero(mImpl->GetBorderlineWidth()))
769       {
770         DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterBorderlineUniform();
771         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH, mImpl->GetBorderlineWidth());
772         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->GetBorderlineColor());
773         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->GetBorderlineOffset());
774       }
775     }
776   }
777 }
778
779 void Visual::Base::SetMixColor(const Vector4& color)
780 {
781   mImpl->mMixColor = color;
782
783   if(mImpl->mRenderer)
784   {
785     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(color));
786     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, color.a);
787   }
788 }
789
790 void Visual::Base::SetMixColor(const Vector3& color)
791 {
792   mImpl->mMixColor.r = color.r;
793   mImpl->mMixColor.g = color.g;
794   mImpl->mMixColor.b = color.b;
795
796   if(mImpl->mRenderer)
797   {
798     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, color);
799   }
800 }
801
802 void Visual::Base::AddEventObserver(Visual::EventObserver& observer)
803 {
804   mImpl->mEventObserver = &observer;
805 }
806
807 void Visual::Base::RemoveEventObserver(Visual::EventObserver& observer)
808 {
809   mImpl->mEventObserver = NULL;
810 }
811
812 void Visual::Base::ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus)
813 {
814   if(mImpl->mResourceStatus != resourceStatus)
815   {
816     mImpl->mResourceStatus = resourceStatus;
817
818     if(mImpl->mEventObserver)
819     {
820       // observer is currently a control impl
821       mImpl->mEventObserver->ResourceReady(*this);
822     }
823   }
824 }
825
826 bool Visual::Base::IsResourceReady() const
827 {
828   return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
829           mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
830 }
831
832 bool Visual::Base::IsSynchronousLoadingRequired() const
833 {
834   return (mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING);
835 }
836
837 Toolkit::Visual::Type Visual::Base::GetType() const
838 {
839   return mImpl->mType;
840 }
841
842 Toolkit::Visual::ResourceStatus Visual::Base::GetResourceStatus() const
843 {
844   return mImpl->mResourceStatus;
845 }
846
847 Visual::FittingMode Visual::Base::GetFittingMode() const
848 {
849   return mImpl->mFittingMode;
850 }
851
852 Visual::Base& Visual::Base::GetVisualObject()
853 {
854   return *this;
855 }
856
857 Renderer Visual::Base::GetRenderer()
858 {
859   return mImpl->mRenderer;
860 }
861
862 Property::Index Visual::Base::GetIntKey(Property::Key key)
863 {
864   if(key.type == Property::Key::INDEX)
865   {
866     return key.indexKey;
867   }
868
869   if(key.stringKey == ANCHOR_POINT)
870   {
871     return Toolkit::Visual::Transform::Property::ANCHOR_POINT;
872   }
873   else if(key.stringKey == EXTRA_SIZE)
874   {
875     return Toolkit::DevelVisual::Transform::Property::EXTRA_SIZE;
876   }
877   else if(key.stringKey == MIX_COLOR)
878   {
879     return Toolkit::Visual::Property::MIX_COLOR;
880   }
881   else if(key.stringKey == OPACITY)
882   {
883     return Toolkit::Visual::Property::OPACITY;
884   }
885   else if(key.stringKey == OFFSET)
886   {
887     return Toolkit::Visual::Transform::Property::OFFSET;
888   }
889   else if(key.stringKey == OFFSET_POLICY)
890   {
891     return Toolkit::Visual::Transform::Property::OFFSET_POLICY;
892   }
893   else if(key.stringKey == ORIGIN)
894   {
895     return Toolkit::Visual::Transform::Property::ORIGIN;
896   }
897   else if(key.stringKey == PREMULTIPLIED_ALPHA)
898   {
899     return Toolkit::Visual::Property::PREMULTIPLIED_ALPHA;
900   }
901   else if(key.stringKey == CUSTOM_SHADER)
902   {
903     return Toolkit::Visual::Property::SHADER;
904   }
905   else if(key.stringKey == SIZE)
906   {
907     return Toolkit::Visual::Transform::Property::SIZE;
908   }
909   else if(key.stringKey == SIZE_POLICY)
910   {
911     return Toolkit::Visual::Transform::Property::SIZE_POLICY;
912   }
913   else if(key.stringKey == TRANSFORM)
914   {
915     return Toolkit::Visual::Property::TRANSFORM;
916   }
917   else if(key.stringKey == VISUAL_FITTING_MODE)
918   {
919     return Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE;
920   }
921   else if(key.stringKey == CORNER_RADIUS)
922   {
923     return Toolkit::DevelVisual::Property::CORNER_RADIUS;
924   }
925   else if(key.stringKey == CORNER_RADIUS_POLICY)
926   {
927     return Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY;
928   }
929   else if(key.stringKey == BORDERLINE_WIDTH)
930   {
931     return Toolkit::DevelVisual::Property::BORDERLINE_WIDTH;
932   }
933   else if(key.stringKey == BORDERLINE_COLOR)
934   {
935     return Toolkit::DevelVisual::Property::BORDERLINE_COLOR;
936   }
937   else if(key.stringKey == BORDERLINE_OFFSET)
938   {
939     return Toolkit::DevelVisual::Property::BORDERLINE_OFFSET;
940   }
941
942   return Property::INVALID_INDEX;
943 }
944
945 Property::Index Visual::Base::GetPropertyIndex(Property::Key key)
946 {
947   switch(GetIntKey(key))
948   {
949     case Dali::Toolkit::Visual::Transform::Property::OFFSET:
950     {
951       return VisualRenderer::Property::TRANSFORM_OFFSET;
952     }
953     case Dali::Toolkit::Visual::Transform::Property::SIZE:
954     {
955       return VisualRenderer::Property::TRANSFORM_SIZE;
956     }
957     case Dali::Toolkit::Visual::Transform::Property::ORIGIN:
958     {
959       return VisualRenderer::Property::TRANSFORM_ORIGIN;
960     }
961     case Dali::Toolkit::Visual::Transform::Property::ANCHOR_POINT:
962     {
963       return VisualRenderer::Property::TRANSFORM_ANCHOR_POINT;
964     }
965     case Dali::Toolkit::Visual::Property::MIX_COLOR:
966     {
967       return VisualRenderer::Property::VISUAL_MIX_COLOR;
968     }
969     case Dali::Toolkit::Visual::Property::OPACITY:
970     {
971       return DevelRenderer::Property::OPACITY;
972     }
973     case Dali::Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
974     {
975       return VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA;
976     }
977     case Dali::Toolkit::DevelVisual::Property::CORNER_RADIUS:
978     {
979       return DecoratedVisualRenderer::Property::CORNER_RADIUS;
980     }
981     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_WIDTH:
982     {
983       return DecoratedVisualRenderer::Property::BORDERLINE_WIDTH;
984     }
985     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
986     {
987       return DecoratedVisualRenderer::Property::BORDERLINE_COLOR;
988     }
989     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
990     {
991       return DecoratedVisualRenderer::Property::BORDERLINE_OFFSET;
992     }
993   }
994
995   Property::Index index = mImpl->mRenderer.GetPropertyIndex(key);
996
997   if(index == Property::INVALID_INDEX)
998   {
999     // Is it a shader property?
1000     Shader shader = mImpl->mRenderer.GetShader();
1001     index         = shader.GetPropertyIndex(key);
1002     if(index != Property::INVALID_INDEX)
1003     {
1004       // Yes - we should register it in the Renderer so it can be set / animated
1005       // independently, as shaders are shared across multiple renderers.
1006       std::string     keyName;
1007       Property::Index keyIndex(Property::INVALID_KEY);
1008       if(key.type == Property::Key::INDEX)
1009       {
1010         keyName  = shader.GetPropertyName(index);
1011         keyIndex = key.indexKey;
1012       }
1013       else
1014       {
1015         keyName = key.stringKey;
1016         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
1017       }
1018       Property::Value value = shader.GetProperty(index);
1019
1020       // We already know that mRenderer didn't have property. So we can assume that it is unique.
1021       index = mImpl->mRenderer.RegisterUniqueProperty(keyIndex, keyName, value);
1022     }
1023   }
1024   return index;
1025 }
1026
1027 void Visual::Base::SetupTransition(
1028   Dali::Animation&                    transition,
1029   Internal::TransitionData::Animator& animator,
1030   Property::Index                     index,
1031   Property::Value&                    initialValue,
1032   Property::Value&                    targetValue)
1033 {
1034   if(index != Property::INVALID_INDEX)
1035   {
1036     if(mImpl->mRenderer)
1037     {
1038       if(animator.animate == false)
1039       {
1040         mImpl->mRenderer.SetProperty(index, targetValue);
1041       }
1042       else
1043       {
1044         if(animator.initialValue.GetType() != Property::NONE)
1045         {
1046           mImpl->mRenderer.SetProperty(index, initialValue);
1047         }
1048
1049         if(!transition)
1050         {
1051           transition = Dali::Animation::New(0.1f);
1052         }
1053
1054         transition.AnimateTo(Property(mImpl->mRenderer, index),
1055                              targetValue,
1056                              animator.alphaFunction,
1057                              TimePeriod(animator.timePeriodDelay,
1058                                         animator.timePeriodDuration));
1059       }
1060     }
1061   }
1062 }
1063
1064 void Visual::Base::AnimateProperty(
1065   Dali::Animation&                    transition,
1066   Internal::TransitionData::Animator& animator)
1067 {
1068 #if defined(DEBUG_ENABLED)
1069   if(gVisualBaseLogFilter->IsEnabledFor(Debug::General))
1070   {
1071     std::ostringstream oss;
1072     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
1073     DALI_LOG_INFO(gVisualBaseLogFilter, Debug::General, oss.str().c_str());
1074   }
1075 #endif
1076
1077   if(animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
1078      animator.propertyKey == MIX_COLOR ||
1079      (mImpl->mType == Toolkit::Visual::COLOR &&
1080       animator.propertyKey == ColorVisual::Property::MIX_COLOR) ||
1081      (mImpl->mType == Toolkit::Visual::PRIMITIVE &&
1082       animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR))
1083   {
1084     AnimateMixColorProperty(transition, animator);
1085   }
1086   else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
1087           animator.propertyKey == OPACITY)
1088   {
1089     AnimateOpacityProperty(transition, animator);
1090   }
1091   else if(mImpl->mRenderer)
1092   {
1093     AnimateRendererProperty(transition, animator);
1094   }
1095 }
1096
1097 void Visual::Base::AnimateOpacityProperty(
1098   Dali::Animation&                    transition,
1099   Internal::TransitionData::Animator& animator)
1100 {
1101   float targetOpacity;
1102   if(animator.targetValue.Get(targetOpacity))
1103   {
1104     mImpl->mMixColor.a = targetOpacity;
1105   }
1106
1107   SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, animator.initialValue, animator.targetValue);
1108 }
1109
1110 void Visual::Base::AnimateRendererProperty(
1111   Dali::Animation&                    transition,
1112   Internal::TransitionData::Animator& animator)
1113 {
1114   // Get actual renderer index (will convert transform keys into visualproperty indices)
1115   Property::Index index = GetPropertyIndex(animator.propertyKey);
1116
1117   if(index != Property::INVALID_INDEX)
1118   {
1119     if(animator.targetValue.GetType() != Property::NONE)
1120     {
1121       // Try writing target value into transform property map
1122       // if it's not a valid key, then it won't alter mTransform
1123       Property::Map map;
1124       if(animator.propertyKey.type == Property::Key::INDEX)
1125       {
1126         map.Add(animator.propertyKey.indexKey, animator.targetValue);
1127       }
1128       else
1129       {
1130         map.Add(animator.propertyKey.stringKey, animator.targetValue);
1131       }
1132
1133       mImpl->mTransform.UpdatePropertyMap(map);
1134     }
1135     SetupTransition(transition, animator, index, animator.initialValue, animator.targetValue);
1136   }
1137 }
1138
1139 void Visual::Base::AnimateMixColorProperty(
1140   Dali::Animation&                    transition,
1141   Internal::TransitionData::Animator& animator)
1142 {
1143   bool animateOpacity = false;
1144
1145   Property::Value initialOpacity;
1146   Property::Value targetOpacity;
1147   Property::Value initialMixColor;
1148   Property::Value targetMixColor;
1149
1150   Vector4 initialColor;
1151   if(animator.initialValue.Get(initialColor))
1152   {
1153     if(animator.initialValue.GetType() == Property::VECTOR4)
1154     {
1155       // if there is an initial color specifying alpha, test it
1156       initialOpacity = initialColor.a;
1157     }
1158     initialMixColor = Vector3(initialColor);
1159   }
1160
1161   // Set target value into data store
1162   if(animator.targetValue.GetType() != Property::NONE)
1163   {
1164     Vector4 mixColor;
1165     animator.targetValue.Get(mixColor);
1166     if(animator.targetValue.GetType() == Property::VECTOR4)
1167     {
1168       mImpl->mMixColor.a = mixColor.a;
1169       targetOpacity      = mixColor.a;
1170       animateOpacity     = true;
1171     }
1172
1173     mImpl->mMixColor.r = mixColor.r;
1174     mImpl->mMixColor.g = mixColor.g;
1175     mImpl->mMixColor.b = mixColor.b;
1176     targetMixColor     = Vector3(mixColor);
1177   }
1178
1179   SetupTransition(transition, animator, VisualRenderer::Property::VISUAL_MIX_COLOR, initialMixColor, targetMixColor);
1180
1181   if(animateOpacity)
1182   {
1183     SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, initialOpacity, targetOpacity);
1184   }
1185 }
1186
1187 Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
1188 {
1189   if(!mImpl->mRenderer)
1190   {
1191     Handle handle;
1192     return Dali::Property(handle, Property::INVALID_INDEX);
1193   }
1194
1195   switch(GetIntKey(key))
1196   {
1197     // Default animatable properties from VisualRenderer
1198     case Toolkit::Visual::Property::MIX_COLOR:
1199     {
1200       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1201     }
1202     case Toolkit::Visual::Property::OPACITY:
1203     {
1204       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
1205     }
1206     case Toolkit::Visual::Transform::Property::OFFSET:
1207     {
1208       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_OFFSET);
1209     }
1210     case Toolkit::Visual::Transform::Property::SIZE:
1211     {
1212       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_SIZE);
1213     }
1214
1215     // Default animatable properties from DecoratedVisualRenderer
1216     case Toolkit::DevelVisual::Property::CORNER_RADIUS:
1217     {
1218       if(IsTypeAvailableForCornerRadius(mImpl->mType))
1219       {
1220         const bool updateShader = !mImpl->mCustomShader && !IsRoundedCornerRequired();
1221
1222         // CornerRadius is animated now. we always have to use corner radius feature.
1223         mImpl->mAlwaysUsingCornerRadius = true;
1224
1225         if(updateShader)
1226         {
1227           // Update each values to renderer
1228           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterCornerRadiusUniform();
1229           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS, mImpl->GetCornerRadius());
1230           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, static_cast<float>(mImpl->GetCornerRadiusPolicy()));
1231
1232           // Change shader
1233           UpdateShader();
1234         }
1235         if(!IsBorderlineRequired())
1236         {
1237           // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
1238           mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
1239         }
1240         return Dali::Property(mImpl->mRenderer, DecoratedVisualRenderer::Property::CORNER_RADIUS);
1241       }
1242       break;
1243     }
1244     case Toolkit::DevelVisual::Property::BORDERLINE_WIDTH:
1245     case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
1246     case Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
1247     {
1248       if(IsTypeAvailableForBorderline(mImpl->mType))
1249       {
1250         const bool updateShader = !mImpl->mCustomShader && !IsBorderlineRequired();
1251
1252         // Borderline is animated now. we always have to use borderline feature.
1253         mImpl->mAlwaysUsingBorderline = true;
1254
1255         if(updateShader)
1256         {
1257           // Update each values to renderer
1258           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterBorderlineUniform();
1259           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH, mImpl->GetBorderlineWidth());
1260           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->GetBorderlineColor());
1261           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->GetBorderlineOffset());
1262
1263           // Change shader
1264           UpdateShader();
1265         }
1266         mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
1267
1268         return Dali::Property(mImpl->mRenderer, GetPropertyIndex(key));
1269       }
1270       break;
1271     }
1272     default:
1273     {
1274       // Special case for MIX_COLOR
1275       if(key.type == Property::Key::INDEX &&
1276          ((mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) ||
1277           (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR)))
1278       {
1279         return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1280       }
1281
1282       // Special case for BLUR_RADIUS
1283       if(mImpl->mType == Toolkit::Visual::COLOR &&
1284          ((key.type == Property::Key::INDEX && key.indexKey == DevelColorVisual::Property::BLUR_RADIUS) ||
1285           (key.type == Property::Key::STRING && key.stringKey == BLUR_RADIUS_NAME)))
1286       {
1287         // Request to color-visual class
1288         return OnGetPropertyObject(key);
1289       }
1290     }
1291   }
1292
1293   // If it is not VisualRenderer property, check registered Renderer and Shader property.
1294   Property::Index index = GetPropertyIndex(key);
1295   if(index != Property::INVALID_INDEX)
1296   {
1297     return Dali::Property(mImpl->mRenderer, index);
1298   }
1299
1300   // We can't find the property in the base class.
1301   // Request to child class
1302   return OnGetPropertyObject(key);
1303 }
1304
1305 } // namespace Internal
1306
1307 } // namespace Toolkit
1308
1309 } // namespace Dali