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