Merge "Refactored ControlRenderer so that derived classes are responsible for the...
[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( Actor& actor, 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::DoSetOnStage( Actor& actor )
238 {
239   InitializeRenderer();
240 }
241
242 void GradientRenderer::DoCreatePropertyMap( Property::Map& map ) const
243 {
244   map.Clear();
245   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
246
247   Gradient::GradientUnits units = mGradient->GetGradientUnits();
248   if( units == Gradient::USER_SPACE_ON_USE )
249   {
250     map.Insert( GRADIENT_UNITS_NAME, UNIT_USER_SPACE );
251   }
252   else // if( units == Gradient::OBJECT_BOUNDING_BOX )
253   {
254     map.Insert( GRADIENT_UNITS_NAME, UNIT_BOUNDING_BOX );
255   }
256
257   Gradient::SpreadMethod spread = mGradient->GetSpreadMethod();
258   if( spread == Gradient::PAD )
259   {
260     map.Insert( GRADIENT_SPREAD_METHOD_NAME, SPREAD_PAD );
261   }
262   else if( spread == Gradient::REFLECT )
263   {
264     map.Insert( GRADIENT_SPREAD_METHOD_NAME, SPREAD_REFLECT );
265   }
266   else // if( units == Gradient::REPEAT )
267   {
268     map.Insert( GRADIENT_SPREAD_METHOD_NAME, SPREAD_REPEAT );
269   }
270
271   const Vector<Gradient::GradientStop>& stops( mGradient->GetStops() );
272   Property::Array offsets;
273   Property::Array colors;
274   for( unsigned int i=0; i<stops.Count(); i++ )
275   {
276     offsets.PushBack( stops[i].mOffset );
277     colors.PushBack( stops[i].mStopColor );
278   }
279
280   map.Insert( GRADIENT_STOP_OFFSET_NAME, offsets );
281   map.Insert( GRADIENT_STOP_COLOR_NAME, colors );
282
283   if( &typeid( *mGradient ) == &typeid(LinearGradient) )
284   {
285     LinearGradient* gradient = static_cast<LinearGradient*>( mGradient.Get() );
286     map.Insert( GRADIENT_START_POSITION_NAME, gradient->GetStartPosition() );
287     map.Insert( GRADIENT_END_POSITION_NAME, gradient->GetEndPosition() );
288   }
289   else // if( &typeid( *mGradient ) == &typeid(RadialGradient) )
290   {
291     RadialGradient* gradient = static_cast<RadialGradient*>( mGradient.Get() );
292     map.Insert( GRADIENT_CENTER_NAME, gradient->GetCenter() );
293     map.Insert( GRADIENT_RADIUS_NAME, gradient->GetRadius() );
294   }
295 }
296
297 void GradientRenderer::InitializeRenderer()
298 {
299   Geometry geometry = mFactoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
300   if( !geometry )
301   {
302     geometry =  RendererFactoryCache::CreateQuadGeometry();
303     mFactoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
304   }
305
306   Gradient::GradientUnits gradientUnits = mGradient->GetGradientUnits();
307   RendererFactoryCache::ShaderType shaderType = GetShaderType( mGradientType, gradientUnits );
308   Shader shader = mFactoryCache.GetShader( shaderType );
309   if( !shader )
310   {
311     shader = Shader::New( VERTEX_SHADER[gradientUnits], FRAGMENT_SHADER[ mGradientType ] );
312     mFactoryCache.SaveShader( shaderType, shader );
313   }
314
315   Material material;
316   material = Material::New( shader );
317   mImpl->mRenderer = Renderer::New( geometry, material );
318
319   Dali::BufferImage lookupTexture = mGradient->GenerateLookupTexture();
320   Sampler sampler = Sampler::New();
321   Dali::WrapMode::Type wrap = GetWrapMode( mGradient->GetSpreadMethod() );
322   sampler.SetWrapMode(  wrap, wrap  );
323
324   material.AddTexture( lookupTexture, UNIFORM_TEXTULRE_NAME, sampler );
325
326   mImpl->mRenderer.RegisterProperty( UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform );
327 }
328
329 bool GradientRenderer::NewGradient(Type gradientType, const Property::Map& propertyMap)
330 {
331   if( gradientType==LINEAR )
332   {
333     Property::Value* startPositionValue = propertyMap.Find( GRADIENT_START_POSITION_NAME );
334     Property::Value* endPositionValue = propertyMap.Find( GRADIENT_END_POSITION_NAME );
335     Vector2 startPosition;
336     Vector2 endPosition;
337
338     if( startPositionValue && startPositionValue->Get(startPosition)
339      && endPositionValue && endPositionValue->Get( endPosition ) )
340     {
341       mGradient = new LinearGradient( startPosition, endPosition );
342     }
343     else
344     {
345       return false;
346     }
347   }
348   else // type==RADIAL
349   {
350     Property::Value* centerValue = propertyMap.Find( GRADIENT_CENTER_NAME );
351     Property::Value* radiusValue = propertyMap.Find( GRADIENT_RADIUS_NAME );
352     Vector2 center;
353     float radius;
354     if( centerValue && centerValue->Get(center)
355         && radiusValue && radiusValue->Get(radius) )
356     {
357       mGradient = new RadialGradient( center, radius );
358     }
359     else
360     {
361       return false;
362     }
363   }
364
365   unsigned int numValidStop = 0u;
366   Property::Value* stopOffsetValue = propertyMap.Find( GRADIENT_STOP_OFFSET_NAME );
367   Property::Value* stopColorValue = propertyMap.Find( GRADIENT_STOP_COLOR_NAME );
368   if( stopOffsetValue && stopColorValue )
369   {
370     Vector<float> offsetArray;
371     Property::Array* colorArray = stopColorValue->GetArray();
372     if( colorArray && GetStopOffsets( stopOffsetValue, offsetArray ))
373     {
374       unsigned int numStop = offsetArray.Count() < colorArray->Count() ?
375                              offsetArray.Count() : colorArray->Count();
376       Vector4 color;
377       for( unsigned int i=0; i<numStop; i++ )
378       {
379         if( (colorArray->GetElementAt(i)).Get(color) )
380         {
381           mGradient->AddStop( offsetArray[i], color);
382           numValidStop++;
383         }
384       }
385     }
386   }
387
388   if( numValidStop < 1u ) // no valid stop
389   {
390     return false;
391   }
392
393   Property::Value* spread = propertyMap.Find( GRADIENT_SPREAD_METHOD_NAME );
394   std::string stringValue ;
395   // The default spread method is PAD.
396   // Only need to set new spread if 'reflect' or 'repeat"
397   if( spread && spread->Get( stringValue ))
398   {
399     if( stringValue == SPREAD_REFLECT )
400     {
401       mGradient->SetSpreadMethod( Gradient::REFLECT );
402     }
403     else if( stringValue == SPREAD_REPEAT )
404     {
405       mGradient->SetSpreadMethod( Gradient::REPEAT );
406     }
407   }
408
409   return true;
410 }
411
412 bool GradientRenderer::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
413 {
414   Vector2 offset2;
415   if( value->Get( offset2 ) )
416   {
417     stopOffsets.PushBack( offset2.x );
418     stopOffsets.PushBack( offset2.y );
419     return true;
420   }
421
422   Vector3 offset3;
423   if( value->Get( offset3 ) )
424   {
425     stopOffsets.PushBack( offset3.x );
426     stopOffsets.PushBack( offset3.y );
427     stopOffsets.PushBack( offset3.z );
428     return true;
429   }
430
431   Vector4 offset4;
432   if( value->Get( offset4 ) )
433   {
434     stopOffsets.PushBack( offset4.x );
435     stopOffsets.PushBack( offset4.y );
436     stopOffsets.PushBack( offset4.z );
437     stopOffsets.PushBack( offset4.w );
438     return true;
439   }
440
441   Property::Array* offsetArray = value->GetArray();
442   if( offsetArray )
443   {
444     unsigned int numStop = offsetArray->Count();
445     float offset;
446     for( unsigned int i=0; i<numStop; i++ )
447     {
448       if( offsetArray->GetElementAt(i).Get(offset) )
449       {
450         stopOffsets.PushBack( offset );
451       }
452     }
453     return true;
454   }
455
456   return false;
457 }
458
459 } // namespace Internal
460
461 } // namespace Toolkit
462
463 } // namespace Dali