[Tizen] Add log if destroyed visual get some signal
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-base-impl.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "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_WIDTH)
66   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_HEIGHT)
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   mImpl = nullptr;
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   mImpl->mDepthIndex = index;
524   if(mImpl->mRenderer)
525   {
526     mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
527   }
528 }
529
530 int Visual::Base::GetDepthIndex() const
531 {
532   return mImpl->mDepthIndex;
533 }
534
535 void Visual::Base::SetOnScene(Actor& actor)
536 {
537   if(!IsOnScene())
538   {
539     // To display the actor correctly, renderer should not be added to actor until all required resources are ready.
540     // Thus the calling of actor.AddRenderer() should happen inside derived class as base class does not know the exact timing.
541     DoSetOnScene(actor);
542
543     if(mImpl->mRenderer)
544     {
545       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
546       mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
547     }
548
549     mImpl->mFlags |= Impl::IS_ON_SCENE;
550   }
551 }
552
553 void Visual::Base::SetOffScene(Actor& actor)
554 {
555   if(IsOnScene())
556   {
557     DoSetOffScene(actor);
558     mImpl->mFlags &= ~Impl::IS_ON_SCENE;
559   }
560 }
561
562 void Visual::Base::CreatePropertyMap(Property::Map& map) const
563 {
564   if(mImpl->mRenderer)
565   {
566     // Update values from Renderer
567     mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(VisualRenderer::Property::VISUAL_MIX_COLOR);
568     mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
569
570     mImpl->mTransform.mOffset = mImpl->mRenderer.GetProperty<Vector2>(VisualRenderer::Property::TRANSFORM_OFFSET);
571     mImpl->mTransform.mSize   = mImpl->mRenderer.GetProperty<Vector2>(VisualRenderer::Property::TRANSFORM_SIZE);
572
573     if(IsRoundedCornerRequired())
574     {
575       mImpl->SetCornerRadius(mImpl->mRenderer.GetProperty<Vector4>(DecoratedVisualRenderer::Property::CORNER_RADIUS));
576     }
577     if(IsBorderlineRequired())
578     {
579       mImpl->SetBorderlineWidth(mImpl->mRenderer.GetProperty<float>(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH));
580       mImpl->SetBorderlineColor(mImpl->mRenderer.GetProperty<Vector4>(DecoratedVisualRenderer::Property::BORDERLINE_COLOR));
581       mImpl->SetBorderlineOffset(mImpl->mRenderer.GetProperty<float>(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET));
582     }
583   }
584
585   DoCreatePropertyMap(map);
586
587   if(mImpl->mCustomShader)
588   {
589     mImpl->mCustomShader->CreatePropertyMap(map);
590   }
591
592   Property::Map transform;
593   mImpl->mTransform.GetPropertyMap(transform);
594   map.Insert(Toolkit::Visual::Property::TRANSFORM, transform);
595
596   bool premultipliedAlpha(IsPreMultipliedAlphaEnabled());
597   map.Insert(Toolkit::Visual::Property::PREMULTIPLIED_ALPHA, premultipliedAlpha);
598
599   // Note, Color and Primitive will also insert their own mix color into the map
600   // which is ok, because they have a different key value range, but uses same cached value anyway.
601   map.Insert(Toolkit::Visual::Property::MIX_COLOR, mImpl->mMixColor); // vec4
602   map.Insert(Toolkit::Visual::Property::OPACITY, mImpl->mMixColor.a);
603
604   auto fittingModeString = Scripting::GetLinearEnumerationName<FittingMode>(
605     mImpl->mFittingMode, VISUAL_FITTING_MODE_TABLE, VISUAL_FITTING_MODE_TABLE_COUNT);
606   map.Insert(Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE, fittingModeString);
607
608   if(IsTypeAvailableForBorderline(mImpl->mType))
609   {
610     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_WIDTH, mImpl->GetBorderlineWidth());
611     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_COLOR, mImpl->GetBorderlineColor());
612     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, mImpl->GetBorderlineOffset());
613   }
614
615   if(IsTypeAvailableForCornerRadius(mImpl->mType))
616   {
617     map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS, mImpl->GetCornerRadius());
618     map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY, mImpl->GetCornerRadiusPolicy());
619   }
620 }
621
622 void Visual::Base::CreateInstancePropertyMap(Property::Map& map) const
623 {
624   DoCreateInstancePropertyMap(map);
625
626   if(mImpl->mCustomShader)
627   {
628     mImpl->mCustomShader->CreatePropertyMap(map);
629   }
630 }
631
632 void Visual::Base::EnablePreMultipliedAlpha(bool preMultiplied)
633 {
634   if(preMultiplied)
635   {
636     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
637   }
638   else
639   {
640     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
641   }
642
643   if(mImpl->mRenderer)
644   {
645     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultiplied);
646     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultiplied);
647   }
648 }
649
650 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
651 {
652   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
653 }
654
655 void Visual::Base::DoSetOffScene(Actor& actor)
656 {
657   actor.RemoveRenderer(mImpl->mRenderer);
658 }
659
660 bool Visual::Base::IsOnScene() const
661 {
662   return mImpl->mFlags & Impl::IS_ON_SCENE;
663 }
664
665 bool Visual::Base::IsRoundedCornerRequired() const
666 {
667   // If VisualType doesn't support rounded corner, always return false.
668   if(IsTypeAvailableForCornerRadius(mImpl->mType))
669   {
670     if(mImpl->mRenderer)
671     {
672       // Update values from Renderer
673       Property::Value value = mImpl->mRenderer.GetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS);
674
675       Vector4 retValue = Vector4::ZERO;
676       if(value.Get(retValue))
677       {
678         if(mImpl->mDecorationData != nullptr || retValue != Vector4::ZERO)
679         {
680           mImpl->SetCornerRadius(retValue);
681         }
682       }
683     }
684     return mImpl->mAlwaysUsingCornerRadius || !(mImpl->GetCornerRadius() == Vector4::ZERO);
685   }
686   return false;
687 }
688
689 bool Visual::Base::IsBorderlineRequired() const
690 {
691   // If VisualType doesn't support borderline, always return false.
692   if(IsTypeAvailableForBorderline(mImpl->mType))
693   {
694     if(mImpl->mRenderer)
695     {
696       // Update values from Renderer
697       Property::Value value = mImpl->mRenderer.GetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH);
698
699       float retValue = 0.0f;
700       if(value.Get(retValue))
701       {
702         if(mImpl->mDecorationData != nullptr || !Dali::EqualsZero(retValue))
703         {
704           mImpl->SetBorderlineWidth(retValue);
705         }
706       }
707     }
708     return mImpl->mAlwaysUsingBorderline || !EqualsZero(mImpl->GetBorderlineWidth());
709   }
710   return false;
711 }
712
713 void Visual::Base::OnDoAction(const Property::Index actionId, const Property::Value& attributes)
714 {
715   // May be overriden by derived class
716 }
717
718 void Visual::Base::OnDoActionExtension(const Property::Index actionId, const Dali::Any attributes)
719 {
720   // May be overriden by derived class
721 }
722
723 void Visual::Base::RegisterMixColor()
724 {
725   if(mImpl->mRenderer)
726   {
727     // All visual renderers now use same mix color / opacity properties.
728     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(mImpl->mMixColor));
729     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, mImpl->mMixColor.a);
730
731     float preMultipliedAlpha = 0.0f;
732     if(IsPreMultipliedAlphaEnabled())
733     {
734       preMultipliedAlpha = 1.0f;
735     }
736     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultipliedAlpha);
737   }
738 }
739
740 void Visual::Base::RegisterDecoration()
741 {
742   if(mImpl->mRenderer)
743   {
744     if(IsTypeAvailableForCornerRadius(mImpl->mType))
745     {
746       if(mImpl->mAlwaysUsingCornerRadius || !(mImpl->GetCornerRadius() == Vector4::ZERO))
747       {
748         DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterCornerRadiusUniform();
749         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS, mImpl->GetCornerRadius());
750         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, static_cast<float>(mImpl->GetCornerRadiusPolicy()));
751       }
752     }
753     if(IsTypeAvailableForBorderline(mImpl->mType))
754     {
755       if(mImpl->mAlwaysUsingBorderline || !EqualsZero(mImpl->GetBorderlineWidth()))
756       {
757         DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterBorderlineUniform();
758         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH, mImpl->GetBorderlineWidth());
759         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->GetBorderlineColor());
760         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->GetBorderlineOffset());
761       }
762     }
763   }
764 }
765
766 void Visual::Base::SetMixColor(const Vector4& color)
767 {
768   mImpl->mMixColor = color;
769
770   if(mImpl->mRenderer)
771   {
772     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(color));
773     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, color.a);
774   }
775 }
776
777 void Visual::Base::SetMixColor(const Vector3& color)
778 {
779   mImpl->mMixColor.r = color.r;
780   mImpl->mMixColor.g = color.g;
781   mImpl->mMixColor.b = color.b;
782
783   if(mImpl->mRenderer)
784   {
785     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, color);
786   }
787 }
788
789 void Visual::Base::AddEventObserver(Visual::EventObserver& observer)
790 {
791   mImpl->mEventObserver = &observer;
792 }
793
794 void Visual::Base::RemoveEventObserver(Visual::EventObserver& observer)
795 {
796   mImpl->mEventObserver = NULL;
797 }
798
799 void Visual::Base::ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus)
800 {
801   if(mImpl->mResourceStatus != resourceStatus)
802   {
803     mImpl->mResourceStatus = resourceStatus;
804
805     if(mImpl->mEventObserver)
806     {
807       // observer is currently a control impl
808       mImpl->mEventObserver->ResourceReady(*this);
809     }
810   }
811 }
812
813 bool Visual::Base::IsResourceReady() const
814 {
815   return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
816           mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
817 }
818
819 bool Visual::Base::IsSynchronousLoadingRequired() const
820 {
821   return (mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING);
822 }
823
824 Toolkit::Visual::Type Visual::Base::GetType() const
825 {
826   return mImpl->mType;
827 }
828
829 Toolkit::Visual::ResourceStatus Visual::Base::GetResourceStatus() const
830 {
831   return mImpl->mResourceStatus;
832 }
833
834 Visual::FittingMode Visual::Base::GetFittingMode() const
835 {
836   return mImpl->mFittingMode;
837 }
838
839 Visual::Base& Visual::Base::GetVisualObject()
840 {
841   return *this;
842 }
843
844 Renderer Visual::Base::GetRenderer()
845 {
846   return mImpl->mRenderer;
847 }
848
849 Property::Index Visual::Base::GetIntKey(Property::Key key)
850 {
851   if(key.type == Property::Key::INDEX)
852   {
853     return key.indexKey;
854   }
855
856   if(key.stringKey == ANCHOR_POINT)
857   {
858     return Toolkit::Visual::Transform::Property::ANCHOR_POINT;
859   }
860   else if(key.stringKey == EXTRA_SIZE)
861   {
862     return Toolkit::DevelVisual::Transform::Property::EXTRA_SIZE;
863   }
864   else if(key.stringKey == MIX_COLOR)
865   {
866     return Toolkit::Visual::Property::MIX_COLOR;
867   }
868   else if(key.stringKey == OPACITY)
869   {
870     return Toolkit::Visual::Property::OPACITY;
871   }
872   else if(key.stringKey == OFFSET)
873   {
874     return Toolkit::Visual::Transform::Property::OFFSET;
875   }
876   else if(key.stringKey == OFFSET_POLICY)
877   {
878     return Toolkit::Visual::Transform::Property::OFFSET_POLICY;
879   }
880   else if(key.stringKey == ORIGIN)
881   {
882     return Toolkit::Visual::Transform::Property::ORIGIN;
883   }
884   else if(key.stringKey == PREMULTIPLIED_ALPHA)
885   {
886     return Toolkit::Visual::Property::PREMULTIPLIED_ALPHA;
887   }
888   else if(key.stringKey == CUSTOM_SHADER)
889   {
890     return Toolkit::Visual::Property::SHADER;
891   }
892   else if(key.stringKey == SIZE)
893   {
894     return Toolkit::Visual::Transform::Property::SIZE;
895   }
896   else if(key.stringKey == SIZE_POLICY)
897   {
898     return Toolkit::Visual::Transform::Property::SIZE_POLICY;
899   }
900   else if(key.stringKey == TRANSFORM)
901   {
902     return Toolkit::Visual::Property::TRANSFORM;
903   }
904   else if(key.stringKey == VISUAL_FITTING_MODE)
905   {
906     return Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE;
907   }
908   else if(key.stringKey == CORNER_RADIUS)
909   {
910     return Toolkit::DevelVisual::Property::CORNER_RADIUS;
911   }
912   else if(key.stringKey == CORNER_RADIUS_POLICY)
913   {
914     return Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY;
915   }
916   else if(key.stringKey == BORDERLINE_WIDTH)
917   {
918     return Toolkit::DevelVisual::Property::BORDERLINE_WIDTH;
919   }
920   else if(key.stringKey == BORDERLINE_COLOR)
921   {
922     return Toolkit::DevelVisual::Property::BORDERLINE_COLOR;
923   }
924   else if(key.stringKey == BORDERLINE_OFFSET)
925   {
926     return Toolkit::DevelVisual::Property::BORDERLINE_OFFSET;
927   }
928
929   return Property::INVALID_INDEX;
930 }
931
932 Property::Index Visual::Base::GetPropertyIndex(Property::Key key)
933 {
934   switch(GetIntKey(key))
935   {
936     case Dali::Toolkit::Visual::Transform::Property::OFFSET:
937     {
938       return VisualRenderer::Property::TRANSFORM_OFFSET;
939     }
940     case Dali::Toolkit::Visual::Transform::Property::SIZE:
941     {
942       return VisualRenderer::Property::TRANSFORM_SIZE;
943     }
944     case Dali::Toolkit::Visual::Transform::Property::ORIGIN:
945     {
946       return VisualRenderer::Property::TRANSFORM_ORIGIN;
947     }
948     case Dali::Toolkit::Visual::Transform::Property::ANCHOR_POINT:
949     {
950       return VisualRenderer::Property::TRANSFORM_ANCHOR_POINT;
951     }
952     case Dali::Toolkit::Visual::Property::MIX_COLOR:
953     {
954       return VisualRenderer::Property::VISUAL_MIX_COLOR;
955     }
956     case Dali::Toolkit::Visual::Property::OPACITY:
957     {
958       return DevelRenderer::Property::OPACITY;
959     }
960     case Dali::Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
961     {
962       return VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA;
963     }
964     case Dali::Toolkit::DevelVisual::Property::CORNER_RADIUS:
965     {
966       return DecoratedVisualRenderer::Property::CORNER_RADIUS;
967     }
968     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_WIDTH:
969     {
970       return DecoratedVisualRenderer::Property::BORDERLINE_WIDTH;
971     }
972     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
973     {
974       return DecoratedVisualRenderer::Property::BORDERLINE_COLOR;
975     }
976     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
977     {
978       return DecoratedVisualRenderer::Property::BORDERLINE_OFFSET;
979     }
980   }
981
982   Property::Index index = mImpl->mRenderer.GetPropertyIndex(key);
983
984   if(index == Property::INVALID_INDEX)
985   {
986     // Is it a shader property?
987     Shader shader = mImpl->mRenderer.GetShader();
988     index         = shader.GetPropertyIndex(key);
989     if(index != Property::INVALID_INDEX)
990     {
991       // Yes - we should register it in the Renderer so it can be set / animated
992       // independently, as shaders are shared across multiple renderers.
993       std::string     keyName;
994       Property::Index keyIndex(Property::INVALID_KEY);
995       if(key.type == Property::Key::INDEX)
996       {
997         keyName  = shader.GetPropertyName(index);
998         keyIndex = key.indexKey;
999       }
1000       else
1001       {
1002         keyName = key.stringKey;
1003         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
1004       }
1005       Property::Value value = shader.GetProperty(index);
1006
1007       // We already know that mRenderer didn't have property. So we can assume that it is unique.
1008       index = mImpl->mRenderer.RegisterUniqueProperty(keyIndex, keyName, value);
1009     }
1010   }
1011   return index;
1012 }
1013
1014 void Visual::Base::SetupTransition(
1015   Dali::Animation&                    transition,
1016   Internal::TransitionData::Animator& animator,
1017   Property::Index                     index,
1018   Property::Value&                    initialValue,
1019   Property::Value&                    targetValue)
1020 {
1021   if(index != Property::INVALID_INDEX)
1022   {
1023     if(mImpl->mRenderer)
1024     {
1025       if(animator.animate == false)
1026       {
1027         mImpl->mRenderer.SetProperty(index, targetValue);
1028       }
1029       else
1030       {
1031         if(animator.initialValue.GetType() != Property::NONE)
1032         {
1033           mImpl->mRenderer.SetProperty(index, initialValue);
1034         }
1035
1036         if(!transition)
1037         {
1038           transition = Dali::Animation::New(0.1f);
1039         }
1040
1041         transition.AnimateTo(Property(mImpl->mRenderer, index),
1042                              targetValue,
1043                              animator.alphaFunction,
1044                              TimePeriod(animator.timePeriodDelay,
1045                                         animator.timePeriodDuration));
1046       }
1047     }
1048   }
1049 }
1050
1051 void Visual::Base::AnimateProperty(
1052   Dali::Animation&                    transition,
1053   Internal::TransitionData::Animator& animator)
1054 {
1055 #if defined(DEBUG_ENABLED)
1056   if(gVisualBaseLogFilter->IsEnabledFor(Debug::General))
1057   {
1058     std::ostringstream oss;
1059     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
1060     DALI_LOG_INFO(gVisualBaseLogFilter, Debug::General, oss.str().c_str());
1061   }
1062 #endif
1063
1064   if(animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
1065      animator.propertyKey == MIX_COLOR ||
1066      (mImpl->mType == Toolkit::Visual::COLOR &&
1067       animator.propertyKey == ColorVisual::Property::MIX_COLOR) ||
1068      (mImpl->mType == Toolkit::Visual::PRIMITIVE &&
1069       animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR))
1070   {
1071     AnimateMixColorProperty(transition, animator);
1072   }
1073   else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
1074           animator.propertyKey == OPACITY)
1075   {
1076     AnimateOpacityProperty(transition, animator);
1077   }
1078   else if(mImpl->mRenderer)
1079   {
1080     AnimateRendererProperty(transition, animator);
1081   }
1082 }
1083
1084 void Visual::Base::AnimateOpacityProperty(
1085   Dali::Animation&                    transition,
1086   Internal::TransitionData::Animator& animator)
1087 {
1088   float targetOpacity;
1089   if(animator.targetValue.Get(targetOpacity))
1090   {
1091     mImpl->mMixColor.a = targetOpacity;
1092   }
1093
1094   SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, animator.initialValue, animator.targetValue);
1095 }
1096
1097 void Visual::Base::AnimateRendererProperty(
1098   Dali::Animation&                    transition,
1099   Internal::TransitionData::Animator& animator)
1100 {
1101   // Get actual renderer index (will convert transform keys into visualproperty indices)
1102   Property::Index index = GetPropertyIndex(animator.propertyKey);
1103
1104   if(index != Property::INVALID_INDEX)
1105   {
1106     if(animator.targetValue.GetType() != Property::NONE)
1107     {
1108       // Try writing target value into transform property map
1109       // if it's not a valid key, then it won't alter mTransform
1110       Property::Map map;
1111       if(animator.propertyKey.type == Property::Key::INDEX)
1112       {
1113         map.Add(animator.propertyKey.indexKey, animator.targetValue);
1114       }
1115       else
1116       {
1117         map.Add(animator.propertyKey.stringKey, animator.targetValue);
1118       }
1119
1120       mImpl->mTransform.UpdatePropertyMap(map);
1121     }
1122     SetupTransition(transition, animator, index, animator.initialValue, animator.targetValue);
1123   }
1124 }
1125
1126 void Visual::Base::AnimateMixColorProperty(
1127   Dali::Animation&                    transition,
1128   Internal::TransitionData::Animator& animator)
1129 {
1130   bool animateOpacity = false;
1131
1132   Property::Value initialOpacity;
1133   Property::Value targetOpacity;
1134   Property::Value initialMixColor;
1135   Property::Value targetMixColor;
1136
1137   Vector4 initialColor;
1138   if(animator.initialValue.Get(initialColor))
1139   {
1140     if(animator.initialValue.GetType() == Property::VECTOR4)
1141     {
1142       // if there is an initial color specifying alpha, test it
1143       initialOpacity = initialColor.a;
1144     }
1145     initialMixColor = Vector3(initialColor);
1146   }
1147
1148   // Set target value into data store
1149   if(animator.targetValue.GetType() != Property::NONE)
1150   {
1151     Vector4 mixColor;
1152     animator.targetValue.Get(mixColor);
1153     if(animator.targetValue.GetType() == Property::VECTOR4)
1154     {
1155       mImpl->mMixColor.a = mixColor.a;
1156       targetOpacity      = mixColor.a;
1157       animateOpacity     = true;
1158     }
1159
1160     mImpl->mMixColor.r = mixColor.r;
1161     mImpl->mMixColor.g = mixColor.g;
1162     mImpl->mMixColor.b = mixColor.b;
1163     targetMixColor     = Vector3(mixColor);
1164   }
1165
1166   SetupTransition(transition, animator, VisualRenderer::Property::VISUAL_MIX_COLOR, initialMixColor, targetMixColor);
1167
1168   if(animateOpacity)
1169   {
1170     SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, initialOpacity, targetOpacity);
1171   }
1172 }
1173
1174 Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
1175 {
1176   if(!mImpl->mRenderer)
1177   {
1178     Handle handle;
1179     return Dali::Property(handle, Property::INVALID_INDEX);
1180   }
1181
1182   switch(GetIntKey(key))
1183   {
1184     // Default animatable properties from VisualRenderer
1185     case Toolkit::Visual::Property::MIX_COLOR:
1186     {
1187       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1188     }
1189     case Toolkit::Visual::Property::OPACITY:
1190     {
1191       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
1192     }
1193     case Toolkit::Visual::Transform::Property::OFFSET:
1194     {
1195       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_OFFSET);
1196     }
1197     case Toolkit::Visual::Transform::Property::SIZE:
1198     {
1199       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_SIZE);
1200     }
1201
1202     // Default animatable properties from DecoratedVisualRenderer
1203     case Toolkit::DevelVisual::Property::CORNER_RADIUS:
1204     {
1205       if(IsTypeAvailableForCornerRadius(mImpl->mType))
1206       {
1207         const bool updateShader = !mImpl->mCustomShader && !IsRoundedCornerRequired();
1208
1209         // CornerRadius is animated now. we always have to use corner radius feature.
1210         mImpl->mAlwaysUsingCornerRadius = true;
1211
1212         if(updateShader)
1213         {
1214           // Update each values to renderer
1215           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterCornerRadiusUniform();
1216           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS, mImpl->GetCornerRadius());
1217           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, static_cast<float>(mImpl->GetCornerRadiusPolicy()));
1218
1219           // Change shader
1220           UpdateShader();
1221         }
1222         if(!IsBorderlineRequired())
1223         {
1224           // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
1225           mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
1226         }
1227         return Dali::Property(mImpl->mRenderer, DecoratedVisualRenderer::Property::CORNER_RADIUS);
1228       }
1229       break;
1230     }
1231     case Toolkit::DevelVisual::Property::BORDERLINE_WIDTH:
1232     case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
1233     case Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
1234     {
1235       if(IsTypeAvailableForBorderline(mImpl->mType))
1236       {
1237         const bool updateShader = !mImpl->mCustomShader && !IsBorderlineRequired();
1238
1239         // Borderline is animated now. we always have to use borderline feature.
1240         mImpl->mAlwaysUsingBorderline = true;
1241
1242         if(updateShader)
1243         {
1244           // Update each values to renderer
1245           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterBorderlineUniform();
1246           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH, mImpl->GetBorderlineWidth());
1247           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->GetBorderlineColor());
1248           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->GetBorderlineOffset());
1249
1250           // Change shader
1251           UpdateShader();
1252         }
1253         mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
1254
1255         return Dali::Property(mImpl->mRenderer, GetPropertyIndex(key));
1256       }
1257       break;
1258     }
1259     default:
1260     {
1261       // Special case for MIX_COLOR
1262       if(key.type == Property::Key::INDEX &&
1263          ((mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) ||
1264           (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR)))
1265       {
1266         return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1267       }
1268
1269       // Special case for BLUR_RADIUS
1270       if(mImpl->mType == Toolkit::Visual::COLOR &&
1271          ((key.type == Property::Key::INDEX && key.indexKey == DevelColorVisual::Property::BLUR_RADIUS) ||
1272           (key.type == Property::Key::STRING && key.stringKey == BLUR_RADIUS_NAME)))
1273       {
1274         // Request to color-visual class
1275         return OnGetPropertyObject(key);
1276       }
1277     }
1278   }
1279
1280   // If it is not VisualRenderer property, check registered Renderer and Shader property.
1281   Property::Index index = GetPropertyIndex(key);
1282   if(index != Property::INVALID_INDEX)
1283   {
1284     return Dali::Property(mImpl->mRenderer, index);
1285   }
1286
1287   // We can't find the property in the base class.
1288   // Request to child class
1289   return OnGetPropertyObject(key);
1290 }
1291
1292 } // namespace Internal
1293
1294 } // namespace Toolkit
1295
1296 } // namespace Dali