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