eaec29d6edd83a896d5c7eeb9f0a6603540e7690
[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     // ImageVisual destroyed so remove texture unless ReleasePolicy is set to never release
191     if((mTextureId != TextureManager::INVALID_TEXTURE_ID) && (mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER))
192     {
193       RemoveTexture();
194     }
195
196     // Remove ExternalTexture When ImageVisual is destroyed
197     if(mImageUrl.IsValid() && mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
198     {
199       mFactoryCache.GetTextureManager().RemoveExternalTexture(mImageUrl.GetUrl());
200     }
201   }
202 }
203
204 void ImageVisual::DoSetProperties(const Property::Map& propertyMap)
205 {
206   // Url is already received in constructor
207   for(Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter)
208   {
209     KeyValuePair keyValue = propertyMap.GetKeyValue(iter);
210     if(keyValue.first.type == Property::Key::INDEX)
211     {
212       DoSetProperty(keyValue.first.indexKey, keyValue.second);
213     }
214     else
215     {
216       if(keyValue.first == IMAGE_FITTING_MODE)
217       {
218         DoSetProperty(Toolkit::ImageVisual::Property::FITTING_MODE, keyValue.second);
219       }
220       else if(keyValue.first == IMAGE_SAMPLING_MODE)
221       {
222         DoSetProperty(Toolkit::ImageVisual::Property::SAMPLING_MODE, keyValue.second);
223       }
224       else if(keyValue.first == IMAGE_DESIRED_WIDTH)
225       {
226         DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_WIDTH, keyValue.second);
227       }
228       else if(keyValue.first == IMAGE_DESIRED_HEIGHT)
229       {
230         DoSetProperty(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, keyValue.second);
231       }
232       else if(keyValue.first == PIXEL_AREA_UNIFORM_NAME)
233       {
234         DoSetProperty(Toolkit::ImageVisual::Property::PIXEL_AREA, keyValue.second);
235       }
236       else if(keyValue.first == IMAGE_WRAP_MODE_U)
237       {
238         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_U, keyValue.second);
239       }
240       else if(keyValue.first == IMAGE_WRAP_MODE_V)
241       {
242         DoSetProperty(Toolkit::ImageVisual::Property::WRAP_MODE_V, keyValue.second);
243       }
244       else if(keyValue.first == SYNCHRONOUS_LOADING)
245       {
246         DoSetProperty(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, keyValue.second);
247       }
248       else if(keyValue.first == IMAGE_ATLASING)
249       {
250         DoSetProperty(Toolkit::ImageVisual::Property::ATLASING, keyValue.second);
251       }
252       else if(keyValue.first == ALPHA_MASK_URL)
253       {
254         DoSetProperty(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, keyValue.second);
255       }
256       else if(keyValue.first == MASK_CONTENT_SCALE_NAME)
257       {
258         DoSetProperty(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, keyValue.second);
259       }
260       else if(keyValue.first == CROP_TO_MASK_NAME)
261       {
262         DoSetProperty(Toolkit::ImageVisual::Property::CROP_TO_MASK, keyValue.second);
263       }
264       else if(keyValue.first == LOAD_POLICY_NAME)
265       {
266         DoSetProperty(Toolkit::ImageVisual::Property::LOAD_POLICY, keyValue.second);
267       }
268       else if(keyValue.first == RELEASE_POLICY_NAME)
269       {
270         DoSetProperty(Toolkit::ImageVisual::Property::RELEASE_POLICY, keyValue.second);
271       }
272       else if(keyValue.first == ORIENTATION_CORRECTION_NAME)
273       {
274         DoSetProperty(Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, keyValue.second);
275       }
276     }
277   }
278   // Load image immediately if LOAD_POLICY requires it
279   if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
280   {
281     auto attemptAtlasing = AttemptAtlasing();
282     LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::CACHED);
283   }
284 }
285
286 void ImageVisual::DoSetProperty(Property::Index index, const Property::Value& value)
287 {
288   switch(index)
289   {
290     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
291     {
292       bool sync = false;
293       if(value.Get(sync))
294       {
295         if(sync)
296         {
297           mImpl->mFlags |= Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
298         }
299         else
300         {
301           mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
302         }
303       }
304       else
305       {
306         DALI_LOG_ERROR("ImageVisual: synchronousLoading property has incorrect type\n");
307       }
308       break;
309     }
310
311     case Toolkit::ImageVisual::Property::DESIRED_WIDTH:
312     {
313       float desiredWidth = 0.0f;
314       if(value.Get(desiredWidth))
315       {
316         mDesiredSize.SetWidth(desiredWidth);
317       }
318       else
319       {
320         DALI_LOG_ERROR("ImageVisual: desiredWidth property has incorrect type\n");
321       }
322       break;
323     }
324
325     case Toolkit::ImageVisual::Property::DESIRED_HEIGHT:
326     {
327       float desiredHeight = 0.0f;
328       if(value.Get(desiredHeight))
329       {
330         mDesiredSize.SetHeight(desiredHeight);
331       }
332       else
333       {
334         DALI_LOG_ERROR("ImageVisual: desiredHeight property has incorrect type\n");
335       }
336       break;
337     }
338
339     case Toolkit::ImageVisual::Property::FITTING_MODE:
340     {
341       int fittingMode = 0;
342       Scripting::GetEnumerationProperty(value, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, fittingMode);
343       mFittingMode = Dali::FittingMode::Type(fittingMode);
344       break;
345     }
346
347     case Toolkit::ImageVisual::Property::SAMPLING_MODE:
348     {
349       int samplingMode = 0;
350       Scripting::GetEnumerationProperty(value, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, samplingMode);
351       mSamplingMode = Dali::SamplingMode::Type(samplingMode);
352       break;
353     }
354
355     case Toolkit::ImageVisual::Property::PIXEL_AREA:
356     {
357       value.Get(mPixelArea);
358       break;
359     }
360
361     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
362     {
363       int wrapMode = 0;
364       Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode);
365       mWrapModeU = Dali::WrapMode::Type(wrapMode);
366       break;
367     }
368
369     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
370     {
371       int wrapMode = 0;
372       Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode);
373       mWrapModeV = Dali::WrapMode::Type(wrapMode);
374       break;
375     }
376
377     case Toolkit::ImageVisual::Property::ATLASING:
378     {
379       value.Get(mAttemptAtlasing);
380       break;
381     }
382
383     case Toolkit::ImageVisual::Property::ALPHA_MASK_URL:
384     {
385       std::string alphaUrl = "";
386       if(value.Get(alphaUrl))
387       {
388         AllocateMaskData();
389         mMaskingData->mAlphaMaskUrl = alphaUrl;
390       }
391       break;
392     }
393
394     case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE:
395     {
396       float scale = 1.0f;
397       if(value.Get(scale))
398       {
399         AllocateMaskData();
400         mMaskingData->mContentScaleFactor = scale;
401       }
402       break;
403     }
404
405     case Toolkit::ImageVisual::Property::CROP_TO_MASK:
406     {
407       bool crop = false;
408       if(value.Get(crop))
409       {
410         AllocateMaskData();
411         mMaskingData->mCropToMask = crop;
412       }
413       break;
414     }
415
416     case Toolkit::ImageVisual::Property::RELEASE_POLICY:
417     {
418       int releasePolicy = 0;
419       Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy);
420       mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy);
421       break;
422     }
423
424     case Toolkit::ImageVisual::Property::LOAD_POLICY:
425     {
426       int loadPolicy = 0;
427       Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy);
428       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
429       break;
430     }
431     case Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION:
432     {
433       bool orientationCorrection(mOrientationCorrection);
434       if(value.Get(orientationCorrection))
435       {
436         mOrientationCorrection = orientationCorrection;
437       }
438       break;
439     }
440   }
441 }
442
443 void ImageVisual::AllocateMaskData()
444 {
445   if(!mMaskingData)
446   {
447     mMaskingData.reset(new TextureManager::MaskingData());
448   }
449 }
450
451 void ImageVisual::GetNaturalSize(Vector2& naturalSize)
452 {
453   if(mDesiredSize.GetWidth() > 0 && mDesiredSize.GetHeight() > 0)
454   {
455     naturalSize.x = mDesiredSize.GetWidth();
456     naturalSize.y = mDesiredSize.GetHeight();
457     return;
458   }
459   else if(mImpl->mRenderer) // Check if we have a loaded image
460   {
461     if(mImpl->mFlags & Impl::IS_ATLASING_APPLIED)
462     {
463       naturalSize.x = mAtlasRectSize.GetWidth();
464       naturalSize.y = mAtlasRectSize.GetHeight();
465       return;
466     }
467
468     auto textureSet = mImpl->mRenderer.GetTextures();
469     if(textureSet)
470     {
471       auto texture = textureSet.GetTexture(0);
472       if(texture)
473       {
474         naturalSize.x = texture.GetWidth();
475         naturalSize.y = texture.GetHeight();
476         return;
477       }
478     }
479   }
480
481   if(mMaskingData != NULL && mMaskingData->mAlphaMaskUrl.IsValid() &&
482      mMaskingData->mCropToMask)
483   {
484     ImageDimensions dimensions = Dali::GetClosestImageSize(mMaskingData->mAlphaMaskUrl.GetUrl());
485     if(dimensions != ImageDimensions(0, 0))
486     {
487       naturalSize.x = dimensions.GetWidth();
488       naturalSize.y = dimensions.GetHeight();
489     }
490     return;
491   }
492   else if(mImageUrl.IsValid())
493   {
494     if(mImageUrl.GetProtocolType() == VisualUrl::LOCAL)
495     {
496       ImageDimensions dimensions = Dali::GetClosestImageSize(mImageUrl.GetUrl());
497
498       if(dimensions != ImageDimensions(0, 0))
499       {
500         naturalSize.x = dimensions.GetWidth();
501         naturalSize.y = dimensions.GetHeight();
502       }
503       else
504       {
505         Texture brokenImage = mFactoryCache.GetBrokenVisualImage();
506
507         naturalSize.x = brokenImage.GetWidth();
508         naturalSize.y = brokenImage.GetWidth();
509       }
510       return;
511     }
512   }
513   naturalSize = Vector2::ZERO;
514 }
515
516 void ImageVisual::OnInitialize()
517 {
518   Geometry geometry;
519
520   // Get the geometry
521   if(mImpl->mCustomShader)
522   {
523     geometry = CreateGeometry(mFactoryCache, mImpl->mCustomShader->mGridSize);
524   }
525   else // Get any geometry associated with the texture
526   {
527     TextureManager& textureManager = mFactoryCache.GetTextureManager();
528
529     uint32_t firstElementCount{0u};
530     uint32_t secondElementCount{0u};
531     geometry = textureManager.GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
532
533     if(!firstElementCount && !secondElementCount) // Otherwise use quad
534     {
535       geometry = CreateGeometry(mFactoryCache, ImageDimensions(1, 1));
536     }
537   }
538
539   Shader shader = GetShader();
540
541   // Create the renderer
542   mImpl->mRenderer = Renderer::New(geometry, shader);
543
544   //Register transform properties
545   mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
546
547   EnablePreMultipliedAlpha(IsPreMultipliedAlphaEnabled());
548 }
549
550 void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& textures, bool orientationCorrection, TextureManager::ReloadPolicy forceReload)
551 {
552   TextureManager& textureManager = mFactoryCache.GetTextureManager();
553
554   ImageAtlasManagerPtr atlasManager        = nullptr;
555   AtlasUploadObserver* atlasUploadObserver = nullptr;
556   auto                 textureObserver     = this;
557
558   if(atlasing)
559   {
560     atlasManager        = mFactoryCache.GetAtlasManager();
561     atlasUploadObserver = this;
562   }
563
564   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
565                              ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
566                              : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
567
568   bool synchronousLoading = IsSynchronousLoadingRequired();
569   bool loadingStatus;
570
571   textures = textureManager.LoadTexture(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mTextureId, atlasRect, mAtlasRectSize, atlasing, loadingStatus, mWrapModeU, mWrapModeV, textureObserver, atlasUploadObserver, atlasManager, mOrientationCorrection, forceReload, preMultiplyOnLoad);
572
573   if(textures)
574   {
575     if(loadingStatus)
576     {
577       mLoadState = TextureManager::LoadState::LOADING;
578     }
579     else
580     {
581       mLoadState = TextureManager::LoadState::LOAD_FINISHED;
582     }
583
584     EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
585   }
586   else if(synchronousLoading)
587   {
588     // Synchronous loading is failed
589     mLoadState = TextureManager::LoadState::LOAD_FAILED;
590   }
591
592   if(atlasing) // Flag needs to be set before creating renderer
593   {
594     mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
595   }
596   else
597   {
598     mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
599   }
600 }
601
602 bool ImageVisual::AttemptAtlasing()
603 {
604   return (!mImpl->mCustomShader && mImageUrl.GetProtocolType() == VisualUrl::LOCAL && mAttemptAtlasing);
605 }
606
607 void ImageVisual::InitializeRenderer()
608 {
609   auto attemptAtlasing = AttemptAtlasing();
610
611   // Load Texture if mTextures is empty.
612   // mTextures is already set, the mTexture can be used to create Renderer.
613   // There are two cases mTextures is empty.
614   // 1. mTextureId == TextureManager::INVALID_TEXTURE_ID
615   //  - Visual is on stage with LoadPolicy::ATTACHED
616   // 2. mTextureId != TextureManager::INVALID_TEXTURE_ID
617   //  - If ReleasePolicy is DESTROYED, InitializeRenderer called every on stage called.
618   //  - Then every resources those contained in Visual are Reset but mTextureId is remained when the Off stage time,
619   //  - So, mTextures needed to be get from texture manager to created resources like mImpl->mRenderer.
620   if(!mTextures)
621   {
622     if(mTextureId == TextureManager::INVALID_TEXTURE_ID)
623     {
624       LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::CACHED);
625     }
626     else
627     {
628       mTextures = mFactoryCache.GetTextureManager().GetTextureSet(mTextureId);
629     }
630   }
631
632   if(mTextures)
633   {
634     mImpl->mRenderer.SetTextures(mTextures);
635     if(DevelTexture::IsNative(mTextures.GetTexture(0)))
636     {
637       UpdateShader();
638     }
639     mTextures.Reset(); // Visual should not keep a handle to the texture after this point.
640   }
641
642   if(attemptAtlasing) // the texture is packed inside atlas
643   {
644     mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
645
646     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
647
648     if(!defaultWrapMode) // custom wrap mode
649     {
650       Vector2 wrapMode(mWrapModeU - WrapMode::CLAMP_TO_EDGE, mWrapModeV - WrapMode::CLAMP_TO_EDGE);
651       wrapMode.Clamp(Vector2::ZERO, Vector2(2.f, 2.f));
652       mImpl->mRenderer.RegisterProperty(WRAP_MODE_UNIFORM_NAME, wrapMode);
653     }
654   }
655 }
656
657 void ImageVisual::DoSetOnScene(Actor& actor)
658 {
659   if(mImageUrl.IsValid())
660   {
661     InitializeRenderer();
662   }
663
664   if(!mImpl->mRenderer)
665   {
666     return;
667   }
668
669   mPlacementActor = actor;
670   // Search the Actor tree to find if Layer UI behaviour set.
671   Layer layer = actor.GetLayer();
672   if(layer && layer.GetProperty<Layer::Behavior>(Layer::Property::BEHAVIOR) == Layer::LAYER_3D)
673   {
674     // Layer 3D set, do not align pixels
675     mImpl->mRenderer.RegisterProperty(PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF);
676   }
677
678   if(mPixelArea != FULL_TEXTURE_RECT)
679   {
680     mImpl->mRenderer.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, mPixelArea);
681   }
682
683   if(mLoadState == TextureManager::LoadState::LOAD_FINISHED)
684   {
685     actor.AddRenderer(mImpl->mRenderer);
686     mPlacementActor.Reset();
687
688     // Image loaded and ready to display
689     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
690   }
691   else if(mLoadState == TextureManager::LoadState::LOAD_FAILED)
692   {
693     Texture brokenImage = mFactoryCache.GetBrokenVisualImage();
694
695     mTextures = TextureSet::New();
696     mTextures.SetTexture(0u, brokenImage);
697     mImpl->mRenderer.SetTextures(mTextures);
698
699     actor.AddRenderer(mImpl->mRenderer);
700     mPlacementActor.Reset();
701
702     ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
703   }
704 }
705
706 void ImageVisual::DoSetOffScene(Actor& actor)
707 {
708   // Visual::Base::SetOffScene only calls DoSetOffScene if mRenderer exists (is on onstage)
709
710   // Image release is dependent on the ReleasePolicy, renderer is removed.
711   actor.RemoveRenderer(mImpl->mRenderer);
712   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
713   {
714     RemoveTexture(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
715     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
716
717     TextureSet textureSet = TextureSet::New();
718     mImpl->mRenderer.SetTextures(textureSet);
719
720     mLoadState = TextureManager::LoadState::NOT_STARTED;
721   }
722
723   mPlacementActor.Reset();
724 }
725
726 void ImageVisual::DoCreatePropertyMap(Property::Map& map) const
727 {
728   map.Clear();
729   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
730
731   bool sync = IsSynchronousLoadingRequired();
732   map.Insert(SYNCHRONOUS_LOADING, sync);
733   if(mImageUrl.IsValid())
734   {
735     map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
736     map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
737     map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
738   }
739
740   map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode);
741   map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
742
743   map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
744   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU);
745   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV);
746
747   map.Insert(Toolkit::ImageVisual::Property::ATLASING, mAttemptAtlasing);
748
749   if(mMaskingData != NULL)
750   {
751     map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl());
752     map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor);
753     map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask);
754   }
755
756   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
757   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
758   map.Insert(Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, mOrientationCorrection);
759 }
760
761 void ImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
762 {
763   map.Clear();
764   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
765   if(mImageUrl.IsValid())
766   {
767     map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
768     map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
769   }
770 }
771
772 void ImageVisual::OnDoAction(const Dali::Property::Index actionName, const Dali::Property::Value& attributes)
773 {
774   // Check if action is valid for this visual type and perform action if possible
775
776   switch(actionName)
777   {
778     case DevelImageVisual::Action::RELOAD:
779     {
780       auto attemptAtlasing = AttemptAtlasing();
781       LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::FORCED);
782       break;
783     }
784   }
785 }
786
787 void ImageVisual::OnSetTransform()
788 {
789   if(mImpl->mRenderer)
790   {
791     mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
792   }
793 }
794
795 bool ImageVisual::IsResourceReady() const
796 {
797   return (mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
798           mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED);
799 }
800
801 void ImageVisual::UpdateShader()
802 {
803   if(mImpl->mRenderer)
804   {
805     Shader shader = GetShader();
806     mImpl->mRenderer.SetShader(shader);
807   }
808 }
809
810 // From existing atlas manager
811 void ImageVisual::UploadCompleted()
812 {
813   // Texture has been uploaded. If weak handle is holding a placement actor,
814   // it is the time to add the renderer to actor.
815   Actor actor = mPlacementActor.GetHandle();
816   if(actor)
817   {
818     mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
819     actor.AddRenderer(mImpl->mRenderer);
820     // reset the weak handle so that the renderer only get added to actor once
821     mPlacementActor.Reset();
822   }
823
824   // Image loaded
825   ResourceReady(Toolkit::Visual::ResourceStatus::READY);
826   mLoadState = TextureManager::LoadState::LOAD_FINISHED;
827 }
828
829 // From Texture Manager
830 void ImageVisual::UploadComplete(bool loadingSuccess, int32_t textureId, TextureSet textureSet, bool usingAtlas, const Vector4& atlasRectangle, bool preMultiplied)
831 {
832   Toolkit::Visual::ResourceStatus resourceStatus;
833   if(mImpl->mRenderer)
834   {
835     if(usingAtlas)
836     {
837       mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
838     }
839
840     EnablePreMultipliedAlpha(preMultiplied);
841
842     Actor actor = mPlacementActor.GetHandle();
843     if(actor)
844     {
845       actor.AddRenderer(mImpl->mRenderer);
846       // reset the weak handle so that the renderer only get added to actor once
847       mPlacementActor.Reset();
848     }
849
850     if(!loadingSuccess)
851     {
852       Texture brokenImage = mFactoryCache.GetBrokenVisualImage();
853
854       textureSet = TextureSet::New();
855       textureSet.SetTexture(0u, brokenImage);
856       mImpl->mRenderer.SetTextures(textureSet);
857     }
858
859     Sampler sampler = Sampler::New();
860     sampler.SetWrapMode(mWrapModeU, mWrapModeV);
861     textureSet.SetSampler(0u, sampler);
862     mImpl->mRenderer.SetTextures(textureSet);
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
936     if(index != Property::INVALID_INDEX)
937     {
938       mFactoryCache.GetAtlasManager()->Remove(textureSet, atlasRect);
939     }
940   }
941 }
942
943 Shader ImageVisual::GetShader()
944 {
945   Shader shader;
946
947   std::string_view vertexShaderView;
948   bool             usesWholeTexture = true;
949   if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty())
950   {
951     vertexShaderView = mImpl->mCustomShader->mVertexShader;
952     usesWholeTexture = false; // Impossible to tell.
953   }
954   else
955   {
956     vertexShaderView = mImageVisualShaderFactory.GetVertexShaderSource();
957   }
958
959   std::string_view fragmentShaderView;
960   if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty())
961   {
962     fragmentShaderView = mImpl->mCustomShader->mFragmentShader;
963   }
964   else
965   {
966     fragmentShaderView = mImageVisualShaderFactory.GetFragmentShaderSource();
967   }
968
969   // If the texture is native, we may need to change prefix and sampler in
970   // the fragment shader
971   bool        modifiedFragmentShader = false;
972   std::string fragmentShaderString;
973   if(mTextures && DevelTexture::IsNative(mTextures.GetTexture(0)))
974   {
975     Texture nativeTexture  = mTextures.GetTexture(0);
976     fragmentShaderString   = std::string(fragmentShaderView);
977     modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShaderString);
978     fragmentShaderView     = fragmentShaderString;
979   }
980
981   const bool useStandardShader = !mImpl->mCustomShader && !modifiedFragmentShader;
982   if(useStandardShader)
983   {
984     // Create and cache the standard shader
985     shader = mImageVisualShaderFactory.GetShader(
986       mFactoryCache,
987       mImpl->mFlags & Impl::IS_ATLASING_APPLIED ? TextureAtlas::ENABLED : TextureAtlas::DISABLED,
988       mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE ? DefaultTextureWrapMode::APPLY : DefaultTextureWrapMode::DO_NOT_APPLY,
989       IsRoundedCornerRequired() ? RoundedCorner::ENABLED : RoundedCorner::DISABLED,
990       IsBorderlineRequired() ? Borderline::ENABLED : Borderline::DISABLED
991     );
992   }
993   else if(mImpl->mCustomShader)
994   {
995     shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints);
996   }
997   else
998   {
999     shader = Shader::New(vertexShaderView, fragmentShaderView);
1000   }
1001
1002   if(usesWholeTexture)
1003   {
1004     shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
1005   }
1006
1007   // Set pixel align off as default.
1008   // ToDo: Pixel align causes issues such as rattling image animation.
1009   // We should trun it off until issues are resolved
1010   shader.RegisterProperty(PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF);
1011
1012   return shader;
1013 }
1014
1015 } // namespace Internal
1016
1017 } // namespace Toolkit
1018
1019 } // namespace Dali