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