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