[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / gradient / gradient-visual.cpp
1 /*
2  * Copyright (c) 2024 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 <dali/devel-api/rendering/renderer-devel.h>
23 #include <dali/devel-api/scripting/enum-helper.h>
24 #include <dali/devel-api/scripting/scripting.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/public-api/common/dali-vector.h>
27 #include <dali/public-api/object/property-array.h>
28 #include <dali/public-api/rendering/decorated-visual-renderer.h>
29 #include <typeinfo>
30
31 // INTERNAL INCLUDES
32 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
33 #include <dali-toolkit/internal/visuals/gradient/linear-gradient.h>
34 #include <dali-toolkit/internal/visuals/gradient/radial-gradient.h>
35 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
36 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
37 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
38 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
39 #include <dali-toolkit/public-api/visuals/gradient-visual-properties.h>
40 #include <dali-toolkit/public-api/visuals/visual-properties.h>
41
42 namespace Dali
43 {
44 namespace Toolkit
45 {
46 namespace Internal
47 {
48 namespace
49 {
50 static constexpr int32_t CUSTOM_PROPERTY_COUNT(1); // alignment
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 static constexpr std::string_view UNIFORM_ALIGNMENT_MATRIX_NAME("uAlignmentMatrix");
65 static constexpr std::string_view UNIFORM_TEXTURE_COORDINATE_SCALE_FACTOR_NAME("uTextureCoordinateScaleFactor");
66
67 // default offset value
68 static constexpr float DEFAULT_OFFSET_MINIMUM = 0.0f;
69 static constexpr float DEFAULT_OFFSET_MAXIMUM = 1.0f;
70
71 VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[16] =
72   {
73     VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX,
74     VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX_ROUNDED_CORNER,
75     VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX_BORDERLINE,
76     VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX_ROUNDED_BORDERLINE,
77     VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE,
78     VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE_ROUNDED_CORNER,
79     VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE_BORDERLINE,
80     VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE_ROUNDED_BORDERLINE,
81     VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX,
82     VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX_ROUNDED_CORNER,
83     VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX_BORDERLINE,
84     VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX_ROUNDED_BORDERLINE,
85     VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE,
86     VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE_ROUNDED_CORNER,
87     VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE_BORDERLINE,
88     VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE_ROUNDED_BORDERLINE,
89 };
90
91 // enum of required list when we select shader
92 enum GradientVisualRequireFlag
93 {
94   DEFAULT        = 0,
95   ROUNDED_CORNER = 1 << 0,
96   BORDERLINE     = 1 << 1,
97   USER_SPACE     = 1 << 2,
98   RADIAL         = 1 << 3,
99 };
100
101 Dali::WrapMode::Type GetWrapMode(Toolkit::GradientVisual::SpreadMethod::Type spread)
102 {
103   switch(spread)
104   {
105     case Toolkit::GradientVisual::SpreadMethod::REPEAT:
106     {
107       return Dali::WrapMode::REPEAT;
108     }
109     case Toolkit::GradientVisual::SpreadMethod::REFLECT:
110     {
111       return Dali::WrapMode::MIRRORED_REPEAT;
112     }
113     case Toolkit::GradientVisual::SpreadMethod::PAD:
114     default:
115     {
116       return Dali::WrapMode::CLAMP_TO_EDGE;
117     }
118   }
119 }
120
121 } // unnamed namespace
122
123 GradientVisualPtr GradientVisual::New(VisualFactoryCache& factoryCache, const Property::Map& properties)
124 {
125   GradientVisualPtr gradientVisualPtr(new GradientVisual(factoryCache));
126   gradientVisualPtr->SetProperties(properties);
127   gradientVisualPtr->Initialize();
128   return gradientVisualPtr;
129 }
130
131 GradientVisual::GradientVisual(VisualFactoryCache& factoryCache)
132 : Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::GRADIENT),
133   mGradientType(LINEAR),
134   mIsOpaque(true)
135 {
136   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
137 }
138
139 GradientVisual::~GradientVisual()
140 {
141 }
142
143 void GradientVisual::DoSetProperties(const Property::Map& propertyMap)
144 {
145   Toolkit::GradientVisual::Units::Type gradientUnits = Toolkit::GradientVisual::Units::OBJECT_BOUNDING_BOX;
146
147   Property::Value* unitsValue = propertyMap.Find(Toolkit::GradientVisual::Property::UNITS, UNITS_NAME);
148   if(unitsValue)
149   {
150     Scripting::GetEnumerationProperty(*unitsValue, UNITS_TABLE, UNITS_TABLE_COUNT, gradientUnits);
151   }
152
153   mGradientType = Type::LINEAR;
154   if(propertyMap.Find(Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME))
155   {
156     mGradientType = Type::RADIAL;
157   }
158
159   if(NewGradient(mGradientType, propertyMap))
160   {
161     mGradient->SetGradientUnits(gradientUnits);
162     mGradientTransform = mGradient->GetAlignmentTransform();
163   }
164   else
165   {
166     DALI_LOG_ERROR("Fail to provide valid properties to create a GradientVisual object\n");
167   }
168 }
169
170 void GradientVisual::OnSetTransform()
171 {
172   if(mImpl->mRenderer)
173   {
174     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
175   }
176 }
177
178 void GradientVisual::DoSetOnScene(Actor& actor)
179 {
180   actor.AddRenderer(mImpl->mRenderer);
181
182   // Gradient Visual generated and ready to display
183   ResourceReady(Toolkit::Visual::ResourceStatus::READY);
184 }
185
186 void GradientVisual::UpdateShader()
187 {
188   if(mImpl->mRenderer)
189   {
190     Shader shader = GenerateShader();
191     mImpl->mRenderer.SetShader(shader);
192   }
193 }
194
195 void GradientVisual::DoCreatePropertyMap(Property::Map& map) const
196 {
197   map.Clear();
198   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::GRADIENT);
199   map.Insert(Toolkit::GradientVisual::Property::UNITS, mGradient->GetGradientUnits());
200   map.Insert(Toolkit::GradientVisual::Property::SPREAD_METHOD, mGradient->GetSpreadMethod());
201
202   const Vector<Gradient::GradientStop>& stops(mGradient->GetStops());
203   Property::Array                       offsets;
204   Property::Array                       colors;
205   for(unsigned int i = 0; i < stops.Count(); i++)
206   {
207     offsets.PushBack(stops[i].mOffset);
208     if(EqualsZero(stops[i].mStopColor.a))
209     {
210       colors.PushBack(Vector4::ZERO);
211     }
212     else
213     {
214       colors.PushBack(Vector4(stops[i].mStopColor.r / stops[i].mStopColor.a,
215                               stops[i].mStopColor.g / stops[i].mStopColor.a,
216                               stops[i].mStopColor.b / stops[i].mStopColor.a,
217                               stops[i].mStopColor.a));
218     }
219   }
220
221   map.Insert(Toolkit::GradientVisual::Property::STOP_OFFSET, offsets);
222   map.Insert(Toolkit::GradientVisual::Property::STOP_COLOR, colors);
223
224   if(&typeid(*mGradient) == &typeid(LinearGradient))
225   {
226     LinearGradient* gradient = static_cast<LinearGradient*>(mGradient.Get());
227     map.Insert(Toolkit::GradientVisual::Property::START_POSITION, gradient->GetStartPosition());
228     map.Insert(Toolkit::GradientVisual::Property::END_POSITION, gradient->GetEndPosition());
229   }
230   else // if( &typeid( *mGradient ) == &typeid(RadialGradient) )
231   {
232     RadialGradient* gradient = static_cast<RadialGradient*>(mGradient.Get());
233     map.Insert(Toolkit::GradientVisual::Property::CENTER, gradient->GetCenter());
234     map.Insert(Toolkit::GradientVisual::Property::RADIUS, gradient->GetRadius());
235   }
236 }
237
238 void GradientVisual::DoCreateInstancePropertyMap(Property::Map& map) const
239 {
240   // Do nothing
241 }
242
243 void GradientVisual::EnablePreMultipliedAlpha(bool preMultiplied)
244 {
245   // Make always enable pre multiplied alpha whether preMultiplied value is false.
246   if(!preMultiplied)
247   {
248     DALI_LOG_WARNING("Note : GradientVisual cannot disable PreMultipliedAlpha\n");
249   }
250 }
251
252 void GradientVisual::OnInitialize()
253 {
254   Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
255   Shader   shader   = GenerateShader();
256
257   // Set up the texture set
258   TextureSet    textureSet    = TextureSet::New();
259   Dali::Texture lookupTexture = mGradient->GenerateLookupTexture();
260   textureSet.SetTexture(0u, lookupTexture);
261   Dali::WrapMode::Type wrap    = GetWrapMode(mGradient->GetSpreadMethod());
262   Sampler              sampler = Sampler::New();
263   sampler.SetWrapMode(wrap, wrap);
264   textureSet.SetSampler(0u, sampler);
265
266   mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
267   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
268   mImpl->mRenderer.SetTextures(textureSet);
269
270   // If opaque and then no need to have blending
271   if(mIsOpaque)
272   {
273     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::OFF);
274   }
275
276   mImpl->mRenderer.RegisterUniqueProperty(UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform);
277
278   float textureSize = static_cast<float>(lookupTexture.GetWidth());
279   mImpl->mRenderer.RegisterUniqueProperty(UNIFORM_TEXTURE_COORDINATE_SCALE_FACTOR_NAME, (textureSize - 1.0f) / textureSize);
280
281   // Register transform properties
282   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
283 }
284
285 bool GradientVisual::NewGradient(Type gradientType, const Property::Map& propertyMap)
286 {
287   if(gradientType == Type::LINEAR)
288   {
289     Property::Value* startPositionValue = propertyMap.Find(Toolkit::GradientVisual::Property::START_POSITION, START_POSITION_NAME);
290     Property::Value* endPositionValue   = propertyMap.Find(Toolkit::GradientVisual::Property::END_POSITION, END_POSITION_NAME);
291     Vector2          startPosition;
292     Vector2          endPosition;
293
294     if(startPositionValue && startPositionValue->Get(startPosition) && endPositionValue && endPositionValue->Get(endPosition))
295     {
296       mGradient = new LinearGradient(startPosition, endPosition);
297     }
298     else
299     {
300       return false;
301     }
302   }
303   else // type==Type::RADIAL
304   {
305     Property::Value* centerValue = propertyMap.Find(Toolkit::GradientVisual::Property::CENTER, CENTER_NAME);
306     Property::Value* radiusValue = propertyMap.Find(Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME);
307     Vector2          center;
308     float            radius;
309     if(centerValue && centerValue->Get(center) && radiusValue && radiusValue->Get(radius))
310     {
311       mGradient = new RadialGradient(center, radius);
312     }
313     else
314     {
315       return false;
316     }
317   }
318
319   unsigned int     numValidStop    = 0u;
320   Property::Value* stopOffsetValue = propertyMap.Find(Toolkit::GradientVisual::Property::STOP_OFFSET, STOP_OFFSET_NAME);
321   Property::Value* stopColorValue  = propertyMap.Find(Toolkit::GradientVisual::Property::STOP_COLOR, STOP_COLOR_NAME);
322   if(stopColorValue)
323   {
324     Vector<float>    offsetArray;
325     Property::Array* colorArray = stopColorValue->GetArray();
326     if(colorArray)
327     {
328       GetStopOffsets(stopOffsetValue, offsetArray);
329       unsigned int numStop = offsetArray.Count() < colorArray->Count() ? offsetArray.Count() : colorArray->Count();
330       Vector4      color;
331       for(unsigned int i = 0; i < numStop; i++)
332       {
333         if((colorArray->GetElementAt(i)).Get(color))
334         {
335           mGradient->AddStop(offsetArray[i], Vector4(color.r * color.a, color.g * color.a, color.b * color.a, color.a));
336           numValidStop++;
337           if(!Equals(color.a, 1.0f, Math::MACHINE_EPSILON_1))
338           {
339             mIsOpaque = false;
340           }
341         }
342       }
343     }
344   }
345
346   if(numValidStop < 1u) // no valid stop
347   {
348     return false;
349   }
350
351   Property::Value* spread = propertyMap.Find(Toolkit::GradientVisual::Property::SPREAD_METHOD, SPREAD_METHOD_NAME);
352   // The default spread method is PAD. Only need to set new spread if it's anything else.
353   if(spread)
354   {
355     Toolkit::GradientVisual::SpreadMethod::Type spreadMethod = Toolkit::GradientVisual::SpreadMethod::PAD;
356     if(Scripting::GetEnumerationProperty(*spread, SPREAD_METHOD_TABLE, SPREAD_METHOD_TABLE_COUNT, spreadMethod))
357     {
358       mGradient->SetSpreadMethod(spreadMethod);
359     }
360   }
361
362   return true;
363 }
364
365 Shader GradientVisual::GenerateShader() const
366 {
367   bool userspaceUnit  = (mGradient->GetGradientUnits() == Toolkit::GradientVisual::Units::USER_SPACE);
368   bool roundedCorner  = IsRoundedCornerRequired();
369   bool borderline     = IsBorderlineRequired();
370   bool radialGradient = (mGradientType == Type::RADIAL);
371
372   int shaderTypeFlag = GradientVisualRequireFlag::DEFAULT;
373   if(roundedCorner)
374   {
375     shaderTypeFlag |= GradientVisualRequireFlag::ROUNDED_CORNER;
376   }
377   if(borderline)
378   {
379     shaderTypeFlag |= GradientVisualRequireFlag::BORDERLINE;
380   }
381   if(userspaceUnit)
382   {
383     shaderTypeFlag |= GradientVisualRequireFlag::USER_SPACE;
384   }
385   if(radialGradient)
386   {
387     shaderTypeFlag |= GradientVisualRequireFlag::RADIAL;
388   }
389
390   VisualFactoryCache::ShaderType shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
391   Shader                         shader     = mFactoryCache.GetShader(shaderType);
392   if(!shader)
393   {
394     std::string vertexShaderPrefixList;
395     std::string fragmentShaderPrefixList;
396
397     if(roundedCorner)
398     {
399       vertexShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
400       fragmentShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
401     }
402     if(borderline)
403     {
404       vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
405       fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
406     }
407     if(radialGradient)
408     {
409       fragmentShaderPrefixList += "#define RADIAL\n";
410     }
411     if(userspaceUnit)
412     {
413       vertexShaderPrefixList += "#define USER_SPACE\n";
414     }
415
416     shader = mFactoryCache.GenerateAndSaveShader(shaderType,
417                                                  Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_GRADIENT_VISUAL_SHADER_VERT.data(),
418                                                  Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderPrefixList + SHADER_GRADIENT_VISUAL_SHADER_FRAG.data());
419   }
420
421   return shader;
422 }
423
424 void GradientVisual::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
425 {
426   if(value) // Only check valve type if a valid Property has been passed in
427   {
428     switch(value->GetType())
429     {
430       case Property::VECTOR2:
431       {
432         Vector2 offset2;
433         value->Get(offset2);
434         stopOffsets.PushBack(offset2.x);
435         stopOffsets.PushBack(offset2.y);
436         break;
437       }
438       case Property::VECTOR3:
439       {
440         Vector3 offset3;
441         value->Get(offset3);
442         stopOffsets.PushBack(offset3.x);
443         stopOffsets.PushBack(offset3.y);
444         stopOffsets.PushBack(offset3.z);
445         break;
446       }
447       case Property::VECTOR4:
448       {
449         Vector4 offset4;
450         value->Get(offset4);
451         stopOffsets.PushBack(offset4.x);
452         stopOffsets.PushBack(offset4.y);
453         stopOffsets.PushBack(offset4.z);
454         stopOffsets.PushBack(offset4.w);
455         break;
456       }
457       case Property::ARRAY:
458       {
459         const Property::Array* offsetArray = value->GetArray();
460         if(offsetArray)
461         {
462           unsigned int numStop = offsetArray->Count();
463           float        offset;
464           for(unsigned int i = 0; i < numStop; i++)
465           {
466             if(offsetArray->GetElementAt(i).Get(offset))
467             {
468               stopOffsets.PushBack(offset);
469             }
470           }
471         }
472         break;
473       }
474       default:
475       {
476         DALI_LOG_WARNING("GetStopOffsets passed unsupported Property Map\n");
477         // Unsupported Type
478       }
479     }
480   }
481
482   if(stopOffsets.Empty())
483   {
484     // Set default offset if none set by Property system, need a minimum and maximum
485     stopOffsets.PushBack(DEFAULT_OFFSET_MINIMUM);
486     stopOffsets.PushBack(DEFAULT_OFFSET_MAXIMUM);
487   }
488 }
489
490 } // namespace Internal
491
492 } // namespace Toolkit
493
494 } // namespace Dali