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