2 * Copyright (c) 2015 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( OBJECT_BOUNDING_BOX ),
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 void Gradient::SetGradientUnits( GradientUnits gradientUnits )
54 mGradientUnits = gradientUnits;
57 Gradient::GradientUnits Gradient::GetGradientUnits() const
59 return mGradientUnits;
62 void Gradient::SetSpreadMethod( SpreadMethod spread )
64 mSpreadMethod = spread;
67 Gradient::SpreadMethod Gradient::GetSpreadMethod() const
72 const Matrix3& Gradient::GetAlignmentTransform() const
74 return mAlignmentTransform;
78 * Following the SVG gradient.
80 * Not only the spread method decides the texture wrap mode:
81 * PAD-->GL_CLAMP_TO_EDGE; REPEAT-->GL_REPEAT; REFLECT-->GL_MIRROR_REPEAT
83 * If the stops have not covered the whole zero to one range,
84 * the REPEAT spread behaves different from the two others in the lookup texture generation.
86 BufferImage Gradient::GenerateLookupTexture()
88 std::sort( mGradientStops.Begin(), mGradientStops.End() );
90 unsigned int numStops = mGradientStops.Count();
93 * If the stops have not covered the whole zero to one range,
94 * for PAD and REFLECT, use the color of the first stop to fill the range [0.0, first stop offset)
95 * and use the color of the last stop to fill the range (last stop offset, 1.0]
96 * for REPEAT, mix the two color of the first and last stop to fill the remainder
98 bool tempFirstStop = false;
99 if( mGradientStops[0].mOffset > 0.f )
101 tempFirstStop = true;
102 Vector4 firstStopColor( mGradientStops[0].mStopColor ); // If spread method is PAD or REFLECT
103 if( mSpreadMethod == REPEAT )
105 firstStopColor = ( mGradientStops[0].mStopColor * (1.f-mGradientStops[numStops-1].mOffset)
106 + mGradientStops[numStops-1].mStopColor * mGradientStops[0].mOffset )
107 / ( mGradientStops[0].mOffset+1.f-mGradientStops[numStops-1].mOffset);
110 mGradientStops.Insert( mGradientStops.Begin(), GradientStop(0.f, firstStopColor) );
114 bool tempLastStop = false;
115 if( mGradientStops[numStops-1].mOffset < 1.f )
118 Vector4 lastStopColor( mGradientStops[numStops-1].mStopColor ); // If spread method is PAD or REFLECT
119 if( mSpreadMethod == REPEAT )
121 lastStopColor = mGradientStops[0].mStopColor;
123 mGradientStops.PushBack( GradientStop(1.f, lastStopColor) );
128 * Generate the pixels with the color transit from one stop to next.
130 unsigned int resolution = EstimateTextureResolution();
131 BufferImage texture = BufferImage::New( resolution, 1 );
132 PixelBuffer* pixels = texture.GetBuffer();
133 int segmentStart = 0;
136 float length = static_cast<float>(resolution);
137 for( unsigned int i=0; i<numStops-1u; i++ )
139 segmentEnd = floorf(mGradientStops[i+1].mOffset * length + 0.5f);
140 if( segmentEnd == segmentStart )
144 float segmentWidth = static_cast<float>(segmentEnd-segmentStart);
146 for( int j = segmentStart; j<segmentEnd; j++ )
148 float ratio = static_cast<float>(j-segmentStart)/segmentWidth;
149 Vector4 currentColor = mGradientStops[i].mStopColor * (1.f-ratio) + mGradientStops[i+1].mStopColor * ratio;
150 pixels[k*4] = static_cast<unsigned char>( 255.f * Clamp( currentColor.r, 0.f, 1.f ) );
151 pixels[k*4+1] = static_cast<unsigned char>( 255.f * Clamp( currentColor.g, 0.f, 1.f ) );
152 pixels[k*4+2] = static_cast<unsigned char>( 255.f * Clamp( currentColor.b, 0.f, 1.f ) );
153 pixels[k*4+3] = static_cast<unsigned char>( 255.f * Clamp( currentColor.a, 0.f, 1.f ) );
156 segmentStart = segmentEnd;
159 // remove the stops added temporarily for generating the pixels, as the spread method might get changed later
162 mGradientStops.Erase( mGradientStops.Begin()+numStops-1 );
166 mGradientStops.Erase( mGradientStops.Begin());
172 unsigned int Gradient::EstimateTextureResolution()
174 float minInterval = 1.0;
175 for( unsigned int i=0, numStops = mGradientStops.Count(); i<numStops-1u; i++ )
177 float interval = mGradientStops[i+1].mOffset - mGradientStops[i].mOffset;
178 minInterval = interval > minInterval ? minInterval:interval;
180 // Use at least three pixels for each segment between two stops
181 unsigned int resolution = static_cast<int>(3.f/(minInterval+Math::MACHINE_EPSILON_100)+0.5f);
182 // Clamp the resolution to handle the overlapping stops
183 if( resolution > MAXIMUM_TEXTURE_RESOLUTION )
185 return MAXIMUM_TEXTURE_RESOLUTION;
191 } // namespace Internal
193 } // namespace Toolkit