Implemented custom shader in ImageRenderer and changed Dissolve-effect to utilise...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / renderers / image / image-renderer.cpp
1 /*
2  * Copyright (c) 2015 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 "image-renderer.h"
20
21 // EXTERNAL HEADER
22 #include <dali/public-api/images/resource-image.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL HEADER
26 #include <dali-toolkit/internal/controls/renderers/renderer-factory-impl.h>
27 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
28 #include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
29 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
30
31 namespace Dali
32 {
33
34 namespace Toolkit
35 {
36
37 namespace Internal
38 {
39
40 namespace
41 {
42 const char * const RENDERER_TYPE("renderer-type");
43 const char * const RENDERER_TYPE_VALUE("image-renderer");
44
45 // property names
46 const char * const IMAGE_URL_NAME( "image-url" );
47 const char * const IMAGE_FITTING_MODE( "image-fitting-mode" );
48 const char * const IMAGE_SAMPLING_MODE( "image-sampling-mode" );
49 const char * const IMAGE_DESIRED_WIDTH( "image-desired-width" );
50 const char * const IMAGE_DESIRED_HEIGHT( "image-desired-height" );
51
52 // fitting modes
53 const char * const SHRINK_TO_FIT("shrink-to-fit");
54 const char * const SCALE_TO_FILL("scale-to-fill");
55 const char * const FIT_WIDTH("fit-width");
56 const char * const FIT_HEIGHT("fit-height");
57 const char * const DEFAULT("default");
58
59 // sampling modes
60 const char * const BOX("box");
61 const char * const NEAREST("nearest");
62 const char * const LINEAR("linear");
63 const char * const BOX_THEN_NEAREST("box-then-nearest");
64 const char * const BOX_THEN_LINEAR("box-then-linear");
65 const char * const NO_FILTER("no-filter");
66 const char * const DONT_CARE("dont-care");
67
68 std::string TEXTURE_UNIFORM_NAME = "sTexture";
69
70 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
71   attribute mediump vec2 aPosition;\n
72   varying mediump vec2 vTexCoord;\n
73   uniform mediump mat4 uMvpMatrix;\n
74   uniform mediump vec3 uSize;\n
75   \n
76   void main()\n
77   {\n
78     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
79     vertexPosition.xyz *= uSize;\n
80     vertexPosition = uMvpMatrix * vertexPosition;\n
81     \n
82     vTexCoord = aPosition + vec2(0.5);\n
83     gl_Position = vertexPosition;\n
84   }\n
85 );
86
87 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
88   varying mediump vec2 vTexCoord;\n
89   uniform sampler2D sTexture;\n
90   uniform lowp vec4 uColor;\n
91   \n
92   void main()\n
93   {\n
94     gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
95   }\n
96 );
97
98 void AddQuadIndices( Vector< unsigned int >& indices, unsigned int rowIdx, unsigned int nextRowIdx )
99 {
100   indices.PushBack( rowIdx );
101   indices.PushBack( nextRowIdx + 1 );
102   indices.PushBack( rowIdx + 1 );
103
104   indices.PushBack( rowIdx );
105   indices.PushBack( nextRowIdx );
106   indices.PushBack( nextRowIdx + 1 );
107 }
108
109 Geometry GenerateGeometry( const Vector< Vector2 >& vertices, const Vector< unsigned int >& indices )
110 {
111   Property::Map vertexFormat;
112   vertexFormat[ "aPosition" ] = Property::VECTOR2;
113   PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat, vertices.Size() );
114   if( vertices.Size() > 0 )
115   {
116     vertexPropertyBuffer.SetData( &vertices[ 0 ] );
117   }
118
119   Property::Map indexFormat;
120   indexFormat[ "indices" ] = Property::INTEGER;
121   PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat, indices.Size() );
122   if( indices.Size() > 0 )
123   {
124     indexPropertyBuffer.SetData( &indices[ 0 ] );
125   }
126
127   // Create the geometry object
128   Geometry geometry = Geometry::New();
129   geometry.AddVertexBuffer( vertexPropertyBuffer );
130   geometry.SetIndexBuffer( indexPropertyBuffer );
131
132   return geometry;
133 }
134
135 Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gridSize )
136 {
137   Geometry geometry;
138
139   if( gridSize == ImageDimensions( 1, 1 ) )
140   {
141     geometry = factoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
142     if( !geometry )
143     {
144       geometry =  factoryCache.CreateQuadGeometry();
145       factoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
146     }
147   }
148   else
149   {
150     uint16_t gridWidth = gridSize.GetWidth();
151     uint16_t gridHeight = gridSize.GetHeight();
152
153     // Create vertices
154     Vector< Vector2 > vertices;
155     vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
156
157     for( int y = 0; y < gridHeight + 1; ++y )
158     {
159       for( int x = 0; x < gridWidth + 1; ++x )
160       {
161         vertices.PushBack( Vector2( (float)x/gridWidth - 0.5f, (float)y/gridHeight  - 0.5f) );
162       }
163     }
164
165     // Create indices
166     Vector< unsigned int > indices;
167     indices.Reserve( gridWidth * gridHeight * 6 );
168
169     unsigned int rowIdx     = 0;
170     unsigned int nextRowIdx = gridWidth + 1;
171     for( int y = 0; y < gridHeight; ++y, ++nextRowIdx, ++rowIdx )
172     {
173       for( int x = 0; x < gridWidth; ++x, ++nextRowIdx, ++rowIdx )
174       {
175         AddQuadIndices( indices, rowIdx, nextRowIdx );
176       }
177     }
178
179     return GenerateGeometry( vertices, indices );
180   }
181
182   return geometry;
183 }
184
185 } //unnamed namespace
186
187 ImageRenderer::ImageRenderer()
188 : ControlRenderer(),
189   mDesiredSize(),
190   mFittingMode( FittingMode::DEFAULT ),
191   mSamplingMode( SamplingMode::DEFAULT )
192 {
193 }
194
195 ImageRenderer::~ImageRenderer()
196 {
197 }
198
199 void ImageRenderer::DoInitialize( RendererFactoryCache& factoryCache, const Property::Map& propertyMap )
200 {
201   Initialize(factoryCache);
202
203   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
204   if( imageURLValue )
205   {
206     imageURLValue->Get( mImageUrl );
207     if( !mImageUrl.empty() )
208     {
209       mImage.Reset();
210     }
211
212     Property::Value* fittingValue = propertyMap.Find( IMAGE_FITTING_MODE );
213     if( fittingValue )
214     {
215       std::string fitting;
216       fittingValue->Get( fitting );
217
218       mFittingMode = FittingMode::DEFAULT;
219       if( fitting == SHRINK_TO_FIT )
220       {
221         mFittingMode = FittingMode::SHRINK_TO_FIT;
222       }
223       else if( fitting == SCALE_TO_FILL )
224       {
225         mFittingMode = FittingMode::SCALE_TO_FILL;
226       }
227       else if( fitting == FIT_WIDTH )
228       {
229         mFittingMode = FittingMode::FIT_WIDTH;
230       }
231       else if( fitting == FIT_HEIGHT )
232       {
233         mFittingMode = FittingMode::FIT_HEIGHT;
234       }
235       else if( fitting == DEFAULT )
236       {
237         mFittingMode = FittingMode::DEFAULT;
238       }
239       else
240       {
241         DALI_ASSERT_ALWAYS("Unknown fitting mode");
242       }
243     }
244
245     Property::Value* samplingValue = propertyMap.Find( IMAGE_SAMPLING_MODE );
246     if( samplingValue )
247     {
248       std::string sampling;
249       samplingValue->Get( sampling );
250
251       mSamplingMode = SamplingMode::DEFAULT;
252       if( sampling == BOX )
253       {
254         mSamplingMode = SamplingMode::BOX;
255       }
256       else if( sampling == NEAREST )
257       {
258         mSamplingMode = SamplingMode::NEAREST;
259       }
260       else if( sampling == LINEAR )
261       {
262         mSamplingMode = SamplingMode::LINEAR;
263       }
264       else if( sampling == BOX_THEN_NEAREST )
265       {
266         mSamplingMode = SamplingMode::BOX_THEN_NEAREST;
267       }
268       else if( sampling == BOX_THEN_LINEAR )
269       {
270         mSamplingMode = SamplingMode::BOX_THEN_LINEAR;
271       }
272       else if( sampling == NO_FILTER )
273       {
274         mSamplingMode = SamplingMode::NO_FILTER;
275       }
276       else if( sampling == DONT_CARE )
277       {
278         mSamplingMode = SamplingMode::DONT_CARE;
279       }
280       else if( sampling == DEFAULT )
281       {
282         mSamplingMode = SamplingMode::DEFAULT;
283       }
284       else
285       {
286         DALI_ASSERT_ALWAYS("Unknown sampling mode");
287       }
288     }
289
290     int desiredWidth = 0;
291     Property::Value* desiredWidthValue = propertyMap.Find( IMAGE_DESIRED_WIDTH );
292     if( desiredWidthValue )
293     {
294       desiredWidthValue->Get( desiredWidth );
295     }
296
297     int desiredHeight = 0;
298     Property::Value* desiredHeightValue = propertyMap.Find( IMAGE_DESIRED_HEIGHT );
299     if( desiredHeightValue )
300     {
301       desiredHeightValue->Get( desiredHeight );
302     }
303
304     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
305   }
306 }
307
308 void ImageRenderer::SetSize( const Vector2& size )
309 {
310   ControlRenderer::SetSize( size );
311 }
312
313 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
314 {
315   if(mImage)
316   {
317     naturalSize.x = mImage.GetWidth();
318     naturalSize.y = mImage.GetHeight();
319     return;
320   }
321   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
322   {
323     naturalSize.x = mDesiredSize.GetWidth();
324     naturalSize.y = mDesiredSize.GetHeight();
325     return;
326   }
327   else if( !mImageUrl.empty() )
328   {
329     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
330     naturalSize.x = dimentions.GetWidth();
331     naturalSize.y = dimentions.GetHeight();
332     return;
333   }
334
335   naturalSize = Vector2::ZERO;
336 }
337
338 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
339 {
340   ControlRenderer::SetClipRect( clipRect );
341 }
342
343 void ImageRenderer::SetOffset( const Vector2& offset )
344 {
345 }
346
347 void ImageRenderer::DoSetOnStage( Actor& actor )
348 {
349   if( !mImageUrl.empty() && !mImage )
350   {
351     mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
352   }
353
354   ApplyImageToSampler();
355 }
356
357 void ImageRenderer::DoSetOffStage( Actor& actor )
358 {
359   //If we own the image then make sure we release it when we go off stage
360   if( !mImageUrl.empty() )
361   {
362     mImage.Reset();
363   }
364 }
365
366 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
367 {
368   map.Clear();
369   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
370   if( !mImageUrl.empty() )
371   {
372     map.Insert( IMAGE_URL_NAME, mImageUrl );
373     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
374     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
375   }
376   else if( mImage )
377   {
378     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
379     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
380
381     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
382     if( resourceImage )
383     {
384       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
385     }
386   }
387
388   switch( mFittingMode )
389   {
390     case Dali::FittingMode::FIT_HEIGHT:
391     {
392       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
393       break;
394     }
395     case Dali::FittingMode::FIT_WIDTH:
396     {
397       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
398       break;
399     }
400     case Dali::FittingMode::SCALE_TO_FILL:
401     {
402       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
403       break;
404     }
405     case Dali::FittingMode::SHRINK_TO_FIT:
406     {
407       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
408       break;
409     }
410     default:
411     {
412       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
413       break;
414     }
415   }
416
417   switch( mSamplingMode )
418   {
419     case Dali::SamplingMode::BOX:
420     {
421       map.Insert( IMAGE_SAMPLING_MODE, BOX );
422       break;
423     }
424     case Dali::SamplingMode::NEAREST:
425     {
426       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
427       break;
428     }
429     case Dali::SamplingMode::LINEAR:
430     {
431       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
432       break;
433     }
434     case Dali::SamplingMode::BOX_THEN_LINEAR:
435     {
436       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
437       break;
438     }
439     case Dali::SamplingMode::BOX_THEN_NEAREST:
440     {
441       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
442       break;
443     }
444     case Dali::SamplingMode::NO_FILTER:
445     {
446       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
447       break;
448     }
449     case Dali::SamplingMode::DONT_CARE:
450     {
451       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
452       break;
453     }
454     default:
455     {
456       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
457       break;
458     }
459   }
460 }
461
462 void ImageRenderer::Initialize( RendererFactoryCache& factoryCache )
463 {
464   if( !mImpl->mCustomShader )
465   {
466     mImpl->mGeometry = CreateGeometry( factoryCache, ImageDimensions( 1, 1 ) );
467
468     mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
469
470     if( !mImpl->mShader )
471     {
472       mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
473       factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, mImpl->mShader );
474     }
475   }
476   else
477   {
478     mImpl->mGeometry = CreateGeometry( factoryCache, mImpl->mCustomShader->mGridSize );
479
480     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
481     {
482       mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
483
484       if( !mImpl->mShader )
485       {
486         mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
487         factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, mImpl->mShader );
488       }
489     }
490     else
491     {
492       mImpl->mShader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
493                                     mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
494                                     mImpl->mCustomShader->mHints );
495     }
496   }
497
498   if( mImpl->mRenderer )
499   {
500     mImpl->mRenderer.SetGeometry( mImpl->mGeometry );
501     Material material = mImpl->mRenderer.GetMaterial();
502     if( material )
503     {
504       material.SetShader( mImpl->mShader );
505     }
506   }
507 }
508
509 void ImageRenderer::SetImage( const std::string& imageUrl )
510 {
511   SetImage( imageUrl, 0, 0, Dali::FittingMode::DEFAULT, Dali::SamplingMode::DEFAULT );
512 }
513
514 void ImageRenderer::SetImage( const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
515 {
516   if( mImageUrl != imageUrl )
517   {
518     mImageUrl = imageUrl;
519     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
520     mFittingMode = fittingMode;
521     mSamplingMode = samplingMode;
522
523     if( !mImageUrl.empty() && mImpl->mIsOnStage )
524     {
525       mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
526       ApplyImageToSampler();
527     }
528     else
529     {
530       mImage.Reset();
531     }
532   }
533 }
534
535 void ImageRenderer::SetImage( Image image )
536 {
537   if( mImage != image )
538   {
539     mImageUrl.clear();
540     mDesiredSize = ImageDimensions();
541     mFittingMode = FittingMode::DEFAULT;
542     mSamplingMode = SamplingMode::DEFAULT;
543     mImage = image;
544
545     if( mImage && mImpl->mIsOnStage )
546     {
547       ApplyImageToSampler();
548     }
549   }
550 }
551
552 Image ImageRenderer::GetImage() const
553 {
554   return mImage;
555 }
556
557 void ImageRenderer::ApplyImageToSampler()
558 {
559   if( mImage )
560   {
561     Material material = mImpl->mRenderer.GetMaterial();
562     if( material )
563     {
564       for( std::size_t i = 0; i < material.GetNumberOfSamplers(); ++i )
565       {
566         Sampler sampler = material.GetSamplerAt( i );
567         if( sampler.GetUniformName() == TEXTURE_UNIFORM_NAME )
568         {
569           sampler.SetImage( mImage );
570           return;
571         }
572       }
573
574       Sampler sampler = Sampler::New( mImage, TEXTURE_UNIFORM_NAME );
575       material.AddSampler( sampler );
576     }
577   }
578 }
579
580 } // namespace Internal
581
582 } // namespace Toolkit
583
584 } // namespace Dali