[dali_2.1.2] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-base-impl.cpp
1 /*
2  * Copyright (c) 2021 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
27 //INTERNAL HEARDER
28 #include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
29 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
30 #include <dali-toolkit/internal/helpers/property-helper.h>
31 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
32 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
33 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
34 #include <dali-toolkit/public-api/visuals/primitive-visual-properties.h>
35 #include <dali-toolkit/public-api/visuals/visual-properties.h>
36
37 namespace
38 {
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gVisualBaseLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VISUAL_BASE");
41 #endif
42
43 const char* const PRE_MULTIPLIED_ALPHA_PROPERTY("preMultipliedAlpha");
44
45 } // namespace
46
47 namespace Dali
48 {
49 namespace Toolkit
50 {
51 namespace Internal
52 {
53 namespace
54 {
55 DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_FITTING_MODE)
56   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_KEEP_ASPECT_RATIO)
57   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FILL)
58   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, OVER_FIT_KEEP_ASPECT_RATIO)
59   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, CENTER)
60   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_WIDTH)
61   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_HEIGHT)
62 DALI_ENUM_TO_STRING_TABLE_END(VISUAL_FITTING_MODE)
63
64 /**
65  * @brief Check whether this visual type can use corner radius feature or not.
66  * @param type VisualType that want to checkup
67  * @return true if type can use corner radius feature.
68  */
69 static bool IsTypeAvailableForCornerRadius(Toolkit::Visual::Type type)
70 {
71   switch(static_cast<Toolkit::DevelVisual::Type>(type))
72   {
73     case Toolkit::Visual::Type::COLOR:
74     case Toolkit::Visual::Type::GRADIENT:
75     case Toolkit::Visual::Type::IMAGE:
76     case Toolkit::Visual::Type::SVG:
77     case Toolkit::Visual::Type::ANIMATED_IMAGE:
78     case Toolkit::DevelVisual::Type::ANIMATED_VECTOR_IMAGE:
79     {
80       return true;
81     }
82     default:
83     {
84       return false;
85     }
86   }
87 }
88
89 /**
90  * @brief Check whether this visual type can use borderline feature or not.
91  * @param type VisualType that want to checkup
92  * @return true if type can use borderline feature.
93  */
94 static bool IsTypeAvailableForBorderline(Toolkit::Visual::Type type)
95 {
96   switch(static_cast<Toolkit::DevelVisual::Type>(type))
97   {
98     case Toolkit::Visual::Type::COLOR:
99     case Toolkit::Visual::Type::GRADIENT:
100     case Toolkit::Visual::Type::IMAGE:
101     case Toolkit::Visual::Type::SVG:
102     case Toolkit::Visual::Type::ANIMATED_IMAGE:
103     case Toolkit::DevelVisual::Type::ANIMATED_VECTOR_IMAGE:
104     {
105       return true;
106     }
107     default:
108     {
109       return false;
110     }
111   }
112 }
113
114 } // namespace
115
116 Visual::Base::Base(VisualFactoryCache& factoryCache, FittingMode fittingMode, Toolkit::Visual::Type type)
117 : mImpl(new Impl(fittingMode, type)),
118   mFactoryCache(factoryCache)
119 {
120 }
121
122 Visual::Base::~Base()
123 {
124   delete mImpl;
125 }
126
127 void Visual::Base::Initialize()
128 {
129   // The Renderer should be created inside derived class here.
130   OnInitialize();
131
132   if(mImpl->mRenderer)
133   {
134     RegisterMixColor();
135
136     if(IsRoundedCornerRequired())
137     {
138       mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
139       mImpl->mRenderer.RegisterProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
140
141       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
142     }
143     if(IsBorderlineRequired())
144     {
145       mImpl->mBorderlineWidthIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_WIDTH,  BORDERLINE_WIDTH,  mImpl->mBorderlineWidth);
146       mImpl->mBorderlineColorIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_COLOR,  BORDERLINE_COLOR,  mImpl->mBorderlineColor);
147       mImpl->mBorderlineOffsetIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_OFFSET, BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
148
149       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
150     }
151   }
152 }
153
154 void Visual::Base::SetCustomShader(const Property::Map& shaderMap)
155 {
156   if(mImpl->mCustomShader)
157   {
158     mImpl->mCustomShader->SetPropertyMap(shaderMap);
159   }
160   else
161   {
162     mImpl->mCustomShader = new Impl::CustomShader(shaderMap);
163   }
164
165   // Let derived class know
166   UpdateShader();
167 }
168
169 void Visual::Base::SetProperties(const Property::Map& propertyMap)
170 {
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(mImpl->mBorderlineWidthIndex != Property::INVALID_INDEX)
300         {
301           mImpl->mRenderer.SetProperty(mImpl->mBorderlineWidthIndex, mImpl->mBorderlineWidth);
302         }
303         break;
304       }
305       case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
306       {
307         Vector4 color;
308         if(value.Get(color))
309         {
310           mImpl->mBorderlineColor = color;
311         }
312
313         if(mImpl->mBorderlineColorIndex != Property::INVALID_INDEX)
314         {
315           mImpl->mRenderer.SetProperty(mImpl->mBorderlineColorIndex, mImpl->mBorderlineColor);
316         }
317         break;
318       }
319       case Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
320       {
321         float offset;
322         if(value.Get(offset))
323         {
324           mImpl->mBorderlineOffset = offset;
325         }
326
327         if(mImpl->mBorderlineOffsetIndex != Property::INVALID_INDEX)
328         {
329           mImpl->mRenderer.SetProperty(mImpl->mBorderlineOffsetIndex, mImpl->mBorderlineOffset);
330         }
331         break;
332       }
333       case Toolkit::DevelVisual::Property::CORNER_RADIUS:
334       {
335         if(value.GetType() == Property::VECTOR4)
336         {
337           // If CORNER_RADIUS Property is Vector4,
338           // Each values mean the radius of
339           // (top-left, top-right, bottom-right, bottom-left)
340           Vector4 radius;
341           if(value.Get(radius))
342           {
343             mImpl->mCornerRadius = radius;
344           }
345         }
346         else
347         {
348           // If CORNER_RADIUS Property is float,
349           // Every corner radius have same value
350           float radius;
351           if(value.Get(radius))
352           {
353             mImpl->mCornerRadius = Vector4(radius, radius, radius, radius);
354           }
355         }
356
357         if(mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
358         {
359           mImpl->mRenderer.SetProperty(mImpl->mCornerRadiusIndex, mImpl->mCornerRadius);
360         }
361         break;
362       }
363       case Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY:
364       {
365         int policy;
366         if(value.Get(policy))
367         {
368           switch(policy)
369           {
370             case Toolkit::Visual::Transform::Policy::RELATIVE:
371             case Toolkit::Visual::Transform::Policy::ABSOLUTE:
372             {
373               mImpl->mCornerRadiusPolicy = policy;
374               break;
375             }
376             default:
377             {
378               DALI_LOG_ERROR("Unsupported policy: %d\n", policy);
379               break;
380             }
381           }
382         }
383         break;
384       }
385     }
386   }
387
388   DoSetProperties(propertyMap);
389 }
390
391 void Visual::Base::SetTransformAndSize(const Property::Map& transform, Size controlSize)
392 {
393   mImpl->mControlSize = controlSize;
394   mImpl->mTransform.UpdatePropertyMap(transform);
395
396 #if defined(DEBUG_ENABLED)
397   std::ostringstream oss;
398   oss << transform;
399   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);
400 #endif
401
402   OnSetTransform();
403 }
404
405 void Visual::Base::SetName(const std::string& name)
406 {
407   mImpl->mName = name;
408 }
409
410 const std::string& Visual::Base::GetName() const
411 {
412   return mImpl->mName;
413 }
414
415 float Visual::Base::GetHeightForWidth(float width)
416 {
417   float   aspectCorrectedHeight = 0.f;
418   Vector2 naturalSize;
419   GetNaturalSize(naturalSize);
420   if(naturalSize.width)
421   {
422     aspectCorrectedHeight = naturalSize.height * width / naturalSize.width;
423   }
424   return aspectCorrectedHeight;
425 }
426
427 float Visual::Base::GetWidthForHeight(float height)
428 {
429   float   aspectCorrectedWidth = 0.f;
430   Vector2 naturalSize;
431   GetNaturalSize(naturalSize);
432   if(naturalSize.height > 0.0f)
433   {
434     aspectCorrectedWidth = naturalSize.width * height / naturalSize.height;
435   }
436   return aspectCorrectedWidth;
437 }
438
439 void Visual::Base::GetNaturalSize(Vector2& naturalSize)
440 {
441   naturalSize = Vector2::ZERO;
442 }
443
444 void Visual::Base::DoAction(const Property::Index actionId, const Property::Value attributes)
445 {
446   OnDoAction(actionId, attributes);
447
448   // Check if action is valid for this visual type and perform action if possible
449   switch(actionId)
450   {
451     case DevelVisual::Action::UPDATE_PROPERTY:
452     {
453       const Property::Map* map = attributes.GetMap();
454       if(map)
455       {
456         SetProperties(*map);
457       }
458       break;
459     }
460   }
461 }
462
463 void Visual::Base::SetDepthIndex(int index)
464 {
465   mImpl->mDepthIndex = index;
466   if(mImpl->mRenderer)
467   {
468     mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
469   }
470 }
471
472 int Visual::Base::GetDepthIndex() const
473 {
474   return mImpl->mDepthIndex;
475 }
476
477 void Visual::Base::SetOnScene(Actor& actor)
478 {
479   if(!IsOnScene())
480   {
481     // To display the actor correctly, renderer should not be added to actor until all required resources are ready.
482     // Thus the calling of actor.AddRenderer() should happen inside derived class as base class does not know the exact timing.
483     DoSetOnScene(actor);
484
485     if(mImpl->mRenderer)
486     {
487       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
488       mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
489     }
490
491     mImpl->mFlags |= Impl::IS_ON_SCENE;
492   }
493 }
494
495 void Visual::Base::SetOffScene(Actor& actor)
496 {
497   if(IsOnScene())
498   {
499     DoSetOffScene(actor);
500     mImpl->mFlags &= ~Impl::IS_ON_SCENE;
501   }
502 }
503
504 void Visual::Base::CreatePropertyMap(Property::Map& map) const
505 {
506   if(mImpl->mRenderer)
507   {
508     // Update values from Renderer
509     mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(mImpl->mMixColorIndex);
510     mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
511     if(mImpl->mTransform.mOffsetIndex != Property::INVALID_INDEX)
512     {
513       mImpl->mTransform.mOffset = mImpl->mRenderer.GetProperty<Vector2>(mImpl->mTransform.mOffsetIndex);
514     }
515     if(mImpl->mTransform.mSizeIndex != Property::INVALID_INDEX)
516     {
517       mImpl->mTransform.mSize = mImpl->mRenderer.GetProperty<Vector2>(mImpl->mTransform.mSizeIndex);
518     }
519     if(mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
520     {
521       mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<Vector4>(mImpl->mCornerRadiusIndex);
522     }
523     if(mImpl->mBorderlineWidthIndex != Property::INVALID_INDEX)
524     {
525       mImpl->mBorderlineWidth = mImpl->mRenderer.GetProperty<float>(mImpl->mBorderlineWidthIndex);
526     }
527     if(mImpl->mBorderlineColorIndex != Property::INVALID_INDEX)
528     {
529       mImpl->mBorderlineColor = mImpl->mRenderer.GetProperty<Vector4>(mImpl->mBorderlineColorIndex);
530     }
531     if(mImpl->mBorderlineOffsetIndex != Property::INVALID_INDEX)
532     {
533       mImpl->mBorderlineOffset = mImpl->mRenderer.GetProperty<float>(mImpl->mBorderlineOffsetIndex);
534     }
535   }
536
537   DoCreatePropertyMap(map);
538
539   if(mImpl->mCustomShader)
540   {
541     mImpl->mCustomShader->CreatePropertyMap(map);
542   }
543
544   Property::Map transform;
545   mImpl->mTransform.GetPropertyMap(transform);
546   map.Insert(Toolkit::Visual::Property::TRANSFORM, transform);
547
548   bool premultipliedAlpha(IsPreMultipliedAlphaEnabled());
549   map.Insert(Toolkit::Visual::Property::PREMULTIPLIED_ALPHA, premultipliedAlpha);
550
551   // Note, Color and Primitive will also insert their own mix color into the map
552   // which is ok, because they have a different key value range.
553   map.Insert(Toolkit::Visual::Property::MIX_COLOR, mImpl->mMixColor); // vec4
554   map.Insert(Toolkit::Visual::Property::OPACITY, mImpl->mMixColor.a);
555
556   auto fittingModeString = Scripting::GetLinearEnumerationName<FittingMode>(
557     mImpl->mFittingMode, VISUAL_FITTING_MODE_TABLE, VISUAL_FITTING_MODE_TABLE_COUNT);
558   map.Insert(Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE, fittingModeString);
559
560   map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
561   map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_COLOR, mImpl->mBorderlineColor);
562   map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
563
564   map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS, mImpl->mCornerRadius);
565   map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY, static_cast<int>(mImpl->mCornerRadiusPolicy));
566 }
567
568 void Visual::Base::CreateInstancePropertyMap(Property::Map& map) const
569 {
570   DoCreateInstancePropertyMap(map);
571
572   if(mImpl->mCustomShader)
573   {
574     mImpl->mCustomShader->CreatePropertyMap(map);
575   }
576 }
577
578 void Visual::Base::EnablePreMultipliedAlpha(bool preMultiplied)
579 {
580   if(preMultiplied)
581   {
582     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
583   }
584   else
585   {
586     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
587   }
588
589   if(mImpl->mRenderer)
590   {
591     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultiplied);
592     mImpl->mRenderer.RegisterProperty(PRE_MULTIPLIED_ALPHA_PROPERTY, static_cast<float>(preMultiplied));
593   }
594 }
595
596 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
597 {
598   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
599 }
600
601 void Visual::Base::DoSetOffScene(Actor& actor)
602 {
603   actor.RemoveRenderer(mImpl->mRenderer);
604 }
605
606 bool Visual::Base::IsOnScene() const
607 {
608   return mImpl->mFlags & Impl::IS_ON_SCENE;
609 }
610
611 bool Visual::Base::IsRoundedCornerRequired() const
612 {
613   // If VisualType doesn't support rounded corner, always return false.
614   if(IsTypeAvailableForCornerRadius(mImpl->mType))
615   {
616     if(mImpl->mRenderer && mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
617     {
618       // Update values from Renderer
619       mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<Vector4>(mImpl->mCornerRadiusIndex);
620     }
621     return !(mImpl->mCornerRadius == Vector4::ZERO) || mImpl->mAlwaysUsingCornerRadius;
622   }
623   return false;
624 }
625
626 bool Visual::Base::IsBorderlineRequired() const
627 {
628   // If VisualType doesn't support borderline, always return false.
629   if(IsTypeAvailableForBorderline(mImpl->mType))
630   {
631     if(mImpl->mRenderer && mImpl->mBorderlineWidthIndex != Property::INVALID_INDEX)
632     {
633       // Update values from Renderer
634       mImpl->mBorderlineWidth = mImpl->mRenderer.GetProperty<float>(mImpl->mBorderlineWidthIndex);
635     }
636     return !EqualsZero(mImpl->mBorderlineWidth) || mImpl->mAlwaysUsingBorderline;
637   }
638   return false;
639 }
640
641 void Visual::Base::OnDoAction(const Property::Index actionId, const Property::Value& attributes)
642 {
643   // May be overriden by derived class
644 }
645
646 void Visual::Base::RegisterMixColor()
647 {
648   // Only register if not already registered.
649   // (Color and Primitive visuals will register their own and save to this index)
650   if(mImpl->mMixColorIndex == Property::INVALID_INDEX)
651   {
652     mImpl->mMixColorIndex = mImpl->mRenderer.RegisterProperty(
653       Toolkit::Visual::Property::MIX_COLOR,
654       MIX_COLOR,
655       Vector3(mImpl->mMixColor));
656   }
657
658   mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, mImpl->mMixColor.a);
659
660   float preMultipliedAlpha = 0.0f;
661   if(IsPreMultipliedAlphaEnabled())
662   {
663     preMultipliedAlpha = 1.0f;
664   }
665   mImpl->mRenderer.RegisterProperty(PRE_MULTIPLIED_ALPHA_PROPERTY, preMultipliedAlpha);
666 }
667
668 void Visual::Base::SetMixColor(const Vector4& color)
669 {
670   mImpl->mMixColor = color;
671
672   if(mImpl->mRenderer)
673   {
674     mImpl->mRenderer.SetProperty(mImpl->mMixColorIndex, Vector3(color));
675     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, color.a);
676   }
677 }
678
679 void Visual::Base::SetMixColor(const Vector3& color)
680 {
681   mImpl->mMixColor.r = color.r;
682   mImpl->mMixColor.g = color.g;
683   mImpl->mMixColor.b = color.b;
684
685   if(mImpl->mRenderer)
686   {
687     mImpl->mRenderer.SetProperty(mImpl->mMixColorIndex, color);
688   }
689 }
690
691 void Visual::Base::AddEventObserver(Visual::EventObserver& observer)
692 {
693   mImpl->mEventObserver = &observer;
694 }
695
696 void Visual::Base::RemoveEventObserver(Visual::EventObserver& observer)
697 {
698   mImpl->mEventObserver = NULL;
699 }
700
701 void Visual::Base::ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus)
702 {
703   if(mImpl->mResourceStatus != resourceStatus)
704   {
705     mImpl->mResourceStatus = resourceStatus;
706
707     if(mImpl->mEventObserver)
708     {
709       // observer is currently a control impl
710       mImpl->mEventObserver->ResourceReady(*this);
711     }
712   }
713 }
714
715 bool Visual::Base::IsResourceReady() const
716 {
717   return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY);
718 }
719
720 bool Visual::Base::IsSynchronousLoadingRequired() const
721 {
722   return (mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING);
723 }
724
725 Toolkit::Visual::Type Visual::Base::GetType() const
726 {
727   return mImpl->mType;
728 }
729
730 Toolkit::Visual::ResourceStatus Visual::Base::GetResourceStatus() const
731 {
732   return mImpl->mResourceStatus;
733 }
734
735 Visual::FittingMode Visual::Base::GetFittingMode() const
736 {
737   return mImpl->mFittingMode;
738 }
739
740 Visual::Base& Visual::Base::GetVisualObject()
741 {
742   return *this;
743 }
744
745 Renderer Visual::Base::GetRenderer()
746 {
747   return mImpl->mRenderer;
748 }
749
750 Property::Index Visual::Base::GetPropertyIndex(Property::Key key)
751 {
752   Property::Index index = mImpl->mRenderer.GetPropertyIndex(key);
753
754   if(index == Property::INVALID_INDEX)
755   {
756     // Is it a shader property?
757     Shader shader = mImpl->mRenderer.GetShader();
758     index         = shader.GetPropertyIndex(key);
759     if(index != Property::INVALID_INDEX)
760     {
761       // Yes - we should register it in the Renderer so it can be set / animated
762       // independently, as shaders are shared across multiple renderers.
763       std::string     keyName;
764       Property::Index keyIndex(Property::INVALID_KEY);
765       if(key.type == Property::Key::INDEX)
766       {
767         keyName  = shader.GetPropertyName(index);
768         keyIndex = key.indexKey;
769       }
770       else
771       {
772         keyName = key.stringKey;
773         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
774       }
775       Property::Value value = shader.GetProperty(index);
776       index                 = mImpl->mRenderer.RegisterProperty(keyIndex, keyName, value);
777     }
778   }
779   return index;
780 }
781
782 void Visual::Base::SetupTransition(
783   Dali::Animation&                    transition,
784   Internal::TransitionData::Animator& animator,
785   Property::Index                     index,
786   Property::Value&                    initialValue,
787   Property::Value&                    targetValue)
788 {
789   if(index != Property::INVALID_INDEX)
790   {
791     if(mImpl->mRenderer)
792     {
793       if(animator.animate == false)
794       {
795         mImpl->mRenderer.SetProperty(index, targetValue);
796       }
797       else
798       {
799         if(animator.initialValue.GetType() != Property::NONE)
800         {
801           mImpl->mRenderer.SetProperty(index, initialValue);
802         }
803
804         if(!transition)
805         {
806           transition = Dali::Animation::New(0.1f);
807         }
808
809         transition.AnimateTo(Property(mImpl->mRenderer, index),
810                              targetValue,
811                              animator.alphaFunction,
812                              TimePeriod(animator.timePeriodDelay,
813                                         animator.timePeriodDuration));
814       }
815     }
816   }
817 }
818
819 void Visual::Base::AnimateProperty(
820   Dali::Animation&                    transition,
821   Internal::TransitionData::Animator& animator)
822 {
823 #if defined(DEBUG_ENABLED)
824   {
825     std::ostringstream oss;
826     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
827     DALI_LOG_INFO(gVisualBaseLogFilter, Debug::General, oss.str().c_str());
828   }
829 #endif
830
831   if(animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
832      animator.propertyKey == MIX_COLOR ||
833      (mImpl->mType == Toolkit::Visual::COLOR &&
834       animator.propertyKey == ColorVisual::Property::MIX_COLOR) ||
835      (mImpl->mType == Toolkit::Visual::PRIMITIVE &&
836       animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR))
837   {
838     AnimateMixColorProperty(transition, animator);
839   }
840   else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
841           animator.propertyKey == OPACITY)
842   {
843     AnimateOpacityProperty(transition, animator);
844   }
845   else if(mImpl->mRenderer)
846   {
847     AnimateRendererProperty(transition, animator);
848   }
849 }
850
851 void Visual::Base::AnimateOpacityProperty(
852   Dali::Animation&                    transition,
853   Internal::TransitionData::Animator& animator)
854 {
855   float targetOpacity;
856   if(animator.targetValue.Get(targetOpacity))
857   {
858     mImpl->mMixColor.a = targetOpacity;
859   }
860
861   SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, animator.initialValue, animator.targetValue);
862 }
863
864 void Visual::Base::AnimateRendererProperty(
865   Dali::Animation&                    transition,
866   Internal::TransitionData::Animator& animator)
867 {
868   Property::Index index = GetPropertyIndex(animator.propertyKey);
869   if(index != Property::INVALID_INDEX)
870   {
871     if(animator.targetValue.GetType() != Property::NONE)
872     {
873       // Try writing target value into transform property map
874       // if it's not a valid key, then it won't alter mTransform
875       Property::Map map;
876       if(animator.propertyKey.type == Property::Key::INDEX)
877       {
878         map.Add(animator.propertyKey.indexKey, animator.targetValue);
879       }
880       else
881       {
882         map.Add(animator.propertyKey.stringKey, animator.targetValue);
883       }
884
885       mImpl->mTransform.UpdatePropertyMap(map);
886     }
887
888     SetupTransition(transition, animator, index, animator.initialValue, animator.targetValue);
889   }
890 }
891
892 void Visual::Base::AnimateMixColorProperty(
893   Dali::Animation&                    transition,
894   Internal::TransitionData::Animator& animator)
895 {
896   Property::Index index          = mImpl->mMixColorIndex;
897   bool            animateOpacity = false;
898
899   Property::Value initialOpacity;
900   Property::Value targetOpacity;
901   Property::Value initialMixColor;
902   Property::Value targetMixColor;
903
904   if(index != Property::INVALID_INDEX)
905   {
906     Vector4 initialColor;
907     if(animator.initialValue.Get(initialColor))
908     {
909       if(animator.initialValue.GetType() == Property::VECTOR4)
910       {
911         // if there is an initial color specifying alpha, test it
912         initialOpacity = initialColor.a;
913       }
914       initialMixColor = Vector3(initialColor);
915     }
916
917     // Set target value into data store
918     if(animator.targetValue.GetType() != Property::NONE)
919     {
920       Vector4 mixColor;
921       animator.targetValue.Get(mixColor);
922       if(animator.targetValue.GetType() == Property::VECTOR4)
923       {
924         mImpl->mMixColor.a = mixColor.a;
925         targetOpacity      = mixColor.a;
926         animateOpacity     = true;
927       }
928
929       mImpl->mMixColor.r = mixColor.r;
930       mImpl->mMixColor.g = mixColor.g;
931       mImpl->mMixColor.b = mixColor.b;
932       targetMixColor     = Vector3(mixColor);
933     }
934
935     SetupTransition(transition, animator, index, initialMixColor, targetMixColor);
936     if(animateOpacity)
937     {
938       SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, initialOpacity, targetOpacity);
939     }
940   }
941 }
942
943 Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
944 {
945   if(!mImpl->mRenderer)
946   {
947     Handle handle;
948     return Dali::Property(handle, Property::INVALID_INDEX);
949   }
950
951   // Mix color or opacity cases
952   if(key.type == Property::Key::INDEX)
953   {
954     if(key.indexKey == Toolkit::Visual::Property::MIX_COLOR || (mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) || (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR))
955     {
956       return Dali::Property(mImpl->mRenderer, mImpl->mMixColorIndex);
957     }
958     else if(key.indexKey == Toolkit::Visual::Property::OPACITY)
959     {
960       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
961     }
962     else if(key.indexKey == Toolkit::Visual::Transform::Property::OFFSET)
963     {
964       return Dali::Property(mImpl->mRenderer, OFFSET);
965     }
966     else if(key.indexKey == Toolkit::Visual::Transform::Property::SIZE)
967     {
968       return Dali::Property(mImpl->mRenderer, SIZE);
969     }
970   }
971   else
972   {
973     if(key.stringKey == MIX_COLOR)
974     {
975       return Dali::Property(mImpl->mRenderer, mImpl->mMixColorIndex);
976     }
977     else if(key.stringKey == OPACITY)
978     {
979       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
980     }
981     else if(key.stringKey == OFFSET)
982     {
983       return Dali::Property(mImpl->mRenderer, OFFSET);
984     }
985     else if(key.stringKey == SIZE)
986     {
987       return Dali::Property(mImpl->mRenderer, SIZE);
988     }
989   }
990
991   // Other cases
992   Property::Index index = GetPropertyIndex(key);
993   if(index == Property::INVALID_INDEX)
994   {
995     if(IsTypeAvailableForBorderline(mImpl->mType) &&
996        ((key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_WIDTH)  || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_WIDTH) ||
997         (key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_COLOR)  || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_COLOR) ||
998         (key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_OFFSET) || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_OFFSET)))
999     {
1000       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
1001
1002       // Register borderline properties
1003       mImpl->mBorderlineWidthIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_WIDTH, BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
1004       mImpl->mBorderlineColorIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_COLOR, BORDERLINE_COLOR, mImpl->mBorderlineColor);
1005       mImpl->mBorderlineOffsetIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_OFFSET, BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
1006
1007       // Borderline is animated now. we always have to use borderline feature.
1008       mImpl->mAlwaysUsingBorderline = true;
1009
1010       index = mImpl->mRenderer.GetPropertyIndex(key);
1011
1012       // Change shader
1013       UpdateShader();
1014     }
1015     else if(IsTypeAvailableForCornerRadius(mImpl->mType) && ((key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::CORNER_RADIUS) || (key.type == Property::Key::STRING && key.stringKey == CORNER_RADIUS)))
1016     {
1017       // Register CORNER_RADIUS property
1018       mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
1019       mImpl->mRenderer.RegisterProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
1020
1021       // ConerRadius is animated now. we always have to use corner radius feature.
1022       mImpl->mAlwaysUsingCornerRadius = true;
1023
1024       if(!IsBorderlineRequired())
1025       {
1026         // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
1027         mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
1028       }
1029
1030       index = mImpl->mCornerRadiusIndex;
1031
1032       // Change shader
1033       UpdateShader();
1034     }
1035     else
1036     {
1037       // We can't find the property in the base class.
1038       // Request to child class
1039       return OnGetPropertyObject(key);
1040     }
1041   }
1042
1043   return Dali::Property(mImpl->mRenderer, index);
1044 }
1045
1046 } // namespace Internal
1047
1048 } // namespace Toolkit
1049
1050 } // namespace Dali