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