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