Fix webp&gif issue
[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           mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
770 }
771
772 bool Visual::Base::IsSynchronousLoadingRequired() const
773 {
774   return (mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING);
775 }
776
777 Toolkit::Visual::Type Visual::Base::GetType() const
778 {
779   return mImpl->mType;
780 }
781
782 Toolkit::Visual::ResourceStatus Visual::Base::GetResourceStatus() const
783 {
784   return mImpl->mResourceStatus;
785 }
786
787 Visual::FittingMode Visual::Base::GetFittingMode() const
788 {
789   return mImpl->mFittingMode;
790 }
791
792 Visual::Base& Visual::Base::GetVisualObject()
793 {
794   return *this;
795 }
796
797 Renderer Visual::Base::GetRenderer()
798 {
799   return mImpl->mRenderer;
800 }
801
802 Property::Index Visual::Base::GetIntKey(Property::Key key)
803 {
804   if(key.type == Property::Key::INDEX)
805   {
806     return key.indexKey;
807   }
808
809   if(key.stringKey == ANCHOR_POINT)
810   {
811     return Toolkit::Visual::Transform::Property::ANCHOR_POINT;
812   }
813   else if(key.stringKey == EXTRA_SIZE)
814   {
815     return Toolkit::DevelVisual::Transform::Property::EXTRA_SIZE;
816   }
817   else if(key.stringKey == MIX_COLOR)
818   {
819     return Toolkit::Visual::Property::MIX_COLOR;
820   }
821   else if(key.stringKey == OPACITY)
822   {
823     return Toolkit::Visual::Property::OPACITY;
824   }
825   else if(key.stringKey == OFFSET)
826   {
827     return Toolkit::Visual::Transform::Property::OFFSET;
828   }
829   else if(key.stringKey == OFFSET_POLICY)
830   {
831     return Toolkit::Visual::Transform::Property::OFFSET_POLICY;
832   }
833   else if(key.stringKey == ORIGIN)
834   {
835     return Toolkit::Visual::Transform::Property::ORIGIN;
836   }
837   else if(key.stringKey == PREMULTIPLIED_ALPHA)
838   {
839     return Toolkit::Visual::Property::PREMULTIPLIED_ALPHA;
840   }
841   else if(key.stringKey == CUSTOM_SHADER)
842   {
843     return Toolkit::Visual::Property::SHADER;
844   }
845   else if(key.stringKey == SIZE)
846   {
847     return Toolkit::Visual::Transform::Property::SIZE;
848   }
849   else if(key.stringKey == SIZE_POLICY)
850   {
851     return Toolkit::Visual::Transform::Property::SIZE_POLICY;
852   }
853   else if(key.stringKey == TRANSFORM)
854   {
855     return Toolkit::Visual::Property::TRANSFORM;
856   }
857   else if(key.stringKey == VISUAL_FITTING_MODE)
858   {
859     return Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE;
860   }
861   else if(key.stringKey == CORNER_RADIUS)
862   {
863     return Toolkit::DevelVisual::Property::CORNER_RADIUS;
864   }
865   else if(key.stringKey == CORNER_RADIUS_POLICY)
866   {
867     return Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY;
868   }
869   else if(key.stringKey == BORDERLINE_WIDTH)
870   {
871     return Toolkit::DevelVisual::Property::BORDERLINE_WIDTH;
872   }
873   else if(key.stringKey == BORDERLINE_COLOR)
874   {
875     return Toolkit::DevelVisual::Property::BORDERLINE_COLOR;
876   }
877   else if(key.stringKey == BORDERLINE_OFFSET)
878   {
879     return Toolkit::DevelVisual::Property::BORDERLINE_OFFSET;
880   }
881
882   return Property::INVALID_INDEX;
883 }
884
885 Property::Index Visual::Base::GetPropertyIndex(Property::Key key)
886 {
887   switch(GetIntKey(key))
888   {
889     case Dali::Toolkit::Visual::Transform::Property::OFFSET:
890     {
891       return VisualRenderer::Property::TRANSFORM_OFFSET;
892     }
893     case Dali::Toolkit::Visual::Transform::Property::SIZE:
894     {
895       return VisualRenderer::Property::TRANSFORM_SIZE;
896     }
897     case Dali::Toolkit::Visual::Transform::Property::ORIGIN:
898     {
899       return VisualRenderer::Property::TRANSFORM_ORIGIN;
900     }
901     case Dali::Toolkit::Visual::Transform::Property::ANCHOR_POINT:
902     {
903       return VisualRenderer::Property::TRANSFORM_ANCHOR_POINT;
904     }
905     case Dali::Toolkit::Visual::Property::MIX_COLOR:
906     {
907       return VisualRenderer::Property::VISUAL_MIX_COLOR;
908     }
909     case Dali::Toolkit::Visual::Property::OPACITY:
910     {
911       return DevelRenderer::Property::OPACITY;
912     }
913     case Dali::Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
914     {
915       return VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA;
916     }
917   }
918
919   Property::Index index = mImpl->mRenderer.GetPropertyIndex(key);
920
921   if(index == Property::INVALID_INDEX)
922   {
923     // Is it a shader property?
924     Shader shader = mImpl->mRenderer.GetShader();
925     index         = shader.GetPropertyIndex(key);
926     if(index != Property::INVALID_INDEX)
927     {
928       // Yes - we should register it in the Renderer so it can be set / animated
929       // independently, as shaders are shared across multiple renderers.
930       std::string     keyName;
931       Property::Index keyIndex(Property::INVALID_KEY);
932       if(key.type == Property::Key::INDEX)
933       {
934         keyName  = shader.GetPropertyName(index);
935         keyIndex = key.indexKey;
936       }
937       else
938       {
939         keyName = key.stringKey;
940         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
941       }
942       Property::Value value = shader.GetProperty(index);
943
944       // We already know that mRenderer didn't have property. So we can assume that it is unique.
945       index = mImpl->mRenderer.RegisterUniqueProperty(keyIndex, keyName, value);
946     }
947   }
948   return index;
949 }
950
951 void Visual::Base::SetupTransition(
952   Dali::Animation&                    transition,
953   Internal::TransitionData::Animator& animator,
954   Property::Index                     index,
955   Property::Value&                    initialValue,
956   Property::Value&                    targetValue)
957 {
958   if(index != Property::INVALID_INDEX)
959   {
960     if(mImpl->mRenderer)
961     {
962       if(animator.animate == false)
963       {
964         mImpl->mRenderer.SetProperty(index, targetValue);
965       }
966       else
967       {
968         if(animator.initialValue.GetType() != Property::NONE)
969         {
970           mImpl->mRenderer.SetProperty(index, initialValue);
971         }
972
973         if(!transition)
974         {
975           transition = Dali::Animation::New(0.1f);
976         }
977
978         transition.AnimateTo(Property(mImpl->mRenderer, index),
979                              targetValue,
980                              animator.alphaFunction,
981                              TimePeriod(animator.timePeriodDelay,
982                                         animator.timePeriodDuration));
983       }
984     }
985   }
986 }
987
988 void Visual::Base::AnimateProperty(
989   Dali::Animation&                    transition,
990   Internal::TransitionData::Animator& animator)
991 {
992 #if defined(DEBUG_ENABLED)
993   {
994     std::ostringstream oss;
995     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
996     DALI_LOG_INFO(gVisualBaseLogFilter, Debug::General, oss.str().c_str());
997   }
998 #endif
999
1000   if(animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
1001      animator.propertyKey == MIX_COLOR ||
1002      (mImpl->mType == Toolkit::Visual::COLOR &&
1003       animator.propertyKey == ColorVisual::Property::MIX_COLOR) ||
1004      (mImpl->mType == Toolkit::Visual::PRIMITIVE &&
1005       animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR))
1006   {
1007     AnimateMixColorProperty(transition, animator);
1008   }
1009   else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
1010           animator.propertyKey == OPACITY)
1011   {
1012     AnimateOpacityProperty(transition, animator);
1013   }
1014   else if(mImpl->mRenderer)
1015   {
1016     AnimateRendererProperty(transition, animator);
1017   }
1018 }
1019
1020 void Visual::Base::AnimateOpacityProperty(
1021   Dali::Animation&                    transition,
1022   Internal::TransitionData::Animator& animator)
1023 {
1024   float targetOpacity;
1025   if(animator.targetValue.Get(targetOpacity))
1026   {
1027     mImpl->mMixColor.a = targetOpacity;
1028   }
1029
1030   SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, animator.initialValue, animator.targetValue);
1031 }
1032
1033 void Visual::Base::AnimateRendererProperty(
1034   Dali::Animation&                    transition,
1035   Internal::TransitionData::Animator& animator)
1036 {
1037   // Get actual renderer index (will convert transform keys into visualproperty indices)
1038   Property::Index index = GetPropertyIndex(animator.propertyKey);
1039
1040   if(index != Property::INVALID_INDEX)
1041   {
1042     if(animator.targetValue.GetType() != Property::NONE)
1043     {
1044       // Try writing target value into transform property map
1045       // if it's not a valid key, then it won't alter mTransform
1046       Property::Map map;
1047       if(animator.propertyKey.type == Property::Key::INDEX)
1048       {
1049         map.Add(animator.propertyKey.indexKey, animator.targetValue);
1050       }
1051       else
1052       {
1053         map.Add(animator.propertyKey.stringKey, animator.targetValue);
1054       }
1055
1056       mImpl->mTransform.UpdatePropertyMap(map);
1057     }
1058     SetupTransition(transition, animator, index, animator.initialValue, animator.targetValue);
1059   }
1060 }
1061
1062 void Visual::Base::AnimateMixColorProperty(
1063   Dali::Animation&                    transition,
1064   Internal::TransitionData::Animator& animator)
1065 {
1066   bool animateOpacity = false;
1067
1068   Property::Value initialOpacity;
1069   Property::Value targetOpacity;
1070   Property::Value initialMixColor;
1071   Property::Value targetMixColor;
1072
1073   Vector4 initialColor;
1074   if(animator.initialValue.Get(initialColor))
1075   {
1076     if(animator.initialValue.GetType() == Property::VECTOR4)
1077     {
1078       // if there is an initial color specifying alpha, test it
1079       initialOpacity = initialColor.a;
1080     }
1081     initialMixColor = Vector3(initialColor);
1082   }
1083
1084   // Set target value into data store
1085   if(animator.targetValue.GetType() != Property::NONE)
1086   {
1087     Vector4 mixColor;
1088     animator.targetValue.Get(mixColor);
1089     if(animator.targetValue.GetType() == Property::VECTOR4)
1090     {
1091       mImpl->mMixColor.a = mixColor.a;
1092       targetOpacity      = mixColor.a;
1093       animateOpacity     = true;
1094     }
1095
1096     mImpl->mMixColor.r = mixColor.r;
1097     mImpl->mMixColor.g = mixColor.g;
1098     mImpl->mMixColor.b = mixColor.b;
1099     targetMixColor     = Vector3(mixColor);
1100   }
1101
1102   SetupTransition(transition, animator, VisualRenderer::Property::VISUAL_MIX_COLOR, initialMixColor, targetMixColor);
1103
1104   if(animateOpacity)
1105   {
1106     SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, initialOpacity, targetOpacity);
1107   }
1108 }
1109
1110 Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
1111 {
1112   if(!mImpl->mRenderer)
1113   {
1114     Handle handle;
1115     return Dali::Property(handle, Property::INVALID_INDEX);
1116   }
1117
1118   // Default animatable properties from VisualRenderer
1119   switch(GetIntKey(key))
1120   {
1121     case Toolkit::Visual::Property::MIX_COLOR:
1122     {
1123       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1124     }
1125     case Toolkit::Visual::Property::OPACITY:
1126     {
1127       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
1128     }
1129     case Toolkit::Visual::Transform::Property::OFFSET:
1130     {
1131       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_OFFSET);
1132     }
1133     case Toolkit::Visual::Transform::Property::SIZE:
1134     {
1135       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_SIZE);
1136     }
1137     default:
1138     {
1139       if(key.type == Property::Key::INDEX &&
1140          ((mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) ||
1141           (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR)))
1142       {
1143         return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1144       }
1145     }
1146   }
1147
1148   // Other cases
1149   Property::Index index = GetPropertyIndex(key);
1150   if(index == Property::INVALID_INDEX)
1151   {
1152     if(IsTypeAvailableForBorderline(mImpl->mType) &&
1153        ((key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_WIDTH) || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_WIDTH) ||
1154         (key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_COLOR) || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_COLOR) ||
1155         (key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_OFFSET) || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_OFFSET)))
1156     {
1157       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
1158
1159       // Register borderline properties
1160       mImpl->mBorderlineWidthIndex  = mImpl->mRenderer.RegisterUniqueProperty(DevelVisual::Property::BORDERLINE_WIDTH, BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
1161       mImpl->mBorderlineColorIndex  = mImpl->mRenderer.RegisterUniqueProperty(DevelVisual::Property::BORDERLINE_COLOR, BORDERLINE_COLOR, mImpl->mBorderlineColor);
1162       mImpl->mBorderlineOffsetIndex = mImpl->mRenderer.RegisterUniqueProperty(DevelVisual::Property::BORDERLINE_OFFSET, BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
1163
1164       // Borderline is animated now. we always have to use borderline feature.
1165       mImpl->mAlwaysUsingBorderline = true;
1166
1167       index = mImpl->mRenderer.GetPropertyIndex(key);
1168
1169       // Change shader
1170       UpdateShader();
1171     }
1172     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)))
1173     {
1174       // Register CORNER_RADIUS property
1175       mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterUniqueProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
1176       mImpl->mRenderer.RegisterUniqueProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
1177
1178       // CornerRadius is animated now. we always have to use corner radius feature.
1179       mImpl->mAlwaysUsingCornerRadius = true;
1180
1181       if(!IsBorderlineRequired())
1182       {
1183         // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
1184         mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
1185       }
1186
1187       index = mImpl->mCornerRadiusIndex;
1188
1189       // Change shader
1190       UpdateShader();
1191     }
1192     else
1193     {
1194       // We can't find the property in the base class.
1195       // Request to child class
1196       return OnGetPropertyObject(key);
1197     }
1198   }
1199   else
1200   {
1201     if(index == mImpl->mBorderlineWidthIndex ||
1202        index == mImpl->mBorderlineColorIndex ||
1203        index == mImpl->mBorderlineOffsetIndex)
1204     {
1205       // Borderline is animated now. we always have to use borderline feature.
1206       mImpl->mAlwaysUsingBorderline = true;
1207     }
1208     if(index == mImpl->mCornerRadiusIndex)
1209     {
1210       // CornerRadius is animated now. we always have to use corner radius feature.
1211       mImpl->mAlwaysUsingCornerRadius = true;
1212     }
1213   }
1214
1215   return Dali::Property(mImpl->mRenderer, index);
1216 }
1217
1218 } // namespace Internal
1219
1220 } // namespace Toolkit
1221
1222 } // namespace Dali