Merge "Changed Atlas manager to use Dali::Texture instead of Dali::Atlas" into devel...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / renderers / gradient / gradient.cpp
1 /*
2  * Copyright (c) 2015 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 #include "gradient.h"
19
20 #include <algorithm>    // std::sort
21 #include <dali/public-api/math/vector4.h>
22
23 namespace
24 {
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);
27 }
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 namespace Internal
36 {
37
38 Gradient::Gradient()
39 : mGradientUnits( OBJECT_BOUNDING_BOX ),
40   mSpreadMethod( PAD )
41 {}
42
43 Gradient::~Gradient()
44 {}
45
46 void Gradient::AddStop( float offset, const Vector4& color )
47 {
48   // the offset is clamped to the range [0.0, 1.0]
49   mGradientStops.PushBack( GradientStop( Clamp( offset, 0.f, 1.f ), color) );
50 }
51
52 const Vector<Gradient::GradientStop>& Gradient::GetStops()
53 {
54   return mGradientStops;
55 }
56
57 void Gradient::SetGradientUnits( GradientUnits gradientUnits )
58 {
59   mGradientUnits = gradientUnits;
60 }
61
62 Gradient::GradientUnits Gradient::GetGradientUnits() const
63 {
64   return mGradientUnits;
65 }
66
67 void Gradient::SetSpreadMethod( SpreadMethod spread )
68 {
69   mSpreadMethod = spread;
70 }
71
72 Gradient::SpreadMethod Gradient::GetSpreadMethod() const
73 {
74   return mSpreadMethod;
75 }
76
77 const Matrix3& Gradient::GetAlignmentTransform() const
78 {
79   return mAlignmentTransform;
80 }
81
82 /**
83  * Following the SVG gradient.
84  *
85  * Not only the spread method decides the texture wrap mode:
86  *    PAD-->GL_CLAMP_TO_EDGE; REPEAT-->GL_REPEAT; REFLECT-->GL_MIRROR_REPEAT
87  *
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.
90  */
91 BufferImage Gradient::GenerateLookupTexture()
92 {
93   std::sort( mGradientStops.Begin(), mGradientStops.End() );
94
95   unsigned int numStops = mGradientStops.Count();
96
97   /**
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
102    */
103   bool tempFirstStop = false;
104   if( mGradientStops[0].mOffset > 0.f )
105   {
106     tempFirstStop = true;
107     Vector4 firstStopColor( mGradientStops[0].mStopColor ); // If spread method is PAD or REFLECT
108     if( mSpreadMethod == REPEAT )
109     {
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);
113     }
114
115     mGradientStops.Insert( mGradientStops.Begin(), GradientStop(0.f, firstStopColor) );
116     numStops++;
117   }
118
119   bool tempLastStop = false;
120   if( mGradientStops[numStops-1].mOffset < 1.f )
121   {
122     tempLastStop = true;
123     Vector4 lastStopColor( mGradientStops[numStops-1].mStopColor ); // If spread method is PAD or REFLECT
124     if( mSpreadMethod == REPEAT )
125     {
126       lastStopColor = mGradientStops[0].mStopColor;
127     }
128     mGradientStops.PushBack( GradientStop(1.f, lastStopColor) );
129     numStops++;
130   }
131
132   /**
133    * Generate the pixels with the color transit from one stop to next.
134    */
135   unsigned int resolution = EstimateTextureResolution();
136   BufferImage texture = BufferImage::New( resolution, 1 );
137   PixelBuffer* pixels = texture.GetBuffer();
138   int segmentStart = 0;
139   int segmentEnd = 0;
140   int k = 0;
141   float length = static_cast<float>(resolution);
142   for( unsigned int i=0; i<numStops-1u; i++ )
143   {
144     segmentEnd = floorf(mGradientStops[i+1].mOffset * length + 0.5f);
145     if( segmentEnd == segmentStart )
146     {
147       continue;
148     }
149     float segmentWidth = static_cast<float>(segmentEnd-segmentStart);
150
151     for( int j = segmentStart; j<segmentEnd; j++ )
152     {
153       float ratio = static_cast<float>(j-segmentStart)/segmentWidth;
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 ) );
159       k++;
160     }
161     segmentStart = segmentEnd;
162   }
163
164   // remove the stops added temporarily for generating the pixels, as the spread method might get changed later
165   if( tempLastStop )
166   {
167     mGradientStops.Erase( mGradientStops.Begin()+numStops-1 );
168   }
169   if( tempFirstStop )
170   {
171     mGradientStops.Erase( mGradientStops.Begin());
172   }
173
174   return texture;
175 }
176
177 unsigned int Gradient::EstimateTextureResolution()
178 {
179   float minInterval = 1.0;
180   for( unsigned int i=0, numStops = mGradientStops.Count(); i<numStops-1u; i++ )
181   {
182     float interval = mGradientStops[i+1].mOffset - mGradientStops[i].mOffset;
183     minInterval = interval > minInterval ? minInterval:interval;
184   }
185   // Use at least three pixels for each segment between two stops
186   unsigned int resolution = static_cast<int>(3.f/(minInterval+Math::MACHINE_EPSILON_100)+0.5f);
187   // Clamp the resolution to handle the overlapping stops
188   if( resolution > MAXIMUM_TEXTURE_RESOLUTION )
189   {
190     return MAXIMUM_TEXTURE_RESOLUTION;
191   }
192
193   return resolution;
194 }
195
196 } // namespace Internal
197
198 } // namespace Toolkit
199
200 } // namespace Dali