Update following the changes of blending&culling options
[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 <cstring> // for strncasecmp
23 #include <dali/public-api/images/resource-image.h>
24 #include <dali/public-api/images/native-image.h>
25 #include <dali/integration-api/debug.h>
26
27 // INTERNAL HEADER
28 #include <dali-toolkit/internal/controls/renderers/renderer-factory-impl.h>
29 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
30 #include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
31 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
32 #include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
33
34 namespace Dali
35 {
36
37 namespace Toolkit
38 {
39
40 namespace Internal
41 {
42
43 namespace
44 {
45 const char HTTP_URL[] = "http://";
46 const char HTTPS_URL[] = "https://";
47
48 const char * const RENDERER_TYPE("rendererType");
49 const char * const RENDERER_TYPE_VALUE("imageRenderer");
50
51 // property names
52 const char * const IMAGE_URL_NAME( "imageUrl" );
53 const char * const IMAGE_FITTING_MODE( "imageFittingMode" );
54 const char * const IMAGE_SAMPLING_MODE( "imageSamplingMode" );
55 const char * const IMAGE_DESIRED_WIDTH( "imageDesiredWidth" );
56 const char * const IMAGE_DESIRED_HEIGHT( "imageDesiredHeight" );
57
58 // fitting modes
59 const char * const SHRINK_TO_FIT("shrinkToFit");
60 const char * const SCALE_TO_FILL("scaleToFill");
61 const char * const FIT_WIDTH("fitWidth");
62 const char * const FIT_HEIGHT("fitHeight");
63 const char * const DEFAULT("default");
64
65 // sampling modes
66 const char * const BOX("box");
67 const char * const NEAREST("nearest");
68 const char * const LINEAR("linear");
69 const char * const BOX_THEN_NEAREST("boxThenNearest");
70 const char * const BOX_THEN_LINEAR("boxThenLinear");
71 const char * const NO_FILTER("noFilter");
72 const char * const DONT_CARE("dontCare");
73
74 const std::string TEXTURE_UNIFORM_NAME = "sTexture";
75 const std::string ATLAS_RECT_UNIFORM_NAME = "uAtlasRect";
76 const std::string PIXEL_AREA_UNIFORM_NAME = "pixelArea";
77
78 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
79
80 const char* DEFAULT_SAMPLER_TYPENAME = "sampler2D";
81
82 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
83   attribute mediump vec2 aPosition;\n
84   uniform mediump mat4 uMvpMatrix;\n
85   uniform mediump vec3 uSize;\n
86   uniform mediump vec4 uAtlasRect;\n
87   uniform mediump vec4 pixelArea;
88   varying mediump vec2 vTexCoord;\n
89   \n
90   void main()\n
91   {\n
92     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
93     vertexPosition.xyz *= uSize;\n
94     vertexPosition = uMvpMatrix * vertexPosition;\n
95     \n
96     vTexCoord = mix( uAtlasRect.xy, uAtlasRect.zw, pixelArea.xy+pixelArea.zw*(aPosition + vec2(0.5) ) );\n
97     gl_Position = vertexPosition;\n
98   }\n
99 );
100
101 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
102   varying mediump vec2 vTexCoord;\n
103   uniform sampler2D sTexture;\n
104   uniform lowp vec4 uColor;\n
105   \n
106   void main()\n
107   {\n
108     gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
109   }\n
110 );
111
112
113 Geometry GenerateGeometry( const Vector< Vector2 >& vertices, const Vector< unsigned int >& indices )
114 {
115   Property::Map vertexFormat;
116   vertexFormat[ "aPosition" ] = Property::VECTOR2;
117   PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat, vertices.Size() );
118   if( vertices.Size() > 0 )
119   {
120     vertexPropertyBuffer.SetData( &vertices[ 0 ] );
121   }
122
123   Property::Map indexFormat;
124   indexFormat[ "indices" ] = Property::INTEGER;
125   PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat, indices.Size() );
126   if( indices.Size() > 0 )
127   {
128     indexPropertyBuffer.SetData( &indices[ 0 ] );
129   }
130
131   // Create the geometry object
132   Geometry geometry = Geometry::New();
133   geometry.AddVertexBuffer( vertexPropertyBuffer );
134   geometry.SetIndexBuffer( indexPropertyBuffer );
135   geometry.SetGeometryType( Geometry::TRIANGLE_STRIP );
136
137   return geometry;
138 }
139
140 Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gridSize )
141 {
142   Geometry geometry;
143
144   if( gridSize == ImageDimensions( 1, 1 ) )
145   {
146     geometry = factoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
147     if( !geometry )
148     {
149       geometry =  factoryCache.CreateQuadGeometry();
150       factoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
151     }
152   }
153   else
154   {
155     uint16_t gridWidth = gridSize.GetWidth();
156     uint16_t gridHeight = gridSize.GetHeight();
157
158     // Create vertices
159     Vector< Vector2 > vertices;
160     vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
161
162     for( int y = 0; y < gridHeight + 1; ++y )
163     {
164       for( int x = 0; x < gridWidth + 1; ++x )
165       {
166         vertices.PushBack( Vector2( (float)x/gridWidth - 0.5f, (float)y/gridHeight  - 0.5f) );
167       }
168     }
169
170     // Create indices
171     Vector< unsigned int > indices;
172     indices.Reserve( (gridWidth+2)*gridHeight*2 - 2);
173
174     for( unsigned int row = 0u; row < gridHeight; ++row )
175     {
176       unsigned int rowStartIndex = row*(gridWidth+1u);
177       unsigned int nextRowStartIndex = rowStartIndex + gridWidth +1u;
178
179       if( row != 0u ) // degenerate index on non-first row
180       {
181         indices.PushBack( rowStartIndex );
182       }
183
184       for( unsigned int column = 0u; column < gridWidth+1u; column++) // main strip
185       {
186         indices.PushBack( rowStartIndex + column);
187         indices.PushBack( nextRowStartIndex + column);
188       }
189
190       if( row != gridHeight-1u ) // degenerate index on non-last row
191       {
192         indices.PushBack( nextRowStartIndex + gridWidth );
193       }
194     }
195
196     return GenerateGeometry( vertices, indices );
197   }
198
199   return geometry;
200 }
201
202 } //unnamed namespace
203
204 ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
205 : ControlRenderer( factoryCache ),
206   mAtlasManager( atlasManager ),
207   mDesiredSize(),
208   mFittingMode( FittingMode::DEFAULT ),
209   mSamplingMode( SamplingMode::DEFAULT ),
210   mIsAlphaPreMultiplied( false ),
211   mNativeFragmentShaderCode( ),
212   mNativeImageFlag( false )
213 {
214 }
215
216 ImageRenderer::~ImageRenderer()
217 {
218 }
219
220 void ImageRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap )
221 {
222   std::string oldImageUrl = mImageUrl;
223
224   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
225   if( imageURLValue )
226   {
227     imageURLValue->Get( mImageUrl );
228     if( !mImageUrl.empty() )
229     {
230       mImage.Reset();
231     }
232
233     Property::Value* fittingValue = propertyMap.Find( IMAGE_FITTING_MODE );
234     if( fittingValue )
235     {
236       std::string fitting;
237       fittingValue->Get( fitting );
238
239       mFittingMode = FittingMode::DEFAULT;
240       if( fitting == SHRINK_TO_FIT )
241       {
242         mFittingMode = FittingMode::SHRINK_TO_FIT;
243       }
244       else if( fitting == SCALE_TO_FILL )
245       {
246         mFittingMode = FittingMode::SCALE_TO_FILL;
247       }
248       else if( fitting == FIT_WIDTH )
249       {
250         mFittingMode = FittingMode::FIT_WIDTH;
251       }
252       else if( fitting == FIT_HEIGHT )
253       {
254         mFittingMode = FittingMode::FIT_HEIGHT;
255       }
256       else if( fitting == DEFAULT )
257       {
258         mFittingMode = FittingMode::DEFAULT;
259       }
260       else
261       {
262         DALI_ASSERT_ALWAYS("Unknown fitting mode");
263       }
264     }
265
266     Property::Value* samplingValue = propertyMap.Find( IMAGE_SAMPLING_MODE );
267     if( samplingValue )
268     {
269       std::string sampling;
270       samplingValue->Get( sampling );
271
272       mSamplingMode = SamplingMode::DEFAULT;
273       if( sampling == BOX )
274       {
275         mSamplingMode = SamplingMode::BOX;
276       }
277       else if( sampling == NEAREST )
278       {
279         mSamplingMode = SamplingMode::NEAREST;
280       }
281       else if( sampling == LINEAR )
282       {
283         mSamplingMode = SamplingMode::LINEAR;
284       }
285       else if( sampling == BOX_THEN_NEAREST )
286       {
287         mSamplingMode = SamplingMode::BOX_THEN_NEAREST;
288       }
289       else if( sampling == BOX_THEN_LINEAR )
290       {
291         mSamplingMode = SamplingMode::BOX_THEN_LINEAR;
292       }
293       else if( sampling == NO_FILTER )
294       {
295         mSamplingMode = SamplingMode::NO_FILTER;
296       }
297       else if( sampling == DONT_CARE )
298       {
299         mSamplingMode = SamplingMode::DONT_CARE;
300       }
301       else if( sampling == DEFAULT )
302       {
303         mSamplingMode = SamplingMode::DEFAULT;
304       }
305       else
306       {
307         DALI_ASSERT_ALWAYS("Unknown sampling mode");
308       }
309     }
310
311     int desiredWidth = 0;
312     Property::Value* desiredWidthValue = propertyMap.Find( IMAGE_DESIRED_WIDTH );
313     if( desiredWidthValue )
314     {
315       desiredWidthValue->Get( desiredWidth );
316     }
317
318     int desiredHeight = 0;
319     Property::Value* desiredHeightValue = propertyMap.Find( IMAGE_DESIRED_HEIGHT );
320     if( desiredHeightValue )
321     {
322       desiredHeightValue->Get( desiredHeight );
323     }
324
325     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
326   }
327
328   // remove old renderer if exit
329   if( mImpl->mRenderer )
330   {
331     if( actor ) //remove old renderer from actor
332     {
333       actor.RemoveRenderer( mImpl->mRenderer );
334     }
335     if( !oldImageUrl.empty() ) //clean old renderer from cache
336     {
337       CleanCache( oldImageUrl );
338     }
339   }
340
341   NativeImage nativeImage = NativeImage::DownCast( mImage );
342
343   if( nativeImage )
344   {
345     SetNativeFragmentShaderCode( nativeImage );
346   }
347
348   // if actor is on stage, create new renderer and apply to actor
349   if( actor && actor.OnStage() )
350   {
351     SetOnStage( actor );
352   }
353 }
354
355 void ImageRenderer::SetSize( const Vector2& size )
356 {
357   ControlRenderer::SetSize( size );
358 }
359
360 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
361 {
362   if(mImage)
363   {
364     naturalSize.x = mImage.GetWidth();
365     naturalSize.y = mImage.GetHeight();
366     return;
367   }
368   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
369   {
370     naturalSize.x = mDesiredSize.GetWidth();
371     naturalSize.y = mDesiredSize.GetHeight();
372     return;
373   }
374   else if( !mImageUrl.empty() )
375   {
376     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
377     naturalSize.x = dimentions.GetWidth();
378     naturalSize.y = dimentions.GetHeight();
379     return;
380   }
381
382   naturalSize = Vector2::ZERO;
383 }
384
385 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
386 {
387   ControlRenderer::SetClipRect( clipRect );
388 }
389
390 void ImageRenderer::SetOffset( const Vector2& offset )
391 {
392 }
393
394 Renderer ImageRenderer::CreateRenderer() const
395 {
396   Geometry geometry;
397   Shader shader;
398
399   // If mImage is nativeImage with custom sampler or prefix, mNativeFragmentShaderCode will be applied.
400   // Renderer can't be shared between NativeImage and other image types.
401   if( !mNativeFragmentShaderCode.empty() )
402   {
403     return CreateNativeImageRenderer();
404   }
405
406   if( !mImpl->mCustomShader )
407   {
408     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
409
410     shader = GetImageShader(mFactoryCache);
411   }
412   else
413   {
414     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
415     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
416     {
417       shader = GetImageShader(mFactoryCache);
418     }
419     else
420     {
421       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
422                              mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
423                              mImpl->mCustomShader->mHints );
424       if( mImpl->mCustomShader->mVertexShader.empty() )
425       {
426         shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
427         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
428       }
429     }
430   }
431
432   Material material = Material::New( shader );
433   return Renderer::New( geometry, material );
434 }
435
436 Renderer ImageRenderer::CreateNativeImageRenderer() const
437 {
438   Geometry geometry;
439   Shader shader;
440
441   if( !mImpl->mCustomShader )
442   {
443     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
444
445     shader  = Shader::New( VERTEX_SHADER, mNativeFragmentShaderCode );
446     shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
447     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
448   }
449   else
450   {
451     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
452     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
453     {
454       shader  = Shader::New( VERTEX_SHADER, mNativeFragmentShaderCode );
455     }
456     else
457     {
458       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
459                              mNativeFragmentShaderCode,
460                              mImpl->mCustomShader->mHints );
461       if( mImpl->mCustomShader->mVertexShader.empty() )
462       {
463         shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
464         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
465       }
466     }
467   }
468
469   Material material = Material::New( shader );
470   return Renderer::New( geometry, material );
471 }
472
473 void ImageRenderer::InitializeRenderer( const std::string& imageUrl )
474 {
475   if( imageUrl.empty() )
476   {
477     return;
478   }
479
480   mImageUrl = imageUrl;
481   mImpl->mRenderer.Reset();
482
483   if( !mImpl->mCustomShader &&
484       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
485       ( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) )
486   {
487     mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
488     if( !mImpl->mRenderer )
489     {
490       Vector4 atlasRect;
491       Material material = mAtlasManager.Add(atlasRect, imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
492       if( material )
493       {
494         Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
495         mImpl->mRenderer = Renderer::New( geometry, material );
496         mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
497       }
498       else // big image, atlasing is not applied
499       {
500         mImpl->mRenderer = CreateRenderer();
501
502         ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
503         image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
504         Material material = mImpl->mRenderer.GetMaterial();
505         material.AddTexture( image, TEXTURE_UNIFORM_NAME );
506       }
507
508       mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
509     }
510
511     mImpl->mFlags |= Impl::IS_FROM_CACHE;
512   }
513   else
514   {
515     // for custom shader or remote image, renderer is not cached and atlas is not applied
516
517     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
518     mImpl->mRenderer = CreateRenderer();
519     ResourceImage resourceImage = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
520     resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
521     ApplyImageToSampler( resourceImage );
522   }
523 }
524
525 void ImageRenderer::InitializeRenderer( const Image& image )
526 {
527   mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
528
529   mImpl->mRenderer = CreateRenderer();
530
531   if( image )
532   {
533     ApplyImageToSampler( image );
534   }
535 }
536
537
538 void ImageRenderer::DoSetOnStage( Actor& actor )
539 {
540   if( !mImageUrl.empty() )
541   {
542     InitializeRenderer( mImageUrl );
543   }
544   else
545   {
546     InitializeRenderer( mImage );
547   }
548
549   mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, mIsAlphaPreMultiplied);
550 }
551
552 void ImageRenderer::DoSetOffStage( Actor& actor )
553 {
554   //If we own the image then make sure we release it when we go off stage
555   if( !mImageUrl.empty() )
556   {
557     actor.RemoveRenderer( mImpl->mRenderer );
558     CleanCache(mImageUrl);
559
560     mImage.Reset();
561   }
562   else
563   {
564     actor.RemoveRenderer( mImpl->mRenderer );
565     mImpl->mRenderer.Reset();
566   }
567 }
568
569 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
570 {
571   map.Clear();
572   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
573   if( !mImageUrl.empty() )
574   {
575     map.Insert( IMAGE_URL_NAME, mImageUrl );
576     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
577     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
578   }
579   else if( mImage )
580   {
581     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
582     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
583
584     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
585     if( resourceImage )
586     {
587       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
588     }
589   }
590
591   switch( mFittingMode )
592   {
593     case Dali::FittingMode::FIT_HEIGHT:
594     {
595       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
596       break;
597     }
598     case Dali::FittingMode::FIT_WIDTH:
599     {
600       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
601       break;
602     }
603     case Dali::FittingMode::SCALE_TO_FILL:
604     {
605       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
606       break;
607     }
608     case Dali::FittingMode::SHRINK_TO_FIT:
609     {
610       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
611       break;
612     }
613     default:
614     {
615       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
616       break;
617     }
618   }
619
620   switch( mSamplingMode )
621   {
622     case Dali::SamplingMode::BOX:
623     {
624       map.Insert( IMAGE_SAMPLING_MODE, BOX );
625       break;
626     }
627     case Dali::SamplingMode::NEAREST:
628     {
629       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
630       break;
631     }
632     case Dali::SamplingMode::LINEAR:
633     {
634       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
635       break;
636     }
637     case Dali::SamplingMode::BOX_THEN_LINEAR:
638     {
639       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
640       break;
641     }
642     case Dali::SamplingMode::BOX_THEN_NEAREST:
643     {
644       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
645       break;
646     }
647     case Dali::SamplingMode::NO_FILTER:
648     {
649       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
650       break;
651     }
652     case Dali::SamplingMode::DONT_CARE:
653     {
654       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
655       break;
656     }
657     default:
658     {
659       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
660       break;
661     }
662   }
663 }
664
665 Shader ImageRenderer::GetImageShader( RendererFactoryCache& factoryCache )
666 {
667   Shader shader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
668   if( !shader )
669   {
670     shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
671     factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
672     shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
673     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
674   }
675   return shader;
676 }
677
678 void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
679 {
680   if( mImageUrl != imageUrl )
681   {
682     std::string oldImageUrl = mImageUrl;
683     mImageUrl = imageUrl;
684     mDesiredSize = size;
685     mFittingMode = fittingMode;
686     mSamplingMode = samplingMode;
687     mImage.Reset();
688
689     if( mImpl->mRenderer )
690     {
691       if( GetIsFromCache() ) // if renderer is from cache, remove the old one
692       {
693         //remove old renderer
694         if( actor )
695         {
696           actor.RemoveRenderer( mImpl->mRenderer );
697         }
698
699         //clean the cache
700         if( !oldImageUrl.empty() )
701         {
702           CleanCache(oldImageUrl);
703         }
704
705         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
706         {
707           SetOnStage(actor);
708         }
709       }
710       else // if renderer is not from cache, reuse the same renderer and only change the texture
711       {
712         ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
713         image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
714         ApplyImageToSampler( image );
715       }
716     }
717   }
718 }
719
720 void ImageRenderer::SetImage( Actor& actor, const Image& image )
721 {
722   if( mImage != image )
723   {
724     NativeImage newNativeImage = NativeImage::DownCast( image );
725     bool newRendererFlag = true;
726
727     if( newNativeImage && !mNativeImageFlag )
728     {
729       SetNativeFragmentShaderCode( newNativeImage );
730     }
731
732     if( ( newNativeImage && mNativeImageFlag ) || ( !newNativeImage && !mNativeImageFlag ) )
733     {
734       newRendererFlag = false;
735     }
736
737     if( newNativeImage )
738     {
739       mNativeImageFlag = true;
740     }
741     else
742     {
743       mNativeFragmentShaderCode.clear();
744       mNativeImageFlag = false;
745     }
746
747     mImage = image;
748
749     if( mImpl->mRenderer )
750     {
751       // if renderer is from cache, remove the old one, and create new renderer
752       if( GetIsFromCache() )
753       {
754         //remove old renderer
755         if( actor )
756         {
757           actor.RemoveRenderer( mImpl->mRenderer );
758         }
759
760         //clean the cache
761         if( !mImageUrl.empty() )
762         {
763           CleanCache(mImageUrl);
764         }
765         mImageUrl.clear();
766
767         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
768         {
769           SetOnStage(actor);
770         }
771       }
772       // if input image is nativeImage and mImage is regular image or the reverse, remove the old one, and create new renderer
773       else if( newRendererFlag )
774       {
775         //remove old renderer
776         if( actor )
777         {
778           actor.RemoveRenderer( mImpl->mRenderer );
779         }
780
781         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
782         {
783           SetOnStage(actor);
784         }
785       }
786       else // if renderer is not from cache, reuse the same renderer and only change the texture
787       {
788         ApplyImageToSampler( image );
789       }
790     }
791
792     mImageUrl.clear();
793     mDesiredSize = ImageDimensions();
794     mFittingMode = FittingMode::DEFAULT;
795     mSamplingMode = SamplingMode::DEFAULT;
796   }
797 }
798
799 void ImageRenderer::EnablePreMultipliedAlpha( bool preMultipled )
800 {
801   mIsAlphaPreMultiplied = preMultipled;
802   if( mImpl->mRenderer )
803   {
804     mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, mIsAlphaPreMultiplied);
805   }
806 }
807
808 void ImageRenderer::ApplyImageToSampler( const Image& image )
809 {
810   if( image )
811   {
812     Material material = mImpl->mRenderer.GetMaterial();
813     if( material )
814     {
815       int index = material.GetTextureIndex( TEXTURE_UNIFORM_NAME );
816       if( index != -1 )
817       {
818         material.SetTextureImage( index, image );
819         return;
820       }
821
822       material.AddTexture( image, TEXTURE_UNIFORM_NAME );
823     }
824   }
825 }
826
827 void ImageRenderer::OnImageLoaded( ResourceImage image )
828 {
829   if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
830   {
831     Image brokenImage = RendererFactory::GetBrokenRendererImage();
832     if( mImpl->mRenderer )
833     {
834       ApplyImageToSampler( brokenImage );
835     }
836   }
837 }
838
839 void ImageRenderer::CleanCache(const std::string& url)
840 {
841   Material material = mImpl->mRenderer.GetMaterial();
842
843   Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
844   Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
845   if( index != Property::INVALID_INDEX )
846   {
847     Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
848     atlasRectValue.Get( atlasRect );
849   }
850
851   mImpl->mRenderer.Reset();
852   if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
853   {
854     mAtlasManager.Remove( material, atlasRect );
855   }
856 }
857
858 void ImageRenderer::SetNativeFragmentShaderCode( Dali::NativeImage& nativeImage )
859 {
860   const char* fragmentPreFix = nativeImage.GetCustomFragmentPreFix();
861   const char* customSamplerTypename = nativeImage.GetCustomSamplerTypename();
862
863   if( fragmentPreFix )
864   {
865     mNativeFragmentShaderCode = fragmentPreFix;
866     mNativeFragmentShaderCode += "\n";
867   }
868
869   if( mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty() )
870   {
871     mNativeFragmentShaderCode += mImpl->mCustomShader->mFragmentShader;
872   }
873   else
874   {
875     mNativeFragmentShaderCode += FRAGMENT_SHADER;
876   }
877
878   if( customSamplerTypename )
879   {
880     mNativeFragmentShaderCode.replace( mNativeFragmentShaderCode.find( DEFAULT_SAMPLER_TYPENAME ), strlen( DEFAULT_SAMPLER_TYPENAME ), customSamplerTypename );
881   }
882
883 }
884
885 } // namespace Internal
886
887 } // namespace Toolkit
888
889 } // namespace Dali