TextVisual implementation.
[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 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 ImageVisual::ImageVisual( VisualFactoryCache& factoryCache )
192 : Visual::Base( factoryCache ),
193   mPixelArea( FULL_TEXTURE_RECT ),
194   mDesiredSize(),
195   mFittingMode( FittingMode::DEFAULT ),
196   mSamplingMode( SamplingMode::DEFAULT ),
197   mWrapModeU( WrapMode::DEFAULT ),
198   mWrapModeV( WrapMode::DEFAULT ),
199   mNativeFragmentShaderCode( ),
200   mNativeImageFlag( false )
201 {
202 }
203
204 ImageVisual::~ImageVisual()
205 {
206 }
207
208 void ImageVisual::DoInitialize( Actor& actor, const Property::Map& propertyMap )
209 {
210   std::string oldImageUrl = mImageUrl;
211
212   Property::Value* imageURLValue = propertyMap.Find( Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME );
213   if( imageURLValue )
214   {
215     imageURLValue->Get( mImageUrl );
216     if( !mImageUrl.empty() )
217     {
218       mImage.Reset();
219     }
220
221     Property::Value* fittingValue = propertyMap.Find( Toolkit::ImageVisual::Property::FITTING_MODE, IMAGE_FITTING_MODE );
222     if( fittingValue )
223     {
224       Scripting::GetEnumerationProperty( *fittingValue, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, mFittingMode );
225     }
226
227     Property::Value* samplingValue = propertyMap.Find( Toolkit::ImageVisual::Property::SAMPLING_MODE, IMAGE_SAMPLING_MODE );
228     if( samplingValue )
229     {
230       Scripting::GetEnumerationProperty( *samplingValue, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, mSamplingMode );
231     }
232
233     int desiredWidth = 0;
234     Property::Value* desiredWidthValue = propertyMap.Find( Toolkit::ImageVisual::Property::DESIRED_WIDTH, IMAGE_DESIRED_WIDTH );
235     if( desiredWidthValue )
236     {
237       desiredWidthValue->Get( desiredWidth );
238     }
239
240     int desiredHeight = 0;
241     Property::Value* desiredHeightValue = propertyMap.Find( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, IMAGE_DESIRED_HEIGHT );
242     if( desiredHeightValue )
243     {
244       desiredHeightValue->Get( desiredHeight );
245     }
246
247     Property::Value* pixelAreaValue = propertyMap.Find( Toolkit::ImageVisual::Property::PIXEL_AREA, PIXEL_AREA_UNIFORM_NAME );
248     if( pixelAreaValue )
249     {
250       pixelAreaValue->Get( mPixelArea );
251     }
252
253     Property::Value* wrapModeValueU = propertyMap.Find( Toolkit::ImageVisual::Property::WRAP_MODE_U, IMAGE_WRAP_MODE_U );
254     if( wrapModeValueU )
255     {
256       Scripting::GetEnumerationProperty( *wrapModeValueU, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, mWrapModeU );
257     }
258
259     Property::Value* wrapModeValueV = propertyMap.Find( Toolkit::ImageVisual::Property::WRAP_MODE_V, IMAGE_WRAP_MODE_V );
260     if( wrapModeValueV )
261     {
262       Scripting::GetEnumerationProperty( *wrapModeValueV, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, mWrapModeV );
263     }
264
265     mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
266   }
267
268   Property::Value* syncLoading = propertyMap.Find( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, SYNCHRONOUS_LOADING );
269   if( syncLoading )
270   {
271     bool sync;
272     syncLoading->Get( sync );
273     if( sync )
274     {
275       mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
276     }
277     else
278     {
279       mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
280     }
281   }
282
283   // if sync loading is required, the loading should start immediately when new image url is set or the actor is off stage
284   // ( for on-stage actor with image url unchanged, resource loading is already finished)
285   if( ( !mImpl->mRenderer || imageURLValue) && IsSynchronousResourceLoading() )
286   {
287     DoSynchronousResourceLoading();
288   }
289
290   // remove old renderer if exit
291   if( mImpl->mRenderer )
292   {
293     if( actor ) //remove old renderer from actor
294     {
295       actor.RemoveRenderer( mImpl->mRenderer );
296     }
297     if( !oldImageUrl.empty() ) //clean old renderer from cache
298     {
299       CleanCache( oldImageUrl );
300     }
301   }
302
303   NativeImage nativeImage = NativeImage::DownCast( mImage );
304
305   if( nativeImage )
306   {
307     SetNativeFragmentShaderCode( nativeImage );
308   }
309
310   // if actor is on stage, create new renderer and apply to actor
311   if( actor && actor.OnStage() )
312   {
313     SetOnStage( actor );
314   }
315 }
316
317 void ImageVisual::GetNaturalSize( Vector2& naturalSize ) const
318 {
319   if(mImage)
320   {
321     naturalSize.x = mImage.GetWidth();
322     naturalSize.y = mImage.GetHeight();
323     return;
324   }
325   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
326   {
327     naturalSize.x = mDesiredSize.GetWidth();
328     naturalSize.y = mDesiredSize.GetHeight();
329     return;
330   }
331   else if( !mImageUrl.empty() )
332   {
333     ImageDimensions dimentions = ResourceImage::GetImageSize( mImageUrl );
334     naturalSize.x = dimentions.GetWidth();
335     naturalSize.y = dimentions.GetHeight();
336     return;
337   }
338
339   naturalSize = Vector2::ZERO;
340 }
341
342 Renderer ImageVisual::CreateRenderer() const
343 {
344   Geometry geometry;
345   Shader shader;
346
347   // If mImage is nativeImage with custom sampler or prefix, mNativeFragmentShaderCode will be applied.
348   // Renderer can't be shared between NativeImage and other image types.
349   if( !mNativeFragmentShaderCode.empty() )
350   {
351     return CreateNativeImageRenderer();
352   }
353
354   if( !mImpl->mCustomShader )
355   {
356     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
357
358     shader = GetImageShader( mFactoryCache,
359                              mImpl->mFlags & Impl::IS_ATLASING_APPLIED,
360                              mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE );
361   }
362   else
363   {
364     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
365     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
366     {
367       shader = GetImageShader(mFactoryCache, false, true);
368     }
369     else
370     {
371       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
372                              mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER_NO_ATLAS : mImpl->mCustomShader->mFragmentShader,
373                              mImpl->mCustomShader->mHints );
374       if( mImpl->mCustomShader->mVertexShader.empty() )
375       {
376         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
377       }
378     }
379   }
380
381   Renderer renderer = Renderer::New( geometry, shader );
382
383   return renderer;
384 }
385
386 Renderer ImageVisual::CreateNativeImageRenderer() const
387 {
388   Geometry geometry;
389   Shader shader;
390
391   if( !mImpl->mCustomShader )
392   {
393     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
394
395     shader  = Shader::New( VERTEX_SHADER, mNativeFragmentShaderCode );
396     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
397   }
398   else
399   {
400     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
401     shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
402                            mNativeFragmentShaderCode,
403                            mImpl->mCustomShader->mHints );
404     if( mImpl->mCustomShader->mVertexShader.empty() )
405     {
406       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
407     }
408   }
409
410   TextureSet textureSet = TextureSet::New();
411   Renderer renderer = Renderer::New( geometry, shader );
412   renderer.SetTextures( textureSet );
413   return renderer;
414 }
415
416
417 bool ImageVisual::IsSynchronousResourceLoading() const
418 {
419   return mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
420 }
421
422 void ImageVisual::DoSynchronousResourceLoading()
423 {
424   if( !mImageUrl.empty() )
425   {
426     BitmapLoader loader = BitmapLoader::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
427     loader.Load();
428     mPixels = loader.GetPixelData();
429   }
430 }
431
432 Image ImageVisual::LoadImage( const std::string& url, bool synchronousLoading )
433 {
434   if( synchronousLoading )
435   {
436     if( !mPixels )
437     {
438       // use broken image
439       return VisualFactoryCache::GetBrokenVisualImage();
440     }
441     Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() );
442     image.Upload( mPixels, 0, 0 );
443     return image;
444   }
445   else
446   {
447     ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
448     resourceImage.LoadingFinishedSignal().Connect( this, &ImageVisual::OnImageLoaded );
449     return resourceImage;
450   }
451 }
452
453 TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, const std::string& url, bool synchronousLoading )
454 {
455   TextureSet textureSet;
456   textureRect = FULL_TEXTURE_RECT;
457   if( synchronousLoading )
458   {
459     if( !mPixels )
460     {
461       // use broken image
462       textureSet = TextureSet::New();
463       TextureSetImage( textureSet, 0u, VisualFactoryCache::GetBrokenVisualImage() );
464     }
465     else
466     {
467       textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, mPixels );
468       mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
469       if( !textureSet ) // big image, no atlasing
470       {
471         mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
472         Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() );
473         image.Upload( mPixels, 0, 0 );
474         textureSet = TextureSet::New();
475         TextureSetImage( textureSet, 0u, image );
476       }
477     }
478   }
479   else
480   {
481     textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, true, this );
482     mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
483     if( !textureSet ) // big image, no atlasing
484     {
485       mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
486       ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
487       resourceImage.LoadingFinishedSignal().Connect( this, &ImageVisual::OnImageLoaded );
488       textureSet = TextureSet::New();
489       TextureSetImage( textureSet, 0u, resourceImage );
490     }
491   }
492
493   if( !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
494   {
495     Sampler sampler = Sampler::New();
496     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
497     textureSet.SetSampler( 0u, sampler );
498   }
499
500   return textureSet;
501 }
502
503 void ImageVisual::InitializeRenderer( const std::string& imageUrl )
504 {
505   if( imageUrl.empty() )
506   {
507     return;
508   }
509
510   mImageUrl = imageUrl;
511   mImpl->mRenderer.Reset();
512   mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
513
514   if( !mImpl->mCustomShader &&
515       ( strncasecmp( imageUrl.c_str(), HTTP_URL,  sizeof(HTTP_URL)  -1 ) != 0 ) && // ignore remote images
516       ( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) )
517   {
518     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
519     bool cacheable =  defaultWrapMode &&  mPixelArea == FULL_TEXTURE_RECT;
520
521     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
522     if( cacheable ) // fetch the renderer from cache if exist
523     {
524       mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
525       mImpl->mFlags |= Impl::IS_FROM_CACHE;
526     }
527
528     if( !mImpl->mRenderer ) // new renderer is needed
529     {
530       Vector4 atlasRect;
531       TextureSet textureSet = CreateTextureSet(atlasRect, imageUrl, IsSynchronousResourceLoading() );
532       Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
533       Shader shader( GetImageShader(mFactoryCache, mImpl->mFlags & Impl::IS_ATLASING_APPLIED, defaultWrapMode) );
534       mImpl->mRenderer = Renderer::New( geometry, shader );
535       mImpl->mRenderer.SetTextures( textureSet );
536
537       if( mImpl->mFlags & Impl::IS_ATLASING_APPLIED ) // the texture is packed inside atlas
538       {
539         mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
540         if( !defaultWrapMode ) // custom wrap mode, renderer is not cached.
541         {
542           Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
543           wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
544           mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
545         }
546       }
547
548       // save the renderer to cache only when default wrap mode and default pixel area is used
549       if( cacheable )
550       {
551         mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
552       }
553     }
554   }
555   else
556   {
557     // for custom shader or remote image, renderer is not cached and atlas is not applied
558     mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
559     mImpl->mRenderer = CreateRenderer();
560     Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
561     ApplyImageToSampler( image );
562   }
563 }
564
565 void ImageVisual::InitializeRenderer( const Image& image )
566 {
567   mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
568
569   mImpl->mRenderer = CreateRenderer();
570
571   if( image )
572   {
573     ApplyImageToSampler( image );
574   }
575 }
576
577 void ImageVisual::UploadCompleted()
578 {
579   // Resource image is loaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
580   Actor actor = mPlacementActor.GetHandle();
581   if( actor )
582   {
583     actor.AddRenderer( mImpl->mRenderer );
584     // reset the weak handle so that the renderer only get added to actor once
585     mPlacementActor.Reset();
586   }
587 }
588
589 void ImageVisual::DoSetOnStage( Actor& actor )
590 {
591   mPlacementActor = actor;
592
593   if( !mImageUrl.empty() )
594   {
595     InitializeRenderer( mImageUrl );
596   }
597   else
598   {
599     InitializeRenderer( mImage );
600   }
601
602   if( mPixelArea != FULL_TEXTURE_RECT )
603   {
604     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
605   }
606
607   if( IsSynchronousResourceLoading() || !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
608   {
609     actor.AddRenderer( mImpl->mRenderer );
610     mPlacementActor.Reset();
611   }
612 }
613
614 void ImageVisual::DoSetOffStage( Actor& actor )
615 {
616   //If we own the image then make sure we release it when we go off stage
617   if( !mImageUrl.empty() )
618   {
619     actor.RemoveRenderer( mImpl->mRenderer );
620     CleanCache(mImageUrl);
621     mImage.Reset();
622   }
623   else
624   {
625     actor.RemoveRenderer( mImpl->mRenderer );
626     mImpl->mRenderer.Reset();
627   }
628   mPlacementActor.Reset();
629 }
630
631 void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
632 {
633   map.Clear();
634   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE );
635
636   bool sync = IsSynchronousResourceLoading();
637   map.Insert( SYNCHRONOUS_LOADING, sync );
638   if( !mImageUrl.empty() )
639   {
640     map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl );
641     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth() );
642     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight() );
643   }
644   else if( mImage )
645   {
646     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
647     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
648
649     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
650     if( resourceImage )
651     {
652       map.Insert( Toolkit::ImageVisual::Property::URL, resourceImage.GetUrl() );
653     }
654   }
655
656   map.Insert( Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode );
657   map.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode );
658
659   map.Insert( Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea );
660   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU );
661   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
662 }
663
664 void ImageVisual::DoSetProperty( Dali::Property::Index index, const Dali::Property::Value& propertyValue )
665 {
666   // TODO
667 }
668
669 Dali::Property::Value ImageVisual::DoGetProperty( Dali::Property::Index index )
670 {
671   // TODO
672   return Dali::Property::Value();
673 }
674
675 Shader ImageVisual::GetImageShader( VisualFactoryCache& factoryCache, bool atlasing, bool defaultTextureWrapping )
676 {
677   Shader shader;
678   if( atlasing )
679   {
680     if( defaultTextureWrapping )
681     {
682       shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP );
683       if( !shader )
684       {
685         shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_CLAMP );
686         factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP, shader );
687       }
688     }
689     else
690     {
691       shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP );
692       if( !shader )
693       {
694         shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_VARIOUS_WRAP );
695         factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP, shader );
696       }
697     }
698   }
699   else
700   {
701     shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER );
702     if( !shader )
703     {
704       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_NO_ATLAS );
705       factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER, shader );
706     }
707   }
708   shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
709   return shader;
710 }
711
712 void ImageVisual::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
713 {
714   if( mImageUrl != imageUrl )
715   {
716     std::string oldImageUrl = mImageUrl;
717     mImageUrl = imageUrl;
718     mDesiredSize = size;
719     mFittingMode = fittingMode;
720     mSamplingMode = samplingMode;
721     mImage.Reset();
722
723     if( IsSynchronousResourceLoading() )
724     {
725       DoSynchronousResourceLoading();
726     }
727
728     if( mImpl->mRenderer )
729     {
730       if( GetIsFromCache() ) // if renderer is from cache, remove the old one
731       {
732         //remove old renderer
733         if( actor )
734         {
735           actor.RemoveRenderer( mImpl->mRenderer );
736         }
737
738         //clean the cache
739         if( !oldImageUrl.empty() )
740         {
741           CleanCache(oldImageUrl);
742         }
743
744         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
745         {
746           SetOnStage(actor);
747         }
748       }
749       else // if renderer is not from cache, reuse the same renderer and only change the texture
750       {
751         Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
752         ApplyImageToSampler( image );
753       }
754     }
755   }
756 }
757
758 void ImageVisual::SetImage( Actor& actor, const Image& image )
759 {
760   if( mImage != image )
761   {
762     NativeImage newNativeImage = NativeImage::DownCast( image );
763     bool newRendererFlag = true;
764
765     if( newNativeImage && !mNativeImageFlag )
766     {
767       SetNativeFragmentShaderCode( newNativeImage );
768     }
769
770     if( ( newNativeImage && mNativeImageFlag ) || ( !newNativeImage && !mNativeImageFlag ) )
771     {
772       newRendererFlag = false;
773     }
774
775     if( newNativeImage )
776     {
777       mNativeImageFlag = true;
778     }
779     else
780     {
781       mNativeFragmentShaderCode.clear();
782       mNativeImageFlag = false;
783     }
784
785     mImage = image;
786
787     if( mImpl->mRenderer )
788     {
789       // if renderer is from cache, remove the old one, and create new renderer
790       if( GetIsFromCache() )
791       {
792         //remove old renderer
793         if( actor )
794         {
795           actor.RemoveRenderer( mImpl->mRenderer );
796         }
797
798         //clean the cache
799         if( !mImageUrl.empty() )
800         {
801           CleanCache(mImageUrl);
802         }
803         mImageUrl.clear();
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       // if input image is nativeImage and mImage is regular image or the reverse, remove the old one, and create new renderer
811       else if( newRendererFlag )
812       {
813         //remove old renderer
814         if( actor )
815         {
816           actor.RemoveRenderer( mImpl->mRenderer );
817         }
818
819         if( actor && actor.OnStage() ) // if actor on stage, create a new renderer and apply to actor
820         {
821           SetOnStage(actor);
822         }
823       }
824       else // if renderer is not from cache, reuse the same renderer and only change the texture
825       {
826         ApplyImageToSampler( image );
827       }
828     }
829
830     mImageUrl.clear();
831     mDesiredSize = ImageDimensions();
832     mFittingMode = FittingMode::DEFAULT;
833     mSamplingMode = SamplingMode::DEFAULT;
834   }
835 }
836
837 void ImageVisual::ApplyImageToSampler( const Image& image )
838 {
839   if( image )
840   {
841     TextureSet textureSet = mImpl->mRenderer.GetTextures();
842     if( !textureSet )
843     {
844       textureSet = TextureSet::New();
845       mImpl->mRenderer.SetTextures( textureSet );
846     }
847     TextureSetImage( textureSet, 0u, image );
848     Sampler sampler = Sampler::New();
849     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
850     textureSet.SetSampler( 0u, sampler );
851   }
852 }
853
854 void ImageVisual::OnImageLoaded( ResourceImage image )
855 {
856   if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
857   {
858     Image brokenImage = VisualFactoryCache::GetBrokenVisualImage();
859     if( mImpl->mRenderer )
860     {
861       ApplyImageToSampler( brokenImage );
862     }
863   }
864 }
865
866 void ImageVisual::CleanCache(const std::string& url)
867 {
868   TextureSet textureSet = mImpl->mRenderer.GetTextures();
869
870   Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
871   Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
872   if( index != Property::INVALID_INDEX )
873   {
874     Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
875     atlasRectValue.Get( atlasRect );
876   }
877
878   mImpl->mRenderer.Reset();
879   if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
880   {
881     mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
882   }
883 }
884
885 void ImageVisual::SetNativeFragmentShaderCode( Dali::NativeImage& nativeImage )
886 {
887   const char* fragmentPreFix = nativeImage.GetCustomFragmentPreFix();
888   const char* customSamplerTypename = nativeImage.GetCustomSamplerTypename();
889
890   if( fragmentPreFix )
891   {
892     mNativeFragmentShaderCode = fragmentPreFix;
893     mNativeFragmentShaderCode += "\n";
894   }
895
896   if( mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty() )
897   {
898     mNativeFragmentShaderCode += mImpl->mCustomShader->mFragmentShader;
899   }
900   else
901   {
902     mNativeFragmentShaderCode += FRAGMENT_SHADER_NO_ATLAS;
903   }
904
905   if( customSamplerTypename )
906   {
907     mNativeFragmentShaderCode.replace( mNativeFragmentShaderCode.find( DEFAULT_SAMPLER_TYPENAME ), strlen( DEFAULT_SAMPLER_TYPENAME ), customSamplerTypename );
908   }
909 }
910
911 } // namespace Internal
912
913 } // namespace Toolkit
914
915 } // namespace Dali