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