Remove useless iteration when debug mode
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-base-impl.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "visual-base-impl.h"
20
21 // EXTERNAL HEADER
22 #include <dali-toolkit/public-api/dali-toolkit-common.h>
23 #include <dali/devel-api/rendering/renderer-devel.h>
24 #include <dali/devel-api/scripting/enum-helper.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/public-api/rendering/decorated-visual-renderer.h>
27 #include <dali/public-api/rendering/visual-renderer.h>
28
29 //INTERNAL HEARDER
30 #include <dali-toolkit/devel-api/visuals/visual-actions-devel.h>
31 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
32 #include <dali-toolkit/internal/helpers/property-helper.h>
33 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
34 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
35 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
36 #include <dali-toolkit/public-api/visuals/primitive-visual-properties.h>
37 #include <dali-toolkit/public-api/visuals/visual-properties.h>
38
39 namespace
40 {
41 #if defined(DEBUG_ENABLED)
42 Debug::Filter* gVisualBaseLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VISUAL_BASE");
43 #endif
44
45 // visual string constants contains OFFSET_SIZE_MODE instead
46 const char* const OFFSET_POLICY("offsetPolicy");
47 const char* const SIZE_POLICY("sizePolicy");
48
49 } // namespace
50
51 namespace Dali
52 {
53 namespace Toolkit
54 {
55 namespace Internal
56 {
57 namespace
58 {
59 DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_FITTING_MODE)
60   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_KEEP_ASPECT_RATIO)
61   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FILL)
62   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, OVER_FIT_KEEP_ASPECT_RATIO)
63   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, CENTER)
64   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_WIDTH)
65   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_HEIGHT)
66 DALI_ENUM_TO_STRING_TABLE_END(VISUAL_FITTING_MODE)
67
68 /**
69  * @brief Check whether this visual type can use corner radius feature or not.
70  * @param type VisualType that want to checkup
71  * @return true if type can use corner radius feature.
72  */
73 static bool IsTypeAvailableForCornerRadius(Toolkit::Visual::Type type)
74 {
75   switch(static_cast<Toolkit::DevelVisual::Type>(type))
76   {
77     case Toolkit::Visual::Type::COLOR:
78     case Toolkit::Visual::Type::GRADIENT:
79     case Toolkit::Visual::Type::IMAGE:
80     case Toolkit::Visual::Type::SVG:
81     case Toolkit::Visual::Type::ANIMATED_IMAGE:
82     case Toolkit::DevelVisual::Type::ANIMATED_VECTOR_IMAGE:
83     {
84       return true;
85     }
86     default:
87     {
88       return false;
89     }
90   }
91 }
92
93 /**
94  * @brief Check whether this visual type can use borderline feature or not.
95  * @param type VisualType that want to checkup
96  * @return true if type can use borderline feature.
97  */
98 static bool IsTypeAvailableForBorderline(Toolkit::Visual::Type type)
99 {
100   switch(static_cast<Toolkit::DevelVisual::Type>(type))
101   {
102     case Toolkit::Visual::Type::COLOR:
103     case Toolkit::Visual::Type::GRADIENT:
104     case Toolkit::Visual::Type::IMAGE:
105     case Toolkit::Visual::Type::SVG:
106     case Toolkit::Visual::Type::ANIMATED_IMAGE:
107     case Toolkit::DevelVisual::Type::ANIMATED_VECTOR_IMAGE:
108     {
109       return true;
110     }
111     default:
112     {
113       return false;
114     }
115   }
116 }
117
118 } // namespace
119
120 Visual::Base::Base(VisualFactoryCache& factoryCache, FittingMode fittingMode, Toolkit::Visual::Type type)
121 : mImpl(new Impl(fittingMode, type)),
122   mFactoryCache(factoryCache)
123 {
124 }
125
126 Visual::Base::~Base()
127 {
128   delete mImpl;
129 }
130
131 void Visual::Base::Initialize()
132 {
133   // The Renderer should be created inside derived class here.
134   OnInitialize();
135
136   if(mImpl->mRenderer)
137   {
138     RegisterMixColor();
139     RegisterDecoration();
140
141     if(IsBorderlineRequired())
142     {
143       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
144     }
145     else if(IsRoundedCornerRequired())
146     {
147       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
148     }
149   }
150 }
151
152 void Visual::Base::SetCustomShader(const Property::Map& shaderMap)
153 {
154   if(mImpl->mCustomShader)
155   {
156     mImpl->mCustomShader->SetPropertyMap(shaderMap);
157   }
158   else
159   {
160     mImpl->mCustomShader = new Impl::CustomShader(shaderMap);
161   }
162
163   // Let derived class know
164   UpdateShader();
165 }
166
167 void Visual::Base::SetProperties(const Property::Map& propertyMap)
168 {
169   bool needUpdateShader = false;
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
298         if(DALI_UNLIKELY(mImpl->mRenderer))
299         {
300           // Unusual case. SetProperty called after OnInitialize().
301           // Assume that DoAction call UPDATE_PROPERTY.
302           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterBorderlineUniform();
303           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
304
305           // Check whether we must update shader.
306           if(!mImpl->mAlwaysUsingBorderline && IsBorderlineRequired())
307           {
308             // Make Blend mode ON_WITHOUT_CULL for transparent mix color.
309             mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
310
311             // Change the shader must not be occured many times. we always have to use borderline feature.
312             mImpl->mAlwaysUsingBorderline = true;
313
314             // Change shader
315             if(!mImpl->mCustomShader)
316             {
317               needUpdateShader = true;
318             }
319           }
320         }
321         break;
322       }
323       case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
324       {
325         Vector4 color;
326         if(value.Get(color))
327         {
328           mImpl->mBorderlineColor = color;
329         }
330
331         if(DALI_UNLIKELY(mImpl->mRenderer))
332         {
333           // Unusual case. SetProperty called after OnInitialize().
334           // Assume that DoAction call UPDATE_PROPERTY.
335           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->mBorderlineColor);
336         }
337         break;
338       }
339       case Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
340       {
341         float offset;
342         if(value.Get(offset))
343         {
344           mImpl->mBorderlineOffset = offset;
345         }
346
347         if(DALI_UNLIKELY(mImpl->mRenderer))
348         {
349           // Unusual case. SetProperty called after OnInitialize().
350           // Assume that DoAction call UPDATE_PROPERTY.
351           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
352         }
353         break;
354       }
355       case Toolkit::DevelVisual::Property::CORNER_RADIUS:
356       {
357         if(value.GetType() == Property::VECTOR4)
358         {
359           // If CORNER_RADIUS Property is Vector4,
360           // Each values mean the radius of
361           // (top-left, top-right, bottom-right, bottom-left)
362           Vector4 radius;
363           if(value.Get(radius))
364           {
365             mImpl->mCornerRadius = radius;
366           }
367         }
368         else
369         {
370           // If CORNER_RADIUS Property is float,
371           // Every corner radius have same value
372           float radius;
373           if(value.Get(radius))
374           {
375             mImpl->mCornerRadius = Vector4(radius, radius, radius, radius);
376           }
377         }
378
379         if(DALI_UNLIKELY(mImpl->mRenderer))
380         {
381           // Unusual case. SetProperty called after OnInitialize().
382           // Assume that DoAction call UPDATE_PROPERTY.
383           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterCornerRadiusUniform();
384           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS, mImpl->mCornerRadius);
385
386           // Check whether we must update shader.
387           if(!mImpl->mAlwaysUsingCornerRadius && IsRoundedCornerRequired())
388           {
389             // Change the shader must not be occured many times. we always have to use corner radius feature.
390             mImpl->mAlwaysUsingCornerRadius = true;
391
392             if(!IsBorderlineRequired())
393             {
394               // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
395               mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
396             }
397
398             // Change shader
399             if(!mImpl->mCustomShader)
400             {
401               needUpdateShader = true;
402             }
403           }
404         }
405
406         break;
407       }
408       case Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY:
409       {
410         int policy;
411         if(value.Get(policy))
412         {
413           switch(policy)
414           {
415             case Toolkit::Visual::Transform::Policy::RELATIVE:
416             case Toolkit::Visual::Transform::Policy::ABSOLUTE:
417             {
418               mImpl->mCornerRadiusPolicy = policy;
419               if(DALI_UNLIKELY(mImpl->mRenderer))
420               {
421                 // Unusual case. SetProperty called after OnInitialize().
422                 // Assume that DoAction call UPDATE_PROPERTY.
423                 mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
424               }
425               break;
426             }
427             default:
428             {
429               DALI_LOG_ERROR("Unsupported policy: %d\n", policy);
430               break;
431             }
432           }
433         }
434         break;
435       }
436     }
437   }
438
439   DoSetProperties(propertyMap);
440
441   if(DALI_UNLIKELY(needUpdateShader))
442   {
443     UpdateShader();
444   }
445 }
446
447 void Visual::Base::SetTransformAndSize(const Property::Map& transform, Size controlSize)
448 {
449   mImpl->mControlSize = controlSize;
450   mImpl->mTransform.UpdatePropertyMap(transform);
451
452 #if defined(DEBUG_ENABLED)
453   std::ostringstream oss;
454   oss << transform;
455   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);
456 #endif
457
458   OnSetTransform();
459 }
460
461 void Visual::Base::SetName(const std::string& name)
462 {
463   mImpl->mName = name;
464 }
465
466 const std::string& Visual::Base::GetName() const
467 {
468   return mImpl->mName;
469 }
470
471 float Visual::Base::GetHeightForWidth(float width)
472 {
473   float   aspectCorrectedHeight = 0.f;
474   Vector2 naturalSize;
475   GetNaturalSize(naturalSize);
476   if(naturalSize.width)
477   {
478     aspectCorrectedHeight = naturalSize.height * width / naturalSize.width;
479   }
480   return aspectCorrectedHeight;
481 }
482
483 float Visual::Base::GetWidthForHeight(float height)
484 {
485   float   aspectCorrectedWidth = 0.f;
486   Vector2 naturalSize;
487   GetNaturalSize(naturalSize);
488   if(naturalSize.height > 0.0f)
489   {
490     aspectCorrectedWidth = naturalSize.width * height / naturalSize.height;
491   }
492   return aspectCorrectedWidth;
493 }
494
495 void Visual::Base::GetNaturalSize(Vector2& naturalSize)
496 {
497   naturalSize = Vector2::ZERO;
498 }
499
500 void Visual::Base::DoAction(const Property::Index actionId, const Property::Value attributes)
501 {
502   OnDoAction(actionId, attributes);
503
504   // Check if action is valid for this visual type and perform action if possible
505   switch(actionId)
506   {
507     case DevelVisual::Action::UPDATE_PROPERTY:
508     {
509       const Property::Map* map = attributes.GetMap();
510       if(map)
511       {
512         SetProperties(*map);
513       }
514       break;
515     }
516   }
517 }
518
519 void Visual::Base::SetDepthIndex(int index)
520 {
521   mImpl->mDepthIndex = index;
522   if(mImpl->mRenderer)
523   {
524     mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
525   }
526 }
527
528 int Visual::Base::GetDepthIndex() const
529 {
530   return mImpl->mDepthIndex;
531 }
532
533 void Visual::Base::SetOnScene(Actor& actor)
534 {
535   if(!IsOnScene())
536   {
537     // To display the actor correctly, renderer should not be added to actor until all required resources are ready.
538     // Thus the calling of actor.AddRenderer() should happen inside derived class as base class does not know the exact timing.
539     DoSetOnScene(actor);
540
541     if(mImpl->mRenderer)
542     {
543       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
544       mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
545     }
546
547     mImpl->mFlags |= Impl::IS_ON_SCENE;
548   }
549 }
550
551 void Visual::Base::SetOffScene(Actor& actor)
552 {
553   if(IsOnScene())
554   {
555     DoSetOffScene(actor);
556     mImpl->mFlags &= ~Impl::IS_ON_SCENE;
557   }
558 }
559
560 void Visual::Base::CreatePropertyMap(Property::Map& map) const
561 {
562   if(mImpl->mRenderer)
563   {
564     // Update values from Renderer
565     mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(VisualRenderer::Property::VISUAL_MIX_COLOR);
566     mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
567
568     mImpl->mTransform.mOffset = mImpl->mRenderer.GetProperty<Vector2>(VisualRenderer::Property::TRANSFORM_OFFSET);
569     mImpl->mTransform.mSize   = mImpl->mRenderer.GetProperty<Vector2>(VisualRenderer::Property::TRANSFORM_SIZE);
570
571     if(IsTypeAvailableForCornerRadius(mImpl->mType))
572     {
573       mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<Vector4>(DecoratedVisualRenderer::Property::CORNER_RADIUS);
574     }
575     if(IsTypeAvailableForBorderline(mImpl->mType))
576     {
577       mImpl->mBorderlineWidth  = mImpl->mRenderer.GetProperty<float>(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH);
578       mImpl->mBorderlineColor  = mImpl->mRenderer.GetProperty<Vector4>(DecoratedVisualRenderer::Property::BORDERLINE_COLOR);
579       mImpl->mBorderlineOffset = mImpl->mRenderer.GetProperty<float>(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET);
580     }
581   }
582
583   DoCreatePropertyMap(map);
584
585   if(mImpl->mCustomShader)
586   {
587     mImpl->mCustomShader->CreatePropertyMap(map);
588   }
589
590   Property::Map transform;
591   mImpl->mTransform.GetPropertyMap(transform);
592   map.Insert(Toolkit::Visual::Property::TRANSFORM, transform);
593
594   bool premultipliedAlpha(IsPreMultipliedAlphaEnabled());
595   map.Insert(Toolkit::Visual::Property::PREMULTIPLIED_ALPHA, premultipliedAlpha);
596
597   // Note, Color and Primitive will also insert their own mix color into the map
598   // which is ok, because they have a different key value range, but uses same cached value anyway.
599   map.Insert(Toolkit::Visual::Property::MIX_COLOR, mImpl->mMixColor); // vec4
600   map.Insert(Toolkit::Visual::Property::OPACITY, mImpl->mMixColor.a);
601
602   auto fittingModeString = Scripting::GetLinearEnumerationName<FittingMode>(
603     mImpl->mFittingMode, VISUAL_FITTING_MODE_TABLE, VISUAL_FITTING_MODE_TABLE_COUNT);
604   map.Insert(Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE, fittingModeString);
605
606   if(IsTypeAvailableForBorderline(mImpl->mType))
607   {
608     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
609     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_COLOR, mImpl->mBorderlineColor);
610     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
611   }
612
613   if(IsTypeAvailableForCornerRadius(mImpl->mType))
614   {
615     map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS, mImpl->mCornerRadius);
616     map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY, static_cast<int>(mImpl->mCornerRadiusPolicy));
617   }
618 }
619
620 void Visual::Base::CreateInstancePropertyMap(Property::Map& map) const
621 {
622   DoCreateInstancePropertyMap(map);
623
624   if(mImpl->mCustomShader)
625   {
626     mImpl->mCustomShader->CreatePropertyMap(map);
627   }
628 }
629
630 void Visual::Base::EnablePreMultipliedAlpha(bool preMultiplied)
631 {
632   if(preMultiplied)
633   {
634     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
635   }
636   else
637   {
638     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
639   }
640
641   if(mImpl->mRenderer)
642   {
643     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultiplied);
644     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultiplied);
645   }
646 }
647
648 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
649 {
650   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
651 }
652
653 void Visual::Base::DoSetOffScene(Actor& actor)
654 {
655   actor.RemoveRenderer(mImpl->mRenderer);
656 }
657
658 bool Visual::Base::IsOnScene() const
659 {
660   return mImpl->mFlags & Impl::IS_ON_SCENE;
661 }
662
663 bool Visual::Base::IsRoundedCornerRequired() const
664 {
665   // If VisualType doesn't support rounded corner, always return false.
666   if(IsTypeAvailableForCornerRadius(mImpl->mType))
667   {
668     if(mImpl->mRenderer)
669     {
670       // Update values from Renderer
671       Property::Value value = mImpl->mRenderer.GetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS);
672       value.Get(mImpl->mCornerRadius);
673     }
674     return mImpl->mAlwaysUsingCornerRadius || !(mImpl->mCornerRadius == Vector4::ZERO);
675   }
676   return false;
677 }
678
679 bool Visual::Base::IsBorderlineRequired() const
680 {
681   // If VisualType doesn't support borderline, always return false.
682   if(IsTypeAvailableForBorderline(mImpl->mType))
683   {
684     if(mImpl->mRenderer)
685     {
686       // Update values from Renderer
687       Property::Value value = mImpl->mRenderer.GetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH);
688       value.Get(mImpl->mBorderlineWidth);
689     }
690     return mImpl->mAlwaysUsingBorderline || !EqualsZero(mImpl->mBorderlineWidth);
691   }
692   return false;
693 }
694
695 void Visual::Base::OnDoAction(const Property::Index actionId, const Property::Value& attributes)
696 {
697   // May be overriden by derived class
698 }
699
700 void Visual::Base::RegisterMixColor()
701 {
702   if(mImpl->mRenderer)
703   {
704     // All visual renderers now use same mix color / opacity properties.
705     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(mImpl->mMixColor));
706     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, mImpl->mMixColor.a);
707
708     float preMultipliedAlpha = 0.0f;
709     if(IsPreMultipliedAlphaEnabled())
710     {
711       preMultipliedAlpha = 1.0f;
712     }
713     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultipliedAlpha);
714   }
715 }
716
717 void Visual::Base::RegisterDecoration()
718 {
719   if(mImpl->mRenderer)
720   {
721     if(IsTypeAvailableForCornerRadius(mImpl->mType))
722     {
723       if(mImpl->mAlwaysUsingCornerRadius || !(mImpl->mCornerRadius == Vector4::ZERO))
724       {
725         DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterCornerRadiusUniform();
726         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS, mImpl->mCornerRadius);
727         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
728       }
729     }
730     if(IsTypeAvailableForBorderline(mImpl->mType))
731     {
732       if(mImpl->mAlwaysUsingBorderline || !EqualsZero(mImpl->mBorderlineWidth))
733       {
734         DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterBorderlineUniform();
735         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
736         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->mBorderlineColor);
737         mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
738       }
739     }
740   }
741 }
742
743 void Visual::Base::SetMixColor(const Vector4& color)
744 {
745   mImpl->mMixColor = color;
746
747   if(mImpl->mRenderer)
748   {
749     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(color));
750     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, color.a);
751   }
752 }
753
754 void Visual::Base::SetMixColor(const Vector3& color)
755 {
756   mImpl->mMixColor.r = color.r;
757   mImpl->mMixColor.g = color.g;
758   mImpl->mMixColor.b = color.b;
759
760   if(mImpl->mRenderer)
761   {
762     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, color);
763   }
764 }
765
766 void Visual::Base::AddEventObserver(Visual::EventObserver& observer)
767 {
768   mImpl->mEventObserver = &observer;
769 }
770
771 void Visual::Base::RemoveEventObserver(Visual::EventObserver& observer)
772 {
773   mImpl->mEventObserver = NULL;
774 }
775
776 void Visual::Base::ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus)
777 {
778   if(mImpl->mResourceStatus != resourceStatus)
779   {
780     mImpl->mResourceStatus = resourceStatus;
781
782     if(mImpl->mEventObserver)
783     {
784       // observer is currently a control impl
785       mImpl->mEventObserver->ResourceReady(*this);
786     }
787   }
788 }
789
790 bool Visual::Base::IsResourceReady() const
791 {
792   return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
793           mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
794 }
795
796 bool Visual::Base::IsSynchronousLoadingRequired() const
797 {
798   return (mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING);
799 }
800
801 Toolkit::Visual::Type Visual::Base::GetType() const
802 {
803   return mImpl->mType;
804 }
805
806 Toolkit::Visual::ResourceStatus Visual::Base::GetResourceStatus() const
807 {
808   return mImpl->mResourceStatus;
809 }
810
811 Visual::FittingMode Visual::Base::GetFittingMode() const
812 {
813   return mImpl->mFittingMode;
814 }
815
816 Visual::Base& Visual::Base::GetVisualObject()
817 {
818   return *this;
819 }
820
821 Renderer Visual::Base::GetRenderer()
822 {
823   return mImpl->mRenderer;
824 }
825
826 Property::Index Visual::Base::GetIntKey(Property::Key key)
827 {
828   if(key.type == Property::Key::INDEX)
829   {
830     return key.indexKey;
831   }
832
833   if(key.stringKey == ANCHOR_POINT)
834   {
835     return Toolkit::Visual::Transform::Property::ANCHOR_POINT;
836   }
837   else if(key.stringKey == EXTRA_SIZE)
838   {
839     return Toolkit::DevelVisual::Transform::Property::EXTRA_SIZE;
840   }
841   else if(key.stringKey == MIX_COLOR)
842   {
843     return Toolkit::Visual::Property::MIX_COLOR;
844   }
845   else if(key.stringKey == OPACITY)
846   {
847     return Toolkit::Visual::Property::OPACITY;
848   }
849   else if(key.stringKey == OFFSET)
850   {
851     return Toolkit::Visual::Transform::Property::OFFSET;
852   }
853   else if(key.stringKey == OFFSET_POLICY)
854   {
855     return Toolkit::Visual::Transform::Property::OFFSET_POLICY;
856   }
857   else if(key.stringKey == ORIGIN)
858   {
859     return Toolkit::Visual::Transform::Property::ORIGIN;
860   }
861   else if(key.stringKey == PREMULTIPLIED_ALPHA)
862   {
863     return Toolkit::Visual::Property::PREMULTIPLIED_ALPHA;
864   }
865   else if(key.stringKey == CUSTOM_SHADER)
866   {
867     return Toolkit::Visual::Property::SHADER;
868   }
869   else if(key.stringKey == SIZE)
870   {
871     return Toolkit::Visual::Transform::Property::SIZE;
872   }
873   else if(key.stringKey == SIZE_POLICY)
874   {
875     return Toolkit::Visual::Transform::Property::SIZE_POLICY;
876   }
877   else if(key.stringKey == TRANSFORM)
878   {
879     return Toolkit::Visual::Property::TRANSFORM;
880   }
881   else if(key.stringKey == VISUAL_FITTING_MODE)
882   {
883     return Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE;
884   }
885   else if(key.stringKey == CORNER_RADIUS)
886   {
887     return Toolkit::DevelVisual::Property::CORNER_RADIUS;
888   }
889   else if(key.stringKey == CORNER_RADIUS_POLICY)
890   {
891     return Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY;
892   }
893   else if(key.stringKey == BORDERLINE_WIDTH)
894   {
895     return Toolkit::DevelVisual::Property::BORDERLINE_WIDTH;
896   }
897   else if(key.stringKey == BORDERLINE_COLOR)
898   {
899     return Toolkit::DevelVisual::Property::BORDERLINE_COLOR;
900   }
901   else if(key.stringKey == BORDERLINE_OFFSET)
902   {
903     return Toolkit::DevelVisual::Property::BORDERLINE_OFFSET;
904   }
905
906   return Property::INVALID_INDEX;
907 }
908
909 Property::Index Visual::Base::GetPropertyIndex(Property::Key key)
910 {
911   switch(GetIntKey(key))
912   {
913     case Dali::Toolkit::Visual::Transform::Property::OFFSET:
914     {
915       return VisualRenderer::Property::TRANSFORM_OFFSET;
916     }
917     case Dali::Toolkit::Visual::Transform::Property::SIZE:
918     {
919       return VisualRenderer::Property::TRANSFORM_SIZE;
920     }
921     case Dali::Toolkit::Visual::Transform::Property::ORIGIN:
922     {
923       return VisualRenderer::Property::TRANSFORM_ORIGIN;
924     }
925     case Dali::Toolkit::Visual::Transform::Property::ANCHOR_POINT:
926     {
927       return VisualRenderer::Property::TRANSFORM_ANCHOR_POINT;
928     }
929     case Dali::Toolkit::Visual::Property::MIX_COLOR:
930     {
931       return VisualRenderer::Property::VISUAL_MIX_COLOR;
932     }
933     case Dali::Toolkit::Visual::Property::OPACITY:
934     {
935       return DevelRenderer::Property::OPACITY;
936     }
937     case Dali::Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
938     {
939       return VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA;
940     }
941     case Dali::Toolkit::DevelVisual::Property::CORNER_RADIUS:
942     {
943       return DecoratedVisualRenderer::Property::CORNER_RADIUS;
944     }
945     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_WIDTH:
946     {
947       return DecoratedVisualRenderer::Property::BORDERLINE_WIDTH;
948     }
949     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
950     {
951       return DecoratedVisualRenderer::Property::BORDERLINE_COLOR;
952     }
953     case Dali::Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
954     {
955       return DecoratedVisualRenderer::Property::BORDERLINE_OFFSET;
956     }
957   }
958
959   Property::Index index = mImpl->mRenderer.GetPropertyIndex(key);
960
961   if(index == Property::INVALID_INDEX)
962   {
963     // Is it a shader property?
964     Shader shader = mImpl->mRenderer.GetShader();
965     index         = shader.GetPropertyIndex(key);
966     if(index != Property::INVALID_INDEX)
967     {
968       // Yes - we should register it in the Renderer so it can be set / animated
969       // independently, as shaders are shared across multiple renderers.
970       std::string     keyName;
971       Property::Index keyIndex(Property::INVALID_KEY);
972       if(key.type == Property::Key::INDEX)
973       {
974         keyName  = shader.GetPropertyName(index);
975         keyIndex = key.indexKey;
976       }
977       else
978       {
979         keyName = key.stringKey;
980         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
981       }
982       Property::Value value = shader.GetProperty(index);
983
984       // We already know that mRenderer didn't have property. So we can assume that it is unique.
985       index = mImpl->mRenderer.RegisterUniqueProperty(keyIndex, keyName, value);
986     }
987   }
988   return index;
989 }
990
991 void Visual::Base::SetupTransition(
992   Dali::Animation&                    transition,
993   Internal::TransitionData::Animator& animator,
994   Property::Index                     index,
995   Property::Value&                    initialValue,
996   Property::Value&                    targetValue)
997 {
998   if(index != Property::INVALID_INDEX)
999   {
1000     if(mImpl->mRenderer)
1001     {
1002       if(animator.animate == false)
1003       {
1004         mImpl->mRenderer.SetProperty(index, targetValue);
1005       }
1006       else
1007       {
1008         if(animator.initialValue.GetType() != Property::NONE)
1009         {
1010           mImpl->mRenderer.SetProperty(index, initialValue);
1011         }
1012
1013         if(!transition)
1014         {
1015           transition = Dali::Animation::New(0.1f);
1016         }
1017
1018         transition.AnimateTo(Property(mImpl->mRenderer, index),
1019                              targetValue,
1020                              animator.alphaFunction,
1021                              TimePeriod(animator.timePeriodDelay,
1022                                         animator.timePeriodDuration));
1023       }
1024     }
1025   }
1026 }
1027
1028 void Visual::Base::AnimateProperty(
1029   Dali::Animation&                    transition,
1030   Internal::TransitionData::Animator& animator)
1031 {
1032 #if defined(DEBUG_ENABLED)
1033   if(gVisualBaseLogFilter->IsEnabledFor(Debug::General))
1034   {
1035     std::ostringstream oss;
1036     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
1037     DALI_LOG_INFO(gVisualBaseLogFilter, Debug::General, oss.str().c_str());
1038   }
1039 #endif
1040
1041   if(animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
1042      animator.propertyKey == MIX_COLOR ||
1043      (mImpl->mType == Toolkit::Visual::COLOR &&
1044       animator.propertyKey == ColorVisual::Property::MIX_COLOR) ||
1045      (mImpl->mType == Toolkit::Visual::PRIMITIVE &&
1046       animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR))
1047   {
1048     AnimateMixColorProperty(transition, animator);
1049   }
1050   else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
1051           animator.propertyKey == OPACITY)
1052   {
1053     AnimateOpacityProperty(transition, animator);
1054   }
1055   else if(mImpl->mRenderer)
1056   {
1057     AnimateRendererProperty(transition, animator);
1058   }
1059 }
1060
1061 void Visual::Base::AnimateOpacityProperty(
1062   Dali::Animation&                    transition,
1063   Internal::TransitionData::Animator& animator)
1064 {
1065   float targetOpacity;
1066   if(animator.targetValue.Get(targetOpacity))
1067   {
1068     mImpl->mMixColor.a = targetOpacity;
1069   }
1070
1071   SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, animator.initialValue, animator.targetValue);
1072 }
1073
1074 void Visual::Base::AnimateRendererProperty(
1075   Dali::Animation&                    transition,
1076   Internal::TransitionData::Animator& animator)
1077 {
1078   // Get actual renderer index (will convert transform keys into visualproperty indices)
1079   Property::Index index = GetPropertyIndex(animator.propertyKey);
1080
1081   if(index != Property::INVALID_INDEX)
1082   {
1083     if(animator.targetValue.GetType() != Property::NONE)
1084     {
1085       // Try writing target value into transform property map
1086       // if it's not a valid key, then it won't alter mTransform
1087       Property::Map map;
1088       if(animator.propertyKey.type == Property::Key::INDEX)
1089       {
1090         map.Add(animator.propertyKey.indexKey, animator.targetValue);
1091       }
1092       else
1093       {
1094         map.Add(animator.propertyKey.stringKey, animator.targetValue);
1095       }
1096
1097       mImpl->mTransform.UpdatePropertyMap(map);
1098     }
1099     SetupTransition(transition, animator, index, animator.initialValue, animator.targetValue);
1100   }
1101 }
1102
1103 void Visual::Base::AnimateMixColorProperty(
1104   Dali::Animation&                    transition,
1105   Internal::TransitionData::Animator& animator)
1106 {
1107   bool animateOpacity = false;
1108
1109   Property::Value initialOpacity;
1110   Property::Value targetOpacity;
1111   Property::Value initialMixColor;
1112   Property::Value targetMixColor;
1113
1114   Vector4 initialColor;
1115   if(animator.initialValue.Get(initialColor))
1116   {
1117     if(animator.initialValue.GetType() == Property::VECTOR4)
1118     {
1119       // if there is an initial color specifying alpha, test it
1120       initialOpacity = initialColor.a;
1121     }
1122     initialMixColor = Vector3(initialColor);
1123   }
1124
1125   // Set target value into data store
1126   if(animator.targetValue.GetType() != Property::NONE)
1127   {
1128     Vector4 mixColor;
1129     animator.targetValue.Get(mixColor);
1130     if(animator.targetValue.GetType() == Property::VECTOR4)
1131     {
1132       mImpl->mMixColor.a = mixColor.a;
1133       targetOpacity      = mixColor.a;
1134       animateOpacity     = true;
1135     }
1136
1137     mImpl->mMixColor.r = mixColor.r;
1138     mImpl->mMixColor.g = mixColor.g;
1139     mImpl->mMixColor.b = mixColor.b;
1140     targetMixColor     = Vector3(mixColor);
1141   }
1142
1143   SetupTransition(transition, animator, VisualRenderer::Property::VISUAL_MIX_COLOR, initialMixColor, targetMixColor);
1144
1145   if(animateOpacity)
1146   {
1147     SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, initialOpacity, targetOpacity);
1148   }
1149 }
1150
1151 Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
1152 {
1153   if(!mImpl->mRenderer)
1154   {
1155     Handle handle;
1156     return Dali::Property(handle, Property::INVALID_INDEX);
1157   }
1158
1159   switch(GetIntKey(key))
1160   {
1161     // Default animatable properties from VisualRenderer
1162     case Toolkit::Visual::Property::MIX_COLOR:
1163     {
1164       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1165     }
1166     case Toolkit::Visual::Property::OPACITY:
1167     {
1168       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
1169     }
1170     case Toolkit::Visual::Transform::Property::OFFSET:
1171     {
1172       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_OFFSET);
1173     }
1174     case Toolkit::Visual::Transform::Property::SIZE:
1175     {
1176       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_SIZE);
1177     }
1178
1179     // Default animatable properties from DecoratedVisualRenderer
1180     case Toolkit::DevelVisual::Property::CORNER_RADIUS:
1181     {
1182       if(IsTypeAvailableForCornerRadius(mImpl->mType))
1183       {
1184         const bool updateShader = !mImpl->mCustomShader && !IsRoundedCornerRequired();
1185
1186         // CornerRadius is animated now. we always have to use corner radius feature.
1187         mImpl->mAlwaysUsingCornerRadius = true;
1188
1189         if(updateShader)
1190         {
1191           // Update each values to renderer
1192           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterCornerRadiusUniform();
1193           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS, mImpl->mCornerRadius);
1194           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
1195
1196           // Change shader
1197           UpdateShader();
1198         }
1199         if(!IsBorderlineRequired())
1200         {
1201           // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
1202           mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
1203         }
1204         return Dali::Property(mImpl->mRenderer, DecoratedVisualRenderer::Property::CORNER_RADIUS);
1205       }
1206       break;
1207     }
1208     case Toolkit::DevelVisual::Property::BORDERLINE_WIDTH:
1209     case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
1210     case Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
1211     {
1212       if(IsTypeAvailableForBorderline(mImpl->mType))
1213       {
1214         const bool updateShader = !mImpl->mCustomShader && !IsBorderlineRequired();
1215
1216         // Borderline is animated now. we always have to use borderline feature.
1217         mImpl->mAlwaysUsingBorderline = true;
1218
1219         if(updateShader)
1220         {
1221           // Update each values to renderer
1222           DownCast<DecoratedVisualRenderer>(mImpl->mRenderer).RegisterBorderlineUniform();
1223           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
1224           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_COLOR, mImpl->mBorderlineColor);
1225           mImpl->mRenderer.SetProperty(DecoratedVisualRenderer::Property::BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
1226
1227           // Change shader
1228           UpdateShader();
1229         }
1230         mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
1231
1232         return Dali::Property(mImpl->mRenderer, GetPropertyIndex(key));
1233       }
1234       break;
1235     }
1236     // Special case for MIX_COLOR
1237     default:
1238     {
1239       if(key.type == Property::Key::INDEX &&
1240          ((mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) ||
1241           (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR)))
1242       {
1243         return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1244       }
1245     }
1246   }
1247
1248   // We can't find the property in the base class.
1249   // Request to child class
1250   return OnGetPropertyObject(key);
1251 }
1252
1253 } // namespace Internal
1254
1255 } // namespace Toolkit
1256
1257 } // namespace Dali