0fad54314888167adac0d49ff7e5ad7d46ea848b
[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     mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
405   }
406
407   ApplyImageToSampler();
408 }
409
410 void ImageRenderer::DoSetOffStage( Actor& actor )
411 {
412   //If we own the image then make sure we release it when we go off stage
413   if( !mImageUrl.empty() )
414   {
415     mImage.Reset();
416   }
417 }
418
419 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
420 {
421   map.Clear();
422   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
423   if( !mImageUrl.empty() )
424   {
425     map.Insert( IMAGE_URL_NAME, mImageUrl );
426     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
427     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
428   }
429   else if( mImage )
430   {
431     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
432     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
433
434     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
435     if( resourceImage )
436     {
437       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
438     }
439   }
440
441   switch( mFittingMode )
442   {
443     case Dali::FittingMode::FIT_HEIGHT:
444     {
445       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
446       break;
447     }
448     case Dali::FittingMode::FIT_WIDTH:
449     {
450       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
451       break;
452     }
453     case Dali::FittingMode::SCALE_TO_FILL:
454     {
455       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
456       break;
457     }
458     case Dali::FittingMode::SHRINK_TO_FIT:
459     {
460       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
461       break;
462     }
463     default:
464     {
465       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
466       break;
467     }
468   }
469
470   switch( mSamplingMode )
471   {
472     case Dali::SamplingMode::BOX:
473     {
474       map.Insert( IMAGE_SAMPLING_MODE, BOX );
475       break;
476     }
477     case Dali::SamplingMode::NEAREST:
478     {
479       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
480       break;
481     }
482     case Dali::SamplingMode::LINEAR:
483     {
484       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
485       break;
486     }
487     case Dali::SamplingMode::BOX_THEN_LINEAR:
488     {
489       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
490       break;
491     }
492     case Dali::SamplingMode::BOX_THEN_NEAREST:
493     {
494       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
495       break;
496     }
497     case Dali::SamplingMode::NO_FILTER:
498     {
499       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
500       break;
501     }
502     case Dali::SamplingMode::DONT_CARE:
503     {
504       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
505       break;
506     }
507     default:
508     {
509       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
510       break;
511     }
512   }
513 }
514
515 void ImageRenderer::SetImage( const std::string& imageUrl )
516 {
517   SetImage( imageUrl, 0, 0, Dali::FittingMode::DEFAULT, Dali::SamplingMode::DEFAULT );
518 }
519
520 void ImageRenderer::SetImage( const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
521 {
522   if( mImageUrl != imageUrl )
523   {
524     mImageUrl = imageUrl;
525     SetCachedRendererKey( mImageUrl );
526     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
527     mFittingMode = fittingMode;
528     mSamplingMode = samplingMode;
529
530     if( !mImageUrl.empty() && mImpl->mIsOnStage )
531     {
532       mImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
533       ApplyImageToSampler();
534     }
535     else
536     {
537       mImage.Reset();
538     }
539   }
540 }
541
542 void ImageRenderer::SetImage( Image image )
543 {
544   if( mImage != image )
545   {
546     mImageUrl.clear();
547     mDesiredSize = ImageDimensions();
548     mFittingMode = FittingMode::DEFAULT;
549     mSamplingMode = SamplingMode::DEFAULT;
550     mImage = image;
551
552     if( mImage && mImpl->mIsOnStage )
553     {
554       ApplyImageToSampler();
555     }
556   }
557 }
558
559 Image ImageRenderer::GetImage() const
560 {
561   return mImage;
562 }
563
564 void ImageRenderer::ApplyImageToSampler()
565 {
566   if( mImage )
567   {
568     Material material = mImpl->mRenderer.GetMaterial();
569     if( material )
570     {
571       int index = material.GetTextureIndex(TEXTURE_UNIFORM_NAME);
572       if( index != -1 )
573       {
574         material.SetTextureImage( index, mImage );
575         return;
576       }
577
578       material.AddTexture( mImage,TEXTURE_UNIFORM_NAME );
579     }
580   }
581 }
582
583 } // namespace Internal
584
585 } // namespace Toolkit
586
587 } // namespace Dali