d0807dfb6fc051943b8c96201950e5275b8c4024
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / gradient / gradient-visual.cpp
1 /*
2  * Copyright (c) 2020 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 "gradient-visual.h"
20
21 // EXTERNAL INCLUDES
22 #include <typeinfo>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/common/dali-vector.h>
25 #include <dali/public-api/object/property-array.h>
26 #include <dali/devel-api/scripting/enum-helper.h>
27 #include <dali/devel-api/scripting/scripting.h>
28 #include <dali/devel-api/rendering/renderer-devel.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/public-api/visuals/gradient-visual-properties.h>
32 #include <dali-toolkit/public-api/visuals/visual-properties.h>
33 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
34 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
35 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
36 #include <dali-toolkit/internal/visuals/gradient/linear-gradient.h>
37 #include <dali-toolkit/internal/visuals/gradient/radial-gradient.h>
38 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
39
40 namespace Dali
41 {
42
43 namespace Toolkit
44 {
45
46 namespace Internal
47 {
48
49 namespace
50 {
51
52 DALI_ENUM_TO_STRING_TABLE_BEGIN( UNITS )
53 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::Units, OBJECT_BOUNDING_BOX )
54 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::Units, USER_SPACE )
55 DALI_ENUM_TO_STRING_TABLE_END( UNITS )
56
57 DALI_ENUM_TO_STRING_TABLE_BEGIN( SPREAD_METHOD )
58 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, PAD )
59 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, REFLECT )
60 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, REPEAT )
61 DALI_ENUM_TO_STRING_TABLE_END( SPREAD_METHOD )
62
63 // uniform names
64 const char * const UNIFORM_ALIGNMENT_MATRIX_NAME( "uAlignmentMatrix" );
65
66 // default offset value
67 const unsigned int DEFAULT_OFFSET_MINIMUM = 0.0f;
68 const unsigned int DEFAULT_OFFSET_MAXIMUM = 1.0f;
69
70 VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[][4] =
71 {
72   {
73     VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE,
74     VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX,
75     VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE_ROUNDED_CORNER,
76     VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX_ROUNDED_CORNER
77   },
78   {
79     VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE,
80     VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX,
81     VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE_ROUNDED_CORNER,
82     VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX_ROUNDED_CORNER
83   }
84 };
85
86 const char* VERTEX_SHADER[] =
87 {
88 // vertex shader for gradient units as OBJECT_BOUNDING_BOX
89 DALI_COMPOSE_SHADER(
90   attribute mediump vec2 aPosition;\n
91   uniform highp   mat4 uMvpMatrix;\n
92   uniform highp   vec3 uSize;\n
93   uniform mediump mat3 uAlignmentMatrix;\n
94   varying mediump vec2 vTexCoord;\n
95   \n
96
97   //Visual size and offset
98   uniform mediump vec2 offset;\n
99   uniform highp   vec2 size;\n
100   uniform mediump vec4 offsetSizeMode;\n
101   uniform mediump vec2 origin;\n
102   uniform mediump vec2 anchorPoint;\n
103
104   vec4 ComputeVertexPosition()\n
105   {\n
106     vec2 visualSize = mix(uSize.xy*size, size, offsetSizeMode.zw );\n
107     vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy);\n
108     return vec4( (aPosition + anchorPoint)*visualSize + (visualOffset + origin)*uSize.xy, 0.0, 1.0 );\n
109   }\n
110
111   void main()\n
112   {\n
113     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
114     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
115     \n
116     gl_Position = uMvpMatrix * ComputeVertexPosition();\n
117   }\n
118 ),
119
120 // vertex shader for gradient units as USER_SPACE
121 DALI_COMPOSE_SHADER(
122   attribute mediump vec2 aPosition;\n
123   uniform highp   mat4 uMvpMatrix;\n
124   uniform highp   vec3 uSize;\n
125   uniform mediump mat3 uAlignmentMatrix;\n
126   varying mediump vec2 vTexCoord;\n
127   \n
128
129   //Visual size and offset
130   uniform mediump vec2 offset;\n
131   uniform highp   vec2 size;\n
132   uniform mediump vec4 offsetSizeMode;\n
133   uniform mediump vec2 origin;\n
134   uniform mediump vec2 anchorPoint;\n
135
136   vec4 ComputeVertexPosition()\n
137   {\n
138     vec2 visualSize = mix(uSize.xy*size, size, offsetSizeMode.zw );\n
139     vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy);\n
140     return vec4( (aPosition + anchorPoint)*visualSize + (visualOffset + origin)*uSize.xy, 0.0, 1.0 );\n
141   }\n
142
143   void main()\n
144   {\n
145     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
146     vertexPosition.xyz *= uSize;\n
147     gl_Position = uMvpMatrix * ComputeVertexPosition();\n
148     \n
149     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
150   }\n
151 ),
152
153 // vertex shader for gradient units as OBJECT_BOUNDING_BOX with corner radius
154 DALI_COMPOSE_SHADER(
155   attribute mediump vec2 aPosition;\n
156   uniform highp   mat4 uMvpMatrix;\n
157   uniform highp   vec3 uSize;\n
158   uniform mediump mat3 uAlignmentMatrix;\n
159   varying mediump vec2 vTexCoord;\n
160   varying mediump vec2 vPosition;\n
161   varying mediump vec2 vRectSize;\n
162   varying mediump float vCornerRadius;\n
163   \n
164   //Visual size and offset
165   uniform mediump vec2 offset;\n
166   uniform highp   vec2 size;\n
167   uniform mediump vec4 offsetSizeMode;\n
168   uniform mediump vec2 origin;\n
169   uniform mediump vec2 anchorPoint;\n
170   uniform mediump float cornerRadius;\n
171   uniform mediump float cornerRadiusPolicy;\n
172
173   vec4 ComputeVertexPosition()\n
174   {\n
175     vec2 visualSize = mix(uSize.xy*size, size, offsetSizeMode.zw );\n
176     vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy);\n
177     mediump float minSize = min( visualSize.x, visualSize.y );\n
178     vCornerRadius = mix( cornerRadius * minSize, cornerRadius, cornerRadiusPolicy);\n
179     vCornerRadius = min( vCornerRadius, minSize * 0.5 );\n
180     vRectSize = visualSize * 0.5 - vCornerRadius;\n
181     vPosition = aPosition * visualSize;\n
182     return vec4( (aPosition + anchorPoint)*visualSize + (visualOffset + origin)*uSize.xy, 0.0, 1.0 );\n
183   }\n
184
185   void main()\n
186   {\n
187     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
188     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
189     \n
190     gl_Position = uMvpMatrix * ComputeVertexPosition();\n
191   }\n
192 ),
193
194 // vertex shader for gradient units as USER_SPACE with corner radius
195 DALI_COMPOSE_SHADER(
196   attribute mediump vec2 aPosition;\n
197   uniform highp   mat4 uMvpMatrix;\n
198   uniform highp   vec3 uSize;\n
199   uniform mediump mat3 uAlignmentMatrix;\n
200   varying mediump vec2 vTexCoord;\n
201   varying mediump vec2 vPosition;\n
202   varying mediump vec2 vRectSize;\n
203   varying mediump float vCornerRadius;\n
204   \n
205   //Visual size and offset
206   uniform mediump vec2 offset;\n
207   uniform highp   vec2 size;\n
208   uniform mediump vec4 offsetSizeMode;\n
209   uniform mediump vec2 origin;\n
210   uniform mediump vec2 anchorPoint;\n
211   uniform mediump float cornerRadius;\n
212   uniform mediump float cornerRadiusPolicy;\n
213
214   vec4 ComputeVertexPosition()\n
215   {\n
216     vec2 visualSize = mix(uSize.xy*size, size, offsetSizeMode.zw );\n
217     vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy);\n
218     mediump float minSize = min( visualSize.x, visualSize.y );\n
219     vCornerRadius = mix( cornerRadius * minSize, cornerRadius, cornerRadiusPolicy);\n
220     vCornerRadius = min( vCornerRadius, minSize * 0.5 );\n
221     vRectSize = visualSize * 0.5 - vCornerRadius;\n
222     vPosition = aPosition * visualSize;\n
223     return vec4( (aPosition + anchorPoint)*visualSize + (visualOffset + origin)*uSize.xy, 0.0, 1.0 );\n
224   }\n
225
226   void main()\n
227   {\n
228     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
229     vertexPosition.xyz *= uSize;\n
230     gl_Position = uMvpMatrix * ComputeVertexPosition();\n
231     \n
232     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
233   }\n
234 )
235 };
236
237 const char* FRAGMENT_SHADER[] =
238 {
239 // fragment shader for linear gradient
240 DALI_COMPOSE_SHADER(
241   uniform sampler2D sTexture;\n // sampler1D?
242   uniform lowp vec4 uColor;\n
243   uniform lowp vec3 mixColor;\n
244   varying mediump vec2 vTexCoord;\n
245   \n
246   void main()\n
247   {\n
248     gl_FragColor = texture2D( sTexture, vec2( vTexCoord.y, 0.5 ) ) * vec4(mixColor, 1.0) * uColor;\n
249   }\n
250 ),
251
252 // fragment shader for radial gradient
253 DALI_COMPOSE_SHADER(
254   uniform sampler2D sTexture;\n // sampler1D?
255   uniform lowp vec4 uColor;\n
256   uniform lowp vec3 mixColor;\n
257   varying mediump vec2 vTexCoord;\n
258   \n
259   void main()\n
260   {\n
261     gl_FragColor = texture2D( sTexture, vec2( length(vTexCoord), 0.5 ) ) * vec4(mixColor, 1.0) * uColor;\n
262   }\n
263 ),
264
265 // fragment shader for linear gradient with corner radius
266 DALI_COMPOSE_SHADER(
267   uniform sampler2D sTexture;\n // sampler1D?
268   uniform lowp vec4 uColor;\n
269   uniform lowp vec3 mixColor;\n
270   varying mediump vec2 vTexCoord;\n
271   varying mediump vec2 vPosition;\n
272   varying mediump vec2 vRectSize;\n
273   varying mediump float vCornerRadius;\n
274   \n
275   void main()\n
276   {\n
277     mediump float dist = length( max( abs( vPosition ), vRectSize ) - vRectSize ) - vCornerRadius;\n
278     gl_FragColor = texture2D( sTexture, vec2( vTexCoord.y, 0.5 ) ) * vec4(mixColor, 1.0) * uColor;\n
279     gl_FragColor *= 1.0 - smoothstep( -1.0, 1.0, dist );\n
280   }\n
281 ),
282
283 // fragment shader for radial gradient with corner radius
284 DALI_COMPOSE_SHADER(
285   uniform sampler2D sTexture;\n // sampler1D?
286   uniform lowp vec4 uColor;\n
287   uniform lowp vec3 mixColor;\n
288   varying mediump vec2 vTexCoord;\n
289   varying mediump vec2 vPosition;\n
290   varying mediump vec2 vRectSize;\n
291   varying mediump float vCornerRadius;\n
292   \n
293   void main()\n
294   {\n
295     mediump float dist = length( max( abs( vPosition ), vRectSize ) - vRectSize ) - vCornerRadius;\n
296     gl_FragColor = texture2D( sTexture, vec2( length(vTexCoord), 0.5 ) ) * vec4(mixColor, 1.0) * uColor;\n
297     gl_FragColor *= 1.0 - smoothstep( -1.0, 1.0, dist );\n
298   }\n
299 )
300 };
301
302 Dali::WrapMode::Type GetWrapMode( Toolkit::GradientVisual::SpreadMethod::Type spread )
303 {
304   switch(spread)
305   {
306     case Toolkit::GradientVisual::SpreadMethod::REPEAT:
307     {
308       return Dali::WrapMode::REPEAT;
309     }
310     case Toolkit::GradientVisual::SpreadMethod::REFLECT:
311     {
312       return Dali::WrapMode::MIRRORED_REPEAT;
313     }
314     case Toolkit::GradientVisual::SpreadMethod::PAD:
315     default:
316     {
317       return Dali::WrapMode::CLAMP_TO_EDGE;
318     }
319   }
320 }
321
322 } // unnamed namespace
323
324 GradientVisualPtr GradientVisual::New( VisualFactoryCache& factoryCache, const Property::Map& properties )
325 {
326   GradientVisualPtr gradientVisualPtr( new GradientVisual( factoryCache ) );
327   gradientVisualPtr->SetProperties( properties );
328   return gradientVisualPtr;
329 }
330
331 GradientVisual::GradientVisual( VisualFactoryCache& factoryCache )
332 : Visual::Base( factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::GRADIENT ),
333   mGradientType( LINEAR ),
334   mIsOpaque( true )
335 {
336   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
337 }
338
339 GradientVisual::~GradientVisual()
340 {
341 }
342
343 void GradientVisual::DoSetProperties( const Property::Map& propertyMap )
344 {
345   Toolkit::GradientVisual::Units::Type gradientUnits = Toolkit::GradientVisual::Units::OBJECT_BOUNDING_BOX;
346
347   Property::Value* unitsValue = propertyMap.Find( Toolkit::GradientVisual::Property::UNITS, UNITS_NAME );
348   if( unitsValue )
349   {
350     Scripting::GetEnumerationProperty( *unitsValue, UNITS_TABLE, UNITS_TABLE_COUNT, gradientUnits );
351   }
352
353   mGradientType = LINEAR;
354   if( propertyMap.Find( Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME ) )
355   {
356     mGradientType = RADIAL;
357   }
358
359   if( NewGradient( mGradientType, propertyMap ) )
360   {
361     mGradient->SetGradientUnits( gradientUnits );
362     mGradientTransform = mGradient->GetAlignmentTransform();
363   }
364   else
365   {
366     DALI_LOG_ERROR( "Fail to provide valid properties to create a GradientVisual object\n" );
367   }
368 }
369
370 void GradientVisual::OnSetTransform()
371 {
372   if( mImpl->mRenderer )
373   {
374     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
375   }
376 }
377
378 void GradientVisual::DoSetOnScene( Actor& actor )
379 {
380   InitializeRenderer();
381
382   actor.AddRenderer( mImpl->mRenderer );
383
384   // Gradient Visual generated and ready to display
385   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
386 }
387
388 void GradientVisual::DoCreatePropertyMap( Property::Map& map ) const
389 {
390   map.Clear();
391   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::GRADIENT );
392   map.Insert( Toolkit::GradientVisual::Property::UNITS, mGradient->GetGradientUnits() );
393   map.Insert( Toolkit::GradientVisual::Property::SPREAD_METHOD, mGradient->GetSpreadMethod() );
394
395   const Vector<Gradient::GradientStop>& stops( mGradient->GetStops() );
396   Property::Array offsets;
397   Property::Array colors;
398   for( unsigned int i=0; i<stops.Count(); i++ )
399   {
400     offsets.PushBack( stops[i].mOffset );
401     if( EqualsZero(stops[i].mStopColor.a) )
402     {
403       colors.PushBack( Vector4::ZERO );
404     }
405     else
406     {
407       colors.PushBack( Vector4( stops[i].mStopColor.r / stops[i].mStopColor.a,
408                                 stops[i].mStopColor.g / stops[i].mStopColor.a,
409                                 stops[i].mStopColor.b / stops[i].mStopColor.a,
410                                 stops[i].mStopColor.a));
411     }
412   }
413
414   map.Insert( Toolkit::GradientVisual::Property::STOP_OFFSET, offsets );
415   map.Insert( Toolkit::GradientVisual::Property::STOP_COLOR, colors );
416
417   if( &typeid( *mGradient ) == &typeid(LinearGradient) )
418   {
419     LinearGradient* gradient = static_cast<LinearGradient*>( mGradient.Get() );
420     map.Insert( Toolkit::GradientVisual::Property::START_POSITION, gradient->GetStartPosition() );
421     map.Insert( Toolkit::GradientVisual::Property::END_POSITION, gradient->GetEndPosition() );
422   }
423   else // if( &typeid( *mGradient ) == &typeid(RadialGradient) )
424   {
425     RadialGradient* gradient = static_cast<RadialGradient*>( mGradient.Get() );
426     map.Insert( Toolkit::GradientVisual::Property::CENTER, gradient->GetCenter() );
427     map.Insert( Toolkit::GradientVisual::Property::RADIUS, gradient->GetRadius() );
428   }
429 }
430
431 void GradientVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
432 {
433   // Do nothing
434 }
435
436 void GradientVisual::InitializeRenderer()
437 {
438   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
439
440   Toolkit::GradientVisual::Units::Type gradientUnits = mGradient->GetGradientUnits();
441   int roundedCorner = IsRoundedCornerRequired() ? 1 : 0;
442   VisualFactoryCache::ShaderType shaderType = SHADER_TYPE_TABLE[mGradientType][gradientUnits + roundedCorner * 2];
443   Shader shader = mFactoryCache.GetShader( shaderType );
444   if( !shader )
445   {
446     shader = Shader::New( VERTEX_SHADER[gradientUnits + roundedCorner * 2], FRAGMENT_SHADER[ mGradientType + roundedCorner * 2 ] );
447     mFactoryCache.SaveShader( shaderType, shader );
448   }
449
450   //Set up the texture set
451   TextureSet textureSet = TextureSet::New();
452   Dali::Texture lookupTexture = mGradient->GenerateLookupTexture();
453   textureSet.SetTexture( 0u, lookupTexture );
454   Dali::WrapMode::Type wrap = GetWrapMode( mGradient->GetSpreadMethod() );
455   Sampler sampler = Sampler::New();
456   sampler.SetWrapMode(  wrap, wrap  );
457   textureSet.SetSampler( 0u, sampler );
458
459   mImpl->mRenderer = Renderer::New( geometry, shader );
460   mImpl->mRenderer.SetTextures( textureSet );
461
462   // If opaque and then no need to have blending
463   if( mIsOpaque )
464   {
465     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::OFF );
466   }
467
468   mImpl->mRenderer.RegisterProperty( UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform );
469
470   //Register transform properties
471   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
472 }
473
474 bool GradientVisual::NewGradient(Type gradientType, const Property::Map& propertyMap)
475 {
476   if( gradientType == LINEAR )
477   {
478     Property::Value* startPositionValue = propertyMap.Find( Toolkit::GradientVisual::Property::START_POSITION, START_POSITION_NAME );
479     Property::Value* endPositionValue = propertyMap.Find( Toolkit::GradientVisual::Property::END_POSITION, END_POSITION_NAME );
480     Vector2 startPosition;
481     Vector2 endPosition;
482
483     if( startPositionValue && startPositionValue->Get(startPosition)
484      && endPositionValue && endPositionValue->Get( endPosition ) )
485     {
486       mGradient = new LinearGradient( startPosition, endPosition );
487     }
488     else
489     {
490       return false;
491     }
492   }
493   else // type==RADIAL
494   {
495     Property::Value* centerValue = propertyMap.Find( Toolkit::GradientVisual::Property::CENTER, CENTER_NAME );
496     Property::Value* radiusValue = propertyMap.Find( Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME );
497     Vector2 center;
498     float radius;
499     if( centerValue && centerValue->Get(center)
500         && radiusValue && radiusValue->Get(radius) )
501     {
502       mGradient = new RadialGradient( center, radius );
503     }
504     else
505     {
506       return false;
507     }
508   }
509
510   unsigned int numValidStop = 0u;
511   Property::Value* stopOffsetValue = propertyMap.Find( Toolkit::GradientVisual::Property::STOP_OFFSET, STOP_OFFSET_NAME );
512   Property::Value* stopColorValue = propertyMap.Find( Toolkit::GradientVisual::Property::STOP_COLOR, STOP_COLOR_NAME );
513   if( stopColorValue )
514   {
515     Vector<float> offsetArray;
516     Property::Array* colorArray = stopColorValue->GetArray();
517     if( colorArray )
518     {
519       GetStopOffsets( stopOffsetValue, offsetArray );
520       unsigned int numStop = offsetArray.Count() < colorArray->Count() ?
521                              offsetArray.Count() : colorArray->Count();
522       Vector4 color;
523       for( unsigned int i=0; i<numStop; i++ )
524       {
525         if( (colorArray->GetElementAt(i)).Get(color) )
526         {
527           mGradient->AddStop( offsetArray[i], Vector4(color.r*color.a, color.g*color.a, color.b*color.a, color.a));
528           numValidStop++;
529           if( ! Equals( color.a, 1.0f, Math::MACHINE_EPSILON_1 ) )
530           {
531             mIsOpaque = false;
532           }
533         }
534       }
535     }
536   }
537
538   if( numValidStop < 1u ) // no valid stop
539   {
540     return false;
541   }
542
543   Property::Value* spread = propertyMap.Find( Toolkit::GradientVisual::Property::SPREAD_METHOD, SPREAD_METHOD_NAME );
544   // The default spread method is PAD. Only need to set new spread if it's anything else.
545   if( spread )
546   {
547     Toolkit::GradientVisual::SpreadMethod::Type spreadMethod = Toolkit::GradientVisual::SpreadMethod::PAD;
548     if( Scripting::GetEnumerationProperty( *spread, SPREAD_METHOD_TABLE, SPREAD_METHOD_TABLE_COUNT, spreadMethod ) )
549     {
550       mGradient->SetSpreadMethod( spreadMethod );
551     }
552   }
553
554   return true;
555 }
556
557 void GradientVisual::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
558 {
559
560   if ( value ) // Only check valve type if a valid Property has been passed in
561   {
562     switch ( value->GetType() )
563     {
564       case Property::VECTOR2:
565       {
566         Vector2 offset2;
567         value->Get( offset2 );
568         stopOffsets.PushBack( offset2.x );
569         stopOffsets.PushBack( offset2.y );
570         break;
571       }
572       case Property::VECTOR3:
573       {
574         Vector3 offset3;
575         value->Get( offset3 );
576         stopOffsets.PushBack( offset3.x );
577         stopOffsets.PushBack( offset3.y );
578         stopOffsets.PushBack( offset3.z );
579         break;
580       }
581       case Property::VECTOR4:
582       {
583         Vector4 offset4;
584         value->Get( offset4 );
585         stopOffsets.PushBack( offset4.x );
586         stopOffsets.PushBack( offset4.y );
587         stopOffsets.PushBack( offset4.z );
588         stopOffsets.PushBack( offset4.w );
589         break;
590       }
591       case Property::ARRAY:
592       {
593         const Property::Array* offsetArray = value->GetArray();
594         if( offsetArray )
595         {
596           unsigned int numStop = offsetArray->Count();
597           float offset;
598           for( unsigned int i=0; i<numStop; i++ )
599           {
600             if( offsetArray->GetElementAt(i).Get(offset) )
601             {
602               stopOffsets.PushBack( offset );
603             }
604           }
605         }
606         break;
607       }
608       default:
609       {
610         DALI_LOG_WARNING("GetStopOffsets passed unsupported Property Map\n");
611         // Unsupported Type
612       }
613     }
614   }
615
616   if ( stopOffsets.Empty() )
617   {
618     // Set default offset if none set by Property system, need a minimum and maximum
619     stopOffsets.PushBack( DEFAULT_OFFSET_MINIMUM );
620     stopOffsets.PushBack( DEFAULT_OFFSET_MAXIMUM );
621   }
622 }
623
624 } // namespace Internal
625
626 } // namespace Toolkit
627
628 } // namespace Dali