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