e88ce9cbc295f1802e13efe5f91d55109a2ab7b5
[platform/core/uifw/dali-core.git] / dali / internal / event / effects / shader-effect-impl.cpp
1 /*
2  * Copyright (c) 2014 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 <dali/internal/event/effects/shader-effect-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/math/matrix.h>
26 #include <dali/public-api/math/matrix3.h>
27 #include <dali/public-api/math/vector2.h>
28 #include <dali/public-api/object/type-registry.h>
29 #include <dali/devel-api/scripting/scripting.h>
30 #include <dali/public-api/shader-effects/shader-effect.h>
31 #include <dali/internal/event/actors/image-actor-impl.h>
32 #include <dali/internal/event/common/property-helper.h>
33 #include <dali/internal/event/images/image-impl.h>
34 #include "dali-shaders.h"
35
36 using std::string;
37
38 namespace Dali
39 {
40
41 namespace Internal
42 {
43
44 namespace
45 {
46
47 // Properties
48
49 //              Name             Type   writable animatable constraint-input  enum for index-checking
50 DALI_PROPERTY_TABLE_BEGIN
51 DALI_PROPERTY( "gridDensity",    FLOAT,   true,    false,   false,   Dali::ShaderEffect::Property::GRID_DENSITY   )
52 DALI_PROPERTY( "image",          MAP,     true,    false,   false,   Dali::ShaderEffect::Property::IMAGE          )
53 DALI_PROPERTY( "program",        MAP,     true,    false,   false,   Dali::ShaderEffect::Property::PROGRAM        )
54 DALI_PROPERTY( "geometryHints",  STRING,  true,    false,   false,   Dali::ShaderEffect::Property::GEOMETRY_HINTS )
55 DALI_PROPERTY_TABLE_END( DEFAULT_ACTOR_PROPERTY_START_INDEX )
56
57 BaseHandle Create()
58 {
59   Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
60
61   return Dali::ShaderEffect(internal.Get());
62 }
63
64 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
65
66 struct WrapperStrings
67 {
68   const char* vertexShaderPrefix;
69   const char* fragmentShaderPrefix;
70   const char* vertexShaderPostfix;
71   const char* fragmentShaderPostfix;
72 };
73
74 WrapperStrings customImageShaderWrappers =
75 {
76   CustomImagePrefixVertex, CustomImagePrefixFragment,
77   CustomImagePostfixVertex, CustomImagePostfixFragment
78 };
79
80 /**
81  * Helper to wrap the program with our default pre and postfix if needed
82  * @param[in] vertexPrefix from application
83  * @param[in] vertexBody from application
84  */
85 std::string WrapVertexShader( const std::string& vertexPrefix, const std::string& vertexBody )
86 {
87   std::string vertexSource = vertexPrefix + customImageShaderWrappers.vertexShaderPrefix;
88
89   // Append the custom vertex shader code if supplied, otherwise append the default
90   if ( vertexBody.length() > 0 )
91   {
92     vertexSource.append( vertexBody );
93   }
94   else
95   {
96     vertexSource.append( customImageShaderWrappers.vertexShaderPostfix );
97   }
98
99   return vertexSource;
100 }
101
102 /**
103  * Helper to wrap the program with our default pre and postfix if needed
104  * @param[in] fragmentPrefix from application
105  * @param[in] fragmentBody from application
106  */
107 std::string WrapFragmentShader( const std::string& fragmentPrefix, const std::string& fragmentBody )
108 {
109   std::string fragmentSource = fragmentPrefix + customImageShaderWrappers.fragmentShaderPrefix;
110
111   // Append the custom fragment shader code if supplied, otherwise append the default
112   if ( fragmentBody.length() > 0 )
113   {
114     fragmentSource.append( fragmentBody );
115   }
116   else
117   {
118     fragmentSource.append( customImageShaderWrappers.fragmentShaderPostfix );
119   }
120
121   return fragmentSource;
122 }
123
124 std::string GetStringProperty(const std::string& field, const Property::Value& property)
125 {
126   std::string retval;
127   const Property::Map* map = property.GetMap();
128   if( map )
129   {
130     const Property::Value* value = map->Find( field );
131     if( value )
132     {
133       value->Get( retval );
134     }
135   }
136
137   return retval;
138 }
139
140 Dali::Shader::Hint::Value ConvertHints( Dali::ShaderEffect::GeometryHints hints)
141 {
142   int convertedHints = Dali::Shader::Hint::NONE;
143
144   if( hints & Dali::ShaderEffect::HINT_BLENDING )
145   {
146     convertedHints |= Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT;
147   }
148   if( !(hints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY) )
149   {
150     convertedHints |= Dali::Shader::Hint::MODIFIES_GEOMETRY;
151   }
152
153   return Dali::Shader::Hint::Value( convertedHints );
154 }
155
156 } // unnamed namespace
157
158 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
159 {
160   ShaderEffectPtr shaderEffect( new ShaderEffect( hints ) );
161   shaderEffect->RegisterObject();
162   return shaderEffect;
163 }
164
165 ShaderEffect::ShaderEffect( Dali::ShaderEffect::GeometryHints hints )
166 : mGridDensity( Dali::ShaderEffect::DEFAULT_GRID_DENSITY ),
167   mGeometryHints( hints )
168 {
169 }
170
171 ShaderEffect::~ShaderEffect()
172 {
173   // Guard to allow handle destruction after Core has been destroyed
174   UnregisterObject();
175 }
176
177 void ShaderEffect::SetEffectImage( Dali::Image image )
178 {
179   // if images are the same, do nothing
180   if ( mEffectImage == image )
181   {
182     return;
183   }
184
185   if ( mEffectImage && mConnectedActors.size() > 0 )
186   {
187     // unset previous image
188     GetImplementation( mEffectImage ).Disconnect();
189   }
190
191   // in case image is empty this will reset our image handle
192   mEffectImage = image;
193
194   if( image )
195   {
196     // tell image that we're using it
197     if (mConnectedActors.size() > 0)
198     {
199       GetImplementation( mEffectImage ).Connect();
200     }
201   }
202
203   //inform connected actors the image has been unset
204   for(std::vector< ActorPtr >::iterator it = mConnectedActors.begin(); it != mConnectedActors.end(); ++it )
205   {
206     ImageActor* imageActor = dynamic_cast< ImageActor* >( it->Get() );
207     if( imageActor )
208     {
209       imageActor->EffectImageUpdated();
210     }
211   }
212 }
213
214 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
215 {
216   // Register the property if it does not exist
217   mShader->RegisterProperty( name, value );
218 }
219
220 void ShaderEffect::SetPrograms( const string& vertexSource, const string& fragmentSource )
221 {
222   SetPrograms( "", "", vertexSource, fragmentSource );
223 }
224
225 void ShaderEffect::SetPrograms( const std::string& vertexPrefix, const std::string& fragmentPrefix,
226                                 const std::string& vertexSource, const std::string& fragmentSource )
227 {
228   mShader = Shader::New( WrapVertexShader( vertexPrefix, vertexSource ),
229                          WrapFragmentShader( fragmentPrefix, fragmentSource ),
230                          ConvertHints( mGeometryHints ) );
231 }
232
233 Vector2 ShaderEffect::GetGridSize( const Vector2& size )
234 {
235   Vector2 gridSize( 1.f, 1.f );
236
237   if( mGridDensity > 0 )
238   {
239     if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_X ) )
240     {
241       gridSize.x = ceil( size.width / mGridDensity );
242     }
243     if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_Y ) )
244     {
245       gridSize.y = ceil( size.height / mGridDensity );
246     }
247   }
248
249   return gridSize;
250 }
251
252 void ShaderEffect::Connect( ActorPtr actor )
253 {
254   if( !actor )
255   {
256     return;
257   }
258
259   std::vector< ActorPtr >::const_iterator it = std::find( mConnectedActors.begin(), mConnectedActors.end(), actor );
260   if( it == mConnectedActors.end() )
261   {
262     mConnectedActors.push_back( actor );
263   }
264
265   if( mEffectImage && mConnectedActors.size() == 1 )
266   {
267     GetImplementation( mEffectImage ).Connect();
268   }
269 }
270 void ShaderEffect::Disconnect( ActorPtr actor )
271 {
272   if( !actor )
273   {
274     return;
275   }
276
277   DALI_ASSERT_DEBUG(mConnectedActors.size() > 0);
278   std::vector< ActorPtr >::iterator match( std::remove( mConnectedActors.begin(), mConnectedActors.end(), actor ) );
279   mConnectedActors.erase( match, mConnectedActors.end() );
280
281   if (mEffectImage && mConnectedActors.size() == 0)
282   {
283      GetImplementation(mEffectImage).Disconnect();
284   }
285 }
286
287 unsigned int ShaderEffect::GetPropertyCount() const
288 {
289   return GetDefaultPropertyCount() + mShader->GetPropertyCount();
290 }
291
292 std::string ShaderEffect::GetPropertyName( Property::Index index ) const
293 {
294   if ( index < DEFAULT_PROPERTY_COUNT )
295   {
296     return GetDefaultPropertyName( index );
297   }
298   else
299   {
300     return mShader->GetPropertyName( index );
301   }
302 }
303
304 Property::Index ShaderEffect::GetPropertyIndex( const std::string& name ) const
305 {
306   Property::Index index = GetDefaultPropertyIndex( name );
307   if( index == Property::INVALID_INDEX )
308   {
309     return mShader->GetPropertyIndex( name );
310   }
311   else
312   {
313     return index;
314   }
315 }
316
317 bool ShaderEffect::IsPropertyWritable( Property::Index index ) const
318 {
319   if ( index < DEFAULT_PROPERTY_COUNT )
320   {
321     return IsDefaultPropertyWritable( index );
322   }
323   else
324   {
325     return mShader->IsPropertyWritable( index );
326   }
327 }
328
329 bool ShaderEffect::IsPropertyAnimatable( Property::Index index ) const
330 {
331   if ( index < DEFAULT_PROPERTY_COUNT )
332   {
333     return IsDefaultPropertyAnimatable( index );
334   }
335   else
336   {
337     return mShader->IsPropertyAnimatable( index );
338   }
339 }
340
341 bool ShaderEffect::IsPropertyAConstraintInput( Property::Index index ) const
342 {
343   if ( index < DEFAULT_PROPERTY_COUNT )
344   {
345     return IsDefaultPropertyAConstraintInput( index );
346   }
347   else
348   {
349     return mShader->IsPropertyAConstraintInput( index );
350   }
351 }
352
353 Property::Type ShaderEffect::GetPropertyType( Property::Index index ) const
354 {
355   if ( index < DEFAULT_PROPERTY_COUNT )
356   {
357     return GetDefaultPropertyType( index );
358   }
359   return mShader->GetPropertyType( index );
360 }
361
362 void ShaderEffect::SetProperty( Property::Index index, const Property::Value& propertyValue )
363 {
364   if ( index < DEFAULT_PROPERTY_COUNT )
365   {
366     SetDefaultProperty( index, propertyValue );
367   }
368   else
369   {
370     mShader->SetProperty( index, propertyValue );
371   }
372 }
373
374 Property::Value ShaderEffect::GetProperty( Property::Index index ) const
375 {
376   if ( index < DEFAULT_PROPERTY_COUNT )
377   {
378     return GetDefaultProperty( index );
379   }
380   return mShader->GetProperty( index );
381 }
382
383 void ShaderEffect::GetPropertyIndices( Property::IndexContainer& indices ) const
384 {
385   mShader->GetPropertyIndices( indices );
386   GetDefaultPropertyIndices( indices );
387 }
388
389 Property::Index ShaderEffect::RegisterProperty( const std::string& name, const Property::Value& propertyValue )
390 {
391   return mShader->RegisterProperty( name, propertyValue );
392 }
393
394 Property::Index ShaderEffect::RegisterProperty( const std::string& name, const Property::Value& propertyValue, Property::AccessMode accessMode )
395 {
396   return mShader->RegisterProperty( name, propertyValue, accessMode );
397 }
398
399 Dali::PropertyNotification ShaderEffect::AddPropertyNotification( Property::Index index,
400                                                                   int componentIndex,
401                                                                   const Dali::PropertyCondition& condition )
402 {
403   return mShader->AddPropertyNotification( index, componentIndex, condition );
404 }
405
406 void ShaderEffect::RemovePropertyNotification( Dali::PropertyNotification propertyNotification )
407 {
408   mShader->RemovePropertyNotification( propertyNotification );
409 }
410
411 void ShaderEffect::RemovePropertyNotifications()
412 {
413   mShader->RemovePropertyNotifications();
414 }
415
416 unsigned int ShaderEffect::GetDefaultPropertyCount() const
417 {
418   return DEFAULT_PROPERTY_COUNT;
419 }
420
421 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
422 {
423   indices.Reserve( indices.Size() + DEFAULT_PROPERTY_COUNT );
424
425   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
426   {
427     indices.PushBack( i );
428   }
429 }
430
431 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
432 {
433   if( index < DEFAULT_PROPERTY_COUNT )
434   {
435     return DEFAULT_PROPERTY_DETAILS[index].name;
436   }
437
438   return NULL;
439 }
440
441 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
442 {
443   Property::Index index = Property::INVALID_INDEX;
444
445   // Look for name in default properties
446   for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
447   {
448     const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
449     if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
450     {
451       index = i;
452       break;
453     }
454   }
455
456   return index;
457 }
458
459 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
460 {
461   return DEFAULT_PROPERTY_DETAILS[ index ].writable;
462 }
463
464 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
465 {
466   return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
467 }
468
469 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
470 {
471   return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
472 }
473
474 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
475 {
476   if( index < DEFAULT_PROPERTY_COUNT )
477   {
478     return DEFAULT_PROPERTY_DETAILS[index].type;
479   }
480
481   // index out of range...return Property::NONE
482   return Property::NONE;
483 }
484
485 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
486 {
487   switch ( index )
488   {
489     case Dali::ShaderEffect::Property::GRID_DENSITY:
490     {
491       propertyValue.Get( mGridDensity );
492       if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_X ) ||
493           ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_Y ) )
494       {
495         //inform all the connected actors
496         for(std::vector< ActorPtr >::iterator it = mConnectedActors.begin(); it != mConnectedActors.end(); ++it )
497         {
498           (*it)->RelayoutRequest();
499         }
500       }
501       break;
502     }
503
504     case Dali::ShaderEffect::Property::IMAGE:
505     {
506       Dali::Image img(Scripting::NewImage( propertyValue ));
507       if(img)
508       {
509         SetEffectImage( img );
510       }
511       else
512       {
513         DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
514       }
515       break;
516     }
517
518     case Dali::ShaderEffect::Property::PROGRAM:
519     {
520       std::string vertexPrefix   = GetStringProperty("vertexPrefix",  propertyValue);
521       std::string fragmentPrefix = GetStringProperty("fragmentPrefix",  propertyValue);
522       std::string vertex         = GetStringProperty("vertex", propertyValue);
523       std::string fragment       = GetStringProperty("fragment", propertyValue);
524
525       SetPrograms( vertexPrefix, fragmentPrefix, vertex, fragment );
526       break;
527     }
528
529     case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
530     {
531       mGeometryHints = Dali::ShaderEffect::HINT_NONE;
532       std::string s = propertyValue.Get<std::string>();
533       if(s == "HINT_NONE")
534       {
535         mGeometryHints = Dali::ShaderEffect::HINT_NONE;
536       }
537       else if(s == "HINT_GRID_X")
538       {
539         mGeometryHints = Dali::ShaderEffect::HINT_GRID_X;
540       }
541       else if(s == "HINT_GRID_Y")
542       {
543         mGeometryHints = Dali::ShaderEffect::HINT_GRID_Y;
544       }
545       else if(s == "HINT_GRID")
546       {
547         mGeometryHints = Dali::ShaderEffect::HINT_GRID;
548       }
549       else if(s == "HINT_DEPTH_BUFFER")
550       {
551         mGeometryHints = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
552       }
553       else if(s == "HINT_BLENDING")
554       {
555         mGeometryHints = Dali::ShaderEffect::HINT_BLENDING;
556       }
557       else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
558       {
559         mGeometryHints = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
560       }
561       else
562       {
563         DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
564       }
565       break;
566     }
567
568     default:
569     {
570       // nothing to do
571       break;
572     }
573   }
574 }
575
576 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
577 {
578   // none of our properties are readable so return empty
579   return Property::Value();
580 }
581
582 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
583 {
584   return mShader->GetSceneObject();
585 }
586
587 const SceneGraph::PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
588 {
589   return mShader->GetSceneObjectAnimatableProperty( index );
590 }
591
592 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
593 {
594   return mShader->GetSceneObjectInputProperty( index );
595 }
596
597 int ShaderEffect::GetPropertyComponentIndex( Property::Index index ) const
598 {
599   return mShader->GetPropertyComponentIndex( index );
600 }
601
602 } // namespace Internal
603
604 } // namespace Dali