ImageRenderer provides property to indicate async/sync resource loading
[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/devel-api/images/atlas.h>
26 #include <dali/devel-api/adaptor-framework/bitmap-loader.h>
27 #include <dali/integration-api/debug.h>
28
29 // INTERNAL HEADER
30 #include <dali-toolkit/internal/controls/renderers/renderer-string-constants.h>
31 #include <dali-toolkit/internal/controls/renderers/renderer-factory-impl.h>
32 #include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
33 #include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
34 #include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
35 #include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
36
37 namespace Dali
38 {
39
40 namespace Toolkit
41 {
42
43 namespace Internal
44 {
45
46 namespace
47 {
48 const char HTTP_URL[] = "http://";
49 const char HTTPS_URL[] = "https://";
50
51 // property names
52 const char * const IMAGE_FITTING_MODE( "fittingMode" );
53 const char * const IMAGE_SAMPLING_MODE( "samplingMode" );
54 const char * const IMAGE_DESIRED_WIDTH( "desiredWidth" );
55 const char * const IMAGE_DESIRED_HEIGHT( "desiredHeight" );
56 const char * const SYNCHRONOUS_LOADING( "synchronousLoading" );
57
58 // fitting modes
59 const char * const SHRINK_TO_FIT("SHRINK_TO_FIT");
60 const char * const SCALE_TO_FILL("SCALE_TO_FILL");
61 const char * const FIT_WIDTH("FIT_WIDTH");
62 const char * const FIT_HEIGHT("FIT_HEIGHT");
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("BOX_THEN_NEAREST");
70 const char * const BOX_THEN_LINEAR("BOX_THEN_LINEAR");
71 const char * const NO_FILTER("NO_FILTER");
72 const char * const DONT_CARE("DONT_CARE");
73
74 const std::string PIXEL_AREA_UNIFORM_NAME = "pixelArea";
75
76 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
77
78 const char* DEFAULT_SAMPLER_TYPENAME = "sampler2D";
79
80 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
81   attribute mediump vec2 aPosition;\n
82   uniform mediump mat4 uMvpMatrix;\n
83   uniform mediump vec3 uSize;\n
84   uniform mediump vec4 uAtlasRect;\n
85   uniform mediump vec4 pixelArea;
86   varying mediump vec2 vTexCoord;\n
87   \n
88   void main()\n
89   {\n
90     mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
91     vertexPosition.xyz *= uSize;\n
92     vertexPosition = uMvpMatrix * vertexPosition;\n
93     \n
94     vTexCoord = mix( uAtlasRect.xy, uAtlasRect.zw, pixelArea.xy+pixelArea.zw*(aPosition + vec2(0.5) ) );\n
95     gl_Position = vertexPosition;\n
96   }\n
97 );
98
99 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
100   varying mediump vec2 vTexCoord;\n
101   uniform sampler2D sTexture;\n
102   uniform lowp vec4 uColor;\n
103   \n
104   void main()\n
105   {\n
106     gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\n
107   }\n
108 );
109
110 Geometry CreateGeometry( RendererFactoryCache& factoryCache, ImageDimensions gridSize )
111 {
112   Geometry geometry;
113
114   if( gridSize == ImageDimensions( 1, 1 ) )
115   {
116     geometry = factoryCache.GetGeometry( RendererFactoryCache::QUAD_GEOMETRY );
117     if( !geometry )
118     {
119       geometry =  Geometry::QUAD();
120       factoryCache.SaveGeometry( RendererFactoryCache::QUAD_GEOMETRY, geometry );
121     }
122   }
123   else
124   {
125     geometry = RendererFactoryCache::CreateGridGeometry( gridSize );
126   }
127
128   return geometry;
129 }
130
131 } //unnamed namespace
132
133 ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
134 : ControlRenderer( factoryCache ),
135   mAtlasManager( atlasManager ),
136   mDesiredSize(),
137   mFittingMode( FittingMode::DEFAULT ),
138   mSamplingMode( SamplingMode::DEFAULT ),
139   mNativeFragmentShaderCode( ),
140   mNativeImageFlag( false )
141 {
142 }
143
144 ImageRenderer::~ImageRenderer()
145 {
146 }
147
148 void ImageRenderer::DoInitialize( Actor& actor, const Property::Map& propertyMap )
149 {
150   std::string oldImageUrl = mImageUrl;
151
152   Property::Value* imageURLValue = propertyMap.Find( IMAGE_URL_NAME );
153   if( imageURLValue )
154   {
155     imageURLValue->Get( mImageUrl );
156     if( !mImageUrl.empty() )
157     {
158       mImage.Reset();
159     }
160
161     Property::Value* fittingValue = propertyMap.Find( IMAGE_FITTING_MODE );
162     if( fittingValue )
163     {
164       std::string fitting;
165       fittingValue->Get( fitting );
166
167       mFittingMode = FittingMode::DEFAULT;
168       if( fitting == SHRINK_TO_FIT )
169       {
170         mFittingMode = FittingMode::SHRINK_TO_FIT;
171       }
172       else if( fitting == SCALE_TO_FILL )
173       {
174         mFittingMode = FittingMode::SCALE_TO_FILL;
175       }
176       else if( fitting == FIT_WIDTH )
177       {
178         mFittingMode = FittingMode::FIT_WIDTH;
179       }
180       else if( fitting == FIT_HEIGHT )
181       {
182         mFittingMode = FittingMode::FIT_HEIGHT;
183       }
184       else if( fitting == DEFAULT )
185       {
186         mFittingMode = FittingMode::DEFAULT;
187       }
188       else
189       {
190         DALI_ASSERT_ALWAYS("Unknown fitting mode");
191       }
192     }
193
194     Property::Value* samplingValue = propertyMap.Find( IMAGE_SAMPLING_MODE );
195     if( samplingValue )
196     {
197       std::string sampling;
198       samplingValue->Get( sampling );
199
200       mSamplingMode = SamplingMode::DEFAULT;
201       if( sampling == BOX )
202       {
203         mSamplingMode = SamplingMode::BOX;
204       }
205       else if( sampling == NEAREST )
206       {
207         mSamplingMode = SamplingMode::NEAREST;
208       }
209       else if( sampling == LINEAR )
210       {
211         mSamplingMode = SamplingMode::LINEAR;
212       }
213       else if( sampling == BOX_THEN_NEAREST )
214       {
215         mSamplingMode = SamplingMode::BOX_THEN_NEAREST;
216       }
217       else if( sampling == BOX_THEN_LINEAR )
218       {
219         mSamplingMode = SamplingMode::BOX_THEN_LINEAR;
220       }
221       else if( sampling == NO_FILTER )
222       {
223         mSamplingMode = SamplingMode::NO_FILTER;
224       }
225       else if( sampling == DONT_CARE )
226       {
227         mSamplingMode = SamplingMode::DONT_CARE;
228       }
229       else if( sampling == DEFAULT )
230       {
231         mSamplingMode = SamplingMode::DEFAULT;
232       }
233       else
234       {
235         DALI_ASSERT_ALWAYS("Unknown sampling mode");
236       }
237     }
238
239     int desiredWidth = 0;
240     Property::Value* desiredWidthValue = propertyMap.Find( IMAGE_DESIRED_WIDTH );
241     if( desiredWidthValue )
242     {
243       desiredWidthValue->Get( desiredWidth );
244     }
245
246     int desiredHeight = 0;
247     Property::Value* desiredHeightValue = propertyMap.Find( IMAGE_DESIRED_HEIGHT );
248     if( desiredHeightValue )
249     {
250       desiredHeightValue->Get( desiredHeight );
251     }
252
253     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
254
255   }
256
257   Property::Value* syncLoading = propertyMap.Find( SYNCHRONOUS_LOADING );
258   if( syncLoading )
259   {
260     bool sync;
261     syncLoading->Get( sync );
262     if( sync )
263     {
264       mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
265     }
266     else
267     {
268       mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
269     }
270   }
271
272   // if sync loading is required, the loading should start immediately when new image url is set or the actor is off stage
273   // ( for on-stage actor with image url unchanged, resource loading is already finished)
274   if( ( !mImpl->mRenderer || imageURLValue) && IsSynchronousResourceLoading() )
275   {
276     DoSynchronousResourceLoading();
277   }
278
279   // remove old renderer if exit
280   if( mImpl->mRenderer )
281   {
282     if( actor ) //remove old renderer from actor
283     {
284       actor.RemoveRenderer( mImpl->mRenderer );
285     }
286     if( !oldImageUrl.empty() ) //clean old renderer from cache
287     {
288       CleanCache( oldImageUrl );
289     }
290   }
291
292   NativeImage nativeImage = NativeImage::DownCast( mImage );
293
294   if( nativeImage )
295   {
296     SetNativeFragmentShaderCode( nativeImage );
297   }
298
299   // if actor is on stage, create new renderer and apply to actor
300   if( actor && actor.OnStage() )
301   {
302     SetOnStage( actor );
303   }
304 }
305
306 void ImageRenderer::SetSize( const Vector2& size )
307 {
308   ControlRenderer::SetSize( size );
309 }
310
311 void ImageRenderer::GetNaturalSize( Vector2& naturalSize ) const
312 {
313   if(mImage)
314   {
315     naturalSize.x = mImage.GetWidth();
316     naturalSize.y = mImage.GetHeight();
317     return;
318   }
319   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
320   {
321     naturalSize.x = mDesiredSize.GetWidth();
322     naturalSize.y = mDesiredSize.GetHeight();
323     return;
324   }
325   else if( !mImageUrl.empty() )
326   {
327     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
328     naturalSize.x = dimentions.GetWidth();
329     naturalSize.y = dimentions.GetHeight();
330     return;
331   }
332
333   naturalSize = Vector2::ZERO;
334 }
335
336 void ImageRenderer::SetClipRect( const Rect<int>& clipRect )
337 {
338   ControlRenderer::SetClipRect( clipRect );
339 }
340
341 void ImageRenderer::SetOffset( const Vector2& offset )
342 {
343 }
344
345 Renderer ImageRenderer::CreateRenderer() const
346 {
347   Geometry geometry;
348   Shader shader;
349
350   // If mImage is nativeImage with custom sampler or prefix, mNativeFragmentShaderCode will be applied.
351   // Renderer can't be shared between NativeImage and other image types.
352   if( !mNativeFragmentShaderCode.empty() )
353   {
354     return CreateNativeImageRenderer();
355   }
356
357   if( !mImpl->mCustomShader )
358   {
359     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
360
361     shader = GetImageShader(mFactoryCache);
362   }
363   else
364   {
365     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
366     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
367     {
368       shader = GetImageShader(mFactoryCache);
369     }
370     else
371     {
372       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
373                              mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
374                              mImpl->mCustomShader->mHints );
375       if( mImpl->mCustomShader->mVertexShader.empty() )
376       {
377         shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
378         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
379       }
380     }
381   }
382
383   Renderer renderer = Renderer::New( geometry, shader );
384
385   return renderer;
386 }
387
388 Renderer ImageRenderer::CreateNativeImageRenderer() const
389 {
390   Geometry geometry;
391   Shader shader;
392
393   if( !mImpl->mCustomShader )
394   {
395     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
396
397     shader  = Shader::New( VERTEX_SHADER, mNativeFragmentShaderCode );
398     shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
399     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
400   }
401   else
402   {
403     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
404     shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
405                            mNativeFragmentShaderCode,
406                            mImpl->mCustomShader->mHints );
407     if( mImpl->mCustomShader->mVertexShader.empty() )
408     {
409       shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
410       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
411     }
412   }
413
414   TextureSet textureSet = TextureSet::New();
415   Renderer renderer = Renderer::New( geometry, shader );
416   renderer.SetTextures( textureSet );
417
418   return renderer;
419 }
420
421
422 bool ImageRenderer::IsSynchronousResourceLoading() const
423 {
424   return mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
425 }
426
427 void ImageRenderer::DoSynchronousResourceLoading()
428 {
429   if( !mImageUrl.empty() )
430   {
431     BitmapLoader loader = BitmapLoader::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
432     loader.Load();
433     mPixels = loader.GetPixelData();
434   }
435 }
436
437 Image ImageRenderer::LoadImage( const std::string& url, bool synchronousLoading )
438 {
439   if( synchronousLoading )
440   {
441     if( !mPixels )
442     {
443       // use broken image
444       return RendererFactory::GetBrokenRendererImage();
445     }
446     Atlas image = Atlas::New( mPixels->GetWidth(), mPixels->GetHeight(), mPixels->GetPixelFormat() );
447     image.Upload( mPixels, 0, 0 );
448     return image;
449   }
450   else
451   {
452     ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
453     resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
454     return resourceImage;
455   }
456 }
457
458 TextureSet ImageRenderer::CreateTextureSet( Vector4& textureRect, const std::string& url, bool synchronousLoading )
459 {
460   TextureSet textureSet;
461   textureRect = FULL_TEXTURE_RECT;
462   if( synchronousLoading )
463   {
464     if( !mPixels )
465     {
466       // use broken image
467       textureSet = TextureSet::New();
468       textureSet.SetImage( 0u, RendererFactory::GetBrokenRendererImage() );
469     }
470     else
471     {
472       textureSet = mAtlasManager.Add(textureRect, mPixels );
473       if( !textureSet ) // big image, no atlasing
474       {
475         Atlas image = Atlas::New( mPixels->GetWidth(), mPixels->GetHeight(), mPixels->GetPixelFormat() );
476         image.Upload( mPixels, 0, 0 );
477         textureSet = TextureSet::New();
478         textureSet.SetImage( 0u, image );
479       }
480     }
481   }
482   else
483   {
484     textureSet = mAtlasManager.Add(textureRect, url, mDesiredSize, mFittingMode, mSamplingMode );
485     if( !textureSet ) // big image, no atlasing
486     {
487       ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
488       resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
489       textureSet = TextureSet::New();
490       textureSet.SetImage( 0u, resourceImage );
491     }
492   }
493
494   return textureSet;
495 }
496
497 void ImageRenderer::InitializeRenderer( const std::string& imageUrl )
498 {
499   if( imageUrl.empty() )
500   {
501     return;
502   }
503
504   mImageUrl = imageUrl;
505   mImpl->mRenderer.Reset();
506
507   if( !mImpl->mCustomShader &&
508       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
509       ( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) )
510   {
511     mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
512     if( !mImpl->mRenderer )
513     {
514       Vector4 atlasRect;
515       TextureSet textureSet = CreateTextureSet(atlasRect, imageUrl, IsSynchronousResourceLoading() );
516       Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
517       Shader shader( GetImageShader(mFactoryCache) );
518       mImpl->mRenderer = Renderer::New( geometry, shader );
519       mImpl->mRenderer.SetTextures( textureSet );
520       if( atlasRect != FULL_TEXTURE_RECT )
521       {
522         mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
523       }
524       mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
525     }
526
527     mImpl->mFlags |= Impl::IS_FROM_CACHE;
528   }
529   else
530   {
531     // for custom shader or remote image, renderer is not cached and atlas is not applied
532
533     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
534     mImpl->mRenderer = CreateRenderer();
535     Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
536     ApplyImageToSampler( image );
537   }
538 }
539
540 void ImageRenderer::InitializeRenderer( const Image& image )
541 {
542   mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
543
544   mImpl->mRenderer = CreateRenderer();
545
546   if( image )
547   {
548     ApplyImageToSampler( image );
549   }
550 }
551
552
553 void ImageRenderer::DoSetOnStage( Actor& actor )
554 {
555   if( !mImageUrl.empty() )
556   {
557     InitializeRenderer( mImageUrl );
558   }
559   else
560   {
561     InitializeRenderer( mImage );
562   }
563
564 }
565
566 void ImageRenderer::DoSetOffStage( Actor& actor )
567 {
568   //If we own the image then make sure we release it when we go off stage
569   if( !mImageUrl.empty() )
570   {
571     actor.RemoveRenderer( mImpl->mRenderer );
572     CleanCache(mImageUrl);
573     mImage.Reset();
574   }
575   else
576   {
577     actor.RemoveRenderer( mImpl->mRenderer );
578     mImpl->mRenderer.Reset();
579   }
580 }
581
582 void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
583 {
584   map.Clear();
585   map.Insert( RENDERER_TYPE, IMAGE_RENDERER );
586
587   bool sync = IsSynchronousResourceLoading();
588   map.Insert( SYNCHRONOUS_LOADING, sync );
589   if( !mImageUrl.empty() )
590   {
591     map.Insert( IMAGE_URL_NAME, mImageUrl );
592     map.Insert( IMAGE_DESIRED_WIDTH, mDesiredSize.GetWidth() );
593     map.Insert( IMAGE_DESIRED_HEIGHT, mDesiredSize.GetHeight() );
594   }
595   else if( mImage )
596   {
597     map.Insert( IMAGE_DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
598     map.Insert( IMAGE_DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
599
600     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
601     if( resourceImage )
602     {
603       map.Insert( IMAGE_URL_NAME, resourceImage.GetUrl() );
604     }
605   }
606
607   switch( mFittingMode )
608   {
609     case Dali::FittingMode::FIT_HEIGHT:
610     {
611       map.Insert( IMAGE_FITTING_MODE, FIT_HEIGHT );
612       break;
613     }
614     case Dali::FittingMode::FIT_WIDTH:
615     {
616       map.Insert( IMAGE_FITTING_MODE, FIT_WIDTH );
617       break;
618     }
619     case Dali::FittingMode::SCALE_TO_FILL:
620     {
621       map.Insert( IMAGE_FITTING_MODE, SCALE_TO_FILL );
622       break;
623     }
624     case Dali::FittingMode::SHRINK_TO_FIT:
625     {
626       map.Insert( IMAGE_FITTING_MODE, SHRINK_TO_FIT );
627       break;
628     }
629     default:
630     {
631       map.Insert( IMAGE_FITTING_MODE, DEFAULT );
632       break;
633     }
634   }
635
636   switch( mSamplingMode )
637   {
638     case Dali::SamplingMode::BOX:
639     {
640       map.Insert( IMAGE_SAMPLING_MODE, BOX );
641       break;
642     }
643     case Dali::SamplingMode::NEAREST:
644     {
645       map.Insert( IMAGE_SAMPLING_MODE, NEAREST );
646       break;
647     }
648     case Dali::SamplingMode::LINEAR:
649     {
650       map.Insert( IMAGE_SAMPLING_MODE, LINEAR );
651       break;
652     }
653     case Dali::SamplingMode::BOX_THEN_LINEAR:
654     {
655       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_LINEAR );
656       break;
657     }
658     case Dali::SamplingMode::BOX_THEN_NEAREST:
659     {
660       map.Insert( IMAGE_SAMPLING_MODE, BOX_THEN_NEAREST );
661       break;
662     }
663     case Dali::SamplingMode::NO_FILTER:
664     {
665       map.Insert( IMAGE_SAMPLING_MODE, NO_FILTER );
666       break;
667     }
668     case Dali::SamplingMode::DONT_CARE:
669     {
670       map.Insert( IMAGE_SAMPLING_MODE, DONT_CARE );
671       break;
672     }
673     default:
674     {
675       map.Insert( IMAGE_SAMPLING_MODE, DEFAULT );
676       break;
677     }
678   }
679 }
680
681 Shader ImageRenderer::GetImageShader( RendererFactoryCache& factoryCache )
682 {
683   Shader shader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
684   if( !shader )
685   {
686     shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
687     factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
688     shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
689     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
690   }
691   return shader;
692 }
693
694 void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
695 {
696   if( mImageUrl != imageUrl )
697   {
698     std::string oldImageUrl = mImageUrl;
699     mImageUrl = imageUrl;
700     mDesiredSize = size;
701     mFittingMode = fittingMode;
702     mSamplingMode = samplingMode;
703     mImage.Reset();
704
705     if( IsSynchronousResourceLoading() )
706     {
707       DoSynchronousResourceLoading();
708     }
709
710     if( mImpl->mRenderer )
711     {
712       if( GetIsFromCache() ) // if renderer is from cache, remove the old one
713       {
714         //remove old renderer
715         if( actor )
716         {
717           actor.RemoveRenderer( mImpl->mRenderer );
718         }
719
720         //clean the cache
721         if( !oldImageUrl.empty() )
722         {
723           CleanCache(oldImageUrl);
724         }
725
726         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
727         {
728           SetOnStage(actor);
729         }
730       }
731       else // if renderer is not from cache, reuse the same renderer and only change the texture
732       {
733         Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
734         ApplyImageToSampler( image );
735       }
736     }
737   }
738 }
739
740 void ImageRenderer::SetImage( Actor& actor, const Image& image )
741 {
742   if( mImage != image )
743   {
744     NativeImage newNativeImage = NativeImage::DownCast( image );
745     bool newRendererFlag = true;
746
747     if( newNativeImage && !mNativeImageFlag )
748     {
749       SetNativeFragmentShaderCode( newNativeImage );
750     }
751
752     if( ( newNativeImage && mNativeImageFlag ) || ( !newNativeImage && !mNativeImageFlag ) )
753     {
754       newRendererFlag = false;
755     }
756
757     if( newNativeImage )
758     {
759       mNativeImageFlag = true;
760     }
761     else
762     {
763       mNativeFragmentShaderCode.clear();
764       mNativeImageFlag = false;
765     }
766
767     mImage = image;
768
769     if( mImpl->mRenderer )
770     {
771       // if renderer is from cache, remove the old one, and create new renderer
772       if( GetIsFromCache() )
773       {
774         //remove old renderer
775         if( actor )
776         {
777           actor.RemoveRenderer( mImpl->mRenderer );
778         }
779
780         //clean the cache
781         if( !mImageUrl.empty() )
782         {
783           CleanCache(mImageUrl);
784         }
785         mImageUrl.clear();
786
787         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
788         {
789           SetOnStage(actor);
790         }
791       }
792       // if input image is nativeImage and mImage is regular image or the reverse, remove the old one, and create new renderer
793       else if( newRendererFlag )
794       {
795         //remove old renderer
796         if( actor )
797         {
798           actor.RemoveRenderer( mImpl->mRenderer );
799         }
800
801         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
802         {
803           SetOnStage(actor);
804         }
805       }
806       else // if renderer is not from cache, reuse the same renderer and only change the texture
807       {
808         ApplyImageToSampler( image );
809       }
810     }
811
812     mImageUrl.clear();
813     mDesiredSize = ImageDimensions();
814     mFittingMode = FittingMode::DEFAULT;
815     mSamplingMode = SamplingMode::DEFAULT;
816   }
817 }
818
819 void ImageRenderer::ApplyImageToSampler( const Image& image )
820 {
821   if( image )
822   {
823     TextureSet textureSet = mImpl->mRenderer.GetTextures();
824     if( !textureSet )
825     {
826       textureSet = TextureSet::New();
827       mImpl->mRenderer.SetTextures( textureSet );
828     }
829     textureSet.SetImage( 0u, image );
830   }
831 }
832
833 void ImageRenderer::OnImageLoaded( ResourceImage image )
834 {
835   if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
836   {
837     Image brokenImage = RendererFactory::GetBrokenRendererImage();
838     if( mImpl->mRenderer )
839     {
840       ApplyImageToSampler( brokenImage );
841     }
842   }
843 }
844
845 void ImageRenderer::CleanCache(const std::string& url)
846 {
847   TextureSet textureSet = mImpl->mRenderer.GetTextures();
848
849   Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
850   Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
851   if( index != Property::INVALID_INDEX )
852   {
853     Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
854     atlasRectValue.Get( atlasRect );
855   }
856
857   mImpl->mRenderer.Reset();
858   if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
859   {
860     mAtlasManager.Remove( textureSet, atlasRect );
861   }
862 }
863
864 void ImageRenderer::SetNativeFragmentShaderCode( Dali::NativeImage& nativeImage )
865 {
866   const char* fragmentPreFix = nativeImage.GetCustomFragmentPreFix();
867   const char* customSamplerTypename = nativeImage.GetCustomSamplerTypename();
868
869   if( fragmentPreFix )
870   {
871     mNativeFragmentShaderCode = fragmentPreFix;
872     mNativeFragmentShaderCode += "\n";
873   }
874
875   if( mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty() )
876   {
877     mNativeFragmentShaderCode += mImpl->mCustomShader->mFragmentShader;
878   }
879   else
880   {
881     mNativeFragmentShaderCode += FRAGMENT_SHADER;
882   }
883
884   if( customSamplerTypename )
885   {
886     mNativeFragmentShaderCode.replace( mNativeFragmentShaderCode.find( DEFAULT_SAMPLER_TYPENAME ), strlen( DEFAULT_SAMPLER_TYPENAME ), customSamplerTypename );
887   }
888 }
889
890 } // namespace Internal
891
892 } // namespace Toolkit
893
894 } // namespace Dali