Add borderline property for visual + Integrate some shaders in one
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-base-impl.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "visual-base-impl.h"
20
21 // EXTERNAL HEADER
22 #include <dali-toolkit/public-api/dali-toolkit-common.h>
23 #include <dali/devel-api/rendering/renderer-devel.h>
24 #include <dali/devel-api/scripting/enum-helper.h>
25 #include <dali/integration-api/debug.h>
26
27 //INTERNAL HEARDER
28 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
29 #include <dali-toolkit/internal/helpers/property-helper.h>
30 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
31 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
32 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
33 #include <dali-toolkit/public-api/visuals/primitive-visual-properties.h>
34 #include <dali-toolkit/public-api/visuals/visual-properties.h>
35
36 namespace
37 {
38 #if defined(DEBUG_ENABLED)
39 Debug::Filter* gVisualBaseLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VISUAL_BASE");
40 #endif
41
42 const char* const PRE_MULTIPLIED_ALPHA_PROPERTY("preMultipliedAlpha");
43
44 } // namespace
45
46 namespace Dali
47 {
48 namespace Toolkit
49 {
50 namespace Internal
51 {
52 namespace
53 {
54 DALI_ENUM_TO_STRING_TABLE_BEGIN(VISUAL_FITTING_MODE)
55   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_KEEP_ASPECT_RATIO)
56   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FILL)
57   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, OVER_FIT_KEEP_ASPECT_RATIO)
58   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, CENTER)
59   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_WIDTH)
60   DALI_ENUM_TO_STRING_WITH_SCOPE(Visual::FittingMode, FIT_HEIGHT)
61 DALI_ENUM_TO_STRING_TABLE_END(VISUAL_FITTING_MODE)
62
63 } // namespace
64
65 Visual::Base::Base(VisualFactoryCache& factoryCache, FittingMode fittingMode, Toolkit::Visual::Type type)
66 : mImpl(new Impl(fittingMode, type)),
67   mFactoryCache(factoryCache)
68 {
69 }
70
71 Visual::Base::~Base()
72 {
73   delete mImpl;
74 }
75
76 void Visual::Base::Initialize()
77 {
78   // The Renderer should be created inside derived class here.
79   OnInitialize();
80
81   if(mImpl->mRenderer)
82   {
83     RegisterMixColor();
84
85     if(IsRoundedCornerRequired())
86     {
87       mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
88       mImpl->mRenderer.RegisterProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
89
90       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
91     }
92     if(IsBorderlineRequired())
93     {
94       mImpl->mBorderlineWidthIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_WIDTH,  BORDERLINE_WIDTH,  mImpl->mBorderlineWidth);
95       mImpl->mBorderlineColorIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_COLOR,  BORDERLINE_COLOR,  mImpl->mBorderlineColor);
96       mImpl->mBorderlineOffsetIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_OFFSET, BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
97
98       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
99     }
100   }
101 }
102
103 void Visual::Base::SetCustomShader(const Property::Map& shaderMap)
104 {
105   if(mImpl->mCustomShader)
106   {
107     mImpl->mCustomShader->SetPropertyMap(shaderMap);
108   }
109   else
110   {
111     mImpl->mCustomShader = new Impl::CustomShader(shaderMap);
112   }
113
114   // Let derived class know
115   UpdateShader();
116 }
117
118 void Visual::Base::SetProperties(const Property::Map& propertyMap)
119 {
120   for(size_t i = 0; i < propertyMap.Count(); ++i)
121   {
122     const KeyValuePair&    pair  = propertyMap.GetKeyValue(i);
123     const Property::Key&   key   = pair.first;
124     const Property::Value& value = pair.second;
125
126     Property::Key matchKey = key;
127     if(matchKey.type == Property::Key::STRING)
128     {
129       if(matchKey == CUSTOM_SHADER)
130       {
131         matchKey = Property::Key(Toolkit::Visual::Property::SHADER);
132       }
133       else if(matchKey == TRANSFORM)
134       {
135         matchKey = Property::Key(Toolkit::Visual::Property::TRANSFORM);
136       }
137       else if(matchKey == PREMULTIPLIED_ALPHA)
138       {
139         matchKey = Property::Key(Toolkit::Visual::Property::PREMULTIPLIED_ALPHA);
140       }
141       else if(matchKey == MIX_COLOR)
142       {
143         matchKey = Property::Key(Toolkit::Visual::Property::MIX_COLOR);
144       }
145       else if(matchKey == OPACITY)
146       {
147         matchKey = Property::Key(Toolkit::Visual::Property::OPACITY);
148       }
149       else if(matchKey == VISUAL_FITTING_MODE)
150       {
151         matchKey = Property::Key(Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE);
152       }
153       else if(matchKey == BORDERLINE_WIDTH)
154       {
155         matchKey = Property::Key(Toolkit::DevelVisual::Property::BORDERLINE_WIDTH);
156       }
157       else if(matchKey == BORDERLINE_COLOR)
158       {
159         matchKey = Property::Key(Toolkit::DevelVisual::Property::BORDERLINE_COLOR);
160       }
161       else if(matchKey == BORDERLINE_OFFSET)
162       {
163         matchKey = Property::Key(Toolkit::DevelVisual::Property::BORDERLINE_OFFSET);
164       }
165       else if(matchKey == CORNER_RADIUS)
166       {
167         matchKey = Property::Key(Toolkit::DevelVisual::Property::CORNER_RADIUS);
168       }
169       else if(matchKey == CORNER_RADIUS_POLICY)
170       {
171         matchKey = Property::Key(Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY);
172       }
173     }
174
175     switch(matchKey.indexKey)
176     {
177       case Toolkit::Visual::Property::SHADER:
178       {
179         Property::Map shaderMap;
180         if(value.Get(shaderMap))
181         {
182           SetCustomShader(shaderMap);
183         }
184         break;
185       }
186
187       case Toolkit::Visual::Property::TRANSFORM:
188       {
189         Property::Map map;
190         if(value.Get(map))
191         {
192           mImpl->mTransform.SetPropertyMap(map);
193         }
194         break;
195       }
196
197       case Toolkit::Visual::Property::PREMULTIPLIED_ALPHA:
198       {
199         bool premultipliedAlpha = false;
200         if(value.Get(premultipliedAlpha))
201         {
202           EnablePreMultipliedAlpha(premultipliedAlpha);
203         }
204         break;
205       }
206
207       case Toolkit::Visual::Property::MIX_COLOR:
208       {
209         Vector4 mixColor;
210         if(value.Get(mixColor))
211         {
212           if(value.GetType() == Property::VECTOR4)
213           {
214             SetMixColor(mixColor);
215           }
216           else
217           {
218             Vector3 mixColor3(mixColor);
219             SetMixColor(mixColor3);
220           }
221         }
222         break;
223       }
224       case Toolkit::Visual::Property::OPACITY:
225       {
226         float opacity;
227         if(value.Get(opacity))
228         {
229           mImpl->mMixColor.a = opacity;
230           SetMixColor(mImpl->mMixColor);
231         }
232         break;
233       }
234       case Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE:
235       {
236         Scripting::GetEnumerationProperty<Visual::FittingMode>(
237           value, VISUAL_FITTING_MODE_TABLE, VISUAL_FITTING_MODE_TABLE_COUNT, mImpl->mFittingMode);
238         break;
239       }
240       case Toolkit::DevelVisual::Property::BORDERLINE_WIDTH:
241       {
242         float width;
243         if(value.Get(width))
244         {
245           mImpl->mBorderlineWidth = width;
246         }
247         break;
248       }
249       case Toolkit::DevelVisual::Property::BORDERLINE_COLOR:
250       {
251         Vector4 color;
252         if(value.Get(color))
253         {
254           mImpl->mBorderlineColor = color;
255         }
256         break;
257       }
258       case Toolkit::DevelVisual::Property::BORDERLINE_OFFSET:
259       {
260         float offset;
261         if(value.Get(offset))
262         {
263           mImpl->mBorderlineOffset = offset;
264         }
265         break;
266       }
267       case Toolkit::DevelVisual::Property::CORNER_RADIUS:
268       {
269         if(value.GetType() == Property::VECTOR4)
270         {
271           // If CORNER_RADIUS Property is Vector4,
272           // Each values mean the radius of
273           // (top-left, top-right, bottom-right, bottom-left)
274           Vector4 radius;
275           if(value.Get(radius))
276           {
277             mImpl->mCornerRadius = radius;
278           }
279         }
280         else
281         {
282           // If CORNER_RADIUS Property is float,
283           // Every corner radius have same value
284           float radius;
285           if(value.Get(radius))
286           {
287             mImpl->mCornerRadius = Vector4(radius, radius, radius, radius);
288           }
289         }
290         break;
291       }
292       case Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY:
293       {
294         int policy;
295         if(value.Get(policy))
296         {
297           switch(policy)
298           {
299             case Toolkit::Visual::Transform::Policy::RELATIVE:
300             case Toolkit::Visual::Transform::Policy::ABSOLUTE:
301             {
302               mImpl->mCornerRadiusPolicy = policy;
303               break;
304             }
305             default:
306             {
307               DALI_LOG_ERROR("Unsupported policy: %d\n", policy);
308               break;
309             }
310           }
311         }
312         break;
313       }
314     }
315   }
316
317   DoSetProperties(propertyMap);
318 }
319
320 void Visual::Base::SetTransformAndSize(const Property::Map& transform, Size controlSize)
321 {
322   mImpl->mControlSize = controlSize;
323   mImpl->mTransform.UpdatePropertyMap(transform);
324
325 #if defined(DEBUG_ENABLED)
326   std::ostringstream oss;
327   oss << transform;
328   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);
329 #endif
330
331   OnSetTransform();
332 }
333
334 void Visual::Base::SetName(const std::string& name)
335 {
336   mImpl->mName = name;
337 }
338
339 const std::string& Visual::Base::GetName() const
340 {
341   return mImpl->mName;
342 }
343
344 float Visual::Base::GetHeightForWidth(float width)
345 {
346   float   aspectCorrectedHeight = 0.f;
347   Vector2 naturalSize;
348   GetNaturalSize(naturalSize);
349   if(naturalSize.width)
350   {
351     aspectCorrectedHeight = naturalSize.height * width / naturalSize.width;
352   }
353   return aspectCorrectedHeight;
354 }
355
356 float Visual::Base::GetWidthForHeight(float height)
357 {
358   float   aspectCorrectedWidth = 0.f;
359   Vector2 naturalSize;
360   GetNaturalSize(naturalSize);
361   if(naturalSize.height > 0.0f)
362   {
363     aspectCorrectedWidth = naturalSize.width * height / naturalSize.height;
364   }
365   return aspectCorrectedWidth;
366 }
367
368 void Visual::Base::GetNaturalSize(Vector2& naturalSize)
369 {
370   naturalSize = Vector2::ZERO;
371 }
372
373 void Visual::Base::DoAction(const Property::Index actionId, const Property::Value attributes)
374 {
375   OnDoAction(actionId, attributes);
376 }
377
378 void Visual::Base::SetDepthIndex(int index)
379 {
380   mImpl->mDepthIndex = index;
381   if(mImpl->mRenderer)
382   {
383     mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
384   }
385 }
386
387 int Visual::Base::GetDepthIndex() const
388 {
389   return mImpl->mDepthIndex;
390 }
391
392 void Visual::Base::SetOnScene(Actor& actor)
393 {
394   if(!IsOnScene())
395   {
396     // To display the actor correctly, renderer should not be added to actor until all required resources are ready.
397     // Thus the calling of actor.AddRenderer() should happen inside derived class as base class does not know the exact timing.
398     DoSetOnScene(actor);
399
400     if(mImpl->mRenderer)
401     {
402       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, IsPreMultipliedAlphaEnabled());
403       mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, mImpl->mDepthIndex);
404     }
405
406     mImpl->mFlags |= Impl::IS_ON_SCENE;
407   }
408 }
409
410 void Visual::Base::SetOffScene(Actor& actor)
411 {
412   if(IsOnScene())
413   {
414     DoSetOffScene(actor);
415     mImpl->mFlags &= ~Impl::IS_ON_SCENE;
416   }
417 }
418
419 void Visual::Base::CreatePropertyMap(Property::Map& map) const
420 {
421   if(mImpl->mRenderer)
422   {
423     // Update values from Renderer
424     mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(mImpl->mMixColorIndex);
425     mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
426     if(mImpl->mTransform.mOffsetIndex != Property::INVALID_INDEX)
427     {
428       mImpl->mTransform.mOffset = mImpl->mRenderer.GetProperty<Vector2>(mImpl->mTransform.mOffsetIndex);
429     }
430     if(mImpl->mTransform.mSizeIndex != Property::INVALID_INDEX)
431     {
432       mImpl->mTransform.mSize = mImpl->mRenderer.GetProperty<Vector2>(mImpl->mTransform.mSizeIndex);
433     }
434     if(mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
435     {
436       mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<Vector4>(mImpl->mCornerRadiusIndex);
437     }
438     if(mImpl->mBorderlineWidthIndex != Property::INVALID_INDEX)
439     {
440       mImpl->mBorderlineWidth = mImpl->mRenderer.GetProperty<float>(mImpl->mBorderlineWidthIndex);
441     }
442     if(mImpl->mBorderlineColorIndex != Property::INVALID_INDEX)
443     {
444       mImpl->mBorderlineColor = mImpl->mRenderer.GetProperty<Vector4>(mImpl->mBorderlineColorIndex);
445     }
446     if(mImpl->mBorderlineOffsetIndex != Property::INVALID_INDEX)
447     {
448       mImpl->mBorderlineOffset = mImpl->mRenderer.GetProperty<float>(mImpl->mBorderlineOffsetIndex);
449     }
450   }
451
452   DoCreatePropertyMap(map);
453
454   if(mImpl->mCustomShader)
455   {
456     mImpl->mCustomShader->CreatePropertyMap(map);
457   }
458
459   Property::Map transform;
460   mImpl->mTransform.GetPropertyMap(transform);
461   map.Insert(Toolkit::Visual::Property::TRANSFORM, transform);
462
463   bool premultipliedAlpha(IsPreMultipliedAlphaEnabled());
464   map.Insert(Toolkit::Visual::Property::PREMULTIPLIED_ALPHA, premultipliedAlpha);
465
466   // Note, Color and Primitive will also insert their own mix color into the map
467   // which is ok, because they have a different key value range.
468   map.Insert(Toolkit::Visual::Property::MIX_COLOR, mImpl->mMixColor); // vec4
469   map.Insert(Toolkit::Visual::Property::OPACITY, mImpl->mMixColor.a);
470
471   auto fittingModeString = Scripting::GetLinearEnumerationName<FittingMode>(
472     mImpl->mFittingMode, VISUAL_FITTING_MODE_TABLE, VISUAL_FITTING_MODE_TABLE_COUNT);
473   map.Insert(Toolkit::DevelVisual::Property::VISUAL_FITTING_MODE, fittingModeString);
474
475   map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
476   map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_COLOR, mImpl->mBorderlineColor);
477   map.Insert(Toolkit::DevelVisual::Property::BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
478
479   map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS, mImpl->mCornerRadius);
480   map.Insert(Toolkit::DevelVisual::Property::CORNER_RADIUS_POLICY, static_cast<int>(mImpl->mCornerRadiusPolicy));
481 }
482
483 void Visual::Base::CreateInstancePropertyMap(Property::Map& map) const
484 {
485   DoCreateInstancePropertyMap(map);
486
487   if(mImpl->mCustomShader)
488   {
489     mImpl->mCustomShader->CreatePropertyMap(map);
490   }
491 }
492
493 void Visual::Base::EnablePreMultipliedAlpha(bool preMultiplied)
494 {
495   if(preMultiplied)
496   {
497     mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
498   }
499   else
500   {
501     mImpl->mFlags &= ~Impl::IS_PREMULTIPLIED_ALPHA;
502   }
503
504   if(mImpl->mRenderer)
505   {
506     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, preMultiplied);
507     mImpl->mRenderer.RegisterProperty(PRE_MULTIPLIED_ALPHA_PROPERTY, static_cast<float>(preMultiplied));
508   }
509 }
510
511 bool Visual::Base::IsPreMultipliedAlphaEnabled() const
512 {
513   return mImpl->mFlags & Impl::IS_PREMULTIPLIED_ALPHA;
514 }
515
516 void Visual::Base::DoSetOffScene(Actor& actor)
517 {
518   actor.RemoveRenderer(mImpl->mRenderer);
519 }
520
521 bool Visual::Base::IsOnScene() const
522 {
523   return mImpl->mFlags & Impl::IS_ON_SCENE;
524 }
525
526 bool Visual::Base::IsRoundedCornerRequired() const
527 {
528   if(mImpl->mRenderer && mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
529   {
530     // Update values from Renderer
531     mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<Vector4>(mImpl->mCornerRadiusIndex);
532   }
533   return !(mImpl->mCornerRadius == Vector4::ZERO) || mImpl->mNeedCornerRadius;
534 }
535
536 bool Visual::Base::IsBorderlineRequired() const
537 {
538   if(mImpl->mRenderer && mImpl->mBorderlineWidthIndex != Property::INVALID_INDEX)
539   {
540     // Update values from Renderer
541     mImpl->mBorderlineWidth = mImpl->mRenderer.GetProperty<float>(mImpl->mBorderlineWidthIndex);
542   }
543   return !EqualsZero(mImpl->mBorderlineWidth) || mImpl->mNeedBorderline;
544 }
545
546 void Visual::Base::OnDoAction(const Property::Index actionId, const Property::Value& attributes)
547 {
548   // May be overriden by derived class
549 }
550
551 void Visual::Base::RegisterMixColor()
552 {
553   // Only register if not already registered.
554   // (Color and Primitive visuals will register their own and save to this index)
555   if(mImpl->mMixColorIndex == Property::INVALID_INDEX)
556   {
557     mImpl->mMixColorIndex = mImpl->mRenderer.RegisterProperty(
558       Toolkit::Visual::Property::MIX_COLOR,
559       MIX_COLOR,
560       Vector3(mImpl->mMixColor));
561   }
562
563   mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, mImpl->mMixColor.a);
564
565   float preMultipliedAlpha = 0.0f;
566   if(IsPreMultipliedAlphaEnabled())
567   {
568     preMultipliedAlpha = 1.0f;
569   }
570   mImpl->mRenderer.RegisterProperty(PRE_MULTIPLIED_ALPHA_PROPERTY, preMultipliedAlpha);
571 }
572
573 void Visual::Base::SetMixColor(const Vector4& color)
574 {
575   mImpl->mMixColor = color;
576
577   if(mImpl->mRenderer)
578   {
579     mImpl->mRenderer.SetProperty(mImpl->mMixColorIndex, Vector3(color));
580     mImpl->mRenderer.SetProperty(DevelRenderer::Property::OPACITY, color.a);
581   }
582 }
583
584 void Visual::Base::SetMixColor(const Vector3& color)
585 {
586   mImpl->mMixColor.r = color.r;
587   mImpl->mMixColor.g = color.g;
588   mImpl->mMixColor.b = color.b;
589
590   if(mImpl->mRenderer)
591   {
592     mImpl->mRenderer.SetProperty(mImpl->mMixColorIndex, color);
593   }
594 }
595
596 void Visual::Base::AddEventObserver(Visual::EventObserver& observer)
597 {
598   mImpl->mEventObserver = &observer;
599 }
600
601 void Visual::Base::RemoveEventObserver(Visual::EventObserver& observer)
602 {
603   mImpl->mEventObserver = NULL;
604 }
605
606 void Visual::Base::ResourceReady(Toolkit::Visual::ResourceStatus resourceStatus)
607 {
608   if(mImpl->mResourceStatus != resourceStatus)
609   {
610     mImpl->mResourceStatus = resourceStatus;
611
612     if(mImpl->mEventObserver)
613     {
614       // observer is currently a control impl
615       mImpl->mEventObserver->ResourceReady(*this);
616     }
617   }
618 }
619
620 bool Visual::Base::IsResourceReady() const
621 {
622   return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY);
623 }
624
625 bool Visual::Base::IsSynchronousLoadingRequired() const
626 {
627   return (mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING);
628 }
629
630 Toolkit::Visual::Type Visual::Base::GetType() const
631 {
632   return mImpl->mType;
633 }
634
635 Toolkit::Visual::ResourceStatus Visual::Base::GetResourceStatus() const
636 {
637   return mImpl->mResourceStatus;
638 }
639
640 Visual::FittingMode Visual::Base::GetFittingMode() const
641 {
642   return mImpl->mFittingMode;
643 }
644
645 Visual::Base& Visual::Base::GetVisualObject()
646 {
647   return *this;
648 }
649
650 Renderer Visual::Base::GetRenderer()
651 {
652   return mImpl->mRenderer;
653 }
654
655 Property::Index Visual::Base::GetPropertyIndex(Property::Key key)
656 {
657   Property::Index index = mImpl->mRenderer.GetPropertyIndex(key);
658
659   if(index == Property::INVALID_INDEX)
660   {
661     // Is it a shader property?
662     Shader shader = mImpl->mRenderer.GetShader();
663     index         = shader.GetPropertyIndex(key);
664     if(index != Property::INVALID_INDEX)
665     {
666       // Yes - we should register it in the Renderer so it can be set / animated
667       // independently, as shaders are shared across multiple renderers.
668       std::string     keyName;
669       Property::Index keyIndex(Property::INVALID_KEY);
670       if(key.type == Property::Key::INDEX)
671       {
672         keyName  = shader.GetPropertyName(index);
673         keyIndex = key.indexKey;
674       }
675       else
676       {
677         keyName = key.stringKey;
678         // Leave keyIndex as INVALID_KEY - it can still be registered against the string key.
679       }
680       Property::Value value = shader.GetProperty(index);
681       index                 = mImpl->mRenderer.RegisterProperty(keyIndex, keyName, value);
682     }
683   }
684   return index;
685 }
686
687 void Visual::Base::SetupTransition(
688   Dali::Animation&                    transition,
689   Internal::TransitionData::Animator& animator,
690   Property::Index                     index,
691   Property::Value&                    initialValue,
692   Property::Value&                    targetValue)
693 {
694   if(index != Property::INVALID_INDEX)
695   {
696     if(mImpl->mRenderer)
697     {
698       if(animator.animate == false)
699       {
700         mImpl->mRenderer.SetProperty(index, targetValue);
701       }
702       else
703       {
704         if(animator.initialValue.GetType() != Property::NONE)
705         {
706           mImpl->mRenderer.SetProperty(index, initialValue);
707         }
708
709         if(!transition)
710         {
711           transition = Dali::Animation::New(0.1f);
712         }
713
714         transition.AnimateTo(Property(mImpl->mRenderer, index),
715                              targetValue,
716                              animator.alphaFunction,
717                              TimePeriod(animator.timePeriodDelay,
718                                         animator.timePeriodDuration));
719       }
720     }
721   }
722 }
723
724 void Visual::Base::AnimateProperty(
725   Dali::Animation&                    transition,
726   Internal::TransitionData::Animator& animator)
727 {
728 #if defined(DEBUG_ENABLED)
729   {
730     std::ostringstream oss;
731     oss << "Visual::Base::AnimateProperty(Visual:" << mImpl->mName << " Property:" << animator.propertyKey << " Target: " << animator.targetValue << std::endl;
732     DALI_LOG_INFO(gVisualBaseLogFilter, Debug::General, oss.str().c_str());
733   }
734 #endif
735
736   if(animator.propertyKey == Toolkit::Visual::Property::MIX_COLOR ||
737      animator.propertyKey == MIX_COLOR ||
738      (mImpl->mType == Toolkit::Visual::COLOR &&
739       animator.propertyKey == ColorVisual::Property::MIX_COLOR) ||
740      (mImpl->mType == Toolkit::Visual::PRIMITIVE &&
741       animator.propertyKey == PrimitiveVisual::Property::MIX_COLOR))
742   {
743     AnimateMixColorProperty(transition, animator);
744   }
745   else if(animator.propertyKey == Toolkit::Visual::Property::OPACITY ||
746           animator.propertyKey == OPACITY)
747   {
748     AnimateOpacityProperty(transition, animator);
749   }
750   else if(mImpl->mRenderer)
751   {
752     AnimateRendererProperty(transition, animator);
753   }
754 }
755
756 void Visual::Base::AnimateOpacityProperty(
757   Dali::Animation&                    transition,
758   Internal::TransitionData::Animator& animator)
759 {
760   float targetOpacity;
761   if(animator.targetValue.Get(targetOpacity))
762   {
763     mImpl->mMixColor.a = targetOpacity;
764   }
765
766   SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, animator.initialValue, animator.targetValue);
767 }
768
769 void Visual::Base::AnimateRendererProperty(
770   Dali::Animation&                    transition,
771   Internal::TransitionData::Animator& animator)
772 {
773   Property::Index index = GetPropertyIndex(animator.propertyKey);
774   if(index != Property::INVALID_INDEX)
775   {
776     if(animator.targetValue.GetType() != Property::NONE)
777     {
778       // Try writing target value into transform property map
779       // if it's not a valid key, then it won't alter mTransform
780       Property::Map map;
781       if(animator.propertyKey.type == Property::Key::INDEX)
782       {
783         map.Add(animator.propertyKey.indexKey, animator.targetValue);
784       }
785       else
786       {
787         map.Add(animator.propertyKey.stringKey, animator.targetValue);
788       }
789
790       mImpl->mTransform.UpdatePropertyMap(map);
791     }
792
793     SetupTransition(transition, animator, index, animator.initialValue, animator.targetValue);
794   }
795 }
796
797 void Visual::Base::AnimateMixColorProperty(
798   Dali::Animation&                    transition,
799   Internal::TransitionData::Animator& animator)
800 {
801   Property::Index index          = mImpl->mMixColorIndex;
802   bool            animateOpacity = false;
803
804   Property::Value initialOpacity;
805   Property::Value targetOpacity;
806   Property::Value initialMixColor;
807   Property::Value targetMixColor;
808
809   if(index != Property::INVALID_INDEX)
810   {
811     Vector4 initialColor;
812     if(animator.initialValue.Get(initialColor))
813     {
814       if(animator.initialValue.GetType() == Property::VECTOR4)
815       {
816         // if there is an initial color specifying alpha, test it
817         initialOpacity = initialColor.a;
818       }
819       initialMixColor = Vector3(initialColor);
820     }
821
822     // Set target value into data store
823     if(animator.targetValue.GetType() != Property::NONE)
824     {
825       Vector4 mixColor;
826       animator.targetValue.Get(mixColor);
827       if(animator.targetValue.GetType() == Property::VECTOR4)
828       {
829         mImpl->mMixColor.a = mixColor.a;
830         targetOpacity      = mixColor.a;
831         animateOpacity     = true;
832       }
833
834       mImpl->mMixColor.r = mixColor.r;
835       mImpl->mMixColor.g = mixColor.g;
836       mImpl->mMixColor.b = mixColor.b;
837       targetMixColor     = Vector3(mixColor);
838     }
839
840     SetupTransition(transition, animator, index, initialMixColor, targetMixColor);
841     if(animateOpacity)
842     {
843       SetupTransition(transition, animator, DevelRenderer::Property::OPACITY, initialOpacity, targetOpacity);
844     }
845   }
846 }
847
848 Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
849 {
850   if(!mImpl->mRenderer)
851   {
852     Handle handle;
853     return Dali::Property(handle, Property::INVALID_INDEX);
854   }
855
856   // Mix color or opacity cases
857   if(key.type == Property::Key::INDEX)
858   {
859     if(key.indexKey == Toolkit::Visual::Property::MIX_COLOR || (mImpl->mType == Toolkit::Visual::COLOR && key.indexKey == ColorVisual::Property::MIX_COLOR) || (mImpl->mType == Toolkit::Visual::PRIMITIVE && key.indexKey == PrimitiveVisual::Property::MIX_COLOR))
860     {
861       return Dali::Property(mImpl->mRenderer, mImpl->mMixColorIndex);
862     }
863     else if(key.indexKey == Toolkit::Visual::Property::OPACITY)
864     {
865       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
866     }
867     else if(key.indexKey == Toolkit::Visual::Transform::Property::OFFSET)
868     {
869       return Dali::Property(mImpl->mRenderer, OFFSET);
870     }
871     else if(key.indexKey == Toolkit::Visual::Transform::Property::SIZE)
872     {
873       return Dali::Property(mImpl->mRenderer, SIZE);
874     }
875   }
876   else
877   {
878     if(key.stringKey == MIX_COLOR)
879     {
880       return Dali::Property(mImpl->mRenderer, mImpl->mMixColorIndex);
881     }
882     else if(key.stringKey == OPACITY)
883     {
884       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
885     }
886     else if(key.stringKey == OFFSET)
887     {
888       return Dali::Property(mImpl->mRenderer, OFFSET);
889     }
890     else if(key.stringKey == SIZE)
891     {
892       return Dali::Property(mImpl->mRenderer, SIZE);
893     }
894   }
895
896   // Other cases
897   Property::Index index = GetPropertyIndex(key);
898   if(index == Property::INVALID_INDEX)
899   {
900     if((key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_WIDTH)  || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_WIDTH) ||
901        (key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_COLOR)  || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_COLOR) ||
902        (key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::BORDERLINE_OFFSET) || (key.type == Property::Key::STRING && key.stringKey == BORDERLINE_OFFSET))
903     {
904       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
905
906       // Register borderline properties
907       mImpl->mBorderlineWidthIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_WIDTH, BORDERLINE_WIDTH, mImpl->mBorderlineWidth);
908       mImpl->mBorderlineColorIndex  = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_COLOR, BORDERLINE_COLOR, mImpl->mBorderlineColor);
909       mImpl->mBorderlineOffsetIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::BORDERLINE_OFFSET, BORDERLINE_OFFSET, mImpl->mBorderlineOffset);
910       mImpl->mNeedBorderline        = true;
911
912       index = mImpl->mRenderer.GetPropertyIndex(key);
913
914       // Change shader
915       UpdateShader();
916     }
917     else if((key.type == Property::Key::INDEX && key.indexKey == DevelVisual::Property::CORNER_RADIUS) || (key.type == Property::Key::STRING && key.stringKey == CORNER_RADIUS))
918     {
919       // Register CORNER_RADIUS property
920       mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
921       mImpl->mRenderer.RegisterProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
922
923       mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
924
925       index                    = mImpl->mCornerRadiusIndex;
926       mImpl->mNeedCornerRadius = true;
927
928       // Change shader
929       UpdateShader();
930     }
931     else
932     {
933       // We can't find the property in the base class.
934       // Request to child class
935       return OnGetPropertyObject(key);
936     }
937   }
938
939   return Dali::Property(mImpl->mRenderer, index);
940 }
941
942 } // namespace Internal
943
944 } // namespace Toolkit
945
946 } // namespace Dali