2 * Copyright (c) 2021 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.
20 #include <dali/public-api/math/vector4.h>
21 #include <algorithm> // std::sort
25 // The maximum width of the lookup texture ( it is a 1-dimension texture with the height as 1 )
26 const unsigned int MAXIMUM_TEXTURE_RESOLUTION(128u);
36 : mGradientUnits(Toolkit::GradientVisual::Units::OBJECT_BOUNDING_BOX),
37 mSpreadMethod(Toolkit::GradientVisual::SpreadMethod::PAD)
45 void Gradient::AddStop(float offset, const Vector4& color)
47 // the offset is clamped to the range [0.0, 1.0]
48 mGradientStops.PushBack(GradientStop(Clamp(offset, 0.f, 1.f), color));
51 const Vector<Gradient::GradientStop>& Gradient::GetStops()
53 return mGradientStops;
56 void Gradient::SetGradientUnits(Toolkit::GradientVisual::Units::Type gradientUnits)
58 mGradientUnits = gradientUnits;
61 Toolkit::GradientVisual::Units::Type Gradient::GetGradientUnits() const
63 return mGradientUnits;
66 void Gradient::SetSpreadMethod(Toolkit::GradientVisual::SpreadMethod::Type spread)
68 mSpreadMethod = spread;
71 Toolkit::GradientVisual::SpreadMethod::Type Gradient::GetSpreadMethod() const
76 const Matrix3& Gradient::GetAlignmentTransform() const
78 return mAlignmentTransform;
82 * Following the SVG gradient.
84 * Not only the spread method decides the texture wrap mode:
85 * PAD-->GL_CLAMP_TO_EDGE; REPEAT-->GL_REPEAT; REFLECT-->GL_MIRROR_REPEAT
87 * If the stops have not covered the whole zero to one range,
88 * the REPEAT spread behaves different from the two others in the lookup texture generation.
90 Dali::Texture Gradient::GenerateLookupTexture()
92 std::sort(mGradientStops.Begin(), mGradientStops.End());
94 unsigned int numStops = mGradientStops.Count();
97 * If the stops have not covered the whole zero to one range,
98 * for PAD and REFLECT, use the color of the first stop to fill the range [0.0, first stop offset)
99 * and use the color of the last stop to fill the range (last stop offset, 1.0]
100 * for REPEAT, mix the two color of the first and last stop to fill the remainder
102 bool tempFirstStop = false;
103 if(mGradientStops[0].mOffset > 0.f)
105 tempFirstStop = true;
106 Vector4 firstStopColor(mGradientStops[0].mStopColor); // If spread method is PAD or REFLECT
107 if(mSpreadMethod == Toolkit::GradientVisual::SpreadMethod::REPEAT)
109 firstStopColor = (mGradientStops[0].mStopColor * (1.f - mGradientStops[numStops - 1].mOffset) + mGradientStops[numStops - 1].mStopColor * mGradientStops[0].mOffset) / (mGradientStops[0].mOffset + 1.f - mGradientStops[numStops - 1].mOffset);
112 mGradientStops.Insert(mGradientStops.Begin(), GradientStop(0.f, firstStopColor));
116 bool tempLastStop = false;
117 if(mGradientStops[numStops - 1].mOffset < 1.f)
120 Vector4 lastStopColor(mGradientStops[numStops - 1].mStopColor); // If spread method is PAD or REFLECT
121 if(mSpreadMethod == Toolkit::GradientVisual::SpreadMethod::REPEAT)
123 lastStopColor = mGradientStops[0].mStopColor;
125 mGradientStops.PushBack(GradientStop(1.f, lastStopColor));
130 * Generate the pixels with the color transit from one stop to next.
132 unsigned int resolution = EstimateTextureResolution();
134 unsigned int bufferSize = resolution * 4u;
135 unsigned char* pixels = new unsigned char[bufferSize];
136 PixelData pixelData = PixelData::New(pixels, bufferSize, resolution, 1u, Pixel::RGBA8888, PixelData::DELETE_ARRAY);
138 int segmentStart = 0;
141 float length = static_cast<float>(resolution);
142 for(unsigned int i = 0; i < numStops - 1u; i++)
144 segmentEnd = floorf(mGradientStops[i + 1].mOffset * length + 0.5f);
145 if(segmentEnd == segmentStart)
149 float segmentWidth = static_cast<float>(segmentEnd - segmentStart);
151 for(int j = segmentStart; j < segmentEnd; j++)
153 float ratio = static_cast<float>(j - segmentStart) / (segmentWidth - 1);
154 Vector4 currentColor = mGradientStops[i].mStopColor * (1.f - ratio) + mGradientStops[i + 1].mStopColor * ratio;
155 pixels[k * 4] = static_cast<unsigned char>(255.f * Clamp(currentColor.r, 0.f, 1.f));
156 pixels[k * 4 + 1] = static_cast<unsigned char>(255.f * Clamp(currentColor.g, 0.f, 1.f));
157 pixels[k * 4 + 2] = static_cast<unsigned char>(255.f * Clamp(currentColor.b, 0.f, 1.f));
158 pixels[k * 4 + 3] = static_cast<unsigned char>(255.f * Clamp(currentColor.a, 0.f, 1.f));
161 segmentStart = segmentEnd;
164 Texture texture = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, resolution, 1u);
165 texture.Upload(pixelData);
167 // remove the stops added temporarily for generating the pixels, as the spread method might get changed later
170 mGradientStops.Erase(mGradientStops.Begin() + numStops - 1);
174 mGradientStops.Erase(mGradientStops.Begin());
180 unsigned int Gradient::EstimateTextureResolution()
182 float minInterval = 1.0;
183 for(unsigned int i = 0, numStops = mGradientStops.Count(); i < numStops - 1u; i++)
185 float interval = mGradientStops[i + 1].mOffset - mGradientStops[i].mOffset;
186 minInterval = interval > minInterval ? minInterval : interval;
188 // Use at least three pixels for each segment between two stops
189 unsigned int resolution = static_cast<int>(3.f / (minInterval + Math::MACHINE_EPSILON_100) + 0.5f);
190 // Clamp the resolution to handle the overlapping stops
191 if(resolution > MAXIMUM_TEXTURE_RESOLUTION)
193 return MAXIMUM_TEXTURE_RESOLUTION;
199 } // namespace Internal
201 } // namespace Toolkit