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