Revert "[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     ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
437     resourceImage.LoadingFinishedSignal().Connect( this, &ImageVisual::OnImageLoaded );
438     return resourceImage;
439   }
440 }
441
442 TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, const std::string& url, bool synchronousLoading )
443 {
444   TextureSet textureSet;
445   textureRect = FULL_TEXTURE_RECT;
446   if( synchronousLoading )
447   {
448     if( !mPixels )
449     {
450       // use broken image
451       textureSet = TextureSet::New();
452       TextureSetImage( textureSet, 0u, VisualFactoryCache::GetBrokenVisualImage() );
453     }
454     else
455     {
456       textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, mPixels );
457       mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
458       if( !textureSet ) // big image, no atlasing
459       {
460         mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
461         Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() );
462         image.Upload( mPixels, 0, 0 );
463         textureSet = TextureSet::New();
464         TextureSetImage( textureSet, 0u, image );
465       }
466     }
467   }
468   else
469   {
470     textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, true, this );
471     mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
472     if( !textureSet ) // big image, no atlasing
473     {
474       mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
475       ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
476       resourceImage.LoadingFinishedSignal().Connect( this, &ImageVisual::OnImageLoaded );
477       textureSet = TextureSet::New();
478       TextureSetImage( textureSet, 0u, resourceImage );
479     }
480   }
481
482   if( !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
483   {
484     Sampler sampler = Sampler::New();
485     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
486     textureSet.SetSampler( 0u, sampler );
487   }
488
489   return textureSet;
490 }
491
492 void ImageVisual::InitializeRenderer( const std::string& imageUrl )
493 {
494   if( imageUrl.empty() )
495   {
496     return;
497   }
498
499   mImageUrl = imageUrl;
500   mImpl->mRenderer.Reset();
501   mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
502
503   if( !mImpl->mCustomShader &&
504       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
505       ( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) )
506   {
507     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
508     bool cacheable =  defaultWrapMode &&  mPixelArea == FULL_TEXTURE_RECT;
509
510     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
511     if( cacheable ) // fetch the renderer from cache if exist
512     {
513       mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
514       mImpl->mFlags |= Impl::IS_FROM_CACHE;
515     }
516
517     if( !mImpl->mRenderer ) // new renderer is needed
518     {
519       Vector4 atlasRect;
520       TextureSet textureSet = CreateTextureSet(atlasRect, imageUrl, IsSynchronousResourceLoading() );
521       Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
522       Shader shader( GetImageShader(mFactoryCache, mImpl->mFlags & Impl::IS_ATLASING_APPLIED, defaultWrapMode) );
523       mImpl->mRenderer = Renderer::New( geometry, shader );
524       mImpl->mRenderer.SetTextures( textureSet );
525
526       if( mImpl->mFlags & Impl::IS_ATLASING_APPLIED ) // the texture is packed inside atlas
527       {
528         mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
529         if( !defaultWrapMode ) // custom wrap mode, renderer is not cached.
530         {
531           Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
532           wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
533           mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
534         }
535       }
536
537       // save the renderer to cache only when default wrap mode and default pixel area is used
538       if( cacheable )
539       {
540         mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
541       }
542     }
543   }
544   else
545   {
546     // for custom shader or remote image, renderer is not cached and atlas is not applied
547     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
548     mImpl->mRenderer = CreateRenderer();
549     Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
550     ApplyImageToSampler( image );
551   }
552 }
553
554 void ImageVisual::InitializeRenderer( const Image& image )
555 {
556   mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
557
558   NativeImage nativeImage = NativeImage::DownCast( image );
559
560   if( nativeImage )
561   {
562     SetNativeFragmentShaderCode( nativeImage );
563   }
564
565   mImpl->mRenderer = CreateRenderer();
566
567   if( image )
568   {
569     ApplyImageToSampler( image );
570   }
571
572   if( mPixelArea != FULL_TEXTURE_RECT )
573   {
574     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
575   }
576 }
577
578
579 void ImageVisual::UploadCompleted()
580 {
581   // Resource image is loaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
582   Actor actor = mPlacementActor.GetHandle();
583   if( actor )
584   {
585     actor.AddRenderer( mImpl->mRenderer );
586     // reset the weak handle so that the renderer only get added to actor once
587     mPlacementActor.Reset();
588   }
589 }
590
591 void ImageVisual::DoSetOnStage( Actor& actor )
592 {
593   mPlacementActor = actor;
594
595   if( !mImageUrl.empty() )
596   {
597     InitializeRenderer( mImageUrl );
598   }
599   else
600   {
601     InitializeRenderer( mImage );
602   }
603
604   if( IsSynchronousResourceLoading() || !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
605   {
606     actor.AddRenderer( mImpl->mRenderer );
607     mPlacementActor.Reset();
608   }
609 }
610
611 void ImageVisual::DoSetOffStage( Actor& actor )
612 {
613   //If we own the image then make sure we release it when we go off stage
614   if( !mImageUrl.empty() )
615   {
616     actor.RemoveRenderer( mImpl->mRenderer );
617     CleanCache(mImageUrl);
618     mImage.Reset();
619   }
620   else
621   {
622     actor.RemoveRenderer( mImpl->mRenderer );
623     mImpl->mRenderer.Reset();
624   }
625   mPlacementActor.Reset();
626 }
627
628 void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
629 {
630   map.Clear();
631   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE );
632
633   bool sync = IsSynchronousResourceLoading();
634   map.Insert( SYNCHRONOUS_LOADING, sync );
635   if( !mImageUrl.empty() )
636   {
637     map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl );
638     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth() );
639     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight() );
640   }
641   else if( mImage )
642   {
643     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
644     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
645
646     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
647     if( resourceImage )
648     {
649       map.Insert( Toolkit::ImageVisual::Property::URL, resourceImage.GetUrl() );
650     }
651   }
652
653   map.Insert( Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode );
654   map.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode );
655
656   map.Insert( Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea );
657   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU );
658   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
659 }
660
661 Shader ImageVisual::GetImageShader( VisualFactoryCache& factoryCache, bool atlasing, bool defaultTextureWrapping )
662 {
663   Shader shader;
664   if( atlasing )
665   {
666     if( defaultTextureWrapping )
667     {
668       shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP );
669       if( !shader )
670       {
671         shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_CLAMP );
672         factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP, shader );
673       }
674     }
675     else
676     {
677       shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP );
678       if( !shader )
679       {
680         shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_VARIOUS_WRAP );
681         factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP, shader );
682       }
683     }
684   }
685   else
686   {
687     shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER );
688     if( !shader )
689     {
690       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_NO_ATLAS );
691       factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER, shader );
692     }
693   }
694   shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
695   return shader;
696 }
697
698 void ImageVisual::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
699 {
700   if( mImageUrl != imageUrl )
701   {
702     std::string oldImageUrl = mImageUrl;
703     mImageUrl = imageUrl;
704     mDesiredSize = size;
705     mFittingMode = fittingMode;
706     mSamplingMode = samplingMode;
707     mImage.Reset();
708
709     if( IsSynchronousResourceLoading() )
710     {
711       DoSynchronousResourceLoading();
712     }
713
714     if( mImpl->mRenderer )
715     {
716       if( GetIsFromCache() ) // if renderer is from cache, remove the old one
717       {
718         //remove old renderer
719         if( actor )
720         {
721           actor.RemoveRenderer( mImpl->mRenderer );
722         }
723
724         //clean the cache
725         if( !oldImageUrl.empty() )
726         {
727           CleanCache(oldImageUrl);
728         }
729
730         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
731         {
732           SetOnStage(actor);
733         }
734       }
735       else // if renderer is not from cache, reuse the same renderer and only change the texture
736       {
737         Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
738         ApplyImageToSampler( image );
739       }
740     }
741   }
742 }
743
744 void ImageVisual::SetImage( Actor& actor, const Image& image )
745 {
746   if( mImage != image )
747   {
748     NativeImage newNativeImage = NativeImage::DownCast( image );
749     bool newRendererFlag = true;
750
751     if( newNativeImage && !mNativeImageFlag )
752     {
753       SetNativeFragmentShaderCode( newNativeImage );
754     }
755
756     if( ( newNativeImage && mNativeImageFlag ) || ( !newNativeImage && !mNativeImageFlag ) )
757     {
758       newRendererFlag = false;
759     }
760
761     if( newNativeImage )
762     {
763       mNativeImageFlag = true;
764     }
765     else
766     {
767       mNativeFragmentShaderCode.clear();
768       mNativeImageFlag = false;
769     }
770
771     mImage = image;
772
773     if( mImpl->mRenderer )
774     {
775       // if renderer is from cache, remove the old one, and create new renderer
776       if( GetIsFromCache() )
777       {
778         //remove old renderer
779         if( actor )
780         {
781           actor.RemoveRenderer( mImpl->mRenderer );
782         }
783
784         //clean the cache
785         if( !mImageUrl.empty() )
786         {
787           CleanCache(mImageUrl);
788         }
789         mImageUrl.clear();
790
791         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
792         {
793           SetOnStage(actor);
794         }
795       }
796       // if input image is nativeImage and mImage is regular image or the reverse, remove the old one, and create new renderer
797       else if( newRendererFlag )
798       {
799         //remove old renderer
800         if( actor )
801         {
802           actor.RemoveRenderer( mImpl->mRenderer );
803         }
804
805         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
806         {
807           SetOnStage(actor);
808         }
809       }
810       else // if renderer is not from cache, reuse the same renderer and only change the texture
811       {
812         ApplyImageToSampler( image );
813       }
814     }
815
816     mImageUrl.clear();
817     mDesiredSize = ImageDimensions();
818     mFittingMode = FittingMode::DEFAULT;
819     mSamplingMode = SamplingMode::DEFAULT;
820   }
821 }
822
823 void ImageVisual::ApplyImageToSampler( const Image& image )
824 {
825   if( image )
826   {
827     TextureSet textureSet = mImpl->mRenderer.GetTextures();
828     if( !textureSet )
829     {
830       textureSet = TextureSet::New();
831       mImpl->mRenderer.SetTextures( textureSet );
832     }
833     TextureSetImage( textureSet, 0u, image );
834     Sampler sampler = Sampler::New();
835     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
836     textureSet.SetSampler( 0u, sampler );
837   }
838 }
839
840 void ImageVisual::OnImageLoaded( ResourceImage image )
841 {
842   if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
843   {
844     Image brokenImage = VisualFactoryCache::GetBrokenVisualImage();
845     if( mImpl->mRenderer )
846     {
847       ApplyImageToSampler( brokenImage );
848     }
849   }
850 }
851
852 void ImageVisual::CleanCache(const std::string& url)
853 {
854   TextureSet textureSet = mImpl->mRenderer.GetTextures();
855
856   Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
857   Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
858   if( index != Property::INVALID_INDEX )
859   {
860     Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
861     atlasRectValue.Get( atlasRect );
862   }
863
864   mImpl->mRenderer.Reset();
865   if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
866   {
867     mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
868   }
869 }
870
871 void ImageVisual::SetNativeFragmentShaderCode( Dali::NativeImage& nativeImage )
872 {
873   const char* fragmentPreFix = nativeImage.GetCustomFragmentPreFix();
874   const char* customSamplerTypename = nativeImage.GetCustomSamplerTypename();
875
876   if( fragmentPreFix )
877   {
878     mNativeFragmentShaderCode = fragmentPreFix;
879     mNativeFragmentShaderCode += "\n";
880   }
881
882   if( mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty() )
883   {
884     mNativeFragmentShaderCode += mImpl->mCustomShader->mFragmentShader;
885   }
886   else
887   {
888     mNativeFragmentShaderCode += FRAGMENT_SHADER_NO_ATLAS;
889   }
890
891   if( customSamplerTypename )
892   {
893     mNativeFragmentShaderCode.replace( mNativeFragmentShaderCode.find( DEFAULT_SAMPLER_TYPENAME ), strlen( DEFAULT_SAMPLER_TYPENAME ), customSamplerTypename );
894   }
895 }
896
897 } // namespace Internal
898
899 } // namespace Toolkit
900
901 } // namespace Dali