2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "gradient-visual.h"
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>
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>
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 )
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 )
63 const char * const UNIFORM_ALIGNMENT_MATRIX_NAME( "uAlignmentMatrix" );
65 // default offset value
66 const unsigned int DEFAULT_OFFSET_MINIMUM = 0.0f;
67 const unsigned int DEFAULT_OFFSET_MAXIMUM = 1.0f;
69 VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[][4] =
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
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
85 const char* VERTEX_SHADER[] =
87 // vertex shader for gradient units as OBJECT_BOUNDING_BOX
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
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
103 vec4 ComputeVertexPosition()\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
112 mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
113 vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
115 gl_Position = uMvpMatrix * ComputeVertexPosition();\n
119 // vertex shader for gradient units as USER_SPACE
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
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
135 vec4 ComputeVertexPosition()\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
144 mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
145 vertexPosition.xyz *= uSize;\n
146 gl_Position = uMvpMatrix * ComputeVertexPosition();\n
148 vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
152 // vertex shader for gradient units as OBJECT_BOUNDING_BOX with corner radius
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
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
172 vec4 ComputeVertexPosition()\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
186 mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
187 vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
189 gl_Position = uMvpMatrix * ComputeVertexPosition();\n
193 // vertex shader for gradient units as USER_SPACE with corner radius
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
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
213 vec4 ComputeVertexPosition()\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
227 mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
228 vertexPosition.xyz *= uSize;\n
229 gl_Position = uMvpMatrix * ComputeVertexPosition();\n
231 vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
236 const char* FRAGMENT_SHADER[] =
238 // fragment shader for linear gradient
240 uniform sampler2D sTexture;\n // sampler1D?
241 uniform lowp vec4 uColor;\n
242 uniform lowp vec3 mixColor;\n
243 varying mediump vec2 vTexCoord;\n
247 gl_FragColor = texture2D( sTexture, vec2( vTexCoord.y, 0.5 ) ) * vec4(mixColor, 1.0) * uColor;\n
251 // fragment shader for radial gradient
253 uniform sampler2D sTexture;\n // sampler1D?
254 uniform lowp vec4 uColor;\n
255 uniform lowp vec3 mixColor;\n
256 varying mediump vec2 vTexCoord;\n
260 gl_FragColor = texture2D( sTexture, vec2( length(vTexCoord), 0.5 ) ) * vec4(mixColor, 1.0) * uColor;\n
264 // fragment shader for linear gradient with corner radius
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
276 mediump float dist = length( max( abs( vPosition ), vRectSize ) - vRectSize ) - vCornerRadius;\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
289 // fragment shader for radial gradient with corner radius
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
301 mediump float dist = length( max( abs( vPosition ), vRectSize ) - vRectSize ) - vCornerRadius;\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
315 Dali::WrapMode::Type GetWrapMode( Toolkit::GradientVisual::SpreadMethod::Type spread )
319 case Toolkit::GradientVisual::SpreadMethod::REPEAT:
321 return Dali::WrapMode::REPEAT;
323 case Toolkit::GradientVisual::SpreadMethod::REFLECT:
325 return Dali::WrapMode::MIRRORED_REPEAT;
327 case Toolkit::GradientVisual::SpreadMethod::PAD:
330 return Dali::WrapMode::CLAMP_TO_EDGE;
335 } // unnamed namespace
337 GradientVisualPtr GradientVisual::New( VisualFactoryCache& factoryCache, const Property::Map& properties )
339 GradientVisualPtr gradientVisualPtr( new GradientVisual( factoryCache ) );
340 gradientVisualPtr->SetProperties( properties );
341 return gradientVisualPtr;
344 GradientVisual::GradientVisual( VisualFactoryCache& factoryCache )
345 : Visual::Base( factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::GRADIENT ),
346 mGradientType( LINEAR ),
349 mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
352 GradientVisual::~GradientVisual()
356 void GradientVisual::DoSetProperties( const Property::Map& propertyMap )
358 Toolkit::GradientVisual::Units::Type gradientUnits = Toolkit::GradientVisual::Units::OBJECT_BOUNDING_BOX;
360 Property::Value* unitsValue = propertyMap.Find( Toolkit::GradientVisual::Property::UNITS, UNITS_NAME );
363 Scripting::GetEnumerationProperty( *unitsValue, UNITS_TABLE, UNITS_TABLE_COUNT, gradientUnits );
366 mGradientType = LINEAR;
367 if( propertyMap.Find( Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME ) )
369 mGradientType = RADIAL;
372 if( NewGradient( mGradientType, propertyMap ) )
374 mGradient->SetGradientUnits( gradientUnits );
375 mGradientTransform = mGradient->GetAlignmentTransform();
379 DALI_LOG_ERROR( "Fail to provide valid properties to create a GradientVisual object\n" );
383 void GradientVisual::OnSetTransform()
385 if( mImpl->mRenderer )
387 mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
391 void GradientVisual::DoSetOnScene( Actor& actor )
393 InitializeRenderer();
395 actor.AddRenderer( mImpl->mRenderer );
397 // Gradient Visual generated and ready to display
398 ResourceReady( Toolkit::Visual::ResourceStatus::READY );
401 void GradientVisual::DoCreatePropertyMap( Property::Map& map ) const
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() );
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++ )
413 offsets.PushBack( stops[i].mOffset );
414 if( EqualsZero(stops[i].mStopColor.a) )
416 colors.PushBack( Vector4::ZERO );
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));
427 map.Insert( Toolkit::GradientVisual::Property::STOP_OFFSET, offsets );
428 map.Insert( Toolkit::GradientVisual::Property::STOP_COLOR, colors );
430 if( &typeid( *mGradient ) == &typeid(LinearGradient) )
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() );
436 else // if( &typeid( *mGradient ) == &typeid(RadialGradient) )
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() );
444 void GradientVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
449 void GradientVisual::InitializeRenderer()
451 Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
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 );
459 shader = Shader::New( VERTEX_SHADER[gradientUnits + roundedCorner * 2], FRAGMENT_SHADER[ mGradientType + roundedCorner * 2 ] );
460 mFactoryCache.SaveShader( shaderType, shader );
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 );
472 mImpl->mRenderer = Renderer::New( geometry, shader );
473 mImpl->mRenderer.SetTextures( textureSet );
475 // If opaque then no need to have blending
478 mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::OFF );
481 mImpl->mRenderer.RegisterProperty( UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform );
483 //Register transform properties
484 mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
487 bool GradientVisual::NewGradient(Type gradientType, const Property::Map& propertyMap)
489 if( gradientType == LINEAR )
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;
496 if( startPositionValue && startPositionValue->Get(startPosition)
497 && endPositionValue && endPositionValue->Get( endPosition ) )
499 mGradient = new LinearGradient( startPosition, endPosition );
508 Property::Value* centerValue = propertyMap.Find( Toolkit::GradientVisual::Property::CENTER, CENTER_NAME );
509 Property::Value* radiusValue = propertyMap.Find( Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME );
512 if( centerValue && centerValue->Get(center)
513 && radiusValue && radiusValue->Get(radius) )
515 mGradient = new RadialGradient( center, radius );
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 );
528 Vector<float> offsetArray;
529 Property::Array* colorArray = stopColorValue->GetArray();
532 GetStopOffsets( stopOffsetValue, offsetArray );
533 unsigned int numStop = offsetArray.Count() < colorArray->Count() ?
534 offsetArray.Count() : colorArray->Count();
536 for( unsigned int i=0; i<numStop; i++ )
538 if( (colorArray->GetElementAt(i)).Get(color) )
540 mGradient->AddStop( offsetArray[i], Vector4(color.r*color.a, color.g*color.a, color.b*color.a, color.a));
542 if( ! Equals( color.a, 1.0f, Math::MACHINE_EPSILON_1 ) )
551 if( numValidStop < 1u ) // no valid stop
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.
560 Toolkit::GradientVisual::SpreadMethod::Type spreadMethod = Toolkit::GradientVisual::SpreadMethod::PAD;
561 if( Scripting::GetEnumerationProperty( *spread, SPREAD_METHOD_TABLE, SPREAD_METHOD_TABLE_COUNT, spreadMethod ) )
563 mGradient->SetSpreadMethod( spreadMethod );
570 void GradientVisual::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
573 if ( value ) // Only check valve type if a valid Property has been passed in
575 switch ( value->GetType() )
577 case Property::VECTOR2:
580 value->Get( offset2 );
581 stopOffsets.PushBack( offset2.x );
582 stopOffsets.PushBack( offset2.y );
585 case Property::VECTOR3:
588 value->Get( offset3 );
589 stopOffsets.PushBack( offset3.x );
590 stopOffsets.PushBack( offset3.y );
591 stopOffsets.PushBack( offset3.z );
594 case Property::VECTOR4:
597 value->Get( offset4 );
598 stopOffsets.PushBack( offset4.x );
599 stopOffsets.PushBack( offset4.y );
600 stopOffsets.PushBack( offset4.z );
601 stopOffsets.PushBack( offset4.w );
604 case Property::ARRAY:
606 const Property::Array* offsetArray = value->GetArray();
609 unsigned int numStop = offsetArray->Count();
611 for( unsigned int i=0; i<numStop; i++ )
613 if( offsetArray->GetElementAt(i).Get(offset) )
615 stopOffsets.PushBack( offset );
623 DALI_LOG_WARNING("GetStopOffsets passed unsupported Property Map\n");
629 if ( stopOffsets.Empty() )
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 );
637 } // namespace Internal
639 } // namespace Toolkit