Reduce Cyclomatic Complexity of Text classes
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / image / image-visual.cpp
1 /*
2  * Copyright (c) 2020 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/devel-api/common/stage.h>
25 #include <dali/devel-api/adaptor-framework/image-loading.h>
26 #include <dali/devel-api/scripting/enum-helper.h>
27 #include <dali/devel-api/scripting/scripting.h>
28 #include <dali/integration-api/debug.h>
29
30 // INTERNAL HEADERS
31 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
32 #include <dali-toolkit/public-api/visuals/visual-properties.h>
33 #include <dali-toolkit/devel-api/visuals/image-visual-actions-devel.h>
34 #include <dali-toolkit/internal/visuals/texture-manager-impl.h>
35 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
36 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
37 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
38 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
39 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
40 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
41 #include <dali-toolkit/internal/visuals/visual-url.h>
42 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
43 #include <dali/devel-api/rendering/renderer-devel.h>
44
45 namespace Dali
46 {
47
48 namespace Toolkit
49 {
50
51 namespace Internal
52 {
53
54 namespace
55 {
56
57 // fitting modes
58 DALI_ENUM_TO_STRING_TABLE_BEGIN( FITTING_MODE )
59 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, SHRINK_TO_FIT )
60 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, SCALE_TO_FILL )
61 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, FIT_WIDTH )
62 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, FIT_HEIGHT )
63 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::FittingMode, DEFAULT )
64 DALI_ENUM_TO_STRING_TABLE_END( FITTING_MODE )
65
66 // sampling modes
67 DALI_ENUM_TO_STRING_TABLE_BEGIN( SAMPLING_MODE )
68 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, BOX )
69 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, NEAREST )
70 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, LINEAR )
71 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, BOX_THEN_NEAREST )
72 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, BOX_THEN_LINEAR )
73 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, NO_FILTER )
74 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, DONT_CARE )
75 DALI_ENUM_TO_STRING_TABLE_END( SAMPLING_MODE )
76
77 // wrap modes
78 DALI_ENUM_TO_STRING_TABLE_BEGIN( WRAP_MODE )
79 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, DEFAULT )
80 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, CLAMP_TO_EDGE )
81 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, REPEAT )
82 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, MIRRORED_REPEAT )
83 DALI_ENUM_TO_STRING_TABLE_END( WRAP_MODE )
84
85 // load policies
86 DALI_ENUM_TO_STRING_TABLE_BEGIN( LOAD_POLICY )
87 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::ImageVisual::LoadPolicy, IMMEDIATE )
88 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::ImageVisual::LoadPolicy, ATTACHED )
89 DALI_ENUM_TO_STRING_TABLE_END( LOAD_POLICY )
90
91 // release policies
92 DALI_ENUM_TO_STRING_TABLE_BEGIN( RELEASE_POLICY )
93 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::ImageVisual::ReleasePolicy, DETACHED )
94 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::ImageVisual::ReleasePolicy, DESTROYED )
95 DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::Toolkit::ImageVisual::ReleasePolicy, NEVER )
96 DALI_ENUM_TO_STRING_TABLE_END( RELEASE_POLICY )
97
98 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
99
100 const float PIXEL_ALIGN_ON = 1.0f;
101 const float PIXEL_ALIGN_OFF = 0.0f;
102
103 Geometry CreateGeometry( VisualFactoryCache& factoryCache, ImageDimensions gridSize )
104 {
105   Geometry geometry;
106
107   if( gridSize == ImageDimensions( 1, 1 ) )
108   {
109     geometry = factoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
110   }
111   else
112   {
113     geometry = VisualFactoryCache::CreateGridGeometry( gridSize );
114   }
115
116   return geometry;
117 }
118
119 } // unnamed namespace
120
121
122 ImageVisualPtr ImageVisual::New( VisualFactoryCache& factoryCache,
123                                  ImageVisualShaderFactory& shaderFactory,
124                                  const VisualUrl& imageUrl,
125                                  const Property::Map& properties,
126                                  ImageDimensions size,
127                                  FittingMode::Type fittingMode,
128                                  Dali::SamplingMode::Type samplingMode )
129 {
130   ImageVisualPtr imageVisualPtr( new ImageVisual( factoryCache, shaderFactory, imageUrl, size, fittingMode, samplingMode ) );
131   imageVisualPtr->SetProperties( properties );
132   return imageVisualPtr;
133 }
134
135 ImageVisualPtr ImageVisual::New( VisualFactoryCache& factoryCache,
136                                  ImageVisualShaderFactory& shaderFactory,
137                                  const VisualUrl& imageUrl,
138                                  ImageDimensions size,
139                                  FittingMode::Type fittingMode,
140                                  Dali::SamplingMode::Type samplingMode )
141 {
142   return new ImageVisual( factoryCache, shaderFactory, imageUrl, size, fittingMode, samplingMode );
143 }
144
145 ImageVisual::ImageVisual( VisualFactoryCache& factoryCache,
146                           ImageVisualShaderFactory& shaderFactory,
147                           const VisualUrl& imageUrl,
148                           ImageDimensions size,
149                           FittingMode::Type fittingMode,
150                           Dali::SamplingMode::Type samplingMode )
151 : Visual::Base( factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::IMAGE ),
152   mPixelArea( FULL_TEXTURE_RECT ),
153   mPlacementActor(),
154   mImageUrl( imageUrl ),
155   mMaskingData( ),
156   mDesiredSize( size ),
157   mTextureId( TextureManager::INVALID_TEXTURE_ID ),
158   mTextures(),
159   mImageVisualShaderFactory( shaderFactory ),
160   mFittingMode( fittingMode ),
161   mSamplingMode( samplingMode ),
162   mWrapModeU( WrapMode::DEFAULT ),
163   mWrapModeV( WrapMode::DEFAULT ),
164   mLoadPolicy( Toolkit::ImageVisual::LoadPolicy::ATTACHED ),
165   mReleasePolicy( Toolkit::ImageVisual::ReleasePolicy::DETACHED ),
166   mAtlasRect( 0.0f, 0.0f, 0.0f, 0.0f ),
167   mAtlasRectSize( 0, 0 ),
168   mAttemptAtlasing( false ),
169   mLoading( false ),
170   mOrientationCorrection( true )
171 {
172   EnablePreMultipliedAlpha( mFactoryCache.GetPreMultiplyOnLoad() );
173 }
174
175 ImageVisual::~ImageVisual()
176 {
177   if( Stage::IsInstalled() )
178   {
179     if( mMaskingData )
180     {
181       // TextureManager could have been deleted before the actor that contains this
182       // ImageVisual is destroyed (e.g. due to stage shutdown). Ensure the stage
183       // is still valid before accessing texture manager.
184       if( mMaskingData->mAlphaMaskId != TextureManager::INVALID_TEXTURE_ID )
185       {
186         TextureManager& textureManager = mFactoryCache.GetTextureManager();
187         textureManager.Remove( mMaskingData->mAlphaMaskId, this );
188       }
189     }
190
191     // ImageVisual destroyed so remove texture unless ReleasePolicy is set to never release
192     if( ( mTextureId != TextureManager::INVALID_TEXTURE_ID  ) && ( mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER ) )
193     {
194       RemoveTexture();
195     }
196   }
197 }
198
199 void ImageVisual::DoSetProperties( const Property::Map& propertyMap )
200 {
201   // Url is already received in constructor
202   for( Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter )
203   {
204     KeyValuePair keyValue = propertyMap.GetKeyValue( iter );
205     if( keyValue.first.type == Property::Key::INDEX )
206     {
207       DoSetProperty( keyValue.first.indexKey, keyValue.second );
208     }
209     else
210     {
211       if( keyValue.first == IMAGE_FITTING_MODE )
212       {
213         DoSetProperty( Toolkit::ImageVisual::Property::FITTING_MODE, keyValue.second );
214       }
215       else if( keyValue.first == IMAGE_SAMPLING_MODE )
216       {
217         DoSetProperty( Toolkit::ImageVisual::Property::SAMPLING_MODE, keyValue.second );
218       }
219       else if( keyValue.first == IMAGE_DESIRED_WIDTH )
220       {
221         DoSetProperty( Toolkit::ImageVisual::Property::DESIRED_WIDTH, keyValue.second );
222       }
223       else if( keyValue.first == IMAGE_DESIRED_HEIGHT )
224       {
225         DoSetProperty( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second );
226       }
227       else if( keyValue.first == PIXEL_AREA_UNIFORM_NAME )
228       {
229         DoSetProperty( Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second );
230       }
231       else if( keyValue.first == IMAGE_WRAP_MODE_U )
232       {
233         DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second );
234       }
235       else if( keyValue.first == IMAGE_WRAP_MODE_V )
236       {
237         DoSetProperty( Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second );
238       }
239       else if( keyValue.first == SYNCHRONOUS_LOADING )
240       {
241         DoSetProperty( Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second );
242       }
243       else if( keyValue.first == IMAGE_ATLASING )
244       {
245         DoSetProperty( Toolkit::ImageVisual::Property::ATLASING, keyValue.second );
246       }
247       else if( keyValue.first == ALPHA_MASK_URL )
248       {
249         DoSetProperty( Toolkit::ImageVisual::Property::ALPHA_MASK_URL, keyValue.second );
250       }
251       else if( keyValue.first == MASK_CONTENT_SCALE_NAME )
252       {
253         DoSetProperty( Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second );
254       }
255       else if( keyValue.first == CROP_TO_MASK_NAME )
256       {
257         DoSetProperty( Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second );
258       }
259       else if ( keyValue.first == LOAD_POLICY_NAME )
260       {
261         DoSetProperty( Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second );
262       }
263       else if( keyValue.first == RELEASE_POLICY_NAME )
264       {
265         DoSetProperty( Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second );
266       }
267       else if( keyValue.first == ORIENTATION_CORRECTION_NAME )
268       {
269         DoSetProperty( Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, keyValue.second );
270       }
271     }
272   }
273   // Load image immediately if LOAD_POLICY requires it
274   if ( mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE )
275   {
276     auto attemptAtlasing = AttemptAtlasing();
277     LoadTexture( attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection,
278                  TextureManager::ReloadPolicy::CACHED  );
279   }
280 }
281
282 void ImageVisual::DoSetProperty( Property::Index index, const Property::Value& value )
283 {
284   switch( index )
285   {
286     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
287     {
288       bool sync = false;
289       if( value.Get( sync ) )
290       {
291         if( sync )
292         {
293           mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
294         }
295         else
296         {
297           mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
298         }
299       }
300       else
301       {
302         DALI_LOG_ERROR("ImageVisual: synchronousLoading property has incorrect type\n");
303       }
304       break;
305     }
306
307     case Toolkit::ImageVisual::Property::DESIRED_WIDTH:
308     {
309       float desiredWidth = 0.0f;
310       if( value.Get( desiredWidth ) )
311       {
312         mDesiredSize.SetWidth( desiredWidth );
313       }
314       else
315       {
316         DALI_LOG_ERROR("ImageVisual: desiredWidth property has incorrect type\n");
317       }
318       break;
319     }
320
321     case Toolkit::ImageVisual::Property::DESIRED_HEIGHT:
322     {
323       float desiredHeight = 0.0f;
324       if( value.Get( desiredHeight ) )
325       {
326         mDesiredSize.SetHeight( desiredHeight );
327       }
328       else
329       {
330         DALI_LOG_ERROR("ImageVisual: desiredHeight property has incorrect type\n");
331       }
332       break;
333     }
334
335     case Toolkit::ImageVisual::Property::FITTING_MODE:
336     {
337       int fittingMode = 0;
338       Scripting::GetEnumerationProperty( value, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, fittingMode );
339       mFittingMode = Dali::FittingMode::Type( fittingMode );
340       break;
341     }
342
343     case Toolkit::ImageVisual::Property::SAMPLING_MODE:
344     {
345       int samplingMode = 0;
346       Scripting::GetEnumerationProperty( value, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, samplingMode );
347       mSamplingMode = Dali::SamplingMode::Type( samplingMode );
348       break;
349     }
350
351     case Toolkit::ImageVisual::Property::PIXEL_AREA:
352     {
353       value.Get( mPixelArea );
354       break;
355     }
356
357     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
358     {
359       int wrapMode = 0;
360       Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode );
361       mWrapModeU = Dali::WrapMode::Type( wrapMode );
362       break;
363     }
364
365     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
366     {
367       int wrapMode = 0;
368       Scripting::GetEnumerationProperty( value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode );
369       mWrapModeV = Dali::WrapMode::Type( wrapMode );
370       break;
371     }
372
373     case Toolkit::ImageVisual::Property::ATLASING:
374     {
375       value.Get( mAttemptAtlasing );
376       break;
377     }
378
379     case Toolkit::ImageVisual::Property::ALPHA_MASK_URL:
380     {
381       std::string alphaUrl = "";
382       if( value.Get( alphaUrl ) )
383       {
384         AllocateMaskData();
385         mMaskingData->mAlphaMaskUrl = alphaUrl;
386       }
387       break;
388     }
389
390     case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE:
391     {
392       float scale = 1.0f;
393       if( value.Get( scale ) )
394       {
395         AllocateMaskData();
396         mMaskingData->mContentScaleFactor = scale;
397       }
398       break;
399     }
400
401     case Toolkit::ImageVisual::Property::CROP_TO_MASK:
402     {
403       bool crop=false;
404       if( value.Get( crop ) )
405       {
406         AllocateMaskData();
407         mMaskingData->mCropToMask = crop;
408       }
409       break;
410     }
411
412     case Toolkit::ImageVisual::Property::RELEASE_POLICY:
413     {
414       int releasePolicy = 0;
415       Scripting::GetEnumerationProperty( value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy );
416       mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type( releasePolicy );
417       break;
418     }
419
420     case Toolkit::ImageVisual::Property::LOAD_POLICY:
421     {
422       int loadPolicy = 0;
423       Scripting::GetEnumerationProperty( value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy );
424       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type( loadPolicy );
425       break;
426     }
427     case Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION:
428     {
429       bool orientationCorrection( mOrientationCorrection );
430       if( value.Get( orientationCorrection ) )
431       {
432         mOrientationCorrection = orientationCorrection;
433       }
434       break;
435     }
436   }
437 }
438
439 void ImageVisual::AllocateMaskData()
440 {
441   if( !mMaskingData )
442   {
443     mMaskingData.reset(new TextureManager::MaskingData());
444   }
445 }
446
447 void ImageVisual::GetNaturalSize( Vector2& naturalSize )
448 {
449   if( mDesiredSize.GetWidth()>0 && mDesiredSize.GetHeight()>0)
450   {
451     naturalSize.x = mDesiredSize.GetWidth();
452     naturalSize.y = mDesiredSize.GetHeight();
453     return;
454   }
455   else if( mImpl->mRenderer ) // Check if we have a loaded image
456   {
457     if( mImpl->mFlags & Impl::IS_ATLASING_APPLIED )
458     {
459       naturalSize.x = mAtlasRectSize.GetWidth();
460       naturalSize.y = mAtlasRectSize.GetHeight();
461       return;
462     }
463
464     auto textureSet = mImpl->mRenderer.GetTextures();
465     if( textureSet )
466     {
467       auto texture = textureSet.GetTexture(0);
468       if( texture )
469       {
470         naturalSize.x = texture.GetWidth();
471         naturalSize.y = texture.GetHeight();
472         return;
473       }
474     }
475   }
476
477   if( mMaskingData != NULL && mMaskingData->mAlphaMaskUrl.IsValid() &&
478            mMaskingData->mCropToMask )
479   {
480     ImageDimensions dimensions = Dali::GetClosestImageSize( mMaskingData->mAlphaMaskUrl.GetUrl() );
481     if( dimensions != ImageDimensions( 0, 0 ) )
482     {
483       naturalSize.x = dimensions.GetWidth();
484       naturalSize.y = dimensions.GetHeight();
485     }
486     return;
487   }
488   else if( mImageUrl.IsValid() )
489   {
490     if( mImageUrl.GetProtocolType() == VisualUrl::LOCAL )
491     {
492       ImageDimensions dimensions = Dali::GetClosestImageSize( mImageUrl.GetUrl() );
493
494       if( dimensions != ImageDimensions( 0, 0 ) )
495       {
496         naturalSize.x = dimensions.GetWidth();
497         naturalSize.y = dimensions.GetHeight();
498       }
499       else
500       {
501         Texture brokenImage = mFactoryCache.GetBrokenVisualImage();
502
503         naturalSize.x = brokenImage.GetWidth();
504         naturalSize.y = brokenImage.GetWidth();
505       }
506       return;
507     }
508   }
509   naturalSize = Vector2::ZERO;
510 }
511
512 void ImageVisual::CreateRenderer( TextureSet& textureSet )
513 {
514   Geometry geometry;
515   Shader shader;
516
517   if( !mImpl->mCustomShader )
518   {
519     TextureManager& textureManager = mFactoryCache.GetTextureManager();
520
521     uint32_t firstElementCount {0u};
522     uint32_t secondElementCount {0u};
523     geometry = textureManager.GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
524     if(!firstElementCount && !secondElementCount)
525     {
526       geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
527     }
528
529     shader = mImageVisualShaderFactory.GetShader( mFactoryCache,
530                              mImpl->mFlags & Impl::IS_ATLASING_APPLIED,
531                              mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE,
532                              IsRoundedCornerRequired() );
533   }
534   else
535   {
536     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
537     if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
538     {
539       // Use custom hints
540       shader = Shader::New( mImageVisualShaderFactory.GetVertexShaderSource(), mImageVisualShaderFactory.GetFragmentShaderSource(), mImpl->mCustomShader->mHints );
541       shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
542     }
543     else
544     {
545       shader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? mImageVisualShaderFactory.GetVertexShaderSource() : mImpl->mCustomShader->mVertexShader,
546                             mImpl->mCustomShader->mFragmentShader.empty() ? mImageVisualShaderFactory.GetFragmentShaderSource() : mImpl->mCustomShader->mFragmentShader,
547                             mImpl->mCustomShader->mHints );
548       if( mImpl->mCustomShader->mVertexShader.empty() )
549       {
550         shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
551       }
552     }
553   }
554
555   // Set pixel align off as default.
556   // ToDo: Pixel align causes issues such as rattling image animation.
557   // We should trun it off until issues are resolved
558   shader.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF );
559
560   mImpl->mRenderer = Renderer::New( geometry, shader );
561   if( textureSet )
562   {
563     mImpl->mRenderer.SetTextures( textureSet );
564   }
565   // else still waiting for texture load to finish.
566
567   //Register transform properties
568   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
569
570   EnablePreMultipliedAlpha( IsPreMultipliedAlphaEnabled() );
571 }
572
573 void ImageVisual::LoadTexture( bool& atlasing, Vector4& atlasRect, TextureSet& textures, bool orientationCorrection,
574                                TextureManager::ReloadPolicy forceReload )
575 {
576   TextureManager& textureManager = mFactoryCache.GetTextureManager();
577
578   ImageAtlasManagerPtr atlasManager = nullptr;
579   AtlasUploadObserver* atlasUploadObserver = nullptr;
580   auto textureObserver = this;
581
582   if( atlasing )
583   {
584     atlasManager = mFactoryCache.GetAtlasManager();
585     atlasUploadObserver = this;
586   }
587
588   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
589     ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
590     : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
591
592   textures = textureManager.LoadTexture( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode,
593                                          mMaskingData, IsSynchronousLoadingRequired(), mTextureId,
594                                          atlasRect, mAtlasRectSize, atlasing, mLoading, mWrapModeU,
595                                          mWrapModeV, textureObserver, atlasUploadObserver, atlasManager,
596                                          mOrientationCorrection, forceReload, preMultiplyOnLoad);
597
598   if( textures )
599   {
600     EnablePreMultipliedAlpha( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD );
601   }
602
603   if( atlasing ) // Flag needs to be set before creating renderer
604   {
605     mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
606   }
607   else
608   {
609     mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
610   }
611 }
612
613 bool ImageVisual::AttemptAtlasing()
614 {
615   return ( ! mImpl->mCustomShader && mImageUrl.GetProtocolType() == VisualUrl::LOCAL && mAttemptAtlasing );
616 }
617
618 void ImageVisual::InitializeRenderer()
619 {
620   auto attemptAtlasing = AttemptAtlasing();
621
622   // Load Texture if mTextures is empty.
623   // mTextures is already set, the mTexture can be used to create Renderer.
624   // There are two cases mTextures is empty.
625   // 1. mTextureId == TextureManager::INVALID_TEXTURE_ID
626   //  - Visual is on stage with LoadPolicy::ATTACHED
627   // 2. mTextureId != TextureManager::INVALID_TEXTURE_ID
628   //  - If ReleasePolicy is DESTROYED, InitializeRenderer called every on stage called.
629   //  - Then every resources those contained in Visual are Reset but mTextureId is remained when the Off stage time,
630   //  - So, mTextures needed to be get from texture manager to created resources like mImpl->mRenderer.
631   if( ! mTextures )
632   {
633     if( mTextureId == TextureManager::INVALID_TEXTURE_ID )
634     {
635       LoadTexture( attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection,
636                    TextureManager::ReloadPolicy::CACHED );
637     }
638     else
639     {
640       mTextures = mFactoryCache.GetTextureManager().GetTextureSet( mTextureId );
641     }
642   }
643
644   CreateRenderer( mTextures );
645   mTextures.Reset(); // Visual should not keep a handle to the texture after this point.
646
647   if( attemptAtlasing ) // the texture is packed inside atlas
648   {
649     mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mAtlasRect );
650
651     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
652
653     if( !defaultWrapMode ) // custom wrap mode
654     {
655       Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
656       wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
657       mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
658     }
659   }
660 }
661
662 void ImageVisual::DoSetOnScene( Actor& actor )
663 {
664   if( mImageUrl.IsValid() )
665   {
666     InitializeRenderer();
667   }
668
669   if( !mImpl->mRenderer )
670   {
671     return;
672   }
673
674   mPlacementActor = actor;
675   // Search the Actor tree to find if Layer UI behaviour set.
676   Layer layer = actor.GetLayer();
677   if( layer && layer.GetProperty<Layer::Behavior>( Layer::Property::BEHAVIOR ) == Layer::LAYER_3D )
678   {
679      // Layer 3D set, do not align pixels
680      mImpl->mRenderer.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF );
681   }
682
683   if( mPixelArea != FULL_TEXTURE_RECT )
684   {
685     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
686   }
687
688   if( mLoading == false )
689   {
690     actor.AddRenderer( mImpl->mRenderer );
691     mPlacementActor.Reset();
692
693     // Image loaded and ready to display
694     ResourceReady( Toolkit::Visual::ResourceStatus::READY );
695   }
696 }
697
698 void ImageVisual::DoSetOffScene( Actor& actor )
699 {
700   // Visual::Base::SetOffScene only calls DoSetOffScene if mRenderer exists (is on onstage)
701
702   // Image release is dependent on the ReleasePolicy, renderer is destroyed.
703   actor.RemoveRenderer( mImpl->mRenderer);
704   if( mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED )
705   {
706     RemoveTexture(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
707     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
708   }
709
710   mLoading = false;
711   mImpl->mRenderer.Reset();
712   mPlacementActor.Reset();
713 }
714
715 void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
716 {
717   map.Clear();
718   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE );
719
720   bool sync = IsSynchronousLoadingRequired();
721   map.Insert( SYNCHRONOUS_LOADING, sync );
722   if( mImageUrl.IsValid() )
723   {
724     map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
725     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth() );
726     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight() );
727   }
728
729   map.Insert( Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode );
730   map.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode );
731
732   map.Insert( Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea );
733   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU );
734   map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
735
736   map.Insert( Toolkit::ImageVisual::Property::ATLASING, mAttemptAtlasing );
737
738   if( mMaskingData != NULL )
739   {
740     map.Insert( Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl() );
741     map.Insert( Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor );
742     map.Insert( Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask );
743   }
744
745   map.Insert( Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy );
746   map.Insert( Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy );
747   map.Insert( Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, mOrientationCorrection );
748 }
749
750 void ImageVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
751 {
752   map.Clear();
753   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE );
754   if( mImageUrl.IsValid() )
755   {
756     map.Insert( Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth() );
757     map.Insert( Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight() );
758   }
759 }
760
761 void ImageVisual::OnDoAction( const Dali::Property::Index actionName, const Dali::Property::Value& attributes )
762 {
763   // Check if action is valid for this visual type and perform action if possible
764
765   switch ( actionName )
766   {
767     case DevelImageVisual::Action::RELOAD:
768     {
769       auto attemptAtlasing = AttemptAtlasing();
770       LoadTexture( attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection,
771                    TextureManager::ReloadPolicy::FORCED );
772       break;
773     }
774   }
775 }
776
777 void ImageVisual::OnSetTransform()
778 {
779   if( mImpl->mRenderer )
780   {
781     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
782   }
783 }
784
785 bool ImageVisual::IsResourceReady() const
786 {
787   return ( mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
788            mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED );
789 }
790
791 // From existing atlas manager
792 void ImageVisual::UploadCompleted()
793 {
794   // Texture has been uploaded. If weak handle is holding a placement actor,
795   // it is the time to add the renderer to actor.
796   Actor actor = mPlacementActor.GetHandle();
797   if( actor )
798   {
799     mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mAtlasRect );
800     actor.AddRenderer( mImpl->mRenderer );
801     // reset the weak handle so that the renderer only get added to actor once
802     mPlacementActor.Reset();
803   }
804   // Image loaded
805   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
806   mLoading = false;
807 }
808
809 // From Texture Manager
810 void ImageVisual::UploadComplete( bool loadingSuccess, int32_t textureId, TextureSet textureSet, bool usingAtlas,
811                                   const Vector4& atlasRectangle, bool preMultiplied )
812 {
813   Toolkit::Visual::ResourceStatus resourceStatus;
814   if( mImpl->mRenderer )
815   {
816     if( usingAtlas )
817     {
818       mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mAtlasRect );
819     }
820
821     EnablePreMultipliedAlpha( preMultiplied );
822
823     Actor actor = mPlacementActor.GetHandle();
824     if( actor )
825     {
826       actor.AddRenderer( mImpl->mRenderer );
827       // reset the weak handle so that the renderer only get added to actor once
828       mPlacementActor.Reset();
829     }
830
831     if( !loadingSuccess )
832     {
833       Texture brokenImage = mFactoryCache.GetBrokenVisualImage();
834
835       textureSet = TextureSet::New();
836       textureSet.SetTexture(0u, brokenImage);
837       mImpl->mRenderer.SetTextures( textureSet );
838     }
839
840     Sampler sampler = Sampler::New();
841     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
842     textureSet.SetSampler( 0u, sampler );
843     mImpl->mRenderer.SetTextures(textureSet);
844
845   }
846
847   // Storing TextureSet needed when renderer staged.
848   if( ! mImpl->mRenderer )
849   {
850     mTextures = textureSet;
851   }
852
853   // Image loaded, set status regardless of staged status.
854   if( loadingSuccess )
855   {
856     resourceStatus = Toolkit::Visual::ResourceStatus::READY;
857   }
858   else
859   {
860     resourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
861   }
862
863   // use geometry if needed
864   if( loadingSuccess )
865   {
866     uint32_t firstElementCount{0u};
867     uint32_t secondElementCount{0u};
868     auto geometry = mFactoryCache.GetTextureManager().GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
869     if (mImpl->mRenderer && geometry)
870     {
871       mImpl->mRenderer.SetGeometry(geometry);
872       Dali::DevelRenderer::DrawCommand drawCommand{};
873       drawCommand.drawType = DevelRenderer::DrawType::INDEXED;
874
875       if (firstElementCount)
876       {
877         drawCommand.firstIndex = 0;
878         drawCommand.elementCount = firstElementCount;
879         drawCommand.queue = DevelRenderer::RENDER_QUEUE_OPAQUE;
880         DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
881       }
882
883       if (secondElementCount)
884       {
885         drawCommand.firstIndex = firstElementCount;
886         drawCommand.elementCount = secondElementCount;
887         drawCommand.queue = DevelRenderer::RENDER_QUEUE_TRANSPARENT;
888         DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
889       }
890     }
891   }
892
893   // Signal to observers ( control ) that resources are ready. Must be all resources.
894   ResourceReady( resourceStatus );
895   mLoading = false;
896 }
897
898 void ImageVisual::RemoveTexture()
899 {
900   if( mTextureId != TextureManager::INVALID_TEXTURE_ID )
901   {
902     mFactoryCache.GetTextureManager().Remove( mTextureId, this );
903     mTextureId = TextureManager::INVALID_TEXTURE_ID;
904   }
905   else
906   {
907     Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
908     Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
909     if( index != Property::INVALID_INDEX )
910     {
911       Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
912       atlasRectValue.Get( atlasRect );
913     }
914
915     TextureSet textureSet = mImpl->mRenderer.GetTextures();
916     mImpl->mRenderer.Reset();
917
918     if( index != Property::INVALID_INDEX )
919     {
920       mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
921     }
922   }
923 }
924
925 } // namespace Internal
926
927 } // namespace Toolkit
928
929 } // namespace Dali