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