2 * Copyright (c) 2016 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 <algorithm> // std::sort
21 #include <dali/public-api/math/vector4.h>
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);
39 : mGradientUnits( Toolkit::GradientVisual::Units::OBJECT_BOUNDING_BOX ),
40 mSpreadMethod( Toolkit::GradientVisual::SpreadMethod::PAD )
46 void Gradient::AddStop( float offset, const Vector4& color )
48 // the offset is clamped to the range [0.0, 1.0]
49 mGradientStops.PushBack( GradientStop( Clamp( offset, 0.f, 1.f ), color) );
52 const Vector<Gradient::GradientStop>& Gradient::GetStops()
54 return mGradientStops;
57 void Gradient::SetGradientUnits( Toolkit::GradientVisual::Units::Type gradientUnits )
59 mGradientUnits = gradientUnits;
62 Toolkit::GradientVisual::Units::Type Gradient::GetGradientUnits() const
64 return mGradientUnits;
67 void Gradient::SetSpreadMethod( Toolkit::GradientVisual::SpreadMethod::Type spread )
69 mSpreadMethod = spread;
72 Toolkit::GradientVisual::SpreadMethod::Type Gradient::GetSpreadMethod() const
77 const Matrix3& Gradient::GetAlignmentTransform() const
79 return mAlignmentTransform;
83 * Following the SVG gradient.
85 * Not only the spread method decides the texture wrap mode:
86 * PAD-->GL_CLAMP_TO_EDGE; REPEAT-->GL_REPEAT; REFLECT-->GL_MIRROR_REPEAT
88 * If the stops have not covered the whole zero to one range,
89 * the REPEAT spread behaves different from the two others in the lookup texture generation.
91 Dali::Texture Gradient::GenerateLookupTexture()
93 std::sort( mGradientStops.Begin(), mGradientStops.End() );
95 unsigned int numStops = mGradientStops.Count();
98 * If the stops have not covered the whole zero to one range,
99 * for PAD and REFLECT, use the color of the first stop to fill the range [0.0, first stop offset)
100 * and use the color of the last stop to fill the range (last stop offset, 1.0]
101 * for REPEAT, mix the two color of the first and last stop to fill the remainder
103 bool tempFirstStop = false;
104 if( mGradientStops[0].mOffset > 0.f )
106 tempFirstStop = true;
107 Vector4 firstStopColor( mGradientStops[0].mStopColor ); // If spread method is PAD or REFLECT
108 if( mSpreadMethod == Toolkit::GradientVisual::SpreadMethod::REPEAT )
110 firstStopColor = ( mGradientStops[0].mStopColor * (1.f-mGradientStops[numStops-1].mOffset)
111 + mGradientStops[numStops-1].mStopColor * mGradientStops[0].mOffset )
112 / ( mGradientStops[0].mOffset+1.f-mGradientStops[numStops-1].mOffset);
115 mGradientStops.Insert( mGradientStops.Begin(), GradientStop(0.f, firstStopColor) );
119 bool tempLastStop = false;
120 if( mGradientStops[numStops-1].mOffset < 1.f )
123 Vector4 lastStopColor( mGradientStops[numStops-1].mStopColor ); // If spread method is PAD or REFLECT
124 if( mSpreadMethod == Toolkit::GradientVisual::SpreadMethod::REPEAT )
126 lastStopColor = mGradientStops[0].mStopColor;
128 mGradientStops.PushBack( GradientStop(1.f, lastStopColor) );
133 * Generate the pixels with the color transit from one stop to next.
135 unsigned int resolution = EstimateTextureResolution();
137 unsigned int bufferSize = resolution * 4u;
138 unsigned char* pixels = new unsigned char[ bufferSize ];
139 PixelData pixelData = PixelData::New( pixels, bufferSize, resolution, 1u, Pixel::RGBA8888, PixelData::DELETE_ARRAY );
141 int segmentStart = 0;
144 float length = static_cast<float>(resolution);
145 for( unsigned int i=0; i<numStops-1u; i++ )
147 segmentEnd = floorf(mGradientStops[i+1].mOffset * length + 0.5f);
148 if( segmentEnd == segmentStart )
152 float segmentWidth = static_cast<float>(segmentEnd-segmentStart);
154 for( int j = segmentStart; j<segmentEnd; j++ )
156 float ratio = static_cast<float>(j-segmentStart)/segmentWidth;
157 Vector4 currentColor = mGradientStops[i].mStopColor * (1.f-ratio) + mGradientStops[i+1].mStopColor * ratio;
158 pixels[k*4] = static_cast<unsigned char>( 255.f * Clamp( currentColor.r, 0.f, 1.f ) );
159 pixels[k*4+1] = static_cast<unsigned char>( 255.f * Clamp( currentColor.g, 0.f, 1.f ) );
160 pixels[k*4+2] = static_cast<unsigned char>( 255.f * Clamp( currentColor.b, 0.f, 1.f ) );
161 pixels[k*4+3] = static_cast<unsigned char>( 255.f * Clamp( currentColor.a, 0.f, 1.f ) );
164 segmentStart = segmentEnd;
167 Texture texture = Texture::New( TextureType::TEXTURE_2D, Pixel::RGBA8888, resolution, 1u );
168 texture.Upload( pixelData );
170 // remove the stops added temporarily for generating the pixels, as the spread method might get changed later
173 mGradientStops.Erase( mGradientStops.Begin()+numStops-1 );
177 mGradientStops.Erase( mGradientStops.Begin());
183 unsigned int Gradient::EstimateTextureResolution()
185 float minInterval = 1.0;
186 for( unsigned int i=0, numStops = mGradientStops.Count(); i<numStops-1u; i++ )
188 float interval = mGradientStops[i+1].mOffset - mGradientStops[i].mOffset;
189 minInterval = interval > minInterval ? minInterval:interval;
191 // Use at least three pixels for each segment between two stops
192 unsigned int resolution = static_cast<int>(3.f/(minInterval+Math::MACHINE_EPSILON_100)+0.5f);
193 // Clamp the resolution to handle the overlapping stops
194 if( resolution > MAXIMUM_TEXTURE_RESOLUTION )
196 return MAXIMUM_TEXTURE_RESOLUTION;
202 } // namespace Internal
204 } // namespace Toolkit