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