Merge "Changed all property & signal names to lowerCamelCase" into devel/master
[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("rendererType");
43 const char * const RENDERER_TYPE_VALUE("imageRenderer");
44
45 // property names
46 const char * const IMAGE_URL_NAME( "imageUrl" );
47 const char * const IMAGE_FITTING_MODE( "imageFittingMode" );
48 const char * const IMAGE_SAMPLING_MODE( "imageSamplingMode" );
49 const char * const IMAGE_DESIRED_WIDTH( "imageDesiredWidth" );
50 const char * const IMAGE_DESIRED_HEIGHT( "imageDesiredHeight" );
51
52 // fitting modes
53 const char * const SHRINK_TO_FIT("shrinkToFit");
54 const char * const SCALE_TO_FILL("scaleToFill");
55 const char * const FIT_WIDTH("fitWidth");
56 const char * const FIT_HEIGHT("fitHeight");
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("boxThenNearest");
64 const char * const BOX_THEN_LINEAR("boxThenLinear");
65 const char * const NO_FILTER("noFilter");
66 const char * const DONT_CARE("dontCare");
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( Actor& actor, const Property::Map& propertyMap )
202 {
203   std::string oldImageUrl = mImageUrl;
204
205   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
206   if( imageURLValue )
207   {
208     imageURLValue->Get( mImageUrl );
209     if( !mImageUrl.empty() )
210     {
211       mImage.Reset();
212     }
213
214     Property::Value* fittingValue = propertyMap.Find( IMAGE_FITTING_MODE );
215     if( fittingValue )
216     {
217       std::string fitting;
218       fittingValue->Get( fitting );
219
220       mFittingMode = FittingMode::DEFAULT;
221       if( fitting == SHRINK_TO_FIT )
222       {
223         mFittingMode = FittingMode::SHRINK_TO_FIT;
224       }
225       else if( fitting == SCALE_TO_FILL )
226       {
227         mFittingMode = FittingMode::SCALE_TO_FILL;
228       }
229       else if( fitting == FIT_WIDTH )
230       {
231         mFittingMode = FittingMode::FIT_WIDTH;
232       }
233       else if( fitting == FIT_HEIGHT )
234       {
235         mFittingMode = FittingMode::FIT_HEIGHT;
236       }
237       else if( fitting == DEFAULT )
238       {
239         mFittingMode = FittingMode::DEFAULT;
240       }
241       else
242       {
243         DALI_ASSERT_ALWAYS("Unknown fitting mode");
244       }
245     }
246
247     Property::Value* samplingValue = propertyMap.Find( IMAGE_SAMPLING_MODE );
248     if( samplingValue )
249     {
250       std::string sampling;
251       samplingValue->Get( sampling );
252
253       mSamplingMode = SamplingMode::DEFAULT;
254       if( sampling == BOX )
255       {
256         mSamplingMode = SamplingMode::BOX;
257       }
258       else if( sampling == NEAREST )
259       {
260         mSamplingMode = SamplingMode::NEAREST;
261       }
262       else if( sampling == LINEAR )
263       {
264         mSamplingMode = SamplingMode::LINEAR;
265       }
266       else if( sampling == BOX_THEN_NEAREST )
267       {
268         mSamplingMode = SamplingMode::BOX_THEN_NEAREST;
269       }
270       else if( sampling == BOX_THEN_LINEAR )
271       {
272         mSamplingMode = SamplingMode::BOX_THEN_LINEAR;
273       }
274       else if( sampling == NO_FILTER )
275       {
276         mSamplingMode = SamplingMode::NO_FILTER;
277       }
278       else if( sampling == DONT_CARE )
279       {
280         mSamplingMode = SamplingMode::DONT_CARE;
281       }
282       else if( sampling == DEFAULT )
283       {
284         mSamplingMode = SamplingMode::DEFAULT;
285       }
286       else
287       {
288         DALI_ASSERT_ALWAYS("Unknown sampling mode");
289       }
290     }
291
292     int desiredWidth = 0;
293     Property::Value* desiredWidthValue = propertyMap.Find( IMAGE_DESIRED_WIDTH );
294     if( desiredWidthValue )
295     {
296       desiredWidthValue->Get( desiredWidth );
297     }
298
299     int desiredHeight = 0;
300     Property::Value* desiredHeightValue = propertyMap.Find( IMAGE_DESIRED_HEIGHT );
301     if( desiredHeightValue )
302     {
303       desiredHeightValue->Get( desiredHeight );
304     }
305
306     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
307   }
308
309   if( mImpl->mRenderer )
310   {
311     //remove old renderer
312     if( actor )
313     {
314       actor.RemoveRenderer( mImpl->mRenderer );
315     }
316
317     //clean the cache
318     if( !oldImageUrl.empty() )
319     {
320       mFactoryCache.CleanRendererCache( oldImageUrl );
321     }
322
323     //Initialize the renderer
324     if( !mImageUrl.empty() )
325     {
326       InitializeRenderer( mImageUrl );
327     }
328     else if( mImage )
329     {
330       InitializeRenderer( mImage );
331     }
332
333     //add the new renderer to the actor
334     if( actor && mImpl->mRenderer )
335     {
336       actor.AddRenderer( mImpl->mRenderer );
337     }
338   }
339 }
340
341 void ImageRenderer::SetSize( const Vector2& size )
342 {
343   ControlRenderer::SetSize( size );
344 }
345
346 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
347 {
348   if(mImage)
349   {
350     naturalSize.x = mImage.GetWidth();
351     naturalSize.y = mImage.GetHeight();
352     return;
353   }
354   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
355   {
356     naturalSize.x = mDesiredSize.GetWidth();
357     naturalSize.y = mDesiredSize.GetHeight();
358     return;
359   }
360   else if( !mImageUrl.empty() )
361   {
362     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
363     naturalSize.x = dimentions.GetWidth();
364     naturalSize.y = dimentions.GetHeight();
365     return;
366   }
367
368   naturalSize = Vector2::ZERO;
369 }
370
371 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
372 {
373   ControlRenderer::SetClipRect( clipRect );
374 }
375
376 void ImageRenderer::SetOffset( const Vector2& offset )
377 {
378 }
379
380 Renderer ImageRenderer::CreateRenderer() const
381 {
382   Geometry geometry;
383   Shader shader;
384
385   if( !mImpl->mCustomShader )
386   {
387     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
388     shader = mFactoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
389     if( !shader )
390     {
391       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
392       mFactoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
393     }
394   }
395   else
396   {
397     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
398     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
399     {
400       shader = mFactoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
401       if( !shader )
402       {
403         shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
404         mFactoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
405       }
406     }
407     else
408     {
409       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
410                              mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
411                              mImpl->mCustomShader->mHints );
412     }
413   }
414
415   Material material = Material::New( shader );
416   return Renderer::New( geometry, material );
417 }
418
419 void ImageRenderer::InitializeRenderer( const std::string& imageUrl )
420 {
421   if( mImageUrl.empty() )
422   {
423     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
424     return;
425   }
426
427   mImpl->mRenderer.Reset();
428   if( !mImpl->mCustomShader )
429   {
430     mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
431     if( !mImpl->mRenderer )
432     {
433       mImpl->mRenderer = CreateRenderer();
434
435       ResourceImage image = Dali::ResourceImage::New( imageUrl );
436       image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
437       Material material = mImpl->mRenderer.GetMaterial();
438       material.AddTexture( image, TEXTURE_UNIFORM_NAME );
439
440       mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
441     }
442     mImpl->mFlags |= Impl::IS_FROM_CACHE;
443   }
444   else
445   {
446     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
447     mImpl->mRenderer = CreateRenderer();
448     ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
449     image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
450     ApplyImageToSampler( image );
451   }
452 }
453
454 void ImageRenderer::InitializeRenderer( const Image& image )
455 {
456   mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
457
458   if( !image )
459   {
460     return;
461   }
462
463   mImpl->mRenderer = CreateRenderer();
464   ApplyImageToSampler( image );
465 }
466
467
468 void ImageRenderer::DoSetOnStage( Actor& actor )
469 {
470   if( !mImageUrl.empty() )
471   {
472     InitializeRenderer( mImageUrl );
473   }
474   else if( mImage )
475   {
476     InitializeRenderer( mImage );
477   }
478
479   if( !GetIsFromCache() )
480   {
481     Image image = mImage;
482     if( !mImageUrl.empty() )
483     {
484       ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
485       resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
486       image = resourceImage;
487     }
488
489     ApplyImageToSampler( image );
490   }
491 }
492
493 void ImageRenderer::DoSetOffStage( Actor& actor )
494 {
495   //If we own the image then make sure we release it when we go off stage
496   if( !mImageUrl.empty() )
497   {
498     //clean the renderer from the cache since it may no longer be in use
499     mFactoryCache.CleanRendererCache( mImageUrl );
500
501     mImage.Reset();
502   }
503 }
504
505 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
506 {
507   map.Clear();
508   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
509   if( !mImageUrl.empty() )
510   {
511     map.Insert( IMAGE_URL_NAME, mImageUrl );
512     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
513     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
514   }
515   else if( mImage )
516   {
517     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
518     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
519
520     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
521     if( resourceImage )
522     {
523       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
524     }
525   }
526
527   switch( mFittingMode )
528   {
529     case Dali::FittingMode::FIT_HEIGHT:
530     {
531       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
532       break;
533     }
534     case Dali::FittingMode::FIT_WIDTH:
535     {
536       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
537       break;
538     }
539     case Dali::FittingMode::SCALE_TO_FILL:
540     {
541       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
542       break;
543     }
544     case Dali::FittingMode::SHRINK_TO_FIT:
545     {
546       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
547       break;
548     }
549     default:
550     {
551       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
552       break;
553     }
554   }
555
556   switch( mSamplingMode )
557   {
558     case Dali::SamplingMode::BOX:
559     {
560       map.Insert( IMAGE_SAMPLING_MODE, BOX );
561       break;
562     }
563     case Dali::SamplingMode::NEAREST:
564     {
565       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
566       break;
567     }
568     case Dali::SamplingMode::LINEAR:
569     {
570       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
571       break;
572     }
573     case Dali::SamplingMode::BOX_THEN_LINEAR:
574     {
575       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
576       break;
577     }
578     case Dali::SamplingMode::BOX_THEN_NEAREST:
579     {
580       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
581       break;
582     }
583     case Dali::SamplingMode::NO_FILTER:
584     {
585       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
586       break;
587     }
588     case Dali::SamplingMode::DONT_CARE:
589     {
590       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
591       break;
592     }
593     default:
594     {
595       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
596       break;
597     }
598   }
599 }
600
601 void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl )
602 {
603   SetImage( actor, imageUrl, 0, 0, Dali::FittingMode::DEFAULT, Dali::SamplingMode::DEFAULT );
604 }
605
606 void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
607 {
608   if( mImageUrl != imageUrl )
609   {
610     if( mImpl->mRenderer )
611     {
612       if( GetIsFromCache() )
613       {
614         //remove old renderer
615         if( actor )
616         {
617           actor.RemoveRenderer( mImpl->mRenderer );
618         }
619
620         //clean the cache
621         if( !mImageUrl.empty() )
622         {
623           mFactoryCache.CleanRendererCache( mImageUrl );
624         }
625
626         //Initialize the renderer
627         InitializeRenderer( imageUrl );
628
629         //add the new renderer to the actor
630         if( actor && mImpl->mRenderer )
631         {
632           actor.AddRenderer( mImpl->mRenderer );
633         }
634       }
635       else
636       {
637         ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
638         image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
639         ApplyImageToSampler( image );
640       }
641     }
642
643     mImageUrl = imageUrl;
644     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
645     mFittingMode = fittingMode;
646     mSamplingMode = samplingMode;
647     mImage.Reset();
648   }
649 }
650
651 void ImageRenderer::SetImage( Actor& actor, const Image& image )
652 {
653   if( mImage != image )
654   {
655     if( mImpl->mRenderer )
656     {
657       if( GetIsFromCache() )
658       {
659         //remove old renderer
660         if( actor )
661         {
662           actor.RemoveRenderer( mImpl->mRenderer );
663         }
664
665         //clean the cache
666         if( !mImageUrl.empty() )
667         {
668           mFactoryCache.CleanRendererCache( mImageUrl );
669         }
670
671         //Initialize the renderer
672         InitializeRenderer( image );
673
674         //add the new renderer to the actor
675         if( actor && mImpl->mRenderer )
676         {
677           actor.AddRenderer( mImpl->mRenderer );
678         }
679       }
680       else
681       {
682         ApplyImageToSampler( image );
683       }
684     }
685
686     mImage = image;
687     mImageUrl.clear();
688     mDesiredSize = ImageDimensions();
689     mFittingMode = FittingMode::DEFAULT;
690     mSamplingMode = SamplingMode::DEFAULT;
691   }
692 }
693
694 void ImageRenderer::ApplyImageToSampler( const Image& image )
695 {
696   if( image )
697   {
698     Material material = mImpl->mRenderer.GetMaterial();
699     if( material )
700     {
701       int index = material.GetTextureIndex( TEXTURE_UNIFORM_NAME );
702       if( index != -1 )
703       {
704         material.SetTextureImage( index, image );
705         return;
706       }
707
708       material.AddTexture( image, TEXTURE_UNIFORM_NAME );
709     }
710   }
711 }
712
713 void ImageRenderer::OnImageLoaded( ResourceImage image )
714 {
715   if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
716   {
717     Image image = RendererFactory::GetBrokenRendererImage();
718     if( mImpl->mRenderer )
719     {
720       ApplyImageToSampler( image );
721     }
722   }
723 }
724
725 } // namespace Internal
726
727 } // namespace Toolkit
728
729 } // namespace Dali