Create visual's border property map for only valid type
[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   if(IsTypeAvailableForBorderline(mImpl->mType))
617   {
618     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
619     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_COLOR, mImpl->mBorderlineColor);
620     map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
621   }
622
623   if(IsTypeAvailableForCornerRadius(mImpl->mType))
624   {
625     map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS, mImpl->mCornerRadius);
626     map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY, static_cast<int>(mImpl->mCornerRadiusPolicy));
627   }
628 }
629
630 void Visual::Base::CreateInstancePropertyMap(Property::Map& map) const
631 {
632   DoCreateInstancePropertyMap(map);
633
634   if(mImpl->mCustomShader)
635   {
636     mImpl->mCustomShader->CreatePropertyMap(map);
637   }
638 }
639
640 void Visual::Base::EnablePreMultipliedAlpha(bool preMultiplied)
641 {
642   if(preMultiplied)
643   {
644     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
645   }
646   else
647   {
648     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
649   }
650
651   if(mImpl->mRenderer)
652   {
653     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultiplied);
654     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultiplied);
655   }
656 }
657
658 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
659 {
660   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
661 }
662
663 void Visual::Base::DoSetOffScene(Actor& actor)
664 {
665   actor.RemoveRenderer(mImpl->mRenderer);
666 }
667
668 bool Visual::Base::IsOnScene() const
669 {
670   return mImpl->mFlags & Impl::IS_ON_SCENE;
671 }
672
673 bool Visual::Base::IsRoundedCornerRequired() const
674 {
675   // If VisualType doesn't support rounded corner, always return false.
676   if(IsTypeAvailableForCornerRadius(mImpl->mType))
677   {
678     if(mImpl->mRenderer && mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
679     {
680       // Update values from Renderer
681       mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<Vector4>(mImpl->mCornerRadiusIndex);
682     }
683     return !(mImpl->mCornerRadius == Vector4::ZERO) || mImpl->mAlwaysUsingCornerRadius;
684   }
685   return false;
686 }
687
688 bool Visual::Base::IsBorderlineRequired() const
689 {
690   // If VisualType doesn't support borderline, always return false.
691   if(IsTypeAvailableForBorderline(mImpl->mType))
692   {
693     if(mImpl->mRenderer && mImpl->mBorderlineWidthIndex != Property::INVALID_INDEX)
694     {
695       // Update values from Renderer
696       mImpl->mBorderlineWidth = mImpl->mRenderer.GetProperty<float>(mImpl->mBorderlineWidthIndex);
697     }
698     return !EqualsZero(mImpl->mBorderlineWidth) || mImpl->mAlwaysUsingBorderline;
699   }
700   return false;
701 }
702
703 void Visual::Base::OnDoAction(const Property::Index actionId, const Property::Value& attributes)
704 {
705   // May be overriden by derived class
706 }
707
708 void Visual::Base::RegisterMixColor()
709 {
710   if(mImpl->mRenderer)
711   {
712     // All visual renderers now use same mix color / opacity properties.
713     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(mImpl->mMixColor));
714     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, mImpl->mMixColor.a);
715
716     float preMultipliedAlpha = 0.0f;
717     if(IsPreMultipliedAlphaEnabled())
718     {
719       preMultipliedAlpha = 1.0f;
720     }
721     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA, preMultipliedAlpha);
722   }
723 }
724
725 void Visual::Base::SetMixColor(const Vector4& color)
726 {
727   mImpl->mMixColor = color;
728
729   if(mImpl->mRenderer)
730   {
731     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, Vector3(color));
732     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, color.a);
733   }
734 }
735
736 void Visual::Base::SetMixColor(const Vector3& color)
737 {
738   mImpl->mMixColor.r = color.r;
739   mImpl->mMixColor.g = color.g;
740   mImpl->mMixColor.b = color.b;
741
742   if(mImpl->mRenderer)
743   {
744     mImpl->mRenderer.SetProperty(VisualRenderer::Property::VISUAL_MIX_COLOR, color);
745   }
746 }
747
748 void Visual::Base::AddEventObserver(Visual::EventObserver& observer)
749 {
750   mImpl->mEventObserver = &observer;
751 }
752
753 void Visual::Base::RemoveEventObserver(Visual::EventObserver& observer)
754 {
755   mImpl->mEventObserver = NULL;
756 }
757
758 void Visual::Base::ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus)
759 {
760   if(mImpl->mResourceStatus != resourceStatus)
761   {
762     mImpl->mResourceStatus = resourceStatus;
763
764     if(mImpl->mEventObserver)
765     {
766       // observer is currently a control impl
767       mImpl->mEventObserver->ResourceReady(*this);
768     }
769   }
770 }
771
772 bool Visual::Base::IsResourceReady() const
773 {
774   return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
775           mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
776 }
777
778 bool Visual::Base::IsSynchronousLoadingRequired() const
779 {
780   return (mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING);
781 }
782
783 Toolkit::Visual::Type Visual::Base::GetType() const
784 {
785   return mImpl->mType;
786 }
787
788 Toolkit::Visual::ResourceStatus Visual::Base::GetResourceStatus() const
789 {
790   return mImpl->mResourceStatus;
791 }
792
793 Visual::FittingMode Visual::Base::GetFittingMode() const
794 {
795   return mImpl->mFittingMode;
796 }
797
798 Visual::Base& Visual::Base::GetVisualObject()
799 {
800   return *this;
801 }
802
803 Renderer Visual::Base::GetRenderer()
804 {
805   return mImpl->mRenderer;
806 }
807
808 Property::Index Visual::Base::GetIntKey(Property::Key key)
809 {
810   if(key.type == Property::Key::INDEX)
811   {
812     return key.indexKey;
813   }
814
815   if(key.stringKey == ANCHOR_POINT)
816   {
817     return Toolkit::Visual::Transform::Property::ANCHOR_POINT;
818   }
819   else if(key.stringKey == EXTRA_SIZE)
820   {
821     return Toolkit::DevelVisual::Transform::Property::EXTRA_SIZE;
822   }
823   else if(key.stringKey == MIX_COLOR)
824   {
825     return Toolkit::Visual::Property::MIX_COLOR;
826   }
827   else if(key.stringKey == OPACITY)
828   {
829     return Toolkit::Visual::Property::OPACITY;
830   }
831   else if(key.stringKey == OFFSET)
832   {
833     return Toolkit::Visual::Transform::Property::OFFSET;
834   }
835   else if(key.stringKey == OFFSET_POLICY)
836   {
837     return Toolkit::Visual::Transform::Property::OFFSET_POLICY;
838   }
839   else if(key.stringKey == ORIGIN)
840   {
841     return Toolkit::Visual::Transform::Property::ORIGIN;
842   }
843   else if(key.stringKey == PREMULTIPLIED_ALPHA)
844   {
845     return Toolkit::Visual::Property::PREMULTIPLIED_ALPHA;
846   }
847   else if(key.stringKey == CUSTOM_SHADER)
848   {
849     return Toolkit::Visual::Property::SHADER;
850   }
851   else if(key.stringKey == SIZE)
852   {
853     return Toolkit::Visual::Transform::Property::SIZE;
854   }
855   else if(key.stringKey == SIZE_POLICY)
856   {
857     return Toolkit::Visual::Transform::Property::SIZE_POLICY;
858   }
859   else if(key.stringKey == TRANSFORM)
860   {
861     return Toolkit::Visual::Property::TRANSFORM;
862   }
863   else if(key.stringKey == VISUAL_FITTING_MODE)
864   {
865     return Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE;
866   }
867   else if(key.stringKey == CORNER_RADIUS)
868   {
869     return Toolkit::DevelVisual::Property::CORNER_RADIUS;
870   }
871   else if(key.stringKey == CORNER_RADIUS_POLICY)
872   {
873     return Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY;
874   }
875   else if(key.stringKey == BORDERLINE_WIDTH)
876   {
877     return Toolkit::DevelVisual::Property::BORDERLINE_WIDTH;
878   }
879   else if(key.stringKey == BORDERLINE_COLOR)
880   {
881     return Toolkit::DevelVisual::Property::BORDERLINE_COLOR;
882   }
883   else if(key.stringKey == BORDERLINE_OFFSET)
884   {
885     return Toolkit::DevelVisual::Property::BORDERLINE_OFFSET;
886   }
887
888   return Property::INVALID_INDEX;
889 }
890
891 Property::Index Visual::Base::GetPropertyIndex(Property::Key key)
892 {
893   switch(GetIntKey(key))
894   {
895     case Dali::Toolkit::Visual::Transform::Property::OFFSET:
896     {
897       return VisualRenderer::Property::TRANSFORM_OFFSET;
898     }
899     case Dali::Toolkit::Visual::Transform::Property::SIZE:
900     {
901       return VisualRenderer::Property::TRANSFORM_SIZE;
902     }
903     case Dali::Toolkit::Visual::Transform::Property::ORIGIN:
904     {
905       return VisualRenderer::Property::TRANSFORM_ORIGIN;
906     }
907     case Dali::Toolkit::Visual::Transform::Property::ANCHOR_POINT:
908     {
909       return VisualRenderer::Property::TRANSFORM_ANCHOR_POINT;
910     }
911     case Dali::Toolkit::Visual::Property::MIX_COLOR:
912     {
913       return VisualRenderer::Property::VISUAL_MIX_COLOR;
914     }
915     case Dali::Toolkit::Visual::Property::OPACITY:
916     {
917       return DevelRenderer::Property::OPACITY;
918     }
919     case Dali::Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
920     {
921       return VisualRenderer::Property::VISUAL_PRE_MULTIPLIED_ALPHA;
922     }
923   }
924
925   Property::Index index = mImpl->mRenderer.GetPropertyIndex(key);
926
927   if(index == Property::INVALID_INDEX)
928   {
929     // Is it a shader property?
930     Shader shader = mImpl->mRenderer.GetShader();
931     index         = shader.GetPropertyIndex(key);
932     if(index != Property::INVALID_INDEX)
933     {
934       // Yes - we should register it in the Renderer so it can be set / animated
935       // independently, as shaders are shared across multiple renderers.
936       std::string     keyName;
937       Property::Index keyIndex(Property::INVALID_KEY);
938       if(key.type == Property::Key::INDEX)
939       {
940         keyName  = shader.GetPropertyName(index);
941         keyIndex = key.indexKey;
942       }
943       else
944       {
945         keyName = key.stringKey;
946         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
947       }
948       Property::Value value = shader.GetProperty(index);
949
950       // We already know that mRenderer didn't have property. So we can assume that it is unique.
951       index = mImpl->mRenderer.RegisterUniqueProperty(keyIndex, keyName, value);
952     }
953   }
954   return index;
955 }
956
957 void Visual::Base::SetupTransition(
958   Dali::Animation&                    transition,
959   Internal::TransitionData::Animator& animator,
960   Property::Index                     index,
961   Property::Value&                    initialValue,
962   Property::Value&                    targetValue)
963 {
964   if(index != Property::INVALID_INDEX)
965   {
966     if(mImpl->mRenderer)
967     {
968       if(animator.animate == false)
969       {
970         mImpl->mRenderer.SetProperty(index, targetValue);
971       }
972       else
973       {
974         if(animator.initialValue.GetType() != Property::NONE)
975         {
976           mImpl->mRenderer.SetProperty(index, initialValue);
977         }
978
979         if(!transition)
980         {
981           transition = Dali::Animation::New(0.1f);
982         }
983
984         transition.AnimateTo(Property(mImpl->mRenderer, index),
985                              targetValue,
986                              animator.alphaFunction,
987                              TimePeriod(animator.timePeriodDelay,
988                                         animator.timePeriodDuration));
989       }
990     }
991   }
992 }
993
994 void Visual::Base::AnimateProperty(
995   Dali::Animation&                    transition,
996   Internal::TransitionData::Animator& animator)
997 {
998 #if defined(DEBUG_ENABLED)
999   {
1000     std::ostringstream oss;
1001     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
1002     DALI_LOG_INFO(gVisualBaseLogFilter, Debug::General, oss.str().c_str());
1003   }
1004 #endif
1005
1006   if(animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
1007      animator.propertyKey == MIX_COLOR ||
1008      (mImpl->mType == Toolkit::Visual::COLOR &&
1009       animator.propertyKey == ColorVisual::Property::MIX_COLOR) ||
1010      (mImpl->mType == Toolkit::Visual::PRIMITIVE &&
1011       animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR))
1012   {
1013     AnimateMixColorProperty(transition, animator);
1014   }
1015   else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
1016           animator.propertyKey == OPACITY)
1017   {
1018     AnimateOpacityProperty(transition, animator);
1019   }
1020   else if(mImpl->mRenderer)
1021   {
1022     AnimateRendererProperty(transition, animator);
1023   }
1024 }
1025
1026 void Visual::Base::AnimateOpacityProperty(
1027   Dali::Animation&                    transition,
1028   Internal::TransitionData::Animator& animator)
1029 {
1030   float targetOpacity;
1031   if(animator.targetValue.Get(targetOpacity))
1032   {
1033     mImpl->mMixColor.a = targetOpacity;
1034   }
1035
1036   SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, animator.initialValue, animator.targetValue);
1037 }
1038
1039 void Visual::Base::AnimateRendererProperty(
1040   Dali::Animation&                    transition,
1041   Internal::TransitionData::Animator& animator)
1042 {
1043   // Get actual renderer index (will convert transform keys into visualproperty indices)
1044   Property::Index index = GetPropertyIndex(animator.propertyKey);
1045
1046   if(index != Property::INVALID_INDEX)
1047   {
1048     if(animator.targetValue.GetType() != Property::NONE)
1049     {
1050       // Try writing target value into transform property map
1051       // if it's not a valid key, then it won't alter mTransform
1052       Property::Map map;
1053       if(animator.propertyKey.type == Property::Key::INDEX)
1054       {
1055         map.Add(animator.propertyKey.indexKey, animator.targetValue);
1056       }
1057       else
1058       {
1059         map.Add(animator.propertyKey.stringKey, animator.targetValue);
1060       }
1061
1062       mImpl->mTransform.UpdatePropertyMap(map);
1063     }
1064     SetupTransition(transition, animator, index, animator.initialValue, animator.targetValue);
1065   }
1066 }
1067
1068 void Visual::Base::AnimateMixColorProperty(
1069   Dali::Animation&                    transition,
1070   Internal::TransitionData::Animator& animator)
1071 {
1072   bool animateOpacity = false;
1073
1074   Property::Value initialOpacity;
1075   Property::Value targetOpacity;
1076   Property::Value initialMixColor;
1077   Property::Value targetMixColor;
1078
1079   Vector4 initialColor;
1080   if(animator.initialValue.Get(initialColor))
1081   {
1082     if(animator.initialValue.GetType() == Property::VECTOR4)
1083     {
1084       // if there is an initial color specifying alpha, test it
1085       initialOpacity = initialColor.a;
1086     }
1087     initialMixColor = Vector3(initialColor);
1088   }
1089
1090   // Set target value into data store
1091   if(animator.targetValue.GetType() != Property::NONE)
1092   {
1093     Vector4 mixColor;
1094     animator.targetValue.Get(mixColor);
1095     if(animator.targetValue.GetType() == Property::VECTOR4)
1096     {
1097       mImpl->mMixColor.a = mixColor.a;
1098       targetOpacity      = mixColor.a;
1099       animateOpacity     = true;
1100     }
1101
1102     mImpl->mMixColor.r = mixColor.r;
1103     mImpl->mMixColor.g = mixColor.g;
1104     mImpl->mMixColor.b = mixColor.b;
1105     targetMixColor     = Vector3(mixColor);
1106   }
1107
1108   SetupTransition(transition, animator, VisualRenderer::Property::VISUAL_MIX_COLOR, initialMixColor, targetMixColor);
1109
1110   if(animateOpacity)
1111   {
1112     SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, initialOpacity, targetOpacity);
1113   }
1114 }
1115
1116 Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
1117 {
1118   if(!mImpl->mRenderer)
1119   {
1120     Handle handle;
1121     return Dali::Property(handle, Property::INVALID_INDEX);
1122   }
1123
1124   // Default animatable properties from VisualRenderer
1125   switch(GetIntKey(key))
1126   {
1127     case Toolkit::Visual::Property::MIX_COLOR:
1128     {
1129       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1130     }
1131     case Toolkit::Visual::Property::OPACITY:
1132     {
1133       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
1134     }
1135     case Toolkit::Visual::Transform::Property::OFFSET:
1136     {
1137       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_OFFSET);
1138     }
1139     case Toolkit::Visual::Transform::Property::SIZE:
1140     {
1141       return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::TRANSFORM_SIZE);
1142     }
1143     default:
1144     {
1145       if(key.type == Property::Key::INDEX &&
1146          ((mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) ||
1147           (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR)))
1148       {
1149         return Dali::Property(mImpl->mRenderer, VisualRenderer::Property::VISUAL_MIX_COLOR);
1150       }
1151     }
1152   }
1153
1154   // Other cases
1155   Property::Index index = GetPropertyIndex(key);
1156   if(index == Property::INVALID_INDEX)
1157   {
1158     if(IsTypeAvailableForBorderline(mImpl->mType) &&
1159        ((key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_WIDTH) || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_WIDTH) ||
1160         (key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_COLOR) || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_COLOR) ||
1161         (key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_OFFSET) || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_OFFSET)))
1162     {
1163       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON_WITHOUT_CULL);
1164
1165       // Register borderline properties
1166       mImpl->mBorderlineWidthIndex  = mImpl->mRenderer.RegisterUniqueProperty(DevelVisual::Property::BORDERLINE_WIDTH, BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
1167       mImpl->mBorderlineColorIndex  = mImpl->mRenderer.RegisterUniqueProperty(DevelVisual::Property::BORDERLINE_COLOR, BORDERLINE_COLOR, mImpl->mBorderlineColor);
1168       mImpl->mBorderlineOffsetIndex = mImpl->mRenderer.RegisterUniqueProperty(DevelVisual::Property::BORDERLINE_OFFSET, BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
1169
1170       // Borderline is animated now. we always have to use borderline feature.
1171       mImpl->mAlwaysUsingBorderline = true;
1172
1173       index = mImpl->mRenderer.GetPropertyIndex(key);
1174
1175       // Change shader
1176       UpdateShader();
1177     }
1178     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)))
1179     {
1180       // Register CORNER_RADIUS property
1181       mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterUniqueProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
1182       mImpl->mRenderer.RegisterUniqueProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
1183
1184       // CornerRadius is animated now. we always have to use corner radius feature.
1185       mImpl->mAlwaysUsingCornerRadius = true;
1186
1187       if(!IsBorderlineRequired())
1188       {
1189         // If IsBorderlineRequired is true, BLEND_MODE is already BlendMode::ON_WITHOUT_CULL. So we don't overwrite it.
1190         mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
1191       }
1192
1193       index = mImpl->mCornerRadiusIndex;
1194
1195       // Change shader
1196       UpdateShader();
1197     }
1198     else
1199     {
1200       // We can't find the property in the base class.
1201       // Request to child class
1202       return OnGetPropertyObject(key);
1203     }
1204   }
1205   else
1206   {
1207     if(index == mImpl->mBorderlineWidthIndex ||
1208        index == mImpl->mBorderlineColorIndex ||
1209        index == mImpl->mBorderlineOffsetIndex)
1210     {
1211       // Borderline is animated now. we always have to use borderline feature.
1212       mImpl->mAlwaysUsingBorderline = true;
1213     }
1214     if(index == mImpl->mCornerRadiusIndex)
1215     {
1216       // CornerRadius is animated now. we always have to use corner radius feature.
1217       mImpl->mAlwaysUsingCornerRadius = true;
1218     }
1219   }
1220
1221   return Dali::Property(mImpl->mRenderer, index);
1222 }
1223
1224 } // namespace Internal
1225
1226 } // namespace Toolkit
1227
1228 } // namespace Dali