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