Pre-multiplied Alpha support for ImageView
[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/integration-api/debug.h>
25
26 // INTERNAL HEADER
27 #include <dali-toolkit/internal/controls/renderers/renderer-factory-impl.h>
28 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
29 #include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
30 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
31 #include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
32
33 namespace Dali
34 {
35
36 namespace Toolkit
37 {
38
39 namespace Internal
40 {
41
42 namespace
43 {
44 const char HTTP_URL[] = "http://";
45 const char HTTPS_URL[] = "https://";
46
47 const char * const RENDERER_TYPE("rendererType");
48 const char * const RENDERER_TYPE_VALUE("imageRenderer");
49
50 // property names
51 const char * const IMAGE_URL_NAME( "imageUrl" );
52 const char * const IMAGE_FITTING_MODE( "imageFittingMode" );
53 const char * const IMAGE_SAMPLING_MODE( "imageSamplingMode" );
54 const char * const IMAGE_DESIRED_WIDTH( "imageDesiredWidth" );
55 const char * const IMAGE_DESIRED_HEIGHT( "imageDesiredHeight" );
56
57 // fitting modes
58 const char * const SHRINK_TO_FIT("shrinkToFit");
59 const char * const SCALE_TO_FILL("scaleToFill");
60 const char * const FIT_WIDTH("fitWidth");
61 const char * const FIT_HEIGHT("fitHeight");
62 const char * const DEFAULT("default");
63
64 // sampling modes
65 const char * const BOX("box");
66 const char * const NEAREST("nearest");
67 const char * const LINEAR("linear");
68 const char * const BOX_THEN_NEAREST("boxThenNearest");
69 const char * const BOX_THEN_LINEAR("boxThenLinear");
70 const char * const NO_FILTER("noFilter");
71 const char * const DONT_CARE("dontCare");
72
73 const std::string TEXTURE_UNIFORM_NAME = "sTexture";
74 const std::string ATLAS_RECT_UNIFORM_NAME = "uAtlasRect";
75 const std::string PIXEL_AREA_UNIFORM_NAME = "pixelArea";
76
77 // Set this uniform to 1.0 for conventional alpha blending; if pre-multiplied alpha blending, set this uniform to 0.0
78 const std::string ALPHA_BLENDING_UNIFORM_NAME = "uAlphaBlending";
79
80 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
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   uniform lowp float uAlphaBlending; // Set to 1.0 for conventional alpha blending; if pre-multiplied alpha blending, set to 0.0
106   \n
107   void main()\n
108   {\n
109     gl_FragColor = texture2D( sTexture, vTexCoord ) * vec4( uColor.rgb*max( uAlphaBlending, uColor.a ), uColor.a );\n
110   }\n
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 {
212 }
213
214 ImageRenderer::~ImageRenderer()
215 {
216 }
217
218 void ImageRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap )
219 {
220   std::string oldImageUrl = mImageUrl;
221
222   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
223   if( imageURLValue )
224   {
225     imageURLValue->Get( mImageUrl );
226     if( !mImageUrl.empty() )
227     {
228       mImage.Reset();
229     }
230
231     Property::Value* fittingValue = propertyMap.Find( IMAGE_FITTING_MODE );
232     if( fittingValue )
233     {
234       std::string fitting;
235       fittingValue->Get( fitting );
236
237       mFittingMode = FittingMode::DEFAULT;
238       if( fitting == SHRINK_TO_FIT )
239       {
240         mFittingMode = FittingMode::SHRINK_TO_FIT;
241       }
242       else if( fitting == SCALE_TO_FILL )
243       {
244         mFittingMode = FittingMode::SCALE_TO_FILL;
245       }
246       else if( fitting == FIT_WIDTH )
247       {
248         mFittingMode = FittingMode::FIT_WIDTH;
249       }
250       else if( fitting == FIT_HEIGHT )
251       {
252         mFittingMode = FittingMode::FIT_HEIGHT;
253       }
254       else if( fitting == DEFAULT )
255       {
256         mFittingMode = FittingMode::DEFAULT;
257       }
258       else
259       {
260         DALI_ASSERT_ALWAYS("Unknown fitting mode");
261       }
262     }
263
264     Property::Value* samplingValue = propertyMap.Find( IMAGE_SAMPLING_MODE );
265     if( samplingValue )
266     {
267       std::string sampling;
268       samplingValue->Get( sampling );
269
270       mSamplingMode = SamplingMode::DEFAULT;
271       if( sampling == BOX )
272       {
273         mSamplingMode = SamplingMode::BOX;
274       }
275       else if( sampling == NEAREST )
276       {
277         mSamplingMode = SamplingMode::NEAREST;
278       }
279       else if( sampling == LINEAR )
280       {
281         mSamplingMode = SamplingMode::LINEAR;
282       }
283       else if( sampling == BOX_THEN_NEAREST )
284       {
285         mSamplingMode = SamplingMode::BOX_THEN_NEAREST;
286       }
287       else if( sampling == BOX_THEN_LINEAR )
288       {
289         mSamplingMode = SamplingMode::BOX_THEN_LINEAR;
290       }
291       else if( sampling == NO_FILTER )
292       {
293         mSamplingMode = SamplingMode::NO_FILTER;
294       }
295       else if( sampling == DONT_CARE )
296       {
297         mSamplingMode = SamplingMode::DONT_CARE;
298       }
299       else if( sampling == DEFAULT )
300       {
301         mSamplingMode = SamplingMode::DEFAULT;
302       }
303       else
304       {
305         DALI_ASSERT_ALWAYS("Unknown sampling mode");
306       }
307     }
308
309     int desiredWidth = 0;
310     Property::Value* desiredWidthValue = propertyMap.Find( IMAGE_DESIRED_WIDTH );
311     if( desiredWidthValue )
312     {
313       desiredWidthValue->Get( desiredWidth );
314     }
315
316     int desiredHeight = 0;
317     Property::Value* desiredHeightValue = propertyMap.Find( IMAGE_DESIRED_HEIGHT );
318     if( desiredHeightValue )
319     {
320       desiredHeightValue->Get( desiredHeight );
321     }
322
323     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
324   }
325
326   // remove old renderer if exit
327   if( mImpl->mRenderer )
328   {
329     if( actor ) //remove old renderer from actor
330     {
331       actor.RemoveRenderer( mImpl->mRenderer );
332     }
333     if( !oldImageUrl.empty() ) //clean old renderer from cache
334     {
335       CleanCache( oldImageUrl );
336     }
337   }
338
339   // if actor is on stage, create new renderer and apply to actor
340   if( actor && actor.OnStage() )
341   {
342     SetOnStage( actor );
343   }
344 }
345
346 void ImageRenderer::SetSize( const Vector2& size )
347 {
348   ControlRenderer::SetSize( size );
349 }
350
351 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
352 {
353   if(mImage)
354   {
355     naturalSize.x = mImage.GetWidth();
356     naturalSize.y = mImage.GetHeight();
357     return;
358   }
359   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
360   {
361     naturalSize.x = mDesiredSize.GetWidth();
362     naturalSize.y = mDesiredSize.GetHeight();
363     return;
364   }
365   else if( !mImageUrl.empty() )
366   {
367     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
368     naturalSize.x = dimentions.GetWidth();
369     naturalSize.y = dimentions.GetHeight();
370     return;
371   }
372
373   naturalSize = Vector2::ZERO;
374 }
375
376 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
377 {
378   ControlRenderer::SetClipRect( clipRect );
379 }
380
381 void ImageRenderer::SetOffset( const Vector2& offset )
382 {
383 }
384
385 Renderer ImageRenderer::CreateRenderer() const
386 {
387   Geometry geometry;
388   Shader shader;
389
390   if( !mImpl->mCustomShader )
391   {
392     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
393     shader = GetImageShader(mFactoryCache);
394   }
395   else
396   {
397     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
398     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
399     {
400       shader = GetImageShader(mFactoryCache);
401     }
402     else
403     {
404       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
405                              mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
406                              mImpl->mCustomShader->mHints );
407       if( mImpl->mCustomShader->mVertexShader.empty() )
408       {
409         shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
410         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
411         shader.RegisterProperty( ALPHA_BLENDING_UNIFORM_NAME, 1.f );
412       }
413     }
414   }
415
416   Material material = Material::New( shader );
417   return Renderer::New( geometry, material );
418 }
419
420 void ImageRenderer::InitializeRenderer( const std::string& imageUrl )
421 {
422   if( imageUrl.empty() )
423   {
424     return;
425   }
426
427   mImageUrl = imageUrl;
428   mImpl->mRenderer.Reset();
429
430   if( !mImpl->mCustomShader &&
431       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
432       ( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) )
433   {
434     mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
435     if( !mImpl->mRenderer )
436     {
437       Vector4 atlasRect;
438       Material material = mAtlasManager.Add(atlasRect, imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
439       if( material )
440       {
441         Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
442         mImpl->mRenderer = Renderer::New( geometry, material );
443         mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
444       }
445       else // big image, atlasing is not applied
446       {
447         mImpl->mRenderer = CreateRenderer();
448
449         ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
450         image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
451         Material material = mImpl->mRenderer.GetMaterial();
452         material.AddTexture( image, TEXTURE_UNIFORM_NAME );
453       }
454
455       mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
456     }
457
458     mImpl->mFlags |= Impl::IS_FROM_CACHE;
459   }
460   else
461   {
462     // for custom shader or remote image, renderer is not cached and atlas is not applied
463
464     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
465     mImpl->mRenderer = CreateRenderer();
466     ResourceImage resourceImage = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
467     resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
468     ApplyImageToSampler( resourceImage );
469   }
470 }
471
472 void ImageRenderer::InitializeRenderer( const Image& image )
473 {
474   mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
475
476   mImpl->mRenderer = CreateRenderer();
477
478   if( image )
479   {
480     ApplyImageToSampler( image );
481   }
482 }
483
484
485 void ImageRenderer::DoSetOnStage( Actor& actor )
486 {
487   if( !mImageUrl.empty() )
488   {
489     InitializeRenderer( mImageUrl );
490   }
491   else
492   {
493     InitializeRenderer( mImage );
494   }
495
496   EnablePreMultipliedAlpha( mIsAlphaPreMultiplied );
497 }
498
499 void ImageRenderer::DoSetOffStage( Actor& actor )
500 {
501   //If we own the image then make sure we release it when we go off stage
502   if( !mImageUrl.empty() )
503   {
504     actor.RemoveRenderer( mImpl->mRenderer );
505     CleanCache(mImageUrl);
506
507     mImage.Reset();
508   }
509   else
510   {
511     actor.RemoveRenderer( mImpl->mRenderer );
512     mImpl->mRenderer.Reset();
513   }
514 }
515
516 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
517 {
518   map.Clear();
519   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
520   if( !mImageUrl.empty() )
521   {
522     map.Insert( IMAGE_URL_NAME, mImageUrl );
523     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
524     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
525   }
526   else if( mImage )
527   {
528     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
529     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
530
531     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
532     if( resourceImage )
533     {
534       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
535     }
536   }
537
538   switch( mFittingMode )
539   {
540     case Dali::FittingMode::FIT_HEIGHT:
541     {
542       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
543       break;
544     }
545     case Dali::FittingMode::FIT_WIDTH:
546     {
547       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
548       break;
549     }
550     case Dali::FittingMode::SCALE_TO_FILL:
551     {
552       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
553       break;
554     }
555     case Dali::FittingMode::SHRINK_TO_FIT:
556     {
557       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
558       break;
559     }
560     default:
561     {
562       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
563       break;
564     }
565   }
566
567   switch( mSamplingMode )
568   {
569     case Dali::SamplingMode::BOX:
570     {
571       map.Insert( IMAGE_SAMPLING_MODE, BOX );
572       break;
573     }
574     case Dali::SamplingMode::NEAREST:
575     {
576       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
577       break;
578     }
579     case Dali::SamplingMode::LINEAR:
580     {
581       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
582       break;
583     }
584     case Dali::SamplingMode::BOX_THEN_LINEAR:
585     {
586       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
587       break;
588     }
589     case Dali::SamplingMode::BOX_THEN_NEAREST:
590     {
591       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
592       break;
593     }
594     case Dali::SamplingMode::NO_FILTER:
595     {
596       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
597       break;
598     }
599     case Dali::SamplingMode::DONT_CARE:
600     {
601       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
602       break;
603     }
604     default:
605     {
606       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
607       break;
608     }
609   }
610 }
611
612 Shader ImageRenderer::GetImageShader( RendererFactoryCache& factoryCache )
613 {
614   Shader shader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
615   if( !shader )
616   {
617     shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
618     factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
619     shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
620     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
621     shader.RegisterProperty( ALPHA_BLENDING_UNIFORM_NAME, 1.f );
622   }
623   return shader;
624 }
625
626 void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
627 {
628   if( mImageUrl != imageUrl )
629   {
630     std::string oldImageUrl = mImageUrl;
631     mImageUrl = imageUrl;
632     mDesiredSize = size;
633     mFittingMode = fittingMode;
634     mSamplingMode = samplingMode;
635     mImage.Reset();
636
637     if( mImpl->mRenderer )
638     {
639       if( GetIsFromCache() ) // if renderer is from cache, remove the old one
640       {
641         //remove old renderer
642         if( actor )
643         {
644           actor.RemoveRenderer( mImpl->mRenderer );
645         }
646
647         //clean the cache
648         if( !oldImageUrl.empty() )
649         {
650           CleanCache(oldImageUrl);
651         }
652
653         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
654         {
655           SetOnStage(actor);
656         }
657       }
658       else // if renderer is not from cache, reuse the same renderer and only change the texture
659       {
660         ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
661         image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
662         ApplyImageToSampler( image );
663       }
664     }
665   }
666 }
667
668 void ImageRenderer::SetImage( Actor& actor, const Image& image )
669 {
670   if( mImage != image )
671   {
672     mImage = image;
673
674     if( mImpl->mRenderer )
675     {
676       if( GetIsFromCache() ) // if renderer is from cache, remove the old one
677       {
678         //remove old renderer
679         if( actor )
680         {
681           actor.RemoveRenderer( mImpl->mRenderer );
682         }
683
684         //clean the cache
685         if( !mImageUrl.empty() )
686         {
687           CleanCache(mImageUrl);
688         }
689         mImageUrl.clear();
690
691         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
692         {
693           SetOnStage(actor);
694         }
695       }
696       else // if renderer is not from cache, reuse the same renderer and only change the texture
697       {
698         ApplyImageToSampler( image );
699       }
700     }
701
702     mImageUrl.clear();
703     mDesiredSize = ImageDimensions();
704     mFittingMode = FittingMode::DEFAULT;
705     mSamplingMode = SamplingMode::DEFAULT;
706   }
707 }
708
709 void ImageRenderer::EnablePreMultipliedAlpha( bool preMultipled )
710 {
711   mIsAlphaPreMultiplied = preMultipled;
712   if( mImpl->mRenderer )
713   {
714     Material material = mImpl->mRenderer.GetMaterial();
715
716     if( preMultipled )
717     {
718       material.SetBlendFunc( BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA,
719                              BlendingFactor::ONE, BlendingFactor::ONE );
720       if( !mImpl->mCustomShader || mImpl->mCustomShader->mVertexShader.empty())
721       {
722         material.RegisterProperty( ALPHA_BLENDING_UNIFORM_NAME, 0.f );
723       }
724     }
725     else
726     {
727       // using default blend func
728       material.SetBlendFunc( BlendingFactor::SRC_ALPHA, BlendingFactor::ONE_MINUS_SRC_ALPHA,
729                               BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA );
730
731       Property::Index index = material.GetPropertyIndex( ALPHA_BLENDING_UNIFORM_NAME );
732       if( index != Property::INVALID_INDEX ) // only set value when the property already exist on the Material
733       {
734         material.SetProperty( index, 1.f );
735       }
736     }
737   }
738 }
739
740 void ImageRenderer::ApplyImageToSampler( const Image& image )
741 {
742   if( image )
743   {
744     Material material = mImpl->mRenderer.GetMaterial();
745     if( material )
746     {
747       int index = material.GetTextureIndex( TEXTURE_UNIFORM_NAME );
748       if( index != -1 )
749       {
750         material.SetTextureImage( index, image );
751         return;
752       }
753
754       material.AddTexture( image, TEXTURE_UNIFORM_NAME );
755     }
756   }
757 }
758
759 void ImageRenderer::OnImageLoaded( ResourceImage image )
760 {
761   if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
762   {
763     Image brokenImage = RendererFactory::GetBrokenRendererImage();
764     if( mImpl->mRenderer )
765     {
766       ApplyImageToSampler( brokenImage );
767     }
768   }
769 }
770
771 void ImageRenderer::CleanCache(const std::string& url)
772 {
773   Material material = mImpl->mRenderer.GetMaterial();
774
775   Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
776   Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
777   if( index != Property::INVALID_INDEX )
778   {
779     Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
780     atlasRectValue.Get( atlasRect );
781   }
782
783   mImpl->mRenderer.Reset();
784   if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
785   {
786     mAtlasManager.Remove( material, atlasRect );
787   }
788 }
789
790 } // namespace Internal
791
792 } // namespace Toolkit
793
794 } // namespace Dali