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