[dali_1.2.12] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / gradient / gradient-visual.cpp
1 /*
2  * Copyright (c) 2016 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-visual.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 #include <dali/devel-api/scripting/enum-helper.h>
28 #include <dali/devel-api/scripting/scripting.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/public-api/visuals/gradient-visual-properties.h>
32 #include <dali-toolkit/devel-api/visual-factory/devel-visual-properties.h>
33 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
34 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
35 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
36 #include <dali-toolkit/internal/visuals/gradient/linear-gradient.h>
37 #include <dali-toolkit/internal/visuals/gradient/radial-gradient.h>
38 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
39
40 namespace Dali
41 {
42
43 namespace Toolkit
44 {
45
46 namespace Internal
47 {
48
49 namespace
50 {
51 // properties: linear gradient
52 const char * const START_POSITION_NAME("startPosition"); // Property::VECTOR2
53 const char * const END_POSITION_NAME("endPosition"); // Property::VECTOR2
54
55 // properties: radial gradient
56 const char * const CENTER_NAME("center"); // Property::VECTOR2
57 const char * const RADIUS_NAME("radius"); // Property::FLOAT
58
59 // properties: linear&radial gradient
60 const char * const STOP_OFFSET_NAME("stopOffset"); // Property::Array FLOAT
61 const char * const STOP_COLOR_NAME("stopColor"); // Property::Array VECTOR4
62 const char * const UNITS_NAME("units"); // Property::String  "userSpaceOnUse | objectBoundingBox"
63 const char * const SPREAD_METHOD_NAME("spreadMethod"); // Property::String  "pad | reflect | repeat"
64
65 DALI_ENUM_TO_STRING_TABLE_BEGIN( UNITS )
66 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::Units, OBJECT_BOUNDING_BOX )
67 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::Units, USER_SPACE )
68 DALI_ENUM_TO_STRING_TABLE_END( UNITS )
69
70 DALI_ENUM_TO_STRING_TABLE_BEGIN( SPREAD_METHOD )
71 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, PAD )
72 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, REFLECT )
73 DALI_ENUM_TO_STRING_WITH_SCOPE( Toolkit::GradientVisual::SpreadMethod, REPEAT )
74 DALI_ENUM_TO_STRING_TABLE_END( SPREAD_METHOD )
75
76 // uniform names
77 const char * const UNIFORM_ALIGNMENT_MATRIX_NAME( "uAlignmentMatrix" );
78
79 // default offset value
80 const unsigned int DEFAULT_OFFSET_MINIMUM = 0.0f;
81 const unsigned int DEFAULT_OFFSET_MAXIMUM = 1.0f;
82
83 VisualFactoryCache::ShaderType GetShaderType( GradientVisual::Type type, Toolkit::GradientVisual::Units::Type units )
84 {
85   if( type == GradientVisual::LINEAR )
86   {
87    if( units == Toolkit::GradientVisual::Units::USER_SPACE )
88    {
89      return VisualFactoryCache::GRADIENT_SHADER_LINEAR_USER_SPACE;
90    }
91    return VisualFactoryCache::GRADIENT_SHADER_LINEAR_BOUNDING_BOX;
92   }
93   else if( units == Toolkit::GradientVisual::Units::USER_SPACE )
94   {
95     return VisualFactoryCache::GRADIENT_SHADER_RADIAL_USER_SPACE;
96   }
97
98   return VisualFactoryCache::GRADIENT_SHADER_RADIAL_BOUNDING_BOX;
99 }
100
101 const char* VERTEX_SHADER[] =
102 {
103 // vertex shader for gradient units as OBJECT_BOUNDING_BOX
104  DALI_COMPOSE_SHADER(
105   attribute mediump vec2 aPosition;\n
106   uniform mediump mat4 uMvpMatrix;\n
107   uniform mediump vec3 uSize;\n
108   uniform mediump mat3 uAlignmentMatrix;\n
109   varying mediump vec2 vTexCoord;\n
110   \n
111   void main()\n
112   {\n
113     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
114     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
115     \n
116     vertexPosition.xyz *= uSize;\n
117     gl_Position = uMvpMatrix * vertexPosition;\n
118   }\n
119 ),
120
121 // vertex shader for gradient units as USER_SPACE
122 DALI_COMPOSE_SHADER(
123   attribute mediump vec2 aPosition;\n
124   uniform mediump mat4 uMvpMatrix;\n
125   uniform mediump vec3 uSize;\n
126   uniform mediump mat3 uAlignmentMatrix;\n
127   varying mediump vec2 vTexCoord;\n
128   \n
129   void main()\n
130   {\n
131     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
132     vertexPosition.xyz *= uSize;\n
133     gl_Position = uMvpMatrix * vertexPosition;\n
134     \n
135     vTexCoord = (uAlignmentMatrix*vertexPosition.xyw).xy;\n
136   }\n
137 )
138 };
139
140 const char* FRAGMENT_SHADER[] =
141 {
142 // fragment shader for linear gradient
143 DALI_COMPOSE_SHADER(
144   uniform sampler2D sTexture;\n // sampler1D?
145   uniform lowp vec4 uColor;\n
146   varying mediump vec2 vTexCoord;\n
147   \n
148   void main()\n
149   {\n
150     gl_FragColor = texture2D( sTexture, vec2( vTexCoord.y, 0.5 ) ) * uColor;\n
151   }\n
152 ),
153
154 // fragment shader for radial gradient
155 DALI_COMPOSE_SHADER(
156   uniform sampler2D sTexture;\n // sampler1D?
157   uniform lowp vec4 uColor;\n
158   varying mediump vec2 vTexCoord;\n
159   \n
160   void main()\n
161   {\n
162     gl_FragColor = texture2D( sTexture, vec2( length(vTexCoord), 0.5 ) ) * uColor;\n
163   }\n
164 )
165 };
166
167 Dali::WrapMode::Type GetWrapMode( Toolkit::GradientVisual::SpreadMethod::Type spread )
168 {
169   switch(spread)
170   {
171     case Toolkit::GradientVisual::SpreadMethod::REPEAT:
172     {
173       return Dali::WrapMode::REPEAT;
174     }
175     case Toolkit::GradientVisual::SpreadMethod::REFLECT:
176     {
177       return Dali::WrapMode::MIRRORED_REPEAT;
178     }
179     case Toolkit::GradientVisual::SpreadMethod::PAD:
180     default:
181     {
182       return Dali::WrapMode::CLAMP_TO_EDGE;
183     }
184   }
185 }
186
187 } // unnamed namespace
188
189 GradientVisualPtr GradientVisual::New( VisualFactoryCache& factoryCache )
190 {
191   return new GradientVisual( factoryCache );
192 }
193
194 GradientVisual::GradientVisual( VisualFactoryCache& factoryCache )
195 : Visual::Base( factoryCache ),
196   mGradientType( LINEAR )
197 {
198   mImpl->mFlags |= Impl::IS_PREMULTIPLIED_ALPHA;
199 }
200
201 GradientVisual::~GradientVisual()
202 {
203 }
204
205 void GradientVisual::DoSetProperties( const Property::Map& propertyMap )
206 {
207   Toolkit::GradientVisual::Units::Type gradientUnits = Toolkit::GradientVisual::Units::OBJECT_BOUNDING_BOX;
208
209   Property::Value* unitsValue = propertyMap.Find( Toolkit::GradientVisual::Property::UNITS, UNITS_NAME );
210   if( unitsValue )
211   {
212     Scripting::GetEnumerationProperty( *unitsValue, UNITS_TABLE, UNITS_TABLE_COUNT, gradientUnits );
213   }
214
215   mGradientType = LINEAR;
216   if( propertyMap.Find( Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME ) )
217   {
218     mGradientType = RADIAL;
219   }
220
221   if( NewGradient( mGradientType, propertyMap ) )
222   {
223     mGradient->SetGradientUnits( gradientUnits );
224     mGradientTransform = mGradient->GetAlignmentTransform();
225   }
226   else
227   {
228     DALI_LOG_ERROR( "Fail to provide valid properties to create a GradientVisual object\n" );
229   }
230 }
231
232 void GradientVisual::SetSize( const Vector2& size )
233 {
234   Visual::Base::SetSize( size );
235 }
236
237 void GradientVisual::DoSetOnStage( Actor& actor )
238 {
239   InitializeRenderer();
240
241   actor.AddRenderer( mImpl->mRenderer );
242 }
243
244 void GradientVisual::DoCreatePropertyMap( Property::Map& map ) const
245 {
246   map.Clear();
247   map.Insert( Toolkit::VisualProperty::TYPE, Toolkit::Visual::GRADIENT );
248   map.Insert( Toolkit::GradientVisual::Property::UNITS, mGradient->GetGradientUnits() );
249   map.Insert( Toolkit::GradientVisual::Property::SPREAD_METHOD, mGradient->GetSpreadMethod() );
250
251   const Vector<Gradient::GradientStop>& stops( mGradient->GetStops() );
252   Property::Array offsets;
253   Property::Array colors;
254   for( unsigned int i=0; i<stops.Count(); i++ )
255   {
256     offsets.PushBack( stops[i].mOffset );
257     if( EqualsZero(stops[i].mStopColor.a) )
258     {
259       colors.PushBack( Vector4::ZERO );
260     }
261     else
262     {
263       colors.PushBack( Vector4( stops[i].mStopColor.r / stops[i].mStopColor.a,
264                                 stops[i].mStopColor.g / stops[i].mStopColor.a,
265                                 stops[i].mStopColor.b / stops[i].mStopColor.a,
266                                 stops[i].mStopColor.a));
267     }
268   }
269
270   map.Insert( Toolkit::GradientVisual::Property::STOP_OFFSET, offsets );
271   map.Insert( Toolkit::GradientVisual::Property::STOP_COLOR, colors );
272
273   if( &typeid( *mGradient ) == &typeid(LinearGradient) )
274   {
275     LinearGradient* gradient = static_cast<LinearGradient*>( mGradient.Get() );
276     map.Insert( Toolkit::GradientVisual::Property::START_POSITION, gradient->GetStartPosition() );
277     map.Insert( Toolkit::GradientVisual::Property::END_POSITION, gradient->GetEndPosition() );
278   }
279   else // if( &typeid( *mGradient ) == &typeid(RadialGradient) )
280   {
281     RadialGradient* gradient = static_cast<RadialGradient*>( mGradient.Get() );
282     map.Insert( Toolkit::GradientVisual::Property::CENTER, gradient->GetCenter() );
283     map.Insert( Toolkit::GradientVisual::Property::RADIUS, gradient->GetRadius() );
284   }
285 }
286
287 void GradientVisual::DoSetProperty( Dali::Property::Index index, const Dali::Property::Value& propertyValue )
288 {
289   // TODO
290 }
291
292 Dali::Property::Value GradientVisual::DoGetProperty( Dali::Property::Index index )
293 {
294   // TODO
295   return Dali::Property::Value();
296 }
297
298 void GradientVisual::InitializeRenderer()
299 {
300   Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
301   if( !geometry )
302   {
303     geometry =  VisualFactoryCache::CreateQuadGeometry();
304     mFactoryCache.SaveGeometry( VisualFactoryCache::QUAD_GEOMETRY, geometry );
305   }
306
307   Toolkit::GradientVisual::Units::Type gradientUnits = mGradient->GetGradientUnits();
308   VisualFactoryCache::ShaderType shaderType = GetShaderType( mGradientType, gradientUnits );
309   Shader shader = mFactoryCache.GetShader( shaderType );
310   if( !shader )
311   {
312     shader = Shader::New( VERTEX_SHADER[gradientUnits], FRAGMENT_SHADER[ mGradientType ] );
313     mFactoryCache.SaveShader( shaderType, shader );
314   }
315
316   //Set up the texture set
317   TextureSet textureSet = TextureSet::New();
318   Dali::Texture lookupTexture = mGradient->GenerateLookupTexture();
319   textureSet.SetTexture( 0u, lookupTexture );
320   Dali::WrapMode::Type wrap = GetWrapMode( mGradient->GetSpreadMethod() );
321   Sampler sampler = Sampler::New();
322   sampler.SetWrapMode(  wrap, wrap  );
323   textureSet.SetSampler( 0u, sampler );
324
325   mImpl->mRenderer = Renderer::New( geometry, shader );
326   mImpl->mRenderer.SetTextures( textureSet );
327
328   mImpl->mRenderer.RegisterProperty( UNIFORM_ALIGNMENT_MATRIX_NAME, mGradientTransform );
329 }
330
331 bool GradientVisual::NewGradient(Type gradientType, const Property::Map& propertyMap)
332 {
333   if( gradientType == LINEAR )
334   {
335     Property::Value* startPositionValue = propertyMap.Find( Toolkit::GradientVisual::Property::START_POSITION, START_POSITION_NAME );
336     Property::Value* endPositionValue = propertyMap.Find( Toolkit::GradientVisual::Property::END_POSITION, END_POSITION_NAME );
337     Vector2 startPosition;
338     Vector2 endPosition;
339
340     if( startPositionValue && startPositionValue->Get(startPosition)
341      && endPositionValue && endPositionValue->Get( endPosition ) )
342     {
343       mGradient = new LinearGradient( startPosition, endPosition );
344     }
345     else
346     {
347       return false;
348     }
349   }
350   else // type==RADIAL
351   {
352     Property::Value* centerValue = propertyMap.Find( Toolkit::GradientVisual::Property::CENTER, CENTER_NAME );
353     Property::Value* radiusValue = propertyMap.Find( Toolkit::GradientVisual::Property::RADIUS, RADIUS_NAME );
354     Vector2 center;
355     float radius;
356     if( centerValue && centerValue->Get(center)
357         && radiusValue && radiusValue->Get(radius) )
358     {
359       mGradient = new RadialGradient( center, radius );
360     }
361     else
362     {
363       return false;
364     }
365   }
366
367   unsigned int numValidStop = 0u;
368   Property::Value* stopOffsetValue = propertyMap.Find( Toolkit::GradientVisual::Property::STOP_OFFSET, STOP_OFFSET_NAME );
369   Property::Value* stopColorValue = propertyMap.Find( Toolkit::GradientVisual::Property::STOP_COLOR, STOP_COLOR_NAME );
370   if( stopColorValue )
371   {
372     Vector<float> offsetArray;
373     Property::Array* colorArray = stopColorValue->GetArray();
374     if( colorArray )
375     {
376       GetStopOffsets( stopOffsetValue, offsetArray );
377       unsigned int numStop = offsetArray.Count() < colorArray->Count() ?
378                              offsetArray.Count() : colorArray->Count();
379       Vector4 color;
380       for( unsigned int i=0; i<numStop; i++ )
381       {
382         if( (colorArray->GetElementAt(i)).Get(color) )
383         {
384           mGradient->AddStop( offsetArray[i], Vector4(color.r*color.a, color.g*color.a, color.b*color.a, color.a));
385           numValidStop++;
386         }
387       }
388     }
389   }
390
391   if( numValidStop < 1u ) // no valid stop
392   {
393     return false;
394   }
395
396   Property::Value* spread = propertyMap.Find( Toolkit::GradientVisual::Property::SPREAD_METHOD, SPREAD_METHOD_NAME );
397   // The default spread method is PAD. Only need to set new spread if it's anything else.
398   if( spread )
399   {
400     Toolkit::GradientVisual::SpreadMethod::Type spreadMethod = Toolkit::GradientVisual::SpreadMethod::PAD;
401     if( Scripting::GetEnumerationProperty( *spread, SPREAD_METHOD_TABLE, SPREAD_METHOD_TABLE_COUNT, spreadMethod ) );
402     {
403       mGradient->SetSpreadMethod( spreadMethod );
404     }
405   }
406
407   return true;
408 }
409
410 void GradientVisual::GetStopOffsets(const Property::Value* value, Vector<float>& stopOffsets)
411 {
412
413   if ( value ) // Only check valve type if a valid Property has been passed in
414   {
415     switch ( value->GetType() )
416     {
417       case Property::VECTOR2:
418       {
419         Vector2 offset2;
420         value->Get( offset2 );
421         stopOffsets.PushBack( offset2.x );
422         stopOffsets.PushBack( offset2.y );
423         break;
424       }
425       case Property::VECTOR3:
426       {
427         Vector3 offset3;
428         value->Get( offset3 );
429         stopOffsets.PushBack( offset3.x );
430         stopOffsets.PushBack( offset3.y );
431         stopOffsets.PushBack( offset3.z );
432         break;
433       }
434       case Property::VECTOR4:
435       {
436         Vector4 offset4;
437         value->Get( offset4 );
438         stopOffsets.PushBack( offset4.x );
439         stopOffsets.PushBack( offset4.y );
440         stopOffsets.PushBack( offset4.z );
441         stopOffsets.PushBack( offset4.w );
442         break;
443       }
444       case Property::ARRAY:
445       {
446         Property::Array* offsetArray = value->GetArray();
447         if( offsetArray )
448         {
449           unsigned int numStop = offsetArray->Count();
450           float offset;
451           for( unsigned int i=0; i<numStop; i++ )
452           {
453             if( offsetArray->GetElementAt(i).Get(offset) )
454             {
455               stopOffsets.PushBack( offset );
456             }
457           }
458         }
459         break;
460       }
461       default:
462       {
463         DALI_LOG_WARNING("GetStopOffsets passed unsupported Property Map\n");
464         // Unsupported Type
465       }
466     }
467   }
468
469   if ( stopOffsets.Empty() )
470   {
471     // Set default offset if none set by Property system, need a minimum and maximum
472     stopOffsets.PushBack( DEFAULT_OFFSET_MINIMUM );
473     stopOffsets.PushBack( DEFAULT_OFFSET_MAXIMUM );
474   }
475 }
476
477 } // namespace Internal
478
479 } // namespace Toolkit
480
481 } // namespace Dali