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