2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "gradient-visual.h"
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>
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>
50 static constexpr int32_t CUSTOM_PROPERTY_COUNT(1); // alignment
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)
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)
64 static constexpr std::string_view UNIFORM_ALIGNMENT_MATRIX_NAME("uAlignmentMatrix");
65 static constexpr std::string_view UNIFORM_TEXTURE_COORDINATE_SCALE_FACTOR_NAME("uTextureCoordinateScaleFactor");
67 // default offset value
68 static constexpr float DEFAULT_OFFSET_MINIMUM = 0.0f;
69 static constexpr float DEFAULT_OFFSET_MAXIMUM = 1.0f;
71 VisualFactoryCache::ShaderType SHADER_TYPE_TABLE[16] =
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,
91 // enum of required list when we select shader
92 enum GradientVisualRequireFlag
95 ROUNDED_CORNER = 1 << 0,
101 Dali::WrapMode::Type GetWrapMode(Toolkit::GradientVisual::SpreadMethod::Type spread)
105 case Toolkit::GradientVisual::SpreadMethod::REPEAT:
107 return Dali::WrapMode::REPEAT;
109 case Toolkit::GradientVisual::SpreadMethod::REFLECT:
111 return Dali::WrapMode::MIRRORED_REPEAT;
113 case Toolkit::GradientVisual::SpreadMethod::PAD:
116 return Dali::WrapMode::CLAMP_TO_EDGE;
121 } // unnamed namespace
123 GradientVisualPtr GradientVisual::New(VisualFactoryCache& factoryCache, const Property::Map& properties)
125 GradientVisualPtr gradientVisualPtr(new GradientVisual(factoryCache));
126 gradientVisualPtr->SetProperties(properties);
127 gradientVisualPtr->Initialize();
128 return gradientVisualPtr;
131 GradientVisual::GradientVisual(VisualFactoryCache& factoryCache)
132 : Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::GRADIENT),
133 mGradientType(LINEAR),
136 mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
139 GradientVisual::~GradientVisual()
143 void GradientVisual::DoSetProperties(const Property::Map& propertyMap)
145 Toolkit::GradientVisual::Units::Type gradientUnits = Toolkit::GradientVisual::Units::OBJECT_BOUNDING_BOX;
147 Property::Value* unitsValue = propertyMap.Find(Toolkit::GradientVisual::Property::UNITS, UNITS_NAME);
150 Scripting::GetEnumerationProperty(*unitsValue, UNITS_TABLE, UNITS_TABLE_COUNT, gradientUnits);
153 mGradientType = Type::LINEAR;
154 if(propertyMap.Find(Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME))
156 mGradientType = Type::RADIAL;
159 if(NewGradient(mGradientType, propertyMap))
161 mGradient->SetGradientUnits(gradientUnits);
162 mGradientTransform = mGradient->GetAlignmentTransform();
166 DALI_LOG_ERROR("Fail to provide valid properties to create a GradientVisual object\n");
170 void GradientVisual::OnSetTransform()
174 mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
178 void GradientVisual::DoSetOnScene(Actor& actor)
180 actor.AddRenderer(mImpl->mRenderer);
182 // Gradient Visual generated and ready to display
183 ResourceReady(Toolkit::Visual::ResourceStatus::READY);
186 void GradientVisual::UpdateShader()
190 Shader shader = GenerateShader();
191 mImpl->mRenderer.SetShader(shader);
195 void GradientVisual::DoCreatePropertyMap(Property::Map& map) const
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());
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++)
207 offsets.PushBack(stops[i].mOffset);
208 if(EqualsZero(stops[i].mStopColor.a))
210 colors.PushBack(Vector4::ZERO);
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));
221 map.Insert(Toolkit::GradientVisual::Property::STOP_OFFSET, offsets);
222 map.Insert(Toolkit::GradientVisual::Property::STOP_COLOR, colors);
224 if(&typeid(*mGradient) == &typeid(LinearGradient))
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());
230 else // if( &typeid( *mGradient ) == &typeid(RadialGradient) )
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());
238 void GradientVisual::DoCreateInstancePropertyMap(Property::Map& map) const
243 void GradientVisual::EnablePreMultipliedAlpha(bool preMultiplied)
245 // Make always enable pre multiplied alpha whether preMultiplied value is false.
248 DALI_LOG_WARNING("Note : GradientVisual cannot disable PreMultipliedAlpha\n");
252 void GradientVisual::OnInitialize()
254 Geometry geometry = mFactoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
255 Shader shader = GenerateShader();
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)
264 Sampler sampler = Sampler::New();
265 sampler.SetWrapMode(wrap, wrap);
266 textureSet.SetSampler(0u, sampler);
269 mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
270 mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
271 mImpl->mRenderer.SetTextures(textureSet);
273 // If opaque and then no need to have blending
276 mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::OFF);
279 mImpl->mRenderer.RegisterUniqueProperty(UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform);
281 float textureSize = static_cast<float>(lookupTexture.GetWidth());
282 mImpl->mRenderer.RegisterUniqueProperty(UNIFORM_TEXTURE_COORDINATE_SCALE_FACTOR_NAME, (textureSize - 1.0f) / textureSize);
284 // Register transform properties
285 mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
288 bool GradientVisual::NewGradient(Type gradientType, const Property::Map& propertyMap)
290 if(gradientType == Type::LINEAR)
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;
297 if(startPositionValue && startPositionValue->Get(startPosition) && endPositionValue && endPositionValue->Get(endPosition))
299 mGradient = new LinearGradient(startPosition, endPosition);
306 else // type==Type::RADIAL
308 Property::Value* centerValue = propertyMap.Find(Toolkit::GradientVisual::Property::CENTER, CENTER_NAME);
309 Property::Value* radiusValue = propertyMap.Find(Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME);
312 if(centerValue && centerValue->Get(center) && radiusValue && radiusValue->Get(radius))
314 mGradient = new RadialGradient(center, radius);
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);
327 Vector<float> offsetArray;
328 Property::Array* colorArray = stopColorValue->GetArray();
331 GetStopOffsets(stopOffsetValue, offsetArray);
332 unsigned int numStop = offsetArray.Count() < colorArray->Count() ? offsetArray.Count() : colorArray->Count();
334 for(unsigned int i = 0; i < numStop; i++)
336 if((colorArray->GetElementAt(i)).Get(color))
338 mGradient->AddStop(offsetArray[i], Vector4(color.r * color.a, color.g * color.a, color.b * color.a, color.a));
340 if(!Equals(color.a, 1.0f, Math::MACHINE_EPSILON_1))
349 if(numValidStop < 1u) // no valid stop
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.
358 Toolkit::GradientVisual::SpreadMethod::Type spreadMethod = Toolkit::GradientVisual::SpreadMethod::PAD;
359 if(Scripting::GetEnumerationProperty(*spread, SPREAD_METHOD_TABLE, SPREAD_METHOD_TABLE_COUNT, spreadMethod))
361 mGradient->SetSpreadMethod(spreadMethod);
368 Shader GradientVisual::GenerateShader() const
370 bool userspaceUnit = (mGradient->GetGradientUnits() == Toolkit::GradientVisual::Units::USER_SPACE);
371 bool roundedCorner = IsRoundedCornerRequired();
372 bool borderline = IsBorderlineRequired();
373 bool radialGradient = (mGradientType == Type::RADIAL);
375 int shaderTypeFlag = GradientVisualRequireFlag::DEFAULT;
378 shaderTypeFlag |= GradientVisualRequireFlag::ROUNDED_CORNER;
382 shaderTypeFlag |= GradientVisualRequireFlag::BORDERLINE;
386 shaderTypeFlag |= GradientVisualRequireFlag::USER_SPACE;
390 shaderTypeFlag |= GradientVisualRequireFlag::RADIAL;
393 VisualFactoryCache::ShaderType shaderType = SHADER_TYPE_TABLE[shaderTypeFlag];
394 Shader shader = mFactoryCache.GetShader(shaderType);
397 std::string vertexShaderPrefixList;
398 std::string fragmentShaderPrefixList;
402 vertexShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
403 fragmentShaderPrefixList += "#define IS_REQUIRED_ROUNDED_CORNER\n";
407 vertexShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
408 fragmentShaderPrefixList += "#define IS_REQUIRED_BORDERLINE\n";
412 fragmentShaderPrefixList += "#define RADIAL\n";
416 vertexShaderPrefixList += "#define USER_SPACE\n";
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());
427 void GradientVisual::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
429 if(value) // Only check valve type if a valid Property has been passed in
431 switch(value->GetType())
433 case Property::VECTOR2:
437 stopOffsets.PushBack(offset2.x);
438 stopOffsets.PushBack(offset2.y);
441 case Property::VECTOR3:
445 stopOffsets.PushBack(offset3.x);
446 stopOffsets.PushBack(offset3.y);
447 stopOffsets.PushBack(offset3.z);
450 case Property::VECTOR4:
454 stopOffsets.PushBack(offset4.x);
455 stopOffsets.PushBack(offset4.y);
456 stopOffsets.PushBack(offset4.z);
457 stopOffsets.PushBack(offset4.w);
460 case Property::ARRAY:
462 const Property::Array* offsetArray = value->GetArray();
465 unsigned int numStop = offsetArray->Count();
467 for(unsigned int i = 0; i < numStop; i++)
469 if(offsetArray->GetElementAt(i).Get(offset))
471 stopOffsets.PushBack(offset);
479 DALI_LOG_WARNING("GetStopOffsets passed unsupported Property Map\n");
485 if(stopOffsets.Empty())
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);
493 } // namespace Internal
495 } // namespace Toolkit