Change geometry to use triangle strip
[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()
190 : ControlRenderer(),
191   mDesiredSize(),
192   mFittingMode( FittingMode::DEFAULT ),
193   mSamplingMode( SamplingMode::DEFAULT )
194 {
195 }
196
197 ImageRenderer::~ImageRenderer()
198 {
199 }
200
201 void ImageRenderer::DoInitialize( RendererFactoryCache& factoryCache, const Property::Map& propertyMap )
202 {
203   Initialize(factoryCache);
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
310 void ImageRenderer::SetSize( const Vector2& size )
311 {
312   ControlRenderer::SetSize( size );
313 }
314
315 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
316 {
317   if(mImage)
318   {
319     naturalSize.x = mImage.GetWidth();
320     naturalSize.y = mImage.GetHeight();
321     return;
322   }
323   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
324   {
325     naturalSize.x = mDesiredSize.GetWidth();
326     naturalSize.y = mDesiredSize.GetHeight();
327     return;
328   }
329   else if( !mImageUrl.empty() )
330   {
331     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
332     naturalSize.x = dimentions.GetWidth();
333     naturalSize.y = dimentions.GetHeight();
334     return;
335   }
336
337   naturalSize = Vector2::ZERO;
338 }
339
340 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
341 {
342   ControlRenderer::SetClipRect( clipRect );
343 }
344
345 void ImageRenderer::SetOffset( const Vector2& offset )
346 {
347 }
348
349 void ImageRenderer::DoSetOnStage( Actor& actor )
350 {
351   if( !mImageUrl.empty() && !mImage )
352   {
353     mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
354   }
355
356   ApplyImageToSampler();
357 }
358
359 void ImageRenderer::DoSetOffStage( Actor& actor )
360 {
361   //If we own the image then make sure we release it when we go off stage
362   if( !mImageUrl.empty() )
363   {
364     mImage.Reset();
365   }
366 }
367
368 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
369 {
370   map.Clear();
371   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
372   if( !mImageUrl.empty() )
373   {
374     map.Insert( IMAGE_URL_NAME, mImageUrl );
375     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
376     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
377   }
378   else if( mImage )
379   {
380     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
381     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
382
383     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
384     if( resourceImage )
385     {
386       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
387     }
388   }
389
390   switch( mFittingMode )
391   {
392     case Dali::FittingMode::FIT_HEIGHT:
393     {
394       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
395       break;
396     }
397     case Dali::FittingMode::FIT_WIDTH:
398     {
399       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
400       break;
401     }
402     case Dali::FittingMode::SCALE_TO_FILL:
403     {
404       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
405       break;
406     }
407     case Dali::FittingMode::SHRINK_TO_FIT:
408     {
409       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
410       break;
411     }
412     default:
413     {
414       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
415       break;
416     }
417   }
418
419   switch( mSamplingMode )
420   {
421     case Dali::SamplingMode::BOX:
422     {
423       map.Insert( IMAGE_SAMPLING_MODE, BOX );
424       break;
425     }
426     case Dali::SamplingMode::NEAREST:
427     {
428       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
429       break;
430     }
431     case Dali::SamplingMode::LINEAR:
432     {
433       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
434       break;
435     }
436     case Dali::SamplingMode::BOX_THEN_LINEAR:
437     {
438       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
439       break;
440     }
441     case Dali::SamplingMode::BOX_THEN_NEAREST:
442     {
443       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
444       break;
445     }
446     case Dali::SamplingMode::NO_FILTER:
447     {
448       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
449       break;
450     }
451     case Dali::SamplingMode::DONT_CARE:
452     {
453       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
454       break;
455     }
456     default:
457     {
458       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
459       break;
460     }
461   }
462 }
463
464 void ImageRenderer::Initialize( RendererFactoryCache& factoryCache )
465 {
466   if( !mImpl->mCustomShader )
467   {
468     mImpl->mGeometry = CreateGeometry( factoryCache, ImageDimensions( 1, 1 ) );
469
470     mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
471
472     if( !mImpl->mShader )
473     {
474       mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
475       factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, mImpl->mShader );
476     }
477   }
478   else
479   {
480     mImpl->mGeometry = CreateGeometry( factoryCache, mImpl->mCustomShader->mGridSize );
481
482     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
483     {
484       mImpl->mShader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
485
486       if( !mImpl->mShader )
487       {
488         mImpl->mShader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
489         factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, mImpl->mShader );
490       }
491     }
492     else
493     {
494       mImpl->mShader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
495                                     mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
496                                     mImpl->mCustomShader->mHints );
497     }
498   }
499
500   if( mImpl->mRenderer )
501   {
502     mImpl->mRenderer.SetGeometry( mImpl->mGeometry );
503     Material material = mImpl->mRenderer.GetMaterial();
504     if( material )
505     {
506       material.SetShader( mImpl->mShader );
507     }
508   }
509 }
510
511 void ImageRenderer::SetImage( const std::string& imageUrl )
512 {
513   SetImage( imageUrl, 0, 0, Dali::FittingMode::DEFAULT, Dali::SamplingMode::DEFAULT );
514 }
515
516 void ImageRenderer::SetImage( const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
517 {
518   if( mImageUrl != imageUrl )
519   {
520     mImageUrl = imageUrl;
521     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
522     mFittingMode = fittingMode;
523     mSamplingMode = samplingMode;
524
525     if( !mImageUrl.empty() && mImpl->mIsOnStage )
526     {
527       mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
528       ApplyImageToSampler();
529     }
530     else
531     {
532       mImage.Reset();
533     }
534   }
535 }
536
537 void ImageRenderer::SetImage( Image image )
538 {
539   if( mImage != image )
540   {
541     mImageUrl.clear();
542     mDesiredSize = ImageDimensions();
543     mFittingMode = FittingMode::DEFAULT;
544     mSamplingMode = SamplingMode::DEFAULT;
545     mImage = image;
546
547     if( mImage && mImpl->mIsOnStage )
548     {
549       ApplyImageToSampler();
550     }
551   }
552 }
553
554 Image ImageRenderer::GetImage() const
555 {
556   return mImage;
557 }
558
559 void ImageRenderer::ApplyImageToSampler()
560 {
561   if( mImage )
562   {
563     Material material = mImpl->mRenderer.GetMaterial();
564     if( material )
565     {
566       for( std::size_t i = 0; i < material.GetNumberOfSamplers(); ++i )
567       {
568         Sampler sampler = material.GetSamplerAt( i );
569         if( sampler.GetUniformName() == TEXTURE_UNIFORM_NAME )
570         {
571           sampler.SetImage( mImage );
572           return;
573         }
574       }
575
576       Sampler sampler = Sampler::New( mImage, TEXTURE_UNIFORM_NAME );
577       material.AddSampler( sampler );
578     }
579   }
580 }
581
582 } // namespace Internal
583
584 } // namespace Toolkit
585
586 } // namespace Dali