ee0042ab3a535939ecfe6545c9c95c315e11491a
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / image / image-visual.cpp
1 /*
2  * Copyright (c) 2017 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/common/stage.h>
25 #include <dali/public-api/images/resource-image.h>
26 #include <dali/public-api/images/native-image.h>
27 #include <dali/devel-api/images/texture-set-image.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/texture-manager-impl.h>
38 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
39 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
40 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
41 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
42 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
43 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
44 #include <dali-toolkit/internal/visuals/visual-url.h>
45
46 namespace Dali
47 {
48
49 namespace Toolkit
50 {
51
52 namespace Internal
53 {
54
55 namespace
56 {
57
58 // property names
59 const char * const IMAGE_FITTING_MODE( "fittingMode" );
60 const char * const IMAGE_SAMPLING_MODE( "samplingMode" );
61 const char * const IMAGE_DESIRED_WIDTH( "desiredWidth" );
62 const char * const IMAGE_DESIRED_HEIGHT( "desiredHeight" );
63 const char * const SYNCHRONOUS_LOADING( "synchronousLoading" );
64 const char * const IMAGE_ATLASING("atlasing");
65 const char * const ALPHA_MASK_URL("alphaMaskUrl");
66
67 // fitting modes
68 DALI_ENUM_TO_STRING_TABLE_BEGIN( FITTING_MODE )
69 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, SHRINK_TO_FIT )
70 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, SCALE_TO_FILL )
71 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, FIT_WIDTH )
72 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, FIT_HEIGHT )
73 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, DEFAULT )
74 DALI_ENUM_TO_STRING_TABLE_END( FITTING_MODE )
75
76 // sampling modes
77 DALI_ENUM_TO_STRING_TABLE_BEGIN( SAMPLING_MODE )
78 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, BOX )
79 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, NEAREST )
80 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, LINEAR )
81 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, BOX_THEN_NEAREST )
82 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, BOX_THEN_LINEAR )
83 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, NO_FILTER )
84 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, DONT_CARE )
85 DALI_ENUM_TO_STRING_TABLE_END( SAMPLING_MODE )
86
87 // wrap modes
88 DALI_ENUM_TO_STRING_TABLE_BEGIN( WRAP_MODE )
89 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, DEFAULT )
90 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, CLAMP_TO_EDGE )
91 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, REPEAT )
92 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, MIRRORED_REPEAT )
93 DALI_ENUM_TO_STRING_TABLE_END( WRAP_MODE )
94
95 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
96
97 const char* DEFAULT_SAMPLER_TYPENAME = "sampler2D";
98
99 const float PIXEL_ALIGN_ON = 1.0f;
100 const float PIXEL_ALIGN_OFF = 0.0f;
101
102 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
103   attribute mediump vec2 aPosition;\n
104   uniform mediump mat4 uModelMatrix;\n
105   uniform mediump mat4 uViewMatrix;\n
106   uniform mediump mat4 uProjection;\n
107   uniform mediump vec3 uSize;\n
108   uniform mediump vec4 pixelArea;
109   varying mediump vec2 vTexCoord;\n
110   uniform lowp float uPixelAligned;\n
111   \n
112   //Visual size and offset
113   uniform mediump vec2 offset;\n
114   uniform mediump vec2 size;\n
115   uniform mediump vec4 offsetSizeMode;\n
116   uniform mediump vec2 origin;\n
117   uniform mediump vec2 anchorPoint;\n
118 \n
119   vec4 ComputeVertexPosition()\n
120   {\n
121     vec2 visualSize = mix(uSize.xy*size, size, offsetSizeMode.zw );\n
122     vec2 visualOffset = mix( offset, offset/uSize.xy, offsetSizeMode.xy);\n
123     return vec4( (aPosition + anchorPoint)*visualSize + (visualOffset + origin)*uSize.xy, 0.0, 1.0 );\n
124   }\n
125 \n
126   void main()\n
127   {\n
128     mediump vec4 vertexPosition = uViewMatrix * uModelMatrix * ComputeVertexPosition();\n
129     vec4 alignedVertexPosition = vertexPosition;\n
130     alignedVertexPosition.xy = floor ( vertexPosition.xy );\n // Pixel alignment
131     vertexPosition = uProjection * mix( vertexPosition, alignedVertexPosition, uPixelAligned );\n
132     vTexCoord = pixelArea.xy+pixelArea.zw*(aPosition + vec2(0.5) );\n
133     gl_Position = vertexPosition;\n
134   }\n
135 );
136
137 const char* FRAGMENT_SHADER_NO_ATLAS = DALI_COMPOSE_SHADER(
138   varying mediump vec2 vTexCoord;\n
139   uniform sampler2D sTexture;\n
140   uniform lowp vec4 uColor;\n
141   uniform lowp vec3 mixColor;\n
142   uniform lowp float opacity;\n
143   uniform lowp float preMultipliedAlpha;\n
144   \n
145   lowp vec4 visualMixColor()\n
146   {\n
147     return vec4( mixColor * mix( 1.0, opacity, preMultipliedAlpha ), opacity );\n
148   }\n
149   void main()\n
150   {\n
151       gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor * visualMixColor();\n
152   }\n
153 );
154
155 const char* FRAGMENT_SHADER_ATLAS_CLAMP = DALI_COMPOSE_SHADER(
156     varying mediump vec2 vTexCoord;\n
157     uniform sampler2D sTexture;\n
158     uniform mediump vec4 uAtlasRect;\n
159     uniform lowp vec4 uColor;\n
160     uniform lowp vec3 mixColor;\n
161     uniform lowp float opacity;\n
162     uniform lowp float preMultipliedAlpha;\n
163     \n
164     lowp vec4 visualMixColor()\n
165     {\n
166         return vec4( mixColor * mix( 1.0, opacity, preMultipliedAlpha ), opacity );\n
167     }\n
168     \n
169     void main()\n
170     {\n
171         mediump vec2 texCoord = clamp( mix( uAtlasRect.xy, uAtlasRect.zw, vTexCoord ), uAtlasRect.xy, uAtlasRect.zw );\n
172         gl_FragColor = texture2D( sTexture, texCoord ) * uColor * visualMixColor();\n
173      }\n
174 );
175
176 const char* FRAGMENT_SHADER_ATLAS_VARIOUS_WRAP = DALI_COMPOSE_SHADER(
177     varying mediump vec2 vTexCoord;\n
178     uniform sampler2D sTexture;\n
179     uniform mediump vec4 uAtlasRect;\n
180     // WrapMode -- 0: CLAMP; 1: REPEAT; 2: REFLECT;
181     uniform lowp vec2 wrapMode;\n
182     uniform lowp vec4 uColor;\n
183     uniform lowp vec3 mixColor;\n
184     uniform lowp float opacity;\n
185     uniform lowp float preMultipliedAlpha;\n
186     \n
187     mediump float wrapCoordinate( mediump vec2 range, mediump float coordinate, lowp float wrap )\n
188     {\n
189       mediump float coord;\n
190       if( wrap > 1.5 )\n // REFLECT
191         coord = 1.0-abs(fract(coordinate*0.5)*2.0 - 1.0);\n
192       else \n// warp == 0 or 1
193         coord = mix(coordinate, fract( coordinate ), wrap);\n
194       return clamp( mix(range.x, range.y, coord), range.x, range.y );
195     }\n
196     \n
197     lowp vec4 visualMixColor()\n
198     {\n
199       return vec4( mixColor * mix( 1.0, opacity, preMultipliedAlpha ), opacity );\n
200     }\n
201     \n
202     void main()\n
203     {\n
204         mediump vec2 texCoord = vec2( wrapCoordinate( uAtlasRect.xz, vTexCoord.x, wrapMode.x ),
205                                       wrapCoordinate( uAtlasRect.yw, vTexCoord.y, wrapMode.y ) );\n
206         gl_FragColor = texture2D( sTexture, texCoord ) * uColor * visualMixColor();\n
207     }\n
208 );
209
210 Geometry CreateGeometry( VisualFactoryCache& factoryCache, ImageDimensions gridSize )
211 {
212   Geometry geometry;
213
214   if( gridSize == ImageDimensions( 1, 1 ) )
215   {
216     geometry = factoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
217   }
218   else
219   {
220     geometry = VisualFactoryCache::CreateGridGeometry( gridSize );
221   }
222
223   return geometry;
224 }
225
226 } // unnamed namespace
227
228 ImageVisualPtr ImageVisual::New( VisualFactoryCache& factoryCache,
229                                  const VisualUrl& imageUrl,
230                                  const Property::Map& properties,
231                                  ImageDimensions size,
232                                  FittingMode::Type fittingMode,
233                                  Dali::SamplingMode::Type samplingMode )
234 {
235   ImageVisualPtr imageVisualPtr( new ImageVisual( factoryCache, imageUrl, size, fittingMode, samplingMode ) );
236   imageVisualPtr->SetProperties( properties );
237   return imageVisualPtr;
238 }
239
240 ImageVisualPtr ImageVisual::New( VisualFactoryCache& factoryCache,
241                                  const VisualUrl& imageUrl,
242                                  ImageDimensions size,
243                                  FittingMode::Type fittingMode,
244                                  Dali::SamplingMode::Type samplingMode )
245 {
246   return new ImageVisual( factoryCache, imageUrl, size, fittingMode, samplingMode );
247 }
248
249 ImageVisualPtr ImageVisual::New( VisualFactoryCache& factoryCache, const Image& image )
250 {
251   return new ImageVisual( factoryCache, image );
252 }
253
254 ImageVisual::ImageVisual( VisualFactoryCache& factoryCache,
255                           const VisualUrl& imageUrl,
256                           ImageDimensions size,
257                           FittingMode::Type fittingMode,
258                           Dali::SamplingMode::Type samplingMode )
259 : Visual::Base( factoryCache ),
260   mImage(),
261   mPixels(),
262   mPixelArea( FULL_TEXTURE_RECT ),
263   mPlacementActor(),
264   mImageUrl( imageUrl ),
265   mMaskingData( NULL ),
266   mDesiredSize( size ),
267   mTextureId( TextureManager::INVALID_TEXTURE_ID ),
268   mFittingMode( fittingMode ),
269   mSamplingMode( samplingMode ),
270   mWrapModeU( WrapMode::DEFAULT ),
271   mWrapModeV( WrapMode::DEFAULT ),
272   mAttemptAtlasing( false ),
273   mTextureLoading( false )
274 {
275 }
276
277 ImageVisual::ImageVisual( VisualFactoryCache& factoryCache, const Image& image )
278 : Visual::Base( factoryCache ),
279   mImage( image ),
280   mPixels(),
281   mPixelArea( FULL_TEXTURE_RECT ),
282   mPlacementActor(),
283   mImageUrl(),
284   mMaskingData( NULL ),
285   mDesiredSize(),
286   mTextureId( TextureManager::INVALID_TEXTURE_ID ),
287   mFittingMode( FittingMode::DEFAULT ),
288   mSamplingMode( SamplingMode::DEFAULT ),
289   mWrapModeU( WrapMode::DEFAULT ),
290   mWrapModeV( WrapMode::DEFAULT ),
291   mAttemptAtlasing( false ),
292   mTextureLoading( false )
293 {
294 }
295
296 ImageVisual::~ImageVisual()
297 {
298   delete mMaskingData;
299 }
300
301 void ImageVisual::DoSetProperties( const Property::Map& propertyMap )
302 {
303   // Url is already received in constructor
304   for( Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter )
305   {
306     KeyValuePair keyValue = propertyMap.GetKeyValue( iter );
307     if( keyValue.first.type == Property::Key::INDEX )
308     {
309       DoSetProperty( keyValue.first.indexKey, keyValue.second );
310     }
311     else
312     {
313       if( keyValue.first == IMAGE_FITTING_MODE )
314       {
315         DoSetProperty( Toolkit::ImageVisual::Property::FITTING_MODE, keyValue.second );
316       }
317       else if( keyValue.first == IMAGE_SAMPLING_MODE )
318       {
319         DoSetProperty( Toolkit::ImageVisual::Property::SAMPLING_MODE, keyValue.second );
320       }
321       else if( keyValue.first == IMAGE_DESIRED_WIDTH )
322       {
323         DoSetProperty( Toolkit::ImageVisual::Property::DESIRED_WIDTH, keyValue.second );
324       }
325       else if( keyValue.first == IMAGE_DESIRED_HEIGHT )
326       {
327         DoSetProperty( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second );
328       }
329       else if( keyValue.first == PIXEL_AREA_UNIFORM_NAME )
330       {
331         DoSetProperty( Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second );
332       }
333       else if( keyValue.first == IMAGE_WRAP_MODE_U )
334       {
335         DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second );
336       }
337       else if( keyValue.first == IMAGE_WRAP_MODE_V )
338       {
339         DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second );
340       }
341       else if( keyValue.first == SYNCHRONOUS_LOADING )
342       {
343         DoSetProperty( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second );
344       }
345       else if ( keyValue.first == IMAGE_ATLASING )
346       {
347         DoSetProperty( Toolkit::DevelImageVisual::Property::ATLASING, keyValue.second );
348       }
349       else if ( keyValue.first == ALPHA_MASK_URL )
350       {
351         DoSetProperty( Toolkit::DevelImageVisual::Property::ALPHA_MASK_URL, keyValue.second );
352       }
353       else if ( keyValue.first == MASK_CONTENT_SCALE_NAME )
354       {
355         DoSetProperty( Toolkit::DevelImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second );
356       }
357       else if ( keyValue.first == CROP_TO_MASK_NAME )
358       {
359         DoSetProperty( Toolkit::DevelImageVisual::Property::CROP_TO_MASK, keyValue.second );
360       }
361     }
362   }
363
364   if( ( mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING ) && mImageUrl.IsValid() )
365   {
366     // if sync loading is required, the loading should start
367     // immediately when new image url is set or the actor is off stage
368     // ( for on-stage actor with image url unchanged, resource loading
369     // is already finished )
370     LoadResourceSynchronously();
371   }
372 }
373
374 void ImageVisual::DoSetProperty( Property::Index index, const Property::Value& value )
375 {
376   switch( index )
377   {
378     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
379     {
380       bool sync;
381       if( value.Get( sync ) )
382       {
383         if( sync )
384         {
385           mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
386         }
387         else
388         {
389           mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
390         }
391       }
392       else
393       {
394         DALI_LOG_ERROR("ImageVisual: synchronousLoading property has incorrect type\n");
395       }
396       break;
397     }
398
399     case Toolkit::ImageVisual::Property::DESIRED_WIDTH:
400     {
401       float desiredWidth;
402       if( value.Get( desiredWidth ) )
403       {
404         mDesiredSize.SetWidth( desiredWidth );
405       }
406       else
407       {
408         DALI_LOG_ERROR("ImageVisual: desiredWidth property has incorrect type\n");
409       }
410       break;
411     }
412
413     case Toolkit::ImageVisual::Property::DESIRED_HEIGHT:
414     {
415       float desiredHeight;
416       if( value.Get( desiredHeight ) )
417       {
418         mDesiredSize.SetHeight( desiredHeight );
419       }
420       else
421       {
422         DALI_LOG_ERROR("ImageVisual: desiredHeight property has incorrect type\n");
423       }
424       break;
425     }
426
427     case Toolkit::ImageVisual::Property::FITTING_MODE:
428     {
429       int fittingMode;
430       Scripting::GetEnumerationProperty( value, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, fittingMode );
431       mFittingMode = Dali::FittingMode::Type( fittingMode );
432       break;
433     }
434
435     case Toolkit::ImageVisual::Property::SAMPLING_MODE:
436     {
437       int samplingMode;
438       Scripting::GetEnumerationProperty( value, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, samplingMode );
439       mSamplingMode = Dali::SamplingMode::Type( samplingMode );
440       break;
441     }
442
443     case Toolkit::ImageVisual::Property::PIXEL_AREA:
444     {
445       value.Get( mPixelArea );
446       break;
447     }
448
449     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
450     {
451       int wrapMode;
452       Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode );
453       mWrapModeU = Dali::WrapMode::Type( wrapMode );
454       break;
455     }
456
457     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
458     {
459       int wrapMode;
460       Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode );
461       mWrapModeV = Dali::WrapMode::Type( wrapMode );
462       break;
463     }
464
465     case Toolkit::DevelImageVisual::Property::ATLASING:
466     {
467       bool atlasing = false;
468       mAttemptAtlasing = value.Get( atlasing );
469       break;
470     }
471
472     case Toolkit::DevelImageVisual::Property::ALPHA_MASK_URL:
473     {
474       std::string alphaUrl;
475       if( value.Get( alphaUrl ) )
476       {
477         AllocateMaskData();
478         // Immediately trigger the alpha mask loading (it may just get a cached value)
479         mMaskingData->SetImage( alphaUrl );
480       }
481       break;
482     }
483
484     case Toolkit::DevelImageVisual::Property::MASK_CONTENT_SCALE:
485     {
486       float scale;
487       if( value.Get( scale ) )
488       {
489         AllocateMaskData();
490         mMaskingData->mContentScaleFactor = scale;
491       }
492       break;
493     }
494
495     case Toolkit::DevelImageVisual::Property::CROP_TO_MASK:
496     {
497       bool crop=false;
498       if( value.Get( crop ) )
499       {
500         AllocateMaskData();
501         mMaskingData->mCropToMask = crop;
502       }
503       break;
504     }
505   }
506 }
507
508 void ImageVisual::AllocateMaskData()
509 {
510   if( mMaskingData == NULL )
511   {
512     TextureManager& textureManager = mFactoryCache.GetTextureManager();
513     mMaskingData = new MaskingData(textureManager);
514   }
515 }
516
517 void ImageVisual::GetNaturalSize( Vector2& naturalSize )
518 {
519   if(mImage)
520   {
521     naturalSize.x = mImage.GetWidth();
522     naturalSize.y = mImage.GetHeight();
523     return;
524   }
525   else if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
526   {
527     naturalSize.x = mDesiredSize.GetWidth();
528     naturalSize.y = mDesiredSize.GetHeight();
529     return;
530   }
531   else if( mImpl->mRenderer ) // Check if we have a loaded image
532   {
533     auto textureSet = mImpl->mRenderer.GetTextures();
534
535     if( textureSet )
536     {
537       auto texture = textureSet.GetTexture(0);
538       naturalSize.x = texture.GetWidth();
539       naturalSize.y = texture.GetHeight();
540       return;
541     }
542   }
543
544   if( mMaskingData != NULL && mMaskingData->mAlphaMaskUrl.IsValid() &&
545            mMaskingData->mCropToMask )
546   {
547     ImageDimensions dimensions = Dali::GetClosestImageSize( mMaskingData->mAlphaMaskUrl.GetUrl() );
548     if( dimensions != ImageDimensions( 0, 0 ) )
549     {
550       naturalSize.x = dimensions.GetWidth();
551       naturalSize.y = dimensions.GetHeight();
552     }
553     return;
554   }
555   else if( mImageUrl.IsValid() )
556   {
557     if( mImageUrl.GetProtocolType() == VisualUrl::LOCAL )
558     {
559       ImageDimensions dimensions = Dali::GetClosestImageSize( mImageUrl.GetUrl() );
560
561       if( dimensions != ImageDimensions( 0, 0 ) )
562       {
563         naturalSize.x = dimensions.GetWidth();
564         naturalSize.y = dimensions.GetHeight();
565       }
566       else
567       {
568         Image brokenImage = VisualFactoryCache::GetBrokenVisualImage();
569
570         naturalSize.x = brokenImage.GetWidth();
571         naturalSize.y = brokenImage.GetWidth();
572       }
573       return;
574     }
575   }
576
577   naturalSize = Vector2::ZERO;
578 }
579
580 void ImageVisual::CreateRenderer( TextureSet& textureSet )
581 {
582   Geometry geometry;
583   Shader shader;
584
585   if( !mImpl->mCustomShader )
586   {
587     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
588
589     shader = GetImageShader( mFactoryCache,
590                              mImpl->mFlags & Impl::IS_ATLASING_APPLIED,
591                              mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE );
592   }
593   else
594   {
595     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
596     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
597     {
598       shader = GetImageShader( mFactoryCache, false, true );
599     }
600     else
601     {
602       shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
603                              mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER_NO_ATLAS : mImpl->mCustomShader->mFragmentShader,
604                              mImpl->mCustomShader->mHints );
605       if( mImpl->mCustomShader->mVertexShader.empty() )
606       {
607         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
608       }
609     }
610   }
611
612   // Set pixel align off as default.
613   // ToDo: Pixel align causes issues such as rattling image animation.
614   // We should trun it off until issues are resolved
615   shader.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF );
616
617   mImpl->mRenderer = Renderer::New( geometry, shader );
618   if( textureSet )
619   {
620     mImpl->mRenderer.SetTextures( textureSet );
621   }
622   // else still waiting for texture load to finish.
623
624   //Register transform properties
625   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
626 }
627
628 void ImageVisual::CreateNativeImageRenderer( NativeImage& nativeImage )
629 {
630   Geometry geometry;
631   Shader shader;
632
633   std::string fragmentShader;
634   const char* fragmentPreFix = nativeImage.GetCustomFragmentPreFix();
635   const char* customSamplerTypename = nativeImage.GetCustomSamplerTypename();
636
637   if( fragmentPreFix )
638   {
639     fragmentShader = fragmentPreFix;
640     fragmentShader += "\n";
641   }
642
643   if( mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty() )
644   {
645     fragmentShader += mImpl->mCustomShader->mFragmentShader;
646   }
647   else
648   {
649     fragmentShader += FRAGMENT_SHADER_NO_ATLAS;
650   }
651
652   if( customSamplerTypename )
653   {
654     fragmentShader.replace( fragmentShader.find( DEFAULT_SAMPLER_TYPENAME ), strlen( DEFAULT_SAMPLER_TYPENAME ), customSamplerTypename );
655   }
656
657   if( !mImpl->mCustomShader )
658   {
659     geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
660
661     shader  = Shader::New( VERTEX_SHADER, fragmentShader );
662     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
663   }
664   else
665   {
666     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
667     shader  = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
668                            fragmentShader,
669                            mImpl->mCustomShader->mHints );
670     if( mImpl->mCustomShader->mVertexShader.empty() )
671     {
672       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
673     }
674   }
675
676   mImpl->mRenderer = Renderer::New( geometry, shader );
677
678   //Register transform properties
679   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
680 }
681
682
683 bool ImageVisual::IsSynchronousResourceLoading() const
684 {
685   return mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
686 }
687
688 void ImageVisual::LoadResourceSynchronously()
689 {
690   if( mImageUrl.IsValid() )
691   {
692     Devel::PixelBuffer pixelBuffer = LoadImageFromFile( mImageUrl.GetUrl(), mDesiredSize, mFittingMode, mSamplingMode );
693
694     if( pixelBuffer )
695     {
696       mPixels = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
697     }
698     mTextureLoading = false;
699   }
700 }
701
702 TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, bool synchronousLoading, bool attemptAtlasing )
703 {
704   TextureSet textureSet;
705
706   mTextureLoading = false;
707
708   textureRect = FULL_TEXTURE_RECT;
709   if( synchronousLoading )
710   {
711     if( !mPixels )
712     {
713       // use broken image
714       textureSet = TextureSet::New();
715       TextureSetImage( textureSet, 0u, VisualFactoryCache::GetBrokenVisualImage() );
716     }
717     else
718     {
719       if( attemptAtlasing )
720       {
721         textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, mPixels );
722         mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
723       }
724       if( !textureSet ) // big image, no atlasing or atlasing failed
725       {
726         mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
727         Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, mPixels.GetPixelFormat(),
728                                         mPixels.GetWidth(), mPixels.GetHeight() );
729         texture.Upload( mPixels );
730         textureSet = TextureSet::New();
731         textureSet.SetTexture( 0u, texture );
732       }
733     }
734   }
735   else
736   {
737     if( attemptAtlasing )
738     {
739       textureSet = mFactoryCache.GetAtlasManager()->Add( textureRect, mImageUrl.GetUrl(), mDesiredSize, mFittingMode, true, this );
740       mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
741       mTextureLoading = true;
742     }
743     if( !textureSet ) // big image, no atlasing or atlasing failed
744     {
745       mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
746       TextureManager& textureManager = mFactoryCache.GetTextureManager();
747       if( mMaskingData == NULL )
748       {
749         mTextureId = textureManager.RequestLoad( mImageUrl, mDesiredSize, mFittingMode,
750                                                  mSamplingMode, TextureManager::NO_ATLAS, this );
751       }
752       else
753       {
754         mTextureId = textureManager.RequestLoad( mImageUrl,
755                                                  mMaskingData->mAlphaMaskId,
756                                                  mMaskingData->mContentScaleFactor,
757                                                  mDesiredSize,
758                                                  mFittingMode, mSamplingMode,
759                                                  TextureManager::NO_ATLAS,
760                                                  mMaskingData->mCropToMask,
761                                                  this );
762       }
763
764       TextureManager::LoadState loadState = textureManager.GetTextureState( mTextureId );
765
766       mTextureLoading = ( loadState == TextureManager::LOADING );
767
768       if( loadState == TextureManager::UPLOADED )
769       {
770         // UploadComplete has already been called - keep the same texture set
771         textureSet = textureManager.GetTextureSet(mTextureId);
772       }
773     }
774   }
775
776   if( ! (mImpl->mFlags & Impl::IS_ATLASING_APPLIED) && textureSet )
777   {
778     Sampler sampler = Sampler::New();
779     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
780     textureSet.SetSampler( 0u, sampler );
781   }
782
783   return textureSet;
784 }
785
786 void ImageVisual::InitializeRenderer()
787 {
788   mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
789
790   if( ! mImpl->mCustomShader && mImageUrl.GetProtocolType() == VisualUrl::LOCAL )
791   {
792     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
793
794     Vector4 atlasRect;
795     // texture set has to be created first as we need to know if atlasing succeeded or not
796     // when selecting the shader
797     TextureSet textures = CreateTextureSet( atlasRect, IsSynchronousResourceLoading(), mAttemptAtlasing );
798     CreateRenderer( textures );
799
800     if( mImpl->mFlags & Impl::IS_ATLASING_APPLIED ) // the texture is packed inside atlas
801     {
802       mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
803       if( !defaultWrapMode ) // custom wrap mode, renderer is not cached.
804       {
805         Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
806         wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
807         mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
808       }
809     }
810   }
811   else
812   {
813     // for custom shader or remote image, atlas is not applied
814     Vector4 atlasRect; // ignored in this case
815     TextureSet textures = CreateTextureSet( atlasRect, IsSynchronousResourceLoading(), false );
816     CreateRenderer( textures );
817   }
818 }
819
820 void ImageVisual::InitializeRenderer( const Image& image )
821 {
822   // don't reuse CreateTextureSet
823   TextureSet textures = TextureSet::New();
824
825   NativeImage nativeImage = NativeImage::DownCast( image );
826   if( nativeImage )
827   {
828     CreateNativeImageRenderer( nativeImage );
829     DALI_ASSERT_DEBUG( textures );
830     mImpl->mRenderer.SetTextures( textures );
831   }
832   else
833   {
834     // reuse existing code for regular images
835     CreateRenderer( textures );
836   }
837   ApplyImageToSampler( image );
838 }
839
840 void ImageVisual::DoSetOnStage( Actor& actor )
841 {
842   if( mImageUrl.IsValid() )
843   {
844     InitializeRenderer();
845   }
846   else if ( mImage )
847   {
848     InitializeRenderer( mImage );
849   }
850
851   if ( !mImpl->mRenderer )
852   {
853     return;
854   }
855
856   mPlacementActor = actor;
857   // Search the Actor tree to find if Layer UI behaviour set.
858   Layer layer = actor.GetLayer();
859   if ( layer && layer.GetBehavior() == Layer::LAYER_3D )
860   {
861      // Layer 3D set, do not align pixels
862      mImpl->mRenderer.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF );
863   }
864
865   if( mPixelArea != FULL_TEXTURE_RECT )
866   {
867     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
868   }
869
870   if( mTextureLoading == false )
871   {
872     actor.AddRenderer( mImpl->mRenderer );
873     mPlacementActor.Reset();
874
875     // Image loaded and ready to display
876     ResourceReady();
877   }
878 }
879
880 void ImageVisual::DoSetOffStage( Actor& actor )
881 {
882   // Visual::Base::SetOffStage only calls DoSetOffStage if mRenderer exists (is on onstage)
883
884   //If we own the image then make sure we release it when we go off stage
885   actor.RemoveRenderer( mImpl->mRenderer);
886   if( mImageUrl.IsValid() )
887   {
888     RemoveTexture( mImageUrl.GetUrl() );
889     mImage.Reset();
890   }
891   mTextureLoading = false;
892   mImpl->mRenderer.Reset();
893   mPlacementActor.Reset();
894 }
895
896 void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
897 {
898   map.Clear();
899   map.Insert( Toolkit::DevelVisual::Property::TYPE, Toolkit::Visual::IMAGE );
900
901   bool sync = IsSynchronousResourceLoading();
902   map.Insert( SYNCHRONOUS_LOADING, sync );
903   if( mImageUrl.IsValid() )
904   {
905     map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
906     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth() );
907     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight() );
908   }
909   else if( mImage )
910   {
911     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
912     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
913
914     ResourceImage resourceImage = ResourceImage::DownCast(mImage);
915     if( resourceImage )
916     {
917       map.Insert( Toolkit::ImageVisual::Property::URL, resourceImage.GetUrl() );
918     }
919   }
920
921   map.Insert( Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode );
922   map.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode );
923
924   map.Insert( Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea );
925   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU );
926   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
927
928   map.Insert( Toolkit::DevelImageVisual::Property::ATLASING, mAttemptAtlasing );
929   if( mMaskingData != NULL )
930   {
931     map.Insert( Toolkit::DevelImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl() );
932     map.Insert( Toolkit::DevelImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor );
933     map.Insert( Toolkit::DevelImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask );
934   }
935 }
936
937 void ImageVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
938 {
939   map.Clear();
940   map.Insert( Toolkit::DevelVisual::Property::TYPE, Toolkit::Visual::IMAGE );
941   if( mImageUrl.IsValid() )
942   {
943     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth() );
944     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight() );
945   }
946   else if( mImage )
947   {
948     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, static_cast<int>(mImage.GetWidth()) );
949     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, static_cast<int>(mImage.GetHeight()) );
950   }
951 }
952
953 void ImageVisual::OnSetTransform()
954 {
955   if( mImpl->mRenderer )
956   {
957     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
958   }
959 }
960
961 Shader ImageVisual::GetImageShader( VisualFactoryCache& factoryCache, bool atlasing, bool defaultTextureWrapping )
962 {
963   Shader shader;
964   if( atlasing )
965   {
966     if( defaultTextureWrapping )
967     {
968       shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP );
969       if( !shader )
970       {
971         shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_CLAMP );
972         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
973         factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP, shader );
974       }
975     }
976     else
977     {
978       shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP );
979       if( !shader )
980       {
981         shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_VARIOUS_WRAP );
982         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
983         factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP, shader );
984       }
985     }
986   }
987   else
988   {
989     shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER );
990     if( !shader )
991     {
992       shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_NO_ATLAS );
993       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
994       factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER, shader );
995     }
996   }
997
998   return shader;
999 }
1000
1001 void ImageVisual::ApplyImageToSampler( const Image& image )
1002 {
1003   if( image )
1004   {
1005     TextureSet textureSet = mImpl->mRenderer.GetTextures();
1006     DALI_ASSERT_DEBUG( textureSet ); // texture set should always exist by this time
1007
1008     TextureSetImage( textureSet, 0u, image );
1009     Sampler sampler = Sampler::New();
1010     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
1011     textureSet.SetSampler( 0u, sampler );
1012   }
1013 }
1014
1015 // From existing atlas manager
1016 void ImageVisual::UploadCompleted()
1017 {
1018   // Texture has been uploaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
1019   Actor actor = mPlacementActor.GetHandle();
1020   if( actor )
1021   {
1022     actor.AddRenderer( mImpl->mRenderer );
1023
1024     // reset the weak handle so that the renderer only get added to actor once
1025     mPlacementActor.Reset();
1026   }
1027   mTextureLoading = false;
1028 }
1029
1030 // From Texture Manager
1031 void ImageVisual::UploadComplete( bool loadingSuccess, int32_t textureId, TextureSet textureSet, bool usingAtlas, const Vector4& atlasRectangle )
1032 {
1033   Actor actor = mPlacementActor.GetHandle();
1034   if( actor )
1035   {
1036     if( mImpl->mRenderer )
1037     {
1038       actor.AddRenderer( mImpl->mRenderer );
1039       // reset the weak handle so that the renderer only get added to actor once
1040       mPlacementActor.Reset();
1041
1042       if( loadingSuccess )
1043       {
1044         Sampler sampler = Sampler::New();
1045         sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
1046         textureSet.SetSampler( 0u, sampler );
1047         mImpl->mRenderer.SetTextures(textureSet);
1048       }
1049       else
1050       {
1051         Image brokenImage = VisualFactoryCache::GetBrokenVisualImage();
1052
1053         textureSet = TextureSet::New();
1054         mImpl->mRenderer.SetTextures( textureSet );
1055
1056         ApplyImageToSampler( brokenImage );
1057       }
1058       // Image loaded and ready to display
1059       ResourceReady();
1060     }
1061   }
1062   mTextureLoading = false;
1063 }
1064
1065 void ImageVisual::RemoveTexture(const std::string& url)
1066 {
1067   if( mTextureId != TextureManager::INVALID_TEXTURE_ID )
1068   {
1069     mFactoryCache.GetTextureManager().Remove( mTextureId );
1070     mTextureId = TextureManager::INVALID_TEXTURE_ID;
1071   }
1072   else
1073   {
1074     Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
1075     Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
1076     if( index != Property::INVALID_INDEX )
1077     {
1078       Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
1079       atlasRectValue.Get( atlasRect );
1080     }
1081
1082     TextureSet textureSet = mImpl->mRenderer.GetTextures();
1083     mImpl->mRenderer.Reset();
1084
1085     if( index != Property::INVALID_INDEX )
1086     {
1087       mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
1088     }
1089   }
1090 }
1091
1092
1093 ImageVisual::MaskingData::MaskingData( TextureManager& textureManager )
1094 : mTextureManager( textureManager ),
1095   mAlphaMaskUrl(),
1096   mAlphaMaskId( TextureManager::INVALID_TEXTURE_ID ),
1097   mContentScaleFactor( 1.0f ),
1098   mCropToMask( true )
1099 {
1100 }
1101
1102 ImageVisual::MaskingData::~MaskingData()
1103 {
1104   if( Stage::IsInstalled() )
1105   {
1106     // TextureManager could have been deleted before the actor that contains this
1107     // ImageVisual is destroyed (e.g. due to stage shutdown). Ensure the stage
1108     // is still valid before accessing texture manager.
1109     if( mAlphaMaskId != TextureManager::INVALID_TEXTURE_ID )
1110     {
1111       mTextureManager.Remove( mAlphaMaskId );
1112     }
1113   }
1114 }
1115
1116 void ImageVisual::MaskingData::SetImage( const std::string& maskUrl )
1117 {
1118   mAlphaMaskUrl = maskUrl;
1119   mAlphaMaskId = mTextureManager.RequestMaskLoad( mAlphaMaskUrl );
1120 }
1121
1122 } // namespace Internal
1123
1124 } // namespace Toolkit
1125
1126 } // namespace Dali