9ba7369c2177d745a64a716207f39f83f7f9fed9
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / renderers / gradient / gradient-renderer.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 // CLASS HEADER
19 #include "gradient-renderer.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/common/dali-vector.h>
24 #include <dali/public-api/images/buffer-image.h>
25 #include <dali/public-api/object/property-array.h>
26
27 //INTERNAL INCLUDES
28 #include <dali-toolkit/internal/controls/renderers/renderer-factory-impl.h>
29 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
30 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
31 #include <dali-toolkit/internal/controls/renderers/gradient/linear-gradient.h>
32 #include <dali-toolkit/internal/controls/renderers/gradient/radial-gradient.h>
33
34 namespace Dali
35 {
36
37 namespace Toolkit
38 {
39
40 namespace Internal
41 {
42
43 namespace
44 {
45 // properties: linear gradient
46 const char * const GRADIENT_START_POSITION_NAME("gradient-start-position"); // Property::VECTOR2
47 const char * const GRADIENT_END_POSITION_NAME("gradient-end-position"); // Property::VECTOR2
48
49 // properties: radial gradient
50 const char * const GRADIENT_CENTER_NAME("gradient-center"); // Property::VECTOR2
51 const char * const GRADIENT_RADIUS_NAME("gradient-radius"); // Property::FLOAT
52
53 // properties: linear&radial gradient
54 const char * const GRADIENT_STOP_OFFSET_NAME("gradient-stop-offset"); // Property::Array FLOAT
55 const char * const GRADIENT_STOP_COLOR_NAME("gradient-stop-color"); // Property::Array VECTOR4
56 const char * const GRADIENT_UNITS_NAME("gradient-units"); // Property::String  "userSpaceOnUse | objectBoundingBox"
57 const char * const GRADIENT_SPREAD_METHOD_NAME("gradient-spread-method"); // Property::String  "pad | reflect | repeat"
58
59 // string values
60 const char * const UNIT_USER_SPACE("user-space");
61 const char * const SPREAD_REFLECT("reflect");
62 const char * const SPREAD_REPEAT("repeat");
63
64 // uniform names
65 const char * const UNIFORM_ALIGNMENT_MATRIX_NAME( "uAlignmentMatrix" );
66 const char * const UNIFORM_TEXTULRE_NAME("sTexture");
67
68
69 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
70   attribute mediump vec2 aPosition;\n
71   uniform mediump mat4 uMvpMatrix;\n
72   uniform mediump vec3 uSize;\n
73   uniform mediump mat3 uAlignmentMatrix;\n
74   varying mediump vec2 vTexCoord;\n
75   \n
76   void main()\n
77   {\n
78     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
79     vertexPosition.xyz *= uSize;\n
80     gl_Position = uMvpMatrix * vertexPosition;\n
81     \n
82     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
83   }\n
84 );
85
86 const char* FRAGMENT_SHADER_LINEAR = DALI_COMPOSE_SHADER(
87   uniform sampler2D sTexture;\n // sampler1D?
88   uniform lowp vec4 uColor;\n
89   varying mediump vec2 vTexCoord;\n
90   \n
91   void main()\n
92   {\n
93     gl_FragColor = texture2D( sTexture, vec2( vTexCoord.y, 0.5 ) ) * uColor;\n
94   }\n
95 );
96
97 const char* FRAGMENT_SHADER_RADIAL = DALI_COMPOSE_SHADER(
98   uniform sampler2D sTexture;\n // sampler1D?
99   uniform lowp vec4 uColor;\n
100   varying mediump vec2 vTexCoord;\n
101   \n
102   void main()\n
103   {\n
104     gl_FragColor = texture2D( sTexture, vec2( length(vTexCoord), 0.5 ) ) * uColor;\n
105   }\n
106 );
107
108 Sampler::WrapMode GetWrapMode( Gradient::SpreadMethod spread )
109 {
110   switch(spread)
111   {
112     case Gradient::REPEAT:
113     {
114       return Sampler::REPEAT;
115     }
116     case Gradient::REFLECT:
117     {
118       return Sampler::MIRRORED_REPEAT;
119     }
120     case Gradient::PAD:
121     default:
122     {
123       return Sampler::CLAMP_TO_EDGE;
124     }
125   }
126 }
127
128 }
129
130
131 GradientRenderer::GradientRenderer()
132 :mGradientTransformIndex( Property::INVALID_INDEX )
133 {
134 }
135
136 GradientRenderer::~GradientRenderer()
137 {
138 }
139
140 void GradientRenderer::Initialize( RendererFactoryCache& factoryCache, const Property::Map& propertyMap )
141 {
142   mImpl->mGeometry = factoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
143   if( !(mImpl->mGeometry) )
144   {
145     mImpl->mGeometry =  RendererFactoryCache::CreateQuadGeometry();
146     factoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, mImpl->mGeometry );
147   }
148
149   Type gradientType;
150   if( propertyMap.Find( GRADIENT_RADIUS_NAME ))
151   {
152     mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::GRADIENT_SHADER_RADIAL );
153     if( !(mImpl->mShader) )
154     {
155       mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_RADIAL );
156       factoryCache.SaveShader( RendererFactoryCache::GRADIENT_SHADER_RADIAL, mImpl->mShader );
157     }
158     gradientType = RADIAL;
159   }
160   else
161   {
162     mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::GRADIENT_SHADER_LINEAR );
163     if( !(mImpl->mShader) )
164     {
165       mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_LINEAR );
166       factoryCache.SaveShader( RendererFactoryCache::GRADIENT_SHADER_LINEAR, mImpl->mShader );
167     }
168     gradientType = LINEAR;
169   }
170
171   if( NewGradient(gradientType, propertyMap) )
172   {
173     mGradientTransform = mGradient->GetAlignmentTransform();
174   }
175   else
176   {
177     DALI_LOG_ERROR( "Fail to provide valid properties to create a GradientRenderer object" );
178   }
179 }
180
181 void GradientRenderer::SetSize( const Vector2& size )
182 {
183   ControlRenderer::SetSize( size );
184
185   if( mGradient->GetGradientUnits() == Gradient::OBJECT_BOUNDING_BOX )
186   {
187     // Apply scaling
188     Matrix3 scaling( 1.f/(size.x+Math::MACHINE_EPSILON_100), 0.f, 0.f,
189                      0.f, 1.f/(size.y+Math::MACHINE_EPSILON_100), 0.f, 0.5f, 0.5f, 1.f );
190     Matrix3::Multiply( mGradientTransform, scaling, mGradient->GetAlignmentTransform() );
191
192     if( mImpl->mRenderer )
193     {
194       (mImpl->mRenderer).SetProperty( mGradientTransformIndex, mGradientTransform );
195     }
196   }
197 }
198
199 void GradientRenderer::SetClipRect( const Rect<int>& clipRect )
200 {
201   ControlRenderer::SetClipRect( clipRect );
202
203   //ToDo: renderer responds to the clipRect change
204 }
205
206 void GradientRenderer::SetOffset( const Vector2& offset )
207 {
208   //ToDo: renderer applies the offset
209 }
210
211 void GradientRenderer::SetOnStage( Actor& actor )
212 {
213   ControlRenderer::SetOnStage(actor);
214
215   mGradientTransformIndex = (mImpl->mRenderer).RegisterProperty( UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform );
216
217   Dali::BufferImage lookupTexture = mGradient->GenerateLookupTexture();
218   Sampler sampler = Sampler::New( lookupTexture, UNIFORM_TEXTULRE_NAME );
219   Sampler::WrapMode wrap = GetWrapMode( mGradient->GetSpreadMethod() );
220   sampler.SetWrapMode(  wrap, wrap  );
221   ((mImpl->mRenderer).GetMaterial()).AddSampler( sampler );
222 }
223
224 bool GradientRenderer::NewGradient(Type gradientType, const Property::Map& propertyMap)
225 {
226   if( gradientType==LINEAR )
227   {
228     Property::Value* startPositionValue = propertyMap.Find( GRADIENT_START_POSITION_NAME );
229     Property::Value* endPositionValue = propertyMap.Find( GRADIENT_END_POSITION_NAME );
230     Vector2 startPosition;
231     Vector2 endPosition;
232
233     if( startPositionValue && startPositionValue->Get(startPosition)
234      && endPositionValue && endPositionValue->Get( endPosition ) )
235     {
236       mGradient = new LinearGradient( startPosition, endPosition );
237     }
238     else
239     {
240       return false;
241     }
242   }
243   else // type==RADIAL
244   {
245     Property::Value* centerValue = propertyMap.Find( GRADIENT_CENTER_NAME );
246     Property::Value* radiusValue = propertyMap.Find( GRADIENT_RADIUS_NAME );
247     Vector2 center;
248     float radius;
249     if( centerValue && centerValue->Get(center)
250         && radiusValue && radiusValue->Get(radius) )
251     {
252       mGradient = new RadialGradient( center, radius );
253     }
254     else
255     {
256       return false;
257     }
258   }
259
260   unsigned int numValidStop = 0u;
261   Property::Value* stopOffsetValue = propertyMap.Find( GRADIENT_STOP_OFFSET_NAME );
262   Property::Value* stopColorValue = propertyMap.Find( GRADIENT_STOP_COLOR_NAME );
263   if( stopOffsetValue && stopColorValue )
264   {
265     Property::Array* offsetArray = stopOffsetValue->GetArray();
266     Property::Array* colorArray = stopColorValue->GetArray();
267     if( offsetArray && colorArray )
268     {
269       unsigned int numStop = offsetArray->Count() < colorArray->Count() ?
270                              offsetArray->Count() : colorArray->Count();
271       float offset;
272       Vector4 color;
273       for( unsigned int i=0; i<numStop; i++ )
274       {
275         if( (offsetArray->GetElementAt(i)).Get(offset)
276          && (colorArray->GetElementAt(i)).Get(color) )
277         {
278           mGradient->AddStop( offset, color);
279           numValidStop++;
280         }
281       }
282     }
283   }
284   if( numValidStop < 1u ) // no valid stop
285   {
286     return false;
287   }
288
289   Property::Value* unitsValue = propertyMap.Find( GRADIENT_UNITS_NAME );
290   std::string units;
291   // The default unit is OBJECT_BOUNDING_BOX.
292   // Only need to set new units if 'user-space'
293   if( unitsValue && unitsValue->Get( units ) && units == UNIT_USER_SPACE )
294   {
295      mGradient->SetGradientUnits( Gradient::USER_SPACE_ON_USE );
296   }
297
298   Property::Value* spread = propertyMap.Find( GRADIENT_SPREAD_METHOD_NAME );
299   std::string stringValue ;
300   // The default spread method is PAD.
301   // Only need to set new spread if 'reflect' or 'repeat"
302   if( spread && spread->Get( stringValue ))
303   {
304     if( stringValue == SPREAD_REFLECT )
305     {
306       mGradient->SetSpreadMethod( Gradient::REFLECT );
307     }
308     else if( stringValue == SPREAD_REPEAT )
309     {
310       mGradient->SetSpreadMethod( Gradient::REPEAT );
311     }
312   }
313
314   return true;
315 }
316
317 } // namespace Internal
318
319 } // namespace Toolkit
320
321 } // namespace Dali