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