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