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