Merge "Changed Motion Blur and MotionStretch Effect to use new custom shaders." into...
[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 Geometry GenerateGeometry( const Vector< Vector2 >& vertices, const Vector< unsigned int >& indices )
99 {
100   Property::Map vertexFormat;
101   vertexFormat[ "aPosition" ] = Property::VECTOR2;
102   PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat, vertices.Size() );
103   if( vertices.Size() > 0 )
104   {
105     vertexPropertyBuffer.SetData( &vertices[ 0 ] );
106   }
107
108   Property::Map indexFormat;
109   indexFormat[ "indices" ] = Property::INTEGER;
110   PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat, indices.Size() );
111   if( indices.Size() > 0 )
112   {
113     indexPropertyBuffer.SetData( &indices[ 0 ] );
114   }
115
116   // Create the geometry object
117   Geometry geometry = Geometry::New();
118   geometry.AddVertexBuffer( vertexPropertyBuffer );
119   geometry.SetIndexBuffer( indexPropertyBuffer );
120   geometry.SetGeometryType( Geometry::TRIANGLE_STRIP );
121
122   return geometry;
123 }
124
125 Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gridSize )
126 {
127   Geometry geometry;
128
129   if( gridSize == ImageDimensions( 1, 1 ) )
130   {
131     geometry = factoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
132     if( !geometry )
133     {
134       geometry =  factoryCache.CreateQuadGeometry();
135       factoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
136     }
137   }
138   else
139   {
140     uint16_t gridWidth = gridSize.GetWidth();
141     uint16_t gridHeight = gridSize.GetHeight();
142
143     // Create vertices
144     Vector< Vector2 > vertices;
145     vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
146
147     for( int y = 0; y < gridHeight + 1; ++y )
148     {
149       for( int x = 0; x < gridWidth + 1; ++x )
150       {
151         vertices.PushBack( Vector2( (float)x/gridWidth - 0.5f, (float)y/gridHeight  - 0.5f) );
152       }
153     }
154
155     // Create indices
156     Vector< unsigned int > indices;
157     indices.Reserve( (gridWidth+2)*gridHeight*2 - 2);
158
159     for( unsigned int row = 0u; row < gridHeight; ++row )
160     {
161       unsigned int rowStartIndex = row*(gridWidth+1u);
162       unsigned int nextRowStartIndex = rowStartIndex + gridWidth +1u;
163
164       if( row != 0u ) // degenerate index on non-first row
165       {
166         indices.PushBack( rowStartIndex );
167       }
168
169       for( unsigned int column = 0u; column < gridWidth+1u; column++) // main strip
170       {
171         indices.PushBack( rowStartIndex + column);
172         indices.PushBack( nextRowStartIndex + column);
173       }
174
175       if( row != gridHeight-1u ) // degenerate index on non-last row
176       {
177         indices.PushBack( nextRowStartIndex + gridWidth );
178       }
179     }
180
181     return GenerateGeometry( vertices, indices );
182   }
183
184   return geometry;
185 }
186
187 } //unnamed namespace
188
189 ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache )
190 : ControlRenderer( factoryCache ),
191   mDesiredSize(),
192   mFittingMode( FittingMode::DEFAULT ),
193   mSamplingMode( SamplingMode::DEFAULT )
194 {
195 }
196
197 ImageRenderer::~ImageRenderer()
198 {
199 }
200
201 void ImageRenderer::DoInitialize( const Property::Map& propertyMap )
202 {
203   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
204   if( imageURLValue )
205   {
206     imageURLValue->Get( mImageUrl );
207     if( !mImageUrl.empty() )
208     {
209       SetCachedRendererKey( mImageUrl );
210       mImage.Reset();
211     }
212
213     Property::Value* fittingValue = propertyMap.Find( IMAGE_FITTING_MODE );
214     if( fittingValue )
215     {
216       std::string fitting;
217       fittingValue->Get( fitting );
218
219       mFittingMode = FittingMode::DEFAULT;
220       if( fitting == SHRINK_TO_FIT )
221       {
222         mFittingMode = FittingMode::SHRINK_TO_FIT;
223       }
224       else if( fitting == SCALE_TO_FILL )
225       {
226         mFittingMode = FittingMode::SCALE_TO_FILL;
227       }
228       else if( fitting == FIT_WIDTH )
229       {
230         mFittingMode = FittingMode::FIT_WIDTH;
231       }
232       else if( fitting == FIT_HEIGHT )
233       {
234         mFittingMode = FittingMode::FIT_HEIGHT;
235       }
236       else if( fitting == DEFAULT )
237       {
238         mFittingMode = FittingMode::DEFAULT;
239       }
240       else
241       {
242         DALI_ASSERT_ALWAYS("Unknown fitting mode");
243       }
244     }
245
246     Property::Value* samplingValue = propertyMap.Find( IMAGE_SAMPLING_MODE );
247     if( samplingValue )
248     {
249       std::string sampling;
250       samplingValue->Get( sampling );
251
252       mSamplingMode = SamplingMode::DEFAULT;
253       if( sampling == BOX )
254       {
255         mSamplingMode = SamplingMode::BOX;
256       }
257       else if( sampling == NEAREST )
258       {
259         mSamplingMode = SamplingMode::NEAREST;
260       }
261       else if( sampling == LINEAR )
262       {
263         mSamplingMode = SamplingMode::LINEAR;
264       }
265       else if( sampling == BOX_THEN_NEAREST )
266       {
267         mSamplingMode = SamplingMode::BOX_THEN_NEAREST;
268       }
269       else if( sampling == BOX_THEN_LINEAR )
270       {
271         mSamplingMode = SamplingMode::BOX_THEN_LINEAR;
272       }
273       else if( sampling == NO_FILTER )
274       {
275         mSamplingMode = SamplingMode::NO_FILTER;
276       }
277       else if( sampling == DONT_CARE )
278       {
279         mSamplingMode = SamplingMode::DONT_CARE;
280       }
281       else if( sampling == DEFAULT )
282       {
283         mSamplingMode = SamplingMode::DEFAULT;
284       }
285       else
286       {
287         DALI_ASSERT_ALWAYS("Unknown sampling mode");
288       }
289     }
290
291     int desiredWidth = 0;
292     Property::Value* desiredWidthValue = propertyMap.Find( IMAGE_DESIRED_WIDTH );
293     if( desiredWidthValue )
294     {
295       desiredWidthValue->Get( desiredWidth );
296     }
297
298     int desiredHeight = 0;
299     Property::Value* desiredHeightValue = propertyMap.Find( IMAGE_DESIRED_HEIGHT );
300     if( desiredHeightValue )
301     {
302       desiredHeightValue->Get( desiredHeight );
303     }
304
305     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
306   }
307 }
308
309 void ImageRenderer::SetSize( const Vector2& size )
310 {
311   ControlRenderer::SetSize( size );
312 }
313
314 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
315 {
316   if(mImage)
317   {
318     naturalSize.x = mImage.GetWidth();
319     naturalSize.y = mImage.GetHeight();
320     return;
321   }
322   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
323   {
324     naturalSize.x = mDesiredSize.GetWidth();
325     naturalSize.y = mDesiredSize.GetHeight();
326     return;
327   }
328   else if( !mImageUrl.empty() )
329   {
330     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
331     naturalSize.x = dimentions.GetWidth();
332     naturalSize.y = dimentions.GetHeight();
333     return;
334   }
335
336   naturalSize = Vector2::ZERO;
337 }
338
339 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
340 {
341   ControlRenderer::SetClipRect( clipRect );
342 }
343
344 void ImageRenderer::SetOffset( const Vector2& offset )
345 {
346 }
347
348 void ImageRenderer::InitializeRenderer( Renderer& renderer )
349 {
350   Geometry geometry;
351   Shader shader;
352   if( !mImpl->mCustomShader )
353   {
354     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
355
356     shader = mFactoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
357     if( !shader )
358     {
359       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
360       mFactoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
361     }
362   }
363   else
364   {
365     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
366
367     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
368     {
369       shader = mFactoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
370       if( !shader )
371       {
372         shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
373         mFactoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
374       }
375     }
376     else
377     {
378       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
379                              mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
380                              mImpl->mCustomShader->mHints );
381     }
382   }
383
384   if( !renderer )
385   {
386     Material material = Material::New( shader );
387     renderer = Renderer::New( geometry, material );
388   }
389   else
390   {
391     renderer.SetGeometry( geometry );
392     Material material = renderer.GetMaterial();
393     if( material )
394     {
395       material.SetShader( shader );
396     }
397   }
398 }
399
400 void ImageRenderer::DoSetOnStage( Actor& actor )
401 {
402   if( !mImageUrl.empty() && !mImage )
403   {
404     Dali::ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
405     resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
406
407     mImage = resourceImage;
408   }
409
410   ApplyImageToSampler();
411 }
412
413 void ImageRenderer::DoSetOffStage( Actor& actor )
414 {
415   //If we own the image then make sure we release it when we go off stage
416   if( !mImageUrl.empty() )
417   {
418     mImage.Reset();
419   }
420 }
421
422 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
423 {
424   map.Clear();
425   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
426   if( !mImageUrl.empty() )
427   {
428     map.Insert( IMAGE_URL_NAME, mImageUrl );
429     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
430     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
431   }
432   else if( mImage )
433   {
434     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
435     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
436
437     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
438     if( resourceImage )
439     {
440       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
441     }
442   }
443
444   switch( mFittingMode )
445   {
446     case Dali::FittingMode::FIT_HEIGHT:
447     {
448       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
449       break;
450     }
451     case Dali::FittingMode::FIT_WIDTH:
452     {
453       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
454       break;
455     }
456     case Dali::FittingMode::SCALE_TO_FILL:
457     {
458       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
459       break;
460     }
461     case Dali::FittingMode::SHRINK_TO_FIT:
462     {
463       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
464       break;
465     }
466     default:
467     {
468       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
469       break;
470     }
471   }
472
473   switch( mSamplingMode )
474   {
475     case Dali::SamplingMode::BOX:
476     {
477       map.Insert( IMAGE_SAMPLING_MODE, BOX );
478       break;
479     }
480     case Dali::SamplingMode::NEAREST:
481     {
482       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
483       break;
484     }
485     case Dali::SamplingMode::LINEAR:
486     {
487       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
488       break;
489     }
490     case Dali::SamplingMode::BOX_THEN_LINEAR:
491     {
492       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
493       break;
494     }
495     case Dali::SamplingMode::BOX_THEN_NEAREST:
496     {
497       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
498       break;
499     }
500     case Dali::SamplingMode::NO_FILTER:
501     {
502       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
503       break;
504     }
505     case Dali::SamplingMode::DONT_CARE:
506     {
507       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
508       break;
509     }
510     default:
511     {
512       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
513       break;
514     }
515   }
516 }
517
518 void ImageRenderer::SetImage( const std::string& imageUrl )
519 {
520   SetImage( imageUrl, 0, 0, Dali::FittingMode::DEFAULT, Dali::SamplingMode::DEFAULT );
521 }
522
523 void ImageRenderer::SetImage( const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
524 {
525   if( mImageUrl != imageUrl )
526   {
527     mImageUrl = imageUrl;
528     SetCachedRendererKey( mImageUrl );
529     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
530     mFittingMode = fittingMode;
531     mSamplingMode = samplingMode;
532
533     if( !mImageUrl.empty() && mImpl->mIsOnStage )
534     {
535       Dali::ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
536       resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
537       mImage = resourceImage;
538
539       ApplyImageToSampler();
540     }
541     else
542     {
543       mImage.Reset();
544     }
545   }
546 }
547
548 void ImageRenderer::SetImage( Image image )
549 {
550   if( mImage != image )
551   {
552     mImageUrl.clear();
553     mDesiredSize = ImageDimensions();
554     mFittingMode = FittingMode::DEFAULT;
555     mSamplingMode = SamplingMode::DEFAULT;
556     mImage = image;
557
558     if( mImage && mImpl->mIsOnStage )
559     {
560       ApplyImageToSampler();
561     }
562   }
563 }
564
565 Image ImageRenderer::GetImage() const
566 {
567   return mImage;
568 }
569
570 void ImageRenderer::ApplyImageToSampler()
571 {
572   if( mImage )
573   {
574     Material material = mImpl->mRenderer.GetMaterial();
575     if( material )
576     {
577       int index = material.GetTextureIndex(TEXTURE_UNIFORM_NAME);
578       if( index != -1 )
579       {
580         material.SetTextureImage( index, mImage );
581         return;
582       }
583
584       material.AddTexture( mImage,TEXTURE_UNIFORM_NAME );
585     }
586   }
587 }
588
589 void ImageRenderer::OnImageLoaded( ResourceImage image )
590 {
591   if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
592   {
593     mImage = RendererFactory::GetBrokenRendererImage();
594     if( mImpl->mIsOnStage )
595     {
596       ApplyImageToSampler();
597     }
598   }
599 }
600
601 } // namespace Internal
602
603 } // namespace Toolkit
604
605 } // namespace Dali