96f9f85dc9d41e2171c9219bd7bdbd14c4089df0
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / gradient / gradient-visual.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 "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 #include <dali-toolkit/internal/graphics/generated/gradient-visual-bounding-box-rounded-corner-shader-vert.h>
40 #include <dali-toolkit/internal/graphics/generated/gradient-visual-bounding-box-shader-vert.h>
41 #include <dali-toolkit/internal/graphics/generated/gradient-visual-linear-rounded-corner-shader-frag.h>
42 #include <dali-toolkit/internal/graphics/generated/gradient-visual-linear-shader-frag.h>
43 #include <dali-toolkit/internal/graphics/generated/gradient-visual-radial-rounded-corner-shader-frag.h>
44 #include <dali-toolkit/internal/graphics/generated/gradient-visual-radial-shader-frag.h>
45 #include <dali-toolkit/internal/graphics/generated/gradient-visual-user-space-rounded-corner-shader-vert.h>
46 #include <dali-toolkit/internal/graphics/generated/gradient-visual-user-space-shader-vert.h>
47
48 namespace Dali
49 {
50
51 namespace Toolkit
52 {
53
54 namespace Internal
55 {
56
57 namespace
58 {
59
60 DALI_ENUM_TO_STRING_TABLE_BEGIN( UNITS )
61 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::Units, OBJECT_BOUNDING_BOX )
62 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::Units, USER_SPACE )
63 DALI_ENUM_TO_STRING_TABLE_END( UNITS )
64
65 DALI_ENUM_TO_STRING_TABLE_BEGIN( SPREAD_METHOD )
66 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, PAD )
67 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, REFLECT )
68 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, REPEAT )
69 DALI_ENUM_TO_STRING_TABLE_END( SPREAD_METHOD )
70
71 // uniform names
72 const char * const UNIFORM_ALIGNMENT_MATRIX_NAME( "uAlignmentMatrix" );
73
74 // default offset value
75 const unsigned int DEFAULT_OFFSET_MINIMUM = 0.0f;
76 const unsigned int DEFAULT_OFFSET_MAXIMUM = 1.0f;
77
78 VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[][4] =
79 {
80   {
81     VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE,
82     VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX,
83     VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE_ROUNDED_CORNER,
84     VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX_ROUNDED_CORNER
85   },
86   {
87     VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE,
88     VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX,
89     VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE_ROUNDED_CORNER,
90     VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX_ROUNDED_CORNER
91   }
92 };
93
94 const std::string_view VERTEX_SHADER[] =
95 {
96   // vertex shader for gradient units as OBJECT_BOUNDING_BOX
97   SHADER_GRADIENT_VISUAL_BOUNDING_BOX_SHADER_VERT,
98
99   // vertex shader for gradient units as USER_SPACE
100   SHADER_GRADIENT_VISUAL_USER_SPACE_SHADER_VERT,
101
102   // vertex shader for gradient units as OBJECT_BOUNDING_BOX with corner radius
103   SHADER_GRADIENT_VISUAL_BOUNDING_BOX_ROUNDED_CORNER_SHADER_VERT,
104
105   // vertex shader for gradient units as USER_SPACE with corner radius
106   SHADER_GRADIENT_VISUAL_USER_SPACE_ROUNDED_CORNER_SHADER_VERT
107 };
108
109 const std::string_view FRAGMENT_SHADER[] =
110 {
111   // fragment shader for linear gradient
112   SHADER_GRADIENT_VISUAL_LINEAR_SHADER_FRAG,
113
114   // fragment shader for radial gradient
115   SHADER_GRADIENT_VISUAL_RADIAL_SHADER_FRAG,
116
117   // fragment shader for linear gradient with corner radius
118   SHADER_GRADIENT_VISUAL_LINEAR_ROUNDED_CORNER_SHADER_FRAG,
119
120   // fragment shader for radial gradient with corner radius
121   SHADER_GRADIENT_VISUAL_RADIAL_ROUNDED_CORNER_SHADER_FRAG
122 };
123
124 Dali::WrapMode::Type GetWrapMode( Toolkit::GradientVisual::SpreadMethod::Type spread )
125 {
126   switch(spread)
127   {
128     case Toolkit::GradientVisual::SpreadMethod::REPEAT:
129     {
130       return Dali::WrapMode::REPEAT;
131     }
132     case Toolkit::GradientVisual::SpreadMethod::REFLECT:
133     {
134       return Dali::WrapMode::MIRRORED_REPEAT;
135     }
136     case Toolkit::GradientVisual::SpreadMethod::PAD:
137     default:
138     {
139       return Dali::WrapMode::CLAMP_TO_EDGE;
140     }
141   }
142 }
143
144 } // unnamed namespace
145
146 GradientVisualPtr GradientVisual::New( VisualFactoryCache& factoryCache, const Property::Map& properties )
147 {
148   GradientVisualPtr gradientVisualPtr( new GradientVisual( factoryCache ) );
149   gradientVisualPtr->SetProperties( properties );
150   gradientVisualPtr->Initialize();
151   return gradientVisualPtr;
152 }
153
154 GradientVisual::GradientVisual( VisualFactoryCache& factoryCache )
155 : Visual::Base( factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::GRADIENT ),
156   mGradientType( LINEAR ),
157   mIsOpaque( true )
158 {
159   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
160 }
161
162 GradientVisual::~GradientVisual()
163 {
164 }
165
166 void GradientVisual::DoSetProperties( const Property::Map& propertyMap )
167 {
168   Toolkit::GradientVisual::Units::Type gradientUnits = Toolkit::GradientVisual::Units::OBJECT_BOUNDING_BOX;
169
170   Property::Value* unitsValue = propertyMap.Find( Toolkit::GradientVisual::Property::UNITS, UNITS_NAME );
171   if( unitsValue )
172   {
173     Scripting::GetEnumerationProperty( *unitsValue, UNITS_TABLE, UNITS_TABLE_COUNT, gradientUnits );
174   }
175
176   mGradientType = LINEAR;
177   if( propertyMap.Find( Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME ) )
178   {
179     mGradientType = RADIAL;
180   }
181
182   if( NewGradient( mGradientType, propertyMap ) )
183   {
184     mGradient->SetGradientUnits( gradientUnits );
185     mGradientTransform = mGradient->GetAlignmentTransform();
186   }
187   else
188   {
189     DALI_LOG_ERROR( "Fail to provide valid properties to create a GradientVisual object\n" );
190   }
191 }
192
193 void GradientVisual::OnSetTransform()
194 {
195   if( mImpl->mRenderer )
196   {
197     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
198   }
199 }
200
201 void GradientVisual::DoSetOnScene( Actor& actor )
202 {
203   actor.AddRenderer( mImpl->mRenderer );
204
205   // Gradient Visual generated and ready to display
206   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
207 }
208
209 void GradientVisual::UpdateShader()
210 {
211   if(mImpl->mRenderer)
212   {
213     Shader shader = GetShader();
214     mImpl->mRenderer.SetShader(shader);
215   }
216 }
217
218 void GradientVisual::DoCreatePropertyMap( Property::Map& map ) const
219 {
220   map.Clear();
221   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::GRADIENT );
222   map.Insert( Toolkit::GradientVisual::Property::UNITS, mGradient->GetGradientUnits() );
223   map.Insert( Toolkit::GradientVisual::Property::SPREAD_METHOD, mGradient->GetSpreadMethod() );
224
225   const Vector<Gradient::GradientStop>& stops( mGradient->GetStops() );
226   Property::Array offsets;
227   Property::Array colors;
228   for( unsigned int i=0; i<stops.Count(); i++ )
229   {
230     offsets.PushBack( stops[i].mOffset );
231     if( EqualsZero(stops[i].mStopColor.a) )
232     {
233       colors.PushBack( Vector4::ZERO );
234     }
235     else
236     {
237       colors.PushBack( Vector4( stops[i].mStopColor.r / stops[i].mStopColor.a,
238                                 stops[i].mStopColor.g / stops[i].mStopColor.a,
239                                 stops[i].mStopColor.b / stops[i].mStopColor.a,
240                                 stops[i].mStopColor.a));
241     }
242   }
243
244   map.Insert( Toolkit::GradientVisual::Property::STOP_OFFSET, offsets );
245   map.Insert( Toolkit::GradientVisual::Property::STOP_COLOR, colors );
246
247   if( &typeid( *mGradient ) == &typeid(LinearGradient) )
248   {
249     LinearGradient* gradient = static_cast<LinearGradient*>( mGradient.Get() );
250     map.Insert( Toolkit::GradientVisual::Property::START_POSITION, gradient->GetStartPosition() );
251     map.Insert( Toolkit::GradientVisual::Property::END_POSITION, gradient->GetEndPosition() );
252   }
253   else // if( &typeid( *mGradient ) == &typeid(RadialGradient) )
254   {
255     RadialGradient* gradient = static_cast<RadialGradient*>( mGradient.Get() );
256     map.Insert( Toolkit::GradientVisual::Property::CENTER, gradient->GetCenter() );
257     map.Insert( Toolkit::GradientVisual::Property::RADIUS, gradient->GetRadius() );
258   }
259 }
260
261 void GradientVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
262 {
263   // Do nothing
264 }
265
266 void GradientVisual::OnInitialize()
267 {
268   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
269   Shader   shader   = GetShader();
270
271   //Set up the texture set
272   TextureSet textureSet = TextureSet::New();
273   Dali::Texture lookupTexture = mGradient->GenerateLookupTexture();
274   textureSet.SetTexture( 0u, lookupTexture );
275   Dali::WrapMode::Type wrap = GetWrapMode( mGradient->GetSpreadMethod() );
276   Sampler sampler = Sampler::New();
277   sampler.SetWrapMode(  wrap, wrap  );
278   textureSet.SetSampler( 0u, sampler );
279
280   mImpl->mRenderer = Renderer::New( geometry, shader );
281   mImpl->mRenderer.SetTextures( textureSet );
282
283   // If opaque and then no need to have blending
284   if( mIsOpaque )
285   {
286     mImpl->mRenderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::OFF );
287   }
288
289   mImpl->mRenderer.RegisterProperty( UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform );
290
291   //Register transform properties
292   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
293 }
294
295 bool GradientVisual::NewGradient(Type gradientType, const Property::Map& propertyMap)
296 {
297   if( gradientType == LINEAR )
298   {
299     Property::Value* startPositionValue = propertyMap.Find( Toolkit::GradientVisual::Property::START_POSITION, START_POSITION_NAME );
300     Property::Value* endPositionValue = propertyMap.Find( Toolkit::GradientVisual::Property::END_POSITION, END_POSITION_NAME );
301     Vector2 startPosition;
302     Vector2 endPosition;
303
304     if( startPositionValue && startPositionValue->Get(startPosition)
305      && endPositionValue && endPositionValue->Get( endPosition ) )
306     {
307       mGradient = new LinearGradient( startPosition, endPosition );
308     }
309     else
310     {
311       return false;
312     }
313   }
314   else // type==RADIAL
315   {
316     Property::Value* centerValue = propertyMap.Find( Toolkit::GradientVisual::Property::CENTER, CENTER_NAME );
317     Property::Value* radiusValue = propertyMap.Find( Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME );
318     Vector2 center;
319     float radius;
320     if( centerValue && centerValue->Get(center)
321         && radiusValue && radiusValue->Get(radius) )
322     {
323       mGradient = new RadialGradient( center, radius );
324     }
325     else
326     {
327       return false;
328     }
329   }
330
331   unsigned int numValidStop = 0u;
332   Property::Value* stopOffsetValue = propertyMap.Find( Toolkit::GradientVisual::Property::STOP_OFFSET, STOP_OFFSET_NAME );
333   Property::Value* stopColorValue = propertyMap.Find( Toolkit::GradientVisual::Property::STOP_COLOR, STOP_COLOR_NAME );
334   if( stopColorValue )
335   {
336     Vector<float> offsetArray;
337     Property::Array* colorArray = stopColorValue->GetArray();
338     if( colorArray )
339     {
340       GetStopOffsets( stopOffsetValue, offsetArray );
341       unsigned int numStop = offsetArray.Count() < colorArray->Count() ?
342                              offsetArray.Count() : colorArray->Count();
343       Vector4 color;
344       for( unsigned int i=0; i<numStop; i++ )
345       {
346         if( (colorArray->GetElementAt(i)).Get(color) )
347         {
348           mGradient->AddStop( offsetArray[i], Vector4(color.r*color.a, color.g*color.a, color.b*color.a, color.a));
349           numValidStop++;
350           if( ! Equals( color.a, 1.0f, Math::MACHINE_EPSILON_1 ) )
351           {
352             mIsOpaque = false;
353           }
354         }
355       }
356     }
357   }
358
359   if( numValidStop < 1u ) // no valid stop
360   {
361     return false;
362   }
363
364   Property::Value* spread = propertyMap.Find( Toolkit::GradientVisual::Property::SPREAD_METHOD, SPREAD_METHOD_NAME );
365   // The default spread method is PAD. Only need to set new spread if it's anything else.
366   if( spread )
367   {
368     Toolkit::GradientVisual::SpreadMethod::Type spreadMethod = Toolkit::GradientVisual::SpreadMethod::PAD;
369     if( Scripting::GetEnumerationProperty( *spread, SPREAD_METHOD_TABLE, SPREAD_METHOD_TABLE_COUNT, spreadMethod ) )
370     {
371       mGradient->SetSpreadMethod( spreadMethod );
372     }
373   }
374
375   return true;
376 }
377
378 Shader GradientVisual::GetShader()
379 {
380   Toolkit::GradientVisual::Units::Type gradientUnits = mGradient->GetGradientUnits();
381   int                                  roundedCorner = IsRoundedCornerRequired() ? 1 : 0;
382   VisualFactoryCache::ShaderType       shaderType    = SHADER_TYPE_TABLE[mGradientType][gradientUnits + roundedCorner * 2];
383   Shader                               shader        = mFactoryCache.GetShader(shaderType);
384   if(!shader)
385   {
386     shader = Shader::New(VERTEX_SHADER[gradientUnits + roundedCorner * 2], FRAGMENT_SHADER[mGradientType + roundedCorner * 2]);
387     mFactoryCache.SaveShader(shaderType, shader);
388   }
389
390   return shader;
391 }
392
393 void GradientVisual::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
394 {
395
396   if ( value ) // Only check valve type if a valid Property has been passed in
397   {
398     switch ( value->GetType() )
399     {
400       case Property::VECTOR2:
401       {
402         Vector2 offset2;
403         value->Get( offset2 );
404         stopOffsets.PushBack( offset2.x );
405         stopOffsets.PushBack( offset2.y );
406         break;
407       }
408       case Property::VECTOR3:
409       {
410         Vector3 offset3;
411         value->Get( offset3 );
412         stopOffsets.PushBack( offset3.x );
413         stopOffsets.PushBack( offset3.y );
414         stopOffsets.PushBack( offset3.z );
415         break;
416       }
417       case Property::VECTOR4:
418       {
419         Vector4 offset4;
420         value->Get( offset4 );
421         stopOffsets.PushBack( offset4.x );
422         stopOffsets.PushBack( offset4.y );
423         stopOffsets.PushBack( offset4.z );
424         stopOffsets.PushBack( offset4.w );
425         break;
426       }
427       case Property::ARRAY:
428       {
429         const Property::Array* offsetArray = value->GetArray();
430         if( offsetArray )
431         {
432           unsigned int numStop = offsetArray->Count();
433           float offset;
434           for( unsigned int i=0; i<numStop; i++ )
435           {
436             if( offsetArray->GetElementAt(i).Get(offset) )
437             {
438               stopOffsets.PushBack( offset );
439             }
440           }
441         }
442         break;
443       }
444       default:
445       {
446         DALI_LOG_WARNING("GetStopOffsets passed unsupported Property Map\n");
447         // Unsupported Type
448       }
449     }
450   }
451
452   if ( stopOffsets.Empty() )
453   {
454     // Set default offset if none set by Property system, need a minimum and maximum
455     stopOffsets.PushBack( DEFAULT_OFFSET_MINIMUM );
456     stopOffsets.PushBack( DEFAULT_OFFSET_MAXIMUM );
457   }
458 }
459
460 } // namespace Internal
461
462 } // namespace Toolkit
463
464 } // namespace Dali