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