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