Merge "Change KeyboardFocusManager to always focus when SetCurrentFocus is called...
[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 TEXTURE_RECT_UNIFORM_NAME = "uTextureRect";
75 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
76
77 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
78   attribute mediump vec2 aPosition;\n
79   varying mediump vec2 vTexCoord;\n
80   uniform mediump mat4 uMvpMatrix;\n
81   uniform mediump vec3 uSize;\n
82   uniform mediump vec4 uTextureRect;\n
83   \n
84   void main()\n
85   {\n
86     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
87     vertexPosition.xyz *= uSize;\n
88     vertexPosition = uMvpMatrix * vertexPosition;\n
89     \n
90     vTexCoord = mix( uTextureRect.xy, uTextureRect.zw, aPosition + vec2(0.5));\n
91     gl_Position = vertexPosition;\n
92   }\n
93 );
94
95 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
96   varying mediump vec2 vTexCoord;\n
97   uniform sampler2D sTexture;\n
98   uniform lowp vec4 uColor;\n
99   \n
100   void main()\n
101   {\n
102     gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
103   }\n
104 );
105
106 Geometry GenerateGeometry( const Vector< Vector2 >& vertices, const Vector< unsigned int >& indices )
107 {
108   Property::Map vertexFormat;
109   vertexFormat[ "aPosition" ] = Property::VECTOR2;
110   PropertyBuffer vertexPropertyBuffer = PropertyBuffer::New( vertexFormat, vertices.Size() );
111   if( vertices.Size() > 0 )
112   {
113     vertexPropertyBuffer.SetData( &vertices[ 0 ] );
114   }
115
116   Property::Map indexFormat;
117   indexFormat[ "indices" ] = Property::INTEGER;
118   PropertyBuffer indexPropertyBuffer = PropertyBuffer::New( indexFormat, indices.Size() );
119   if( indices.Size() > 0 )
120   {
121     indexPropertyBuffer.SetData( &indices[ 0 ] );
122   }
123
124   // Create the geometry object
125   Geometry geometry = Geometry::New();
126   geometry.AddVertexBuffer( vertexPropertyBuffer );
127   geometry.SetIndexBuffer( indexPropertyBuffer );
128   geometry.SetGeometryType( Geometry::TRIANGLE_STRIP );
129
130   return geometry;
131 }
132
133 Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gridSize )
134 {
135   Geometry geometry;
136
137   if( gridSize == ImageDimensions( 1, 1 ) )
138   {
139     geometry = factoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
140     if( !geometry )
141     {
142       geometry =  factoryCache.CreateQuadGeometry();
143       factoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
144     }
145   }
146   else
147   {
148     uint16_t gridWidth = gridSize.GetWidth();
149     uint16_t gridHeight = gridSize.GetHeight();
150
151     // Create vertices
152     Vector< Vector2 > vertices;
153     vertices.Reserve( ( gridWidth + 1 ) * ( gridHeight + 1 ) );
154
155     for( int y = 0; y < gridHeight + 1; ++y )
156     {
157       for( int x = 0; x < gridWidth + 1; ++x )
158       {
159         vertices.PushBack( Vector2( (float)x/gridWidth - 0.5f, (float)y/gridHeight  - 0.5f) );
160       }
161     }
162
163     // Create indices
164     Vector< unsigned int > indices;
165     indices.Reserve( (gridWidth+2)*gridHeight*2 - 2);
166
167     for( unsigned int row = 0u; row < gridHeight; ++row )
168     {
169       unsigned int rowStartIndex = row*(gridWidth+1u);
170       unsigned int nextRowStartIndex = rowStartIndex + gridWidth +1u;
171
172       if( row != 0u ) // degenerate index on non-first row
173       {
174         indices.PushBack( rowStartIndex );
175       }
176
177       for( unsigned int column = 0u; column < gridWidth+1u; column++) // main strip
178       {
179         indices.PushBack( rowStartIndex + column);
180         indices.PushBack( nextRowStartIndex + column);
181       }
182
183       if( row != gridHeight-1u ) // degenerate index on non-last row
184       {
185         indices.PushBack( nextRowStartIndex + gridWidth );
186       }
187     }
188
189     return GenerateGeometry( vertices, indices );
190   }
191
192   return geometry;
193 }
194
195 } //unnamed namespace
196
197 ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
198 : ControlRenderer( factoryCache ),
199   mAtlasManager( atlasManager ),
200   mTextureRect( FULL_TEXTURE_RECT ),
201   mDesiredSize(),
202   mFittingMode( FittingMode::DEFAULT ),
203   mSamplingMode( SamplingMode::DEFAULT )
204 {
205 }
206
207 ImageRenderer::~ImageRenderer()
208 {
209 }
210
211 void ImageRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap )
212 {
213   std::string oldImageUrl = mImageUrl;
214
215   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
216   if( imageURLValue )
217   {
218     imageURLValue->Get( mImageUrl );
219     if( !mImageUrl.empty() )
220     {
221       mImage.Reset();
222     }
223
224     Property::Value* fittingValue = propertyMap.Find( IMAGE_FITTING_MODE );
225     if( fittingValue )
226     {
227       std::string fitting;
228       fittingValue->Get( fitting );
229
230       mFittingMode = FittingMode::DEFAULT;
231       if( fitting == SHRINK_TO_FIT )
232       {
233         mFittingMode = FittingMode::SHRINK_TO_FIT;
234       }
235       else if( fitting == SCALE_TO_FILL )
236       {
237         mFittingMode = FittingMode::SCALE_TO_FILL;
238       }
239       else if( fitting == FIT_WIDTH )
240       {
241         mFittingMode = FittingMode::FIT_WIDTH;
242       }
243       else if( fitting == FIT_HEIGHT )
244       {
245         mFittingMode = FittingMode::FIT_HEIGHT;
246       }
247       else if( fitting == DEFAULT )
248       {
249         mFittingMode = FittingMode::DEFAULT;
250       }
251       else
252       {
253         DALI_ASSERT_ALWAYS("Unknown fitting mode");
254       }
255     }
256
257     Property::Value* samplingValue = propertyMap.Find( IMAGE_SAMPLING_MODE );
258     if( samplingValue )
259     {
260       std::string sampling;
261       samplingValue->Get( sampling );
262
263       mSamplingMode = SamplingMode::DEFAULT;
264       if( sampling == BOX )
265       {
266         mSamplingMode = SamplingMode::BOX;
267       }
268       else if( sampling == NEAREST )
269       {
270         mSamplingMode = SamplingMode::NEAREST;
271       }
272       else if( sampling == LINEAR )
273       {
274         mSamplingMode = SamplingMode::LINEAR;
275       }
276       else if( sampling == BOX_THEN_NEAREST )
277       {
278         mSamplingMode = SamplingMode::BOX_THEN_NEAREST;
279       }
280       else if( sampling == BOX_THEN_LINEAR )
281       {
282         mSamplingMode = SamplingMode::BOX_THEN_LINEAR;
283       }
284       else if( sampling == NO_FILTER )
285       {
286         mSamplingMode = SamplingMode::NO_FILTER;
287       }
288       else if( sampling == DONT_CARE )
289       {
290         mSamplingMode = SamplingMode::DONT_CARE;
291       }
292       else if( sampling == DEFAULT )
293       {
294         mSamplingMode = SamplingMode::DEFAULT;
295       }
296       else
297       {
298         DALI_ASSERT_ALWAYS("Unknown sampling mode");
299       }
300     }
301
302     int desiredWidth = 0;
303     Property::Value* desiredWidthValue = propertyMap.Find( IMAGE_DESIRED_WIDTH );
304     if( desiredWidthValue )
305     {
306       desiredWidthValue->Get( desiredWidth );
307     }
308
309     int desiredHeight = 0;
310     Property::Value* desiredHeightValue = propertyMap.Find( IMAGE_DESIRED_HEIGHT );
311     if( desiredHeightValue )
312     {
313       desiredHeightValue->Get( desiredHeight );
314     }
315
316     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
317   }
318
319   // remove old renderer if exit
320   if( mImpl->mRenderer )
321   {
322     if( actor ) //remove old renderer from actor
323     {
324       actor.RemoveRenderer( mImpl->mRenderer );
325     }
326     if( !oldImageUrl.empty() ) //clean old renderer from cache
327     {
328       CleanCache( oldImageUrl );
329     }
330   }
331
332   // if actor is on stage, create new renderer and apply to actor
333   if( actor && actor.OnStage() )
334   {
335     SetOnStage( actor );
336   }
337 }
338
339 void ImageRenderer::SetSize( const Vector2& size )
340 {
341   ControlRenderer::SetSize( size );
342 }
343
344 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
345 {
346   if(mImage)
347   {
348     naturalSize.x = mImage.GetWidth();
349     naturalSize.y = mImage.GetHeight();
350     return;
351   }
352   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
353   {
354     naturalSize.x = mDesiredSize.GetWidth();
355     naturalSize.y = mDesiredSize.GetHeight();
356     return;
357   }
358   else if( !mImageUrl.empty() )
359   {
360     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
361     naturalSize.x = dimentions.GetWidth();
362     naturalSize.y = dimentions.GetHeight();
363     return;
364   }
365
366   naturalSize = Vector2::ZERO;
367 }
368
369 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
370 {
371   ControlRenderer::SetClipRect( clipRect );
372 }
373
374 void ImageRenderer::SetOffset( const Vector2& offset )
375 {
376 }
377
378 Renderer ImageRenderer::CreateRenderer() const
379 {
380   Geometry geometry;
381   Shader shader;
382
383   if( !mImpl->mCustomShader )
384   {
385     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
386     shader = GetImageShader(mFactoryCache);
387   }
388   else
389   {
390     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
391     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
392     {
393       shader = GetImageShader(mFactoryCache);
394     }
395     else
396     {
397       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
398                              mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
399                              mImpl->mCustomShader->mHints );
400     }
401   }
402
403   Material material = Material::New( shader );
404   return Renderer::New( geometry, material );
405 }
406
407 void ImageRenderer::InitializeRenderer( const std::string& imageUrl )
408 {
409   if( imageUrl.empty() )
410   {
411     return;
412   }
413
414   mImageUrl = imageUrl;
415   mImpl->mRenderer.Reset();
416
417   if( !mImpl->mCustomShader &&
418       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
419       ( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) )
420   {
421     mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
422     if( !mImpl->mRenderer )
423     {
424       Material material = mAtlasManager.Add(mTextureRect, imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
425       if( material )
426       {
427         Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
428         mImpl->mRenderer = Renderer::New( geometry, material );
429         SetTextureRectUniform(mTextureRect);
430       }
431       else // big image, atlasing is not applied
432       {
433         mImpl->mRenderer = CreateRenderer();
434         mTextureRect = FULL_TEXTURE_RECT;
435         SetTextureRectUniform(mTextureRect);
436
437         ResourceImage image = Dali::ResourceImage::New( imageUrl );
438         image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
439         Material material = mImpl->mRenderer.GetMaterial();
440         material.AddTexture( image, TEXTURE_UNIFORM_NAME );
441       }
442
443       mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
444     }
445     else
446     {
447       Property::Value textureRect = mImpl->mRenderer.GetProperty( mImpl->mRenderer.GetPropertyIndex(TEXTURE_RECT_UNIFORM_NAME) );
448       textureRect.Get( mTextureRect );
449     }
450     mImpl->mFlags |= Impl::IS_FROM_CACHE;
451   }
452   else
453   {
454     // for custom shader or remote image, renderer is not cached and atlas is not applied
455
456     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
457     mImpl->mRenderer = CreateRenderer();
458     ResourceImage resourceImage = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
459     resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
460     ApplyImageToSampler( resourceImage );
461
462     // custom vertex shader does not need texture rect uniform
463     if( mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty() )
464     {
465       return;
466     }
467
468     mTextureRect = FULL_TEXTURE_RECT;
469     SetTextureRectUniform( mTextureRect );
470   }
471 }
472
473 void ImageRenderer::InitializeRenderer( const Image& image )
474 {
475   mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
476
477   if( !image )
478   {
479     return;
480   }
481
482   mImpl->mRenderer = CreateRenderer();
483   ApplyImageToSampler( image );
484
485   // default shader or custom shader with the default image vertex shader
486   if( !mImpl->mCustomShader || mImpl->mCustomShader->mVertexShader.empty() )
487   {
488     mTextureRect = FULL_TEXTURE_RECT;
489     SetTextureRectUniform( mTextureRect );
490   }
491 }
492
493
494 void ImageRenderer::DoSetOnStage( Actor& actor )
495 {
496   if( !mImageUrl.empty() )
497   {
498     InitializeRenderer( mImageUrl );
499   }
500   else if( mImage )
501   {
502     InitializeRenderer( mImage );
503   }
504 }
505
506 void ImageRenderer::DoSetOffStage( Actor& actor )
507 {
508   //If we own the image then make sure we release it when we go off stage
509   if( !mImageUrl.empty() )
510   {
511     actor.RemoveRenderer( mImpl->mRenderer );
512     CleanCache(mImageUrl);
513
514     mImage.Reset();
515   }
516   else
517   {
518     actor.RemoveRenderer( mImpl->mRenderer );
519     mImpl->mRenderer.Reset();
520   }
521 }
522
523 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
524 {
525   map.Clear();
526   map.Insert( RENDERER_TYPE, RENDERER_TYPE_VALUE );
527   if( !mImageUrl.empty() )
528   {
529     map.Insert( IMAGE_URL_NAME, mImageUrl );
530     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
531     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
532   }
533   else if( mImage )
534   {
535     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
536     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
537
538     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
539     if( resourceImage )
540     {
541       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
542     }
543   }
544
545   switch( mFittingMode )
546   {
547     case Dali::FittingMode::FIT_HEIGHT:
548     {
549       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
550       break;
551     }
552     case Dali::FittingMode::FIT_WIDTH:
553     {
554       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
555       break;
556     }
557     case Dali::FittingMode::SCALE_TO_FILL:
558     {
559       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
560       break;
561     }
562     case Dali::FittingMode::SHRINK_TO_FIT:
563     {
564       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
565       break;
566     }
567     default:
568     {
569       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
570       break;
571     }
572   }
573
574   switch( mSamplingMode )
575   {
576     case Dali::SamplingMode::BOX:
577     {
578       map.Insert( IMAGE_SAMPLING_MODE, BOX );
579       break;
580     }
581     case Dali::SamplingMode::NEAREST:
582     {
583       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
584       break;
585     }
586     case Dali::SamplingMode::LINEAR:
587     {
588       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
589       break;
590     }
591     case Dali::SamplingMode::BOX_THEN_LINEAR:
592     {
593       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
594       break;
595     }
596     case Dali::SamplingMode::BOX_THEN_NEAREST:
597     {
598       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
599       break;
600     }
601     case Dali::SamplingMode::NO_FILTER:
602     {
603       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
604       break;
605     }
606     case Dali::SamplingMode::DONT_CARE:
607     {
608       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
609       break;
610     }
611     default:
612     {
613       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
614       break;
615     }
616   }
617 }
618
619 Shader ImageRenderer::GetImageShader( RendererFactoryCache& factoryCache )
620 {
621   Shader shader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
622   if( !shader )
623   {
624     shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
625     factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
626   }
627   return shader;
628 }
629
630 void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
631 {
632   if( mImageUrl != imageUrl )
633   {
634     std::string oldImageUrl = mImageUrl;
635     mImageUrl = imageUrl;
636     mDesiredSize = size;
637     mFittingMode = fittingMode;
638     mSamplingMode = samplingMode;
639     mImage.Reset();
640
641     if( mImpl->mRenderer )
642     {
643       if( GetIsFromCache() ) // if renderer is from cache, remove the old one
644       {
645         //remove old renderer
646         if( actor )
647         {
648           actor.RemoveRenderer( mImpl->mRenderer );
649         }
650
651         //clean the cache
652         if( !oldImageUrl.empty() )
653         {
654           CleanCache(oldImageUrl);
655         }
656
657         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
658         {
659           SetOnStage(actor);
660         }
661       }
662       else // if renderer is not from cache, reuse the same renderer and only change the texture
663       {
664         ResourceImage image = Dali::ResourceImage::New( imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
665         image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
666         ApplyImageToSampler( image );
667       }
668     }
669   }
670 }
671
672 void ImageRenderer::SetImage( Actor& actor, const Image& image )
673 {
674   if( mImage != image )
675   {
676     mImage = image;
677
678     if( mImpl->mRenderer )
679     {
680       if( GetIsFromCache() ) // if renderer is from cache, remove the old one
681       {
682         //remove old renderer
683         if( actor )
684         {
685           actor.RemoveRenderer( mImpl->mRenderer );
686         }
687
688         //clean the cache
689         if( !mImageUrl.empty() )
690         {
691           CleanCache(mImageUrl);
692         }
693         mImageUrl.clear();
694
695         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
696         {
697           SetOnStage(actor);
698         }
699       }
700       else // if renderer is not from cache, reuse the same renderer and only change the texture
701       {
702         ApplyImageToSampler( image );
703       }
704     }
705
706     mImageUrl.clear();
707     mDesiredSize = ImageDimensions();
708     mFittingMode = FittingMode::DEFAULT;
709     mSamplingMode = SamplingMode::DEFAULT;
710   }
711 }
712
713 void ImageRenderer::ApplyImageToSampler( const Image& image )
714 {
715   if( image )
716   {
717     Material material = mImpl->mRenderer.GetMaterial();
718     if( material )
719     {
720       int index = material.GetTextureIndex( TEXTURE_UNIFORM_NAME );
721       if( index != -1 )
722       {
723         material.SetTextureImage( index, image );
724         return;
725       }
726
727       material.AddTexture( image, TEXTURE_UNIFORM_NAME );
728     }
729   }
730 }
731
732 void ImageRenderer::OnImageLoaded( ResourceImage image )
733 {
734   if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
735   {
736     Image brokenImage = RendererFactory::GetBrokenRendererImage();
737     if( mImpl->mRenderer )
738     {
739       ApplyImageToSampler( brokenImage );
740     }
741   }
742 }
743
744 void ImageRenderer::SetTextureRectUniform( const Vector4& textureRect )
745 {
746   if( mImpl->mRenderer )
747   {
748     // Register/Set property.
749     mImpl->mRenderer.RegisterProperty( TEXTURE_RECT_UNIFORM_NAME, textureRect );
750   }
751 }
752
753 void ImageRenderer::CleanCache(const std::string& url)
754 {
755   Material material = mImpl->mRenderer.GetMaterial();
756   mImpl->mRenderer.Reset();
757   if( mFactoryCache.CleanRendererCache( url ) )
758   {
759     mAtlasManager.Remove( material, mTextureRect );
760   }
761 }
762
763 } // namespace Internal
764
765 } // namespace Toolkit
766
767 } // namespace Dali