Merge "DGEUF-110: Limit ScrollView to scroll maximum 1 page on flick" into devel...
[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 <typeinfo>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/common/dali-vector.h>
25 #include <dali/public-api/images/buffer-image.h>
26 #include <dali/public-api/object/property-array.h>
27
28 //INTERNAL INCLUDES
29 #include <dali-toolkit/internal/controls/renderers/renderer-factory-impl.h>
30 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
31 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
32 #include <dali-toolkit/internal/controls/renderers/gradient/linear-gradient.h>
33 #include <dali-toolkit/internal/controls/renderers/gradient/radial-gradient.h>
34
35 namespace Dali
36 {
37
38 namespace Toolkit
39 {
40
41 namespace Internal
42 {
43
44 namespace
45 {
46 const char * const RENDERER_TYPE("renderer-type");
47 const char * const RENDERER_TYPE_VALUE("gradient-renderer");
48
49 // properties: linear gradient
50 const char * const GRADIENT_START_POSITION_NAME("gradient-start-position"); // Property::VECTOR2
51 const char * const GRADIENT_END_POSITION_NAME("gradient-end-position"); // Property::VECTOR2
52
53 // properties: radial gradient
54 const char * const GRADIENT_CENTER_NAME("gradient-center"); // Property::VECTOR2
55 const char * const GRADIENT_RADIUS_NAME("gradient-radius"); // Property::FLOAT
56
57 // properties: linear&radial gradient
58 const char * const GRADIENT_STOP_OFFSET_NAME("gradient-stop-offset"); // Property::Array FLOAT
59 const char * const GRADIENT_STOP_COLOR_NAME("gradient-stop-color"); // Property::Array VECTOR4
60 const char * const GRADIENT_UNITS_NAME("gradient-units"); // Property::String  "userSpaceOnUse | objectBoundingBox"
61 const char * const GRADIENT_SPREAD_METHOD_NAME("gradient-spread-method"); // Property::String  "pad | reflect | repeat"
62
63 // string values
64 const char * const UNIT_USER_SPACE("user-space");
65 const char * const UNIT_BOUNDING_BOX("object-bounding-box");
66 const char * const SPREAD_PAD("pad");
67 const char * const SPREAD_REFLECT("reflect");
68 const char * const SPREAD_REPEAT("repeat");
69
70 // uniform names
71 const char * const UNIFORM_ALIGNMENT_MATRIX_NAME( "uAlignmentMatrix" );
72 const char * const UNIFORM_TEXTULRE_NAME("sTexture");
73
74 RendererFactoryCache::ShaderType GetShaderType( GradientRenderer::Type type, Gradient::GradientUnits units)
75 {
76   if( type==GradientRenderer::LINEAR )
77   {
78    if( units == Gradient::USER_SPACE_ON_USE )
79    {
80      return RendererFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE;
81    }
82    return RendererFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX;
83   }
84   else if( units == Gradient::USER_SPACE_ON_USE )
85   {
86     return RendererFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE;
87   }
88
89   return RendererFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX;
90 }
91
92 const char* VERTEX_SHADER[] =
93 {
94 // vertex shader for gradient units as USER_SPACE_ON_USE
95 DALI_COMPOSE_SHADER(
96   attribute mediump vec2 aPosition;\n
97   uniform mediump mat4 uMvpMatrix;\n
98   uniform mediump vec3 uSize;\n
99   uniform mediump mat3 uAlignmentMatrix;\n
100   varying mediump vec2 vTexCoord;\n
101   \n
102   void main()\n
103   {\n
104     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
105     vertexPosition.xyz *= uSize;\n
106     gl_Position = uMvpMatrix * vertexPosition;\n
107     \n
108     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
109   }\n
110 ),
111
112 // vertex shader for gradient units as OBJECT_BOUNDING_BOX
113  DALI_COMPOSE_SHADER(
114   attribute mediump vec2 aPosition;\n
115   uniform mediump mat4 uMvpMatrix;\n
116   uniform mediump vec3 uSize;\n
117   uniform mediump mat3 uAlignmentMatrix;\n
118   varying mediump vec2 vTexCoord;\n
119   \n
120   void main()\n
121   {\n
122     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
123     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
124     \n
125     vertexPosition.xyz *= uSize;\n
126     gl_Position = uMvpMatrix * vertexPosition;\n
127   }\n
128 )
129 };
130
131 const char* FRAGMENT_SHADER[] =
132 {
133 // fragment shader for linear gradient
134 DALI_COMPOSE_SHADER(
135   uniform sampler2D sTexture;\n // sampler1D?
136   uniform lowp vec4 uColor;\n
137   varying mediump vec2 vTexCoord;\n
138   \n
139   void main()\n
140   {\n
141     gl_FragColor = texture2D( sTexture, vec2( vTexCoord.y, 0.5 ) ) * uColor;\n
142   }\n
143 ),
144
145 // fragment shader for radial gradient
146 DALI_COMPOSE_SHADER(
147   uniform sampler2D sTexture;\n // sampler1D?
148   uniform lowp vec4 uColor;\n
149   varying mediump vec2 vTexCoord;\n
150   \n
151   void main()\n
152   {\n
153     gl_FragColor = texture2D( sTexture, vec2( length(vTexCoord), 0.5 ) ) * uColor;\n
154   }\n
155 )
156 };
157
158 Dali::WrapMode::Type GetWrapMode( Gradient::SpreadMethod spread )
159 {
160   switch(spread)
161   {
162     case Gradient::REPEAT:
163     {
164       return Dali::WrapMode::REPEAT;
165     }
166     case Gradient::REFLECT:
167     {
168       return Dali::WrapMode::MIRRORED_REPEAT;
169     }
170     case Gradient::PAD:
171     default:
172     {
173       return Dali::WrapMode::CLAMP_TO_EDGE;
174     }
175   }
176 }
177
178 }
179
180
181 GradientRenderer::GradientRenderer( RendererFactoryCache& factoryCache )
182 : ControlRenderer( factoryCache ),
183   mGradientType( LINEAR )
184 {
185 }
186
187 GradientRenderer::~GradientRenderer()
188 {
189 }
190
191 void GradientRenderer::DoInitialize( const Property::Map& propertyMap )
192 {
193   Gradient::GradientUnits gradientUnits = Gradient::OBJECT_BOUNDING_BOX;
194   Property::Value* unitsValue = propertyMap.Find( GRADIENT_UNITS_NAME );
195   std::string units;
196   // The default unit is OBJECT_BOUNDING_BOX.
197   // Only need to set new units if 'user-space'
198   if( unitsValue && unitsValue->Get( units ) && units == UNIT_USER_SPACE )
199   {
200     gradientUnits = Gradient::USER_SPACE_ON_USE;
201   }
202
203   mGradientType = LINEAR;
204   if( propertyMap.Find( GRADIENT_RADIUS_NAME ))
205   {
206     mGradientType = RADIAL;
207   }
208
209   if( NewGradient( mGradientType, propertyMap ) )
210   {
211     mGradient->SetGradientUnits( gradientUnits );
212     mGradientTransform = mGradient->GetAlignmentTransform();
213   }
214   else
215   {
216     DALI_LOG_ERROR( "Fail to provide valid properties to create a GradientRenderer object" );
217   }
218 }
219
220 void GradientRenderer::SetSize( const Vector2& size )
221 {
222   ControlRenderer::SetSize( size );
223 }
224
225 void GradientRenderer::SetClipRect( const Rect<int>& clipRect )
226 {
227   ControlRenderer::SetClipRect( clipRect );
228
229   //ToDo: renderer responds to the clipRect change
230 }
231
232 void GradientRenderer::SetOffset( const Vector2& offset )
233 {
234   //ToDo: renderer applies the offset
235 }
236
237 void GradientRenderer::DoCreatePropertyMap( Property::Map& map ) const
238 {
239   map.Clear();
240   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
241
242   Gradient::GradientUnits units = mGradient->GetGradientUnits();
243   if( units == Gradient::USER_SPACE_ON_USE )
244   {
245     map.Insert( GRADIENT_UNITS_NAME, UNIT_USER_SPACE );
246   }
247   else // if( units == Gradient::OBJECT_BOUNDING_BOX )
248   {
249     map.Insert( GRADIENT_UNITS_NAME, UNIT_BOUNDING_BOX );
250   }
251
252   Gradient::SpreadMethod spread = mGradient->GetSpreadMethod();
253   if( spread == Gradient::PAD )
254   {
255     map.Insert( GRADIENT_SPREAD_METHOD_NAME, SPREAD_PAD );
256   }
257   else if( spread == Gradient::REFLECT )
258   {
259     map.Insert( GRADIENT_SPREAD_METHOD_NAME, SPREAD_REFLECT );
260   }
261   else // if( units == Gradient::REPEAT )
262   {
263     map.Insert( GRADIENT_SPREAD_METHOD_NAME, SPREAD_REPEAT );
264   }
265
266   const Vector<Gradient::GradientStop>& stops( mGradient->GetStops() );
267   Property::Array offsets;
268   Property::Array colors;
269   for( unsigned int i=0; i<stops.Count(); i++ )
270   {
271     offsets.PushBack( stops[i].mOffset );
272     colors.PushBack( stops[i].mStopColor );
273   }
274
275   map.Insert( GRADIENT_STOP_OFFSET_NAME, offsets );
276   map.Insert( GRADIENT_STOP_COLOR_NAME, colors );
277
278   if( &typeid( *mGradient ) == &typeid(LinearGradient) )
279   {
280     LinearGradient* gradient = static_cast<LinearGradient*>( mGradient.Get() );
281     map.Insert( GRADIENT_START_POSITION_NAME, gradient->GetStartPosition() );
282     map.Insert( GRADIENT_END_POSITION_NAME, gradient->GetEndPosition() );
283   }
284   else // if( &typeid( *mGradient ) == &typeid(RadialGradient) )
285   {
286     RadialGradient* gradient = static_cast<RadialGradient*>( mGradient.Get() );
287     map.Insert( GRADIENT_CENTER_NAME, gradient->GetCenter() );
288     map.Insert( GRADIENT_RADIUS_NAME, gradient->GetRadius() );
289   }
290 }
291
292 void GradientRenderer::InitializeRenderer( Dali::Renderer& renderer )
293 {
294   Geometry geometry = mFactoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
295   if( !geometry )
296   {
297     geometry =  RendererFactoryCache::CreateQuadGeometry();
298     mFactoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
299   }
300
301   Gradient::GradientUnits gradientUnits = mGradient->GetGradientUnits();
302   RendererFactoryCache::ShaderType shaderType = GetShaderType( mGradientType, gradientUnits );
303   Shader shader = mFactoryCache.GetShader( shaderType );
304   if( !shader )
305   {
306     shader = Shader::New( VERTEX_SHADER[gradientUnits], FRAGMENT_SHADER[ mGradientType ] );
307     mFactoryCache.SaveShader( shaderType, shader );
308   }
309
310   Material material;
311   if( !renderer )
312   {
313     material = Material::New( shader );
314     renderer = Renderer::New( geometry, material );
315   }
316   else
317   {
318     mImpl->mRenderer.SetGeometry( geometry );
319     material = mImpl->mRenderer.GetMaterial();
320     if( material )
321     {
322       material.SetShader( shader );
323     }
324   }
325
326   Dali::BufferImage lookupTexture = mGradient->GenerateLookupTexture();
327   Sampler sampler = Sampler::New();
328   Dali::WrapMode::Type wrap = GetWrapMode( mGradient->GetSpreadMethod() );
329   sampler.SetWrapMode(  wrap, wrap  );
330
331   material.AddTexture( lookupTexture, UNIFORM_TEXTULRE_NAME, sampler );
332
333   renderer.RegisterProperty( UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform );
334 }
335
336 bool GradientRenderer::NewGradient(Type gradientType, const Property::Map& propertyMap)
337 {
338   if( gradientType==LINEAR )
339   {
340     Property::Value* startPositionValue = propertyMap.Find( GRADIENT_START_POSITION_NAME );
341     Property::Value* endPositionValue = propertyMap.Find( GRADIENT_END_POSITION_NAME );
342     Vector2 startPosition;
343     Vector2 endPosition;
344
345     if( startPositionValue && startPositionValue->Get(startPosition)
346      && endPositionValue && endPositionValue->Get( endPosition ) )
347     {
348       mGradient = new LinearGradient( startPosition, endPosition );
349     }
350     else
351     {
352       return false;
353     }
354   }
355   else // type==RADIAL
356   {
357     Property::Value* centerValue = propertyMap.Find( GRADIENT_CENTER_NAME );
358     Property::Value* radiusValue = propertyMap.Find( GRADIENT_RADIUS_NAME );
359     Vector2 center;
360     float radius;
361     if( centerValue && centerValue->Get(center)
362         && radiusValue && radiusValue->Get(radius) )
363     {
364       mGradient = new RadialGradient( center, radius );
365     }
366     else
367     {
368       return false;
369     }
370   }
371
372   unsigned int numValidStop = 0u;
373   Property::Value* stopOffsetValue = propertyMap.Find( GRADIENT_STOP_OFFSET_NAME );
374   Property::Value* stopColorValue = propertyMap.Find( GRADIENT_STOP_COLOR_NAME );
375   if( stopOffsetValue && stopColorValue )
376   {
377     Vector<float> offsetArray;
378     Property::Array* colorArray = stopColorValue->GetArray();
379     if( colorArray && GetStopOffsets( stopOffsetValue, offsetArray ))
380     {
381       unsigned int numStop = offsetArray.Count() < colorArray->Count() ?
382                              offsetArray.Count() : colorArray->Count();
383       Vector4 color;
384       for( unsigned int i=0; i<numStop; i++ )
385       {
386         if( (colorArray->GetElementAt(i)).Get(color) )
387         {
388           mGradient->AddStop( offsetArray[i], color);
389           numValidStop++;
390         }
391       }
392     }
393   }
394
395   if( numValidStop < 1u ) // no valid stop
396   {
397     return false;
398   }
399
400   Property::Value* spread = propertyMap.Find( GRADIENT_SPREAD_METHOD_NAME );
401   std::string stringValue ;
402   // The default spread method is PAD.
403   // Only need to set new spread if 'reflect' or 'repeat"
404   if( spread && spread->Get( stringValue ))
405   {
406     if( stringValue == SPREAD_REFLECT )
407     {
408       mGradient->SetSpreadMethod( Gradient::REFLECT );
409     }
410     else if( stringValue == SPREAD_REPEAT )
411     {
412       mGradient->SetSpreadMethod( Gradient::REPEAT );
413     }
414   }
415
416   return true;
417 }
418
419 bool GradientRenderer::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
420 {
421   Vector2 offset2;
422   if( value->Get( offset2 ) )
423   {
424     stopOffsets.PushBack( offset2.x );
425     stopOffsets.PushBack( offset2.y );
426     return true;
427   }
428
429   Vector3 offset3;
430   if( value->Get( offset3 ) )
431   {
432     stopOffsets.PushBack( offset3.x );
433     stopOffsets.PushBack( offset3.y );
434     stopOffsets.PushBack( offset3.z );
435     return true;
436   }
437
438   Vector4 offset4;
439   if( value->Get( offset4 ) )
440   {
441     stopOffsets.PushBack( offset4.x );
442     stopOffsets.PushBack( offset4.y );
443     stopOffsets.PushBack( offset4.z );
444     stopOffsets.PushBack( offset4.w );
445     return true;
446   }
447
448   Property::Array* offsetArray = value->GetArray();
449   if( offsetArray )
450   {
451     unsigned int numStop = offsetArray->Count();
452     float offset;
453     for( unsigned int i=0; i<numStop; i++ )
454     {
455       if( offsetArray->GetElementAt(i).Get(offset) )
456       {
457         stopOffsets.PushBack( offset );
458       }
459     }
460     return true;
461   }
462
463   return false;
464 }
465
466 } // namespace Internal
467
468 } // namespace Toolkit
469
470 } // namespace Dali