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