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