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