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