[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   if(wrap != Dali::WrapMode::DEFAULT)
263   {
264     Sampler sampler = Sampler::New();
265     sampler.SetWrapMode(wrap, wrap);
266     textureSet.SetSampler(0u, sampler);
267   }
268
269   mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
270   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
271   mImpl->mRenderer.SetTextures(textureSet);
272
273   // If opaque and then no need to have blending
274   if(mIsOpaque)
275   {
276     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::OFF);
277   }
278
279   mImpl->mRenderer.RegisterUniqueProperty(UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform);
280
281   float textureSize = static_cast<float>(lookupTexture.GetWidth());
282   mImpl->mRenderer.RegisterUniqueProperty(UNIFORM_TEXTURE_COORDINATE_SCALE_FACTOR_NAME, (textureSize - 1.0f) / textureSize);
283
284   // Register transform properties
285   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
286 }
287
288 bool GradientVisual::NewGradient(Type gradientType, const Property::Map& propertyMap)
289 {
290   if(gradientType == Type::LINEAR)
291   {
292     Property::Value* startPositionValue = propertyMap.Find(Toolkit::GradientVisual::Property::START_POSITION, START_POSITION_NAME);
293     Property::Value* endPositionValue   = propertyMap.Find(Toolkit::GradientVisual::Property::END_POSITION, END_POSITION_NAME);
294     Vector2          startPosition;
295     Vector2          endPosition;
296
297     if(startPositionValue && startPositionValue->Get(startPosition) && endPositionValue && endPositionValue->Get(endPosition))
298     {
299       mGradient = new LinearGradient(startPosition, endPosition);
300     }
301     else
302     {
303       return false;
304     }
305   }
306   else // type==Type::RADIAL
307   {
308     Property::Value* centerValue = propertyMap.Find(Toolkit::GradientVisual::Property::CENTER, CENTER_NAME);
309     Property::Value* radiusValue = propertyMap.Find(Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME);
310     Vector2          center;
311     float            radius;
312     if(centerValue && centerValue->Get(center) && radiusValue && radiusValue->Get(radius))
313     {
314       mGradient = new RadialGradient(center, radius);
315     }
316     else
317     {
318       return false;
319     }
320   }
321
322   unsigned int     numValidStop    = 0u;
323   Property::Value* stopOffsetValue = propertyMap.Find(Toolkit::GradientVisual::Property::STOP_OFFSET, STOP_OFFSET_NAME);
324   Property::Value* stopColorValue  = propertyMap.Find(Toolkit::GradientVisual::Property::STOP_COLOR, STOP_COLOR_NAME);
325   if(stopColorValue)
326   {
327     Vector<float>    offsetArray;
328     Property::Array* colorArray = stopColorValue->GetArray();
329     if(colorArray)
330     {
331       GetStopOffsets(stopOffsetValue, offsetArray);
332       unsigned int numStop = offsetArray.Count() < colorArray->Count() ? offsetArray.Count() : colorArray->Count();
333       Vector4      color;
334       for(unsigned int i = 0; i < numStop; i++)
335       {
336         if((colorArray->GetElementAt(i)).Get(color))
337         {
338           mGradient->AddStop(offsetArray[i], Vector4(color.r * color.a, color.g * color.a, color.b * color.a, color.a));
339           numValidStop++;
340           if(!Equals(color.a, 1.0f, Math::MACHINE_EPSILON_1))
341           {
342             mIsOpaque = false;
343           }
344         }
345       }
346     }
347   }
348
349   if(numValidStop < 1u) // no valid stop
350   {
351     return false;
352   }
353
354   Property::Value* spread = propertyMap.Find(Toolkit::GradientVisual::Property::SPREAD_METHOD, SPREAD_METHOD_NAME);
355   // The default spread method is PAD. Only need to set new spread if it's anything else.
356   if(spread)
357   {
358     Toolkit::GradientVisual::SpreadMethod::Type spreadMethod = Toolkit::GradientVisual::SpreadMethod::PAD;
359     if(Scripting::GetEnumerationProperty(*spread, SPREAD_METHOD_TABLE, SPREAD_METHOD_TABLE_COUNT, spreadMethod))
360     {
361       mGradient->SetSpreadMethod(spreadMethod);
362     }
363   }
364
365   return true;
366 }
367
368 Shader GradientVisual::GenerateShader() const
369 {
370   bool userspaceUnit  = (mGradient->GetGradientUnits() == Toolkit::GradientVisual::Units::USER_SPACE);
371   bool roundedCorner  = IsRoundedCornerRequired();
372   bool borderline     = IsBorderlineRequired();
373   bool radialGradient = (mGradientType == Type::RADIAL);
374
375   int shaderTypeFlag = GradientVisualRequireFlag::DEFAULT;
376   if(roundedCorner)
377   {
378     shaderTypeFlag |= GradientVisualRequireFlag::ROUNDED_CORNER;
379   }
380   if(borderline)
381   {
382     shaderTypeFlag |= GradientVisualRequireFlag::BORDERLINE;
383   }
384   if(userspaceUnit)
385   {
386     shaderTypeFlag |= GradientVisualRequireFlag::USER_SPACE;
387   }
388   if(radialGradient)
389   {
390     shaderTypeFlag |= GradientVisualRequireFlag::RADIAL;
391   }
392
393   VisualFactoryCache::ShaderType shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
394   Shader                         shader     = mFactoryCache.GetShader(shaderType);
395   if(!shader)
396   {
397     std::string vertexShaderPrefixList;
398     std::string fragmentShaderPrefixList;
399
400     if(roundedCorner)
401     {
402       vertexShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
403       fragmentShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
404     }
405     if(borderline)
406     {
407       vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
408       fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
409     }
410     if(radialGradient)
411     {
412       fragmentShaderPrefixList += "#define RADIAL\n";
413     }
414     if(userspaceUnit)
415     {
416       vertexShaderPrefixList += "#define USER_SPACE\n";
417     }
418
419     shader = mFactoryCache.GenerateAndSaveShader(shaderType,
420                                                  Dali::Shader::GetVertexShaderPrefix() + vertexShaderPrefixList + SHADER_GRADIENT_VISUAL_SHADER_VERT.data(),
421                                                  Dali::Shader::GetFragmentShaderPrefix() + fragmentShaderPrefixList + SHADER_GRADIENT_VISUAL_SHADER_FRAG.data());
422   }
423
424   return shader;
425 }
426
427 void GradientVisual::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
428 {
429   if(value) // Only check valve type if a valid Property has been passed in
430   {
431     switch(value->GetType())
432     {
433       case Property::VECTOR2:
434       {
435         Vector2 offset2;
436         value->Get(offset2);
437         stopOffsets.PushBack(offset2.x);
438         stopOffsets.PushBack(offset2.y);
439         break;
440       }
441       case Property::VECTOR3:
442       {
443         Vector3 offset3;
444         value->Get(offset3);
445         stopOffsets.PushBack(offset3.x);
446         stopOffsets.PushBack(offset3.y);
447         stopOffsets.PushBack(offset3.z);
448         break;
449       }
450       case Property::VECTOR4:
451       {
452         Vector4 offset4;
453         value->Get(offset4);
454         stopOffsets.PushBack(offset4.x);
455         stopOffsets.PushBack(offset4.y);
456         stopOffsets.PushBack(offset4.z);
457         stopOffsets.PushBack(offset4.w);
458         break;
459       }
460       case Property::ARRAY:
461       {
462         const Property::Array* offsetArray = value->GetArray();
463         if(offsetArray)
464         {
465           unsigned int numStop = offsetArray->Count();
466           float        offset;
467           for(unsigned int i = 0; i < numStop; i++)
468           {
469             if(offsetArray->GetElementAt(i).Get(offset))
470             {
471               stopOffsets.PushBack(offset);
472             }
473           }
474         }
475         break;
476       }
477       default:
478       {
479         DALI_LOG_WARNING("GetStopOffsets passed unsupported Property Map\n");
480         // Unsupported Type
481       }
482     }
483   }
484
485   if(stopOffsets.Empty())
486   {
487     // Set default offset if none set by Property system, need a minimum and maximum
488     stopOffsets.PushBack(DEFAULT_OFFSET_MINIMUM);
489     stopOffsets.PushBack(DEFAULT_OFFSET_MAXIMUM);
490   }
491 }
492
493 } // namespace Internal
494
495 } // namespace Toolkit
496
497 } // namespace Dali