Minor optimization for image visaul and texture manager
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / image / image-visual.cpp
1 /*
2  * Copyright (c) 2023 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 <dali/public-api/adaptor-framework/async-task-manager.h>
31 #include <dali/public-api/rendering/decorated-visual-renderer.h>
32 #include <cstring> // for strlen()
33
34 // INTERNAL HEADERS
35 #include <dali-toolkit/devel-api/visuals/image-visual-actions-devel.h>
36 #include <dali-toolkit/internal/texture-manager/texture-manager-impl.h>
37 #include <dali-toolkit/internal/visuals/image-atlas-manager.h>
38 #include <dali-toolkit/internal/visuals/image-visual-shader-factory.h>
39 #include <dali-toolkit/internal/visuals/image-visual-shader-feature-builder.h>
40 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
41 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
42 #include <dali-toolkit/internal/visuals/visual-factory-cache.h>
43 #include <dali-toolkit/internal/visuals/visual-factory-impl.h>
44 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
45 #include <dali-toolkit/internal/visuals/visual-url.h>
46 #include <dali-toolkit/public-api/visuals/image-visual-properties.h>
47 #include <dali-toolkit/public-api/visuals/visual-properties.h>
48
49 namespace Dali
50 {
51 namespace Toolkit
52 {
53 namespace Internal
54 {
55 namespace
56 {
57 const int CUSTOM_PROPERTY_COUNT(7); // ltr, wrap, pixel area, atlas, pixalign, crop to mask, mask texture ratio
58
59 // fitting modes
60 DALI_ENUM_TO_STRING_TABLE_BEGIN(FITTING_MODE)
61   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SHRINK_TO_FIT)
62   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, SCALE_TO_FILL)
63   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_WIDTH)
64   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, FIT_HEIGHT)
65   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::FittingMode, DEFAULT)
66 DALI_ENUM_TO_STRING_TABLE_END(FITTING_MODE)
67
68 // sampling modes
69 DALI_ENUM_TO_STRING_TABLE_BEGIN(SAMPLING_MODE)
70   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX)
71   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, NEAREST)
72   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, LINEAR)
73   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX_THEN_NEAREST)
74   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, BOX_THEN_LINEAR)
75   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, NO_FILTER)
76   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::SamplingMode, DONT_CARE)
77 DALI_ENUM_TO_STRING_TABLE_END(SAMPLING_MODE)
78
79 // wrap modes
80 DALI_ENUM_TO_STRING_TABLE_BEGIN(WRAP_MODE)
81   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, DEFAULT)
82   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, CLAMP_TO_EDGE)
83   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, REPEAT)
84   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::WrapMode, MIRRORED_REPEAT)
85 DALI_ENUM_TO_STRING_TABLE_END(WRAP_MODE)
86
87 // load policies
88 DALI_ENUM_TO_STRING_TABLE_BEGIN(LOAD_POLICY)
89   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, IMMEDIATE)
90   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::LoadPolicy, ATTACHED)
91 DALI_ENUM_TO_STRING_TABLE_END(LOAD_POLICY)
92
93 // release policies
94 DALI_ENUM_TO_STRING_TABLE_BEGIN(RELEASE_POLICY)
95   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DETACHED)
96   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, DESTROYED)
97   DALI_ENUM_TO_STRING_WITH_SCOPE(Dali::Toolkit::ImageVisual::ReleasePolicy, NEVER)
98 DALI_ENUM_TO_STRING_TABLE_END(RELEASE_POLICY)
99
100 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
101
102 constexpr uint32_t TEXTURE_COUNT_FOR_GPU_ALPHA_MASK = 2u;
103
104 struct NameIndexMatch
105 {
106   const char* const name;
107   Property::Index   index;
108 };
109
110 const NameIndexMatch NAME_INDEX_MATCH_TABLE[] =
111   {
112     {IMAGE_FITTING_MODE, Toolkit::ImageVisual::Property::FITTING_MODE},
113     {IMAGE_SAMPLING_MODE, Toolkit::ImageVisual::Property::SAMPLING_MODE},
114     {IMAGE_DESIRED_WIDTH, Toolkit::ImageVisual::Property::DESIRED_WIDTH},
115     {IMAGE_DESIRED_HEIGHT, Toolkit::ImageVisual::Property::DESIRED_HEIGHT},
116     {PIXEL_AREA_UNIFORM_NAME, Toolkit::ImageVisual::Property::PIXEL_AREA},
117     {IMAGE_WRAP_MODE_U, Toolkit::ImageVisual::Property::WRAP_MODE_U},
118     {IMAGE_WRAP_MODE_V, Toolkit::ImageVisual::Property::WRAP_MODE_V},
119     {SYNCHRONOUS_LOADING, Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING},
120     {IMAGE_ATLASING, Toolkit::ImageVisual::Property::ATLASING},
121     {ALPHA_MASK_URL, Toolkit::ImageVisual::Property::ALPHA_MASK_URL},
122     {MASK_CONTENT_SCALE_NAME, Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE},
123     {CROP_TO_MASK_NAME, Toolkit::ImageVisual::Property::CROP_TO_MASK},
124     {MASKING_TYPE_NAME, Toolkit::DevelImageVisual::Property::MASKING_TYPE},
125     {ENABLE_BROKEN_IMAGE, Toolkit::DevelImageVisual::Property::ENABLE_BROKEN_IMAGE},
126     {LOAD_POLICY_NAME, Toolkit::ImageVisual::Property::LOAD_POLICY},
127     {RELEASE_POLICY_NAME, Toolkit::ImageVisual::Property::RELEASE_POLICY},
128     {ORIENTATION_CORRECTION_NAME, Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION},
129     {FAST_TRACK_UPLOADING_NAME, Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING},
130 };
131 const int NAME_INDEX_MATCH_TABLE_SIZE = sizeof(NAME_INDEX_MATCH_TABLE) / sizeof(NAME_INDEX_MATCH_TABLE[0]);
132
133 Geometry CreateGeometry(VisualFactoryCache& factoryCache, ImageDimensions gridSize)
134 {
135   Geometry geometry;
136
137   if(gridSize == ImageDimensions(1, 1))
138   {
139     geometry = factoryCache.GetGeometry(VisualFactoryCache::QUAD_GEOMETRY);
140   }
141   else
142   {
143     geometry = VisualFactoryCache::CreateGridGeometry(gridSize);
144   }
145
146   return geometry;
147 }
148
149 } // unnamed namespace
150
151 ImageVisualPtr ImageVisual::New(VisualFactoryCache&       factoryCache,
152                                 ImageVisualShaderFactory& shaderFactory,
153                                 const VisualUrl&          imageUrl,
154                                 const Property::Map&      properties,
155                                 ImageDimensions           size,
156                                 FittingMode::Type         fittingMode,
157                                 Dali::SamplingMode::Type  samplingMode)
158 {
159   ImageVisualPtr imageVisualPtr(new ImageVisual(factoryCache, shaderFactory, imageUrl, size, fittingMode, samplingMode));
160   imageVisualPtr->SetProperties(properties);
161   imageVisualPtr->Initialize();
162   return imageVisualPtr;
163 }
164
165 ImageVisualPtr ImageVisual::New(VisualFactoryCache&       factoryCache,
166                                 ImageVisualShaderFactory& shaderFactory,
167                                 const VisualUrl&          imageUrl,
168                                 ImageDimensions           size,
169                                 FittingMode::Type         fittingMode,
170                                 Dali::SamplingMode::Type  samplingMode)
171 {
172   ImageVisualPtr imageVisualPtr(new ImageVisual(factoryCache, shaderFactory, imageUrl, size, fittingMode, samplingMode));
173   imageVisualPtr->Initialize();
174   return imageVisualPtr;
175 }
176
177 ImageVisual::ImageVisual(VisualFactoryCache&       factoryCache,
178                          ImageVisualShaderFactory& shaderFactory,
179                          const VisualUrl&          imageUrl,
180                          ImageDimensions           size,
181                          FittingMode::Type         fittingMode,
182                          Dali::SamplingMode::Type  samplingMode)
183 : Visual::Base(factoryCache, Visual::FittingMode::FILL, Toolkit::Visual::IMAGE),
184   mPixelArea(FULL_TEXTURE_RECT),
185   mPixelAreaIndex(Property::INVALID_INDEX),
186   mPlacementActor(),
187   mImageUrl(imageUrl),
188   mMaskingData(),
189   mDesiredSize(size),
190   mTextureId(TextureManager::INVALID_TEXTURE_ID),
191   mTextures(),
192   mImageVisualShaderFactory(shaderFactory),
193   mFittingMode(fittingMode),
194   mSamplingMode(samplingMode),
195   mWrapModeU(WrapMode::DEFAULT),
196   mWrapModeV(WrapMode::DEFAULT),
197   mLoadPolicy(Toolkit::ImageVisual::LoadPolicy::ATTACHED),
198   mReleasePolicy(Toolkit::ImageVisual::ReleasePolicy::DETACHED),
199   mAtlasRect(0.0f, 0.0f, 0.0f, 0.0f),
200   mAtlasRectSize(0, 0),
201   mLoadState(TextureManager::LoadState::NOT_STARTED),
202   mAttemptAtlasing(false),
203   mOrientationCorrection(true),
204   mEnableBrokenImage(true)
205 {
206   EnablePreMultipliedAlpha(mFactoryCache.GetPreMultiplyOnLoad());
207 }
208
209 ImageVisual::~ImageVisual()
210 {
211   if(Stage::IsInstalled())
212   {
213     if(mImageUrl.IsValid())
214     {
215       // Decrease reference count of External Resources :
216       // EncodedImageBuffer or ExternalTextures.
217       // Ensure the stage is still valid before accessing texture manager.
218       if(mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
219       {
220         TextureManager& textureManager = mFactoryCache.GetTextureManager();
221         textureManager.RemoveExternalTexture(mImageUrl.GetUrl());
222       }
223       else if(mImageUrl.IsBufferResource())
224       {
225         TextureManager& textureManager = mFactoryCache.GetTextureManager();
226         textureManager.RemoveEncodedImageBuffer(mImageUrl.GetUrl());
227       }
228     }
229
230     // ImageVisual destroyed so remove texture unless ReleasePolicy is set to never release
231     if((mTextureId != TextureManager::INVALID_TEXTURE_ID) && (mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER))
232     {
233       RemoveTexture();
234     }
235
236     ResetFastTrackLoadingTask();
237   }
238 }
239
240 void ImageVisual::DoSetProperties(const Property::Map& propertyMap)
241 {
242   // Url is already received in constructor
243   for(Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter)
244   {
245     KeyValuePair keyValue = propertyMap.GetKeyValue(iter);
246     if(keyValue.first.type == Property::Key::INDEX)
247     {
248       DoSetProperty(keyValue.first.indexKey, keyValue.second);
249     }
250     else
251     {
252       for(int i = 0; i < NAME_INDEX_MATCH_TABLE_SIZE; ++i)
253       {
254         if(keyValue.first == NAME_INDEX_MATCH_TABLE[i].name)
255         {
256           DoSetProperty(NAME_INDEX_MATCH_TABLE[i].index, keyValue.second);
257           break;
258         }
259       }
260     }
261   }
262   // Load image immediately if LOAD_POLICY requires it
263   if(mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::IMMEDIATE)
264   {
265     auto attemptAtlasing = AttemptAtlasing();
266     LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::CACHED);
267   }
268 }
269
270 void ImageVisual::DoSetProperty(Property::Index index, const Property::Value& value)
271 {
272   switch(index)
273   {
274     case Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING:
275     {
276       bool sync = false;
277       if(value.Get(sync))
278       {
279         if(sync)
280         {
281           mImpl->mFlags |= Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
282         }
283         else
284         {
285           mImpl->mFlags &= ~Visual::Base::Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
286         }
287       }
288       else
289       {
290         DALI_LOG_ERROR("ImageVisual: synchronousLoading property has incorrect type\n");
291       }
292       break;
293     }
294
295     case Toolkit::ImageVisual::Property::DESIRED_WIDTH:
296     {
297       int32_t desiredWidth = 0;
298       if(value.Get(desiredWidth))
299       {
300         mDesiredSize.SetWidth(desiredWidth);
301       }
302       else
303       {
304         DALI_LOG_ERROR("ImageVisual: desiredWidth property has incorrect type\n");
305       }
306       break;
307     }
308
309     case Toolkit::ImageVisual::Property::DESIRED_HEIGHT:
310     {
311       int32_t desiredHeight = 0;
312       if(value.Get(desiredHeight))
313       {
314         mDesiredSize.SetHeight(desiredHeight);
315       }
316       else
317       {
318         DALI_LOG_ERROR("ImageVisual: desiredHeight property has incorrect type\n");
319       }
320       break;
321     }
322
323     case Toolkit::ImageVisual::Property::FITTING_MODE:
324     {
325       int fittingMode = 0;
326       Scripting::GetEnumerationProperty(value, FITTING_MODE_TABLE, FITTING_MODE_TABLE_COUNT, fittingMode);
327       mFittingMode = Dali::FittingMode::Type(fittingMode);
328       break;
329     }
330
331     case Toolkit::ImageVisual::Property::SAMPLING_MODE:
332     {
333       int samplingMode = 0;
334       Scripting::GetEnumerationProperty(value, SAMPLING_MODE_TABLE, SAMPLING_MODE_TABLE_COUNT, samplingMode);
335       mSamplingMode = Dali::SamplingMode::Type(samplingMode);
336       break;
337     }
338
339     case Toolkit::ImageVisual::Property::PIXEL_AREA:
340     {
341       value.Get(mPixelArea);
342
343       if(DALI_UNLIKELY(mImpl->mRenderer))
344       {
345         // Unusual case. SetProperty called after OnInitialize().
346         // Assume that DoAction call UPDATE_PROPERTY.
347         mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
348       }
349       break;
350     }
351
352     case Toolkit::ImageVisual::Property::WRAP_MODE_U:
353     {
354       int wrapMode = 0;
355       Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode);
356       mWrapModeU = Dali::WrapMode::Type(wrapMode);
357       break;
358     }
359
360     case Toolkit::ImageVisual::Property::WRAP_MODE_V:
361     {
362       int wrapMode = 0;
363       Scripting::GetEnumerationProperty(value, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, wrapMode);
364       mWrapModeV = Dali::WrapMode::Type(wrapMode);
365       break;
366     }
367
368     case Toolkit::ImageVisual::Property::ATLASING:
369     {
370       value.Get(mAttemptAtlasing);
371       break;
372     }
373
374     case Toolkit::ImageVisual::Property::ALPHA_MASK_URL:
375     {
376       std::string alphaUrl = "";
377       if(value.Get(alphaUrl))
378       {
379         AllocateMaskData();
380         mMaskingData->mAlphaMaskUrl = alphaUrl;
381       }
382       break;
383     }
384
385     case Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE:
386     {
387       float scale = 1.0f;
388       if(value.Get(scale))
389       {
390         AllocateMaskData();
391         mMaskingData->mContentScaleFactor = scale;
392       }
393       break;
394     }
395
396     case Toolkit::ImageVisual::Property::CROP_TO_MASK:
397     {
398       bool crop = false;
399       if(value.Get(crop))
400       {
401         AllocateMaskData();
402         mMaskingData->mCropToMask = crop;
403       }
404       break;
405     }
406
407     case Toolkit::DevelImageVisual::Property::MASKING_TYPE:
408     {
409       int maskingType = 0;
410       if(value.Get(maskingType))
411       {
412         AllocateMaskData();
413         if(mImageUrl.IsValid() && mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
414         {
415           // For external textures, only gpu masking is available.
416           // Therefore, MASKING_TYPE is set to MASKING_ON_RENDERING forcelly.
417           mMaskingData->mPreappliedMasking = false;
418         }
419         else
420         {
421           mMaskingData->mPreappliedMasking = (Toolkit::DevelImageVisual::MaskingType::Type(maskingType) == Toolkit::DevelImageVisual::MaskingType::MASKING_ON_LOADING);
422         }
423       }
424       break;
425     }
426
427     case Toolkit::DevelImageVisual::Property::ENABLE_BROKEN_IMAGE:
428     {
429       value.Get(mEnableBrokenImage);
430       break;
431     }
432
433     case Toolkit::ImageVisual::Property::RELEASE_POLICY:
434     {
435       int releasePolicy = 0;
436       Scripting::GetEnumerationProperty(value, RELEASE_POLICY_TABLE, RELEASE_POLICY_TABLE_COUNT, releasePolicy);
437       mReleasePolicy = Toolkit::ImageVisual::ReleasePolicy::Type(releasePolicy);
438       break;
439     }
440
441     case Toolkit::ImageVisual::Property::LOAD_POLICY:
442     {
443       int loadPolicy = 0;
444       Scripting::GetEnumerationProperty(value, LOAD_POLICY_TABLE, LOAD_POLICY_TABLE_COUNT, loadPolicy);
445       mLoadPolicy = Toolkit::ImageVisual::LoadPolicy::Type(loadPolicy);
446       break;
447     }
448     case Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION:
449     {
450       value.Get(mOrientationCorrection);
451       break;
452     }
453
454     case Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING:
455     {
456       value.Get(mUseFastTrackUploading);
457       break;
458     }
459   }
460 }
461
462 void ImageVisual::AllocateMaskData()
463 {
464   if(!mMaskingData)
465   {
466     mMaskingData.reset(new TextureManager::MaskingData());
467     if(mImageUrl.IsValid() && mImageUrl.GetProtocolType() == VisualUrl::TEXTURE)
468     {
469       mMaskingData->mPreappliedMasking = false;
470     }
471   }
472 }
473
474 void ImageVisual::GetNaturalSize(Vector2& naturalSize)
475 {
476   if(mDesiredSize.GetWidth() > 0 && mDesiredSize.GetHeight() > 0)
477   {
478     naturalSize.x = mDesiredSize.GetWidth();
479     naturalSize.y = mDesiredSize.GetHeight();
480     return;
481   }
482   else if(mImpl->mRenderer) // Check if we have a loaded image
483   {
484     if(mImpl->mFlags & Visual::Base::Impl::IS_ATLASING_APPLIED)
485     {
486       naturalSize.x = mAtlasRectSize.GetWidth();
487       naturalSize.y = mAtlasRectSize.GetHeight();
488       return;
489     }
490
491     auto textureSet = mImpl->mRenderer.GetTextures();
492     if(textureSet && textureSet.GetTextureCount())
493     {
494       if(mTextureSize != Vector2::ZERO)
495       {
496         naturalSize = mTextureSize;
497         return;
498       }
499     }
500   }
501
502   if(mMaskingData != NULL && mMaskingData->mAlphaMaskUrl.IsValid() &&
503      mMaskingData->mCropToMask)
504   {
505     ImageDimensions dimensions = Dali::GetClosestImageSize(mMaskingData->mAlphaMaskUrl.GetUrl());
506     if(dimensions != ImageDimensions(0, 0))
507     {
508       naturalSize.x = dimensions.GetWidth();
509       naturalSize.y = dimensions.GetHeight();
510     }
511     return;
512   }
513   else if(mImageUrl.IsValid())
514   {
515     if(mImageUrl.GetProtocolType() == VisualUrl::LOCAL)
516     {
517       ImageDimensions dimensions = Dali::GetClosestImageSize(mImageUrl.GetUrl());
518
519       if(dimensions != ImageDimensions(0, 0))
520       {
521         naturalSize.x = dimensions.GetWidth();
522         naturalSize.y = dimensions.GetHeight();
523       }
524       else
525       {
526         Actor   actor     = mPlacementActor.GetHandle();
527         Vector2 imageSize = Vector2::ZERO;
528         if(actor)
529         {
530           imageSize = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
531         }
532         else
533         {
534           imageSize = mPlacementActorSize;
535         }
536
537         mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
538         Texture brokenImage = mImpl->mRenderer.GetTextures().GetTexture(0);
539         naturalSize.x       = brokenImage.GetWidth();
540         naturalSize.y       = brokenImage.GetWidth();
541       }
542       return;
543     }
544   }
545   naturalSize = Vector2::ZERO;
546 }
547
548 void ImageVisual::OnInitialize()
549 {
550   Geometry geometry;
551
552   // Get the geometry
553   if(mImpl->mCustomShader)
554   {
555     geometry = CreateGeometry(mFactoryCache, mImpl->mCustomShader->mGridSize);
556   }
557   else // Get any geometry associated with the texture
558   {
559     TextureManager& textureManager = mFactoryCache.GetTextureManager();
560
561     uint32_t firstElementCount{0u};
562     uint32_t secondElementCount{0u};
563     geometry = textureManager.GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
564
565     if(!firstElementCount && !secondElementCount) // Otherwise use quad
566     {
567       geometry = CreateGeometry(mFactoryCache, ImageDimensions(1, 1));
568     }
569   }
570
571   // Increase reference count of External Resources :
572   // EncodedImageBuffer or ExternalTextures.
573   // Reference count will be decreased at destructor of the visual.
574   if(mImageUrl.IsValid() && (mImageUrl.IsBufferResource() || mImageUrl.GetProtocolType() == VisualUrl::TEXTURE))
575   {
576     TextureManager& textureManager = mFactoryCache.GetTextureManager();
577     textureManager.UseExternalResource(mImageUrl.GetUrl());
578   }
579
580   Shader shader = GenerateShader();
581
582   // Create the renderer
583   mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
584   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
585
586   //Register transform properties
587   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
588
589   EnablePreMultipliedAlpha(IsPreMultipliedAlphaEnabled());
590
591   if(mMaskingData)
592   {
593     mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast<float>(mMaskingData->mCropToMask));
594   }
595 }
596
597 void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& textures, bool orientationCorrection, TextureManager::ReloadPolicy forceReload)
598 {
599   TextureManager& textureManager = mFactoryCache.GetTextureManager();
600
601   ImageAtlasManagerPtr atlasManager        = nullptr;
602   AtlasUploadObserver* atlasUploadObserver = nullptr;
603   auto                 textureObserver     = this;
604
605   if(atlasing)
606   {
607     atlasManager        = mFactoryCache.GetAtlasManager();
608     atlasUploadObserver = this;
609   }
610
611   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
612                              ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
613                              : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
614
615   bool synchronousLoading = IsSynchronousLoadingRequired();
616   bool loadingStatus      = false;
617
618   // Remove previous loading task.
619   ResetFastTrackLoadingTask();
620
621   // Rare case. If someone call LoadTexture during fast track loading task running, (Ex : Action::RELOAD)
622   // we should remove previously added renderer now.
623   if(mRendererAdded)
624   {
625     Actor actor = mPlacementActor.GetHandle();
626     if(actor)
627     {
628       actor.RemoveRenderer(mImpl->mRenderer);
629       mRendererAdded = false;
630     }
631   }
632
633   /**
634    * @brief Check whether FastTrackUploading is avaliable or not.
635    * @return True if we can use fast track uploading feature. False otherwise.
636    */
637   auto IsFastTrackUploadingAvailable = [&]() {
638     if(mUseFastTrackUploading &&
639        mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::ATTACHED &&
640        mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED &&
641        forceReload == TextureManager::ReloadPolicy::CACHED &&
642        (mImageUrl.GetProtocolType() == VisualUrl::LOCAL || mImageUrl.GetProtocolType() == VisualUrl::REMOTE) &&
643        !synchronousLoading &&
644        !atlasing &&
645        !mImpl->mCustomShader &&
646        !(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid()))
647     {
648       return true;
649     }
650     else if(mUseFastTrackUploading)
651     {
652       DALI_LOG_DEBUG_INFO("FastTrack : Fail to load fast track. mUrl : [%s]%s%s%s%s%s%s%s%s\n",
653                           mImageUrl.GetUrl().c_str(),
654                           (mLoadPolicy != Toolkit::ImageVisual::LoadPolicy::ATTACHED) ? "/ mLoadPolicy != ATTACHED" : "",
655                           (mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::DETACHED) ? "/ mReleasePolicy != DETACHED" : "",
656                           (forceReload != TextureManager::ReloadPolicy::CACHED) ? "/ forceReload != CACHED" : "",
657                           (!(mImageUrl.GetProtocolType() == VisualUrl::LOCAL || mImageUrl.GetProtocolType() == VisualUrl::REMOTE)) ? "/ url is not image" : "",
658                           (synchronousLoading) ? "/ synchronousLoading" : "",
659                           (atlasing) ? "/ atlasing" : "",
660                           (mImpl->mCustomShader) ? "/ use customs shader" : "",
661                           (mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid()) ? "/ use masking url" : "");
662     }
663     return false;
664   };
665
666   if(IsFastTrackUploadingAvailable())
667   {
668     // Enable PremultipliedAlpha first.
669     EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
670
671     // Set new TextureSet with fast track loading task
672     mFastTrackLoadingTask = new FastTrackLoadingTask(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mOrientationCorrection, preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF, mFactoryCache.GetLoadYuvPlanes(), MakeCallback(this, &ImageVisual::FastLoadComplete));
673
674     TextureSet textureSet = TextureSet::New();
675     if(!mFastTrackLoadingTask->mLoadPlanesAvaliable)
676     {
677       DALI_ASSERT_ALWAYS(mFastTrackLoadingTask->mTextures.size() >= 1u);
678       textureSet.SetTexture(0u, mFastTrackLoadingTask->mTextures[0]);
679     }
680     else
681     {
682       DALI_ASSERT_ALWAYS(mFastTrackLoadingTask->mTextures.size() >= 3u);
683       textureSet.SetTexture(2u, mFastTrackLoadingTask->mTextures[2]);
684       textureSet.SetTexture(1u, mFastTrackLoadingTask->mTextures[1]);
685       textureSet.SetTexture(0u, mFastTrackLoadingTask->mTextures[0]);
686
687       // We cannot determine what kind of shader will be used.
688       // Just use unified shader, and then change shader after load completed.
689       mNeedUnifiedYuvAndRgb = true;
690       UpdateShader();
691     }
692     mImpl->mRenderer.SetTextures(textureSet);
693
694     Dali::AsyncTaskManager::Get().AddTask(mFastTrackLoadingTask);
695
696     mLoadState = TextureManager::LoadState::LOADING;
697   }
698   else
699   {
700     textures = textureManager.LoadTexture(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mTextureId, atlasRect, mAtlasRectSize, atlasing, loadingStatus, textureObserver, atlasUploadObserver, atlasManager, mOrientationCorrection, forceReload, preMultiplyOnLoad);
701   }
702
703   if(textures)
704   {
705     if(loadingStatus)
706     {
707       mLoadState = TextureManager::LoadState::LOADING;
708     }
709     else
710     {
711       mLoadState = TextureManager::LoadState::LOAD_FINISHED;
712     }
713
714     EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
715     if(!atlasing)
716     {
717       Sampler sampler = Sampler::New();
718       sampler.SetWrapMode(mWrapModeU, mWrapModeV);
719       textures.SetSampler(0u, sampler);
720     }
721   }
722   else if(synchronousLoading)
723   {
724     // Synchronous loading is failed
725     mLoadState = TextureManager::LoadState::LOAD_FAILED;
726   }
727
728   if(atlasing) // Flag needs to be set before creating renderer
729   {
730     mImpl->mFlags |= Visual::Base::Impl::IS_ATLASING_APPLIED;
731   }
732   else
733   {
734     mImpl->mFlags &= ~Visual::Base::Impl::IS_ATLASING_APPLIED;
735   }
736 }
737
738 bool ImageVisual::AttemptAtlasing()
739 {
740   return (!mImpl->mCustomShader && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource()) && mAttemptAtlasing);
741 }
742
743 void ImageVisual::InitializeRenderer()
744 {
745   auto attemptAtlasing = AttemptAtlasing();
746
747   // Load Texture if mTextures is empty.
748   // mTextures is already set, the mTexture can be used to create Renderer.
749   // There are two cases mTextures is empty.
750   // 1. mTextureId == TextureManager::INVALID_TEXTURE_ID
751   //  - Visual is on stage with LoadPolicy::ATTACHED
752   // 2. mTextureId != TextureManager::INVALID_TEXTURE_ID
753   //  - If ReleasePolicy is DESTROYED, InitializeRenderer called every on stage called.
754   //  - Then every resources those contained in Visual are Reset but mTextureId is remained when the Off stage time,
755   //  - So, mTextures needed to be get from texture manager to created resources like mImpl->mRenderer.
756   if(!mTextures)
757   {
758     if(mTextureId == TextureManager::INVALID_TEXTURE_ID)
759     {
760       LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::CACHED);
761     }
762     else
763     {
764       mTextures = mFactoryCache.GetTextureManager().GetTextureSet(mTextureId);
765       if(!(mImpl->mFlags & Visual::Base::Impl::IS_ATLASING_APPLIED) && mTextures)
766       {
767         Sampler sampler = Sampler::New();
768         sampler.SetWrapMode(mWrapModeU, mWrapModeV);
769         mTextures.SetSampler(0u, sampler);
770       }
771     }
772   }
773
774   if(mTextures)
775   {
776     mImpl->mRenderer.SetTextures(mTextures);
777     ComputeTextureSize();
778     CheckMaskTexture();
779
780     bool needToUpdateShader = DevelTexture::IsNative(mTextures.GetTexture(0));
781
782     if(mTextures.GetTextureCount() == 3)
783     {
784       if(mTextures.GetTexture(0).GetPixelFormat() == Pixel::L8 && mTextures.GetTexture(1).GetPixelFormat() == Pixel::CHROMINANCE_U && mTextures.GetTexture(2).GetPixelFormat() == Pixel::CHROMINANCE_V)
785       {
786         mNeedYuvToRgb      = true;
787         needToUpdateShader = true;
788       }
789     }
790
791     if(needToUpdateShader)
792     {
793       UpdateShader();
794     }
795     mTextures.Reset(); // Visual should not keep a handle to the texture after this point.
796   }
797
798   if(attemptAtlasing) // the texture is packed inside atlas
799   {
800     mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
801
802     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
803
804     if(!defaultWrapMode) // custom wrap mode
805     {
806       Vector2 wrapMode(mWrapModeU - WrapMode::CLAMP_TO_EDGE, mWrapModeV - WrapMode::CLAMP_TO_EDGE);
807       wrapMode.Clamp(Vector2::ZERO, Vector2(2.f, 2.f));
808       mImpl->mRenderer.RegisterProperty(WRAP_MODE_UNIFORM_NAME, wrapMode);
809     }
810   }
811 }
812
813 void ImageVisual::DoSetOnScene(Actor& actor)
814 {
815   if(mImageUrl.IsValid())
816   {
817     InitializeRenderer();
818   }
819
820   if(!mImpl->mRenderer)
821   {
822     return;
823   }
824
825   mPlacementActor = actor;
826
827   if(mPixelArea != FULL_TEXTURE_RECT)
828   {
829     mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
830   }
831
832   if(mLoadState == TextureManager::LoadState::LOAD_FINISHED)
833   {
834     actor.AddRenderer(mImpl->mRenderer);
835     mRendererAdded = true;
836     mPlacementActor.Reset();
837
838     // Image loaded and ready to display
839     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
840   }
841   else if(mLoadState == TextureManager::LoadState::LOAD_FAILED)
842   {
843     ShowBrokenImage();
844     ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
845   }
846   else
847   {
848     if(mFastTrackLoadingTask)
849     {
850       actor.AddRenderer(mImpl->mRenderer);
851       mRendererAdded = true;
852     }
853   }
854 }
855
856 void ImageVisual::DoSetOffScene(Actor& actor)
857 {
858   // Visual::Base::SetOffScene only calls DoSetOffScene if mRenderer exists (is on onstage)
859
860   // Image release is dependent on the ReleasePolicy, renderer is removed.
861   actor.RemoveRenderer(mImpl->mRenderer);
862   mRendererAdded = false;
863
864   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
865   {
866     ResetRenderer();
867   }
868
869   mPlacementActor.Reset();
870 }
871
872 void ImageVisual::DoCreatePropertyMap(Property::Map& map) const
873 {
874   map.Clear();
875   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
876
877   bool sync = IsSynchronousLoadingRequired();
878   map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync);
879   if(mImageUrl.IsValid())
880   {
881     map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
882     map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
883     map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
884   }
885
886   map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode);
887   map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
888
889   if(mImpl->mRenderer && mPixelAreaIndex != Property::INVALID_INDEX)
890   {
891     // Update values from Renderer
892     Vector4 pixelArea = mImpl->mRenderer.GetProperty<Vector4>(mPixelAreaIndex);
893     map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, pixelArea);
894   }
895   else
896   {
897     map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
898   }
899
900   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU);
901   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV);
902
903   map.Insert(Toolkit::ImageVisual::Property::ATLASING, mAttemptAtlasing);
904
905   if(mMaskingData != NULL)
906   {
907     map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl());
908     map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor);
909     map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask);
910     map.Insert(Toolkit::DevelImageVisual::Property::MASKING_TYPE, mMaskingData->mPreappliedMasking ? DevelImageVisual::MaskingType::MASKING_ON_LOADING : DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
911   }
912
913   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
914   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
915   map.Insert(Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, mOrientationCorrection);
916
917   map.Insert(Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING, mUseFastTrackUploading);
918 }
919
920 void ImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
921 {
922   map.Clear();
923   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
924   if(mImageUrl.IsValid())
925   {
926     map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
927     map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
928   }
929 }
930
931 void ImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
932 {
933   // Check if action is valid for this visual type and perform action if possible
934
935   switch(actionId)
936   {
937     case DevelImageVisual::Action::RELOAD:
938     {
939       auto attemptAtlasing = AttemptAtlasing();
940       LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::FORCED);
941       break;
942     }
943   }
944 }
945
946 void ImageVisual::OnSetTransform()
947 {
948   if(mImpl->mRenderer)
949   {
950     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
951   }
952 }
953
954 void ImageVisual::UpdateShader()
955 {
956   if(mImpl->mRenderer)
957   {
958     Shader shader = GenerateShader();
959     mImpl->mRenderer.SetShader(shader);
960   }
961 }
962
963 // From existing atlas manager
964 void ImageVisual::UploadCompleted()
965 {
966   // Texture has been uploaded. If weak handle is holding a placement actor,
967   // it is the time to add the renderer to actor.
968   Actor actor = mPlacementActor.GetHandle();
969   if(actor)
970   {
971     mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
972     actor.AddRenderer(mImpl->mRenderer);
973     mRendererAdded = true;
974     // reset the weak handle so that the renderer only get added to actor once
975     mPlacementActor.Reset();
976   }
977
978   // Image loaded
979   ResourceReady(Toolkit::Visual::ResourceStatus::READY);
980   mLoadState = TextureManager::LoadState::LOAD_FINISHED;
981 }
982
983 // From FastTrackLoadingTask
984 void ImageVisual::FastLoadComplete(FastTrackLoadingTaskPtr task)
985 {
986   Toolkit::Visual::ResourceStatus resourceStatus;
987
988   DALI_ASSERT_ALWAYS(mFastTrackLoadingTask == task && "Task was not canceled successfully!");
989   DALI_ASSERT_ALWAYS(mRendererAdded && "Some FastTrack logic missed!");
990
991   Actor actor = mPlacementActor.GetHandle();
992
993   if(mFastTrackLoadingTask && mFastTrackLoadingTask->mLoadSuccess)
994   {
995     resourceStatus = Toolkit::Visual::ResourceStatus::READY;
996     mLoadState     = TextureManager::LoadState::LOAD_FINISHED;
997
998     // Change premultiplied alpha flag after change renderer.
999     EnablePreMultipliedAlpha(mFastTrackLoadingTask->mPremultiplied);
1000
1001     if(mFastTrackLoadingTask->mLoadPlanesAvaliable)
1002     {
1003       if(mFastTrackLoadingTask->mPlanesLoaded)
1004       {
1005         // Let we use regular yuv cases.
1006         mNeedYuvToRgb = true;
1007       }
1008       else
1009       {
1010         // Let we use regular image cases.
1011         mNeedYuvToRgb = false;
1012
1013         auto textureSet = mImpl->mRenderer.GetTextures();
1014         DALI_ASSERT_ALWAYS(textureSet && textureSet.GetTextureCount() > 0u && "Previous texture set must exist!");
1015
1016         Dali::TextureSet newTextureSet = TextureSet::New();
1017         newTextureSet.SetTexture(0u, textureSet.GetTexture(0u));
1018         mImpl->mRenderer.SetTextures(newTextureSet);
1019       }
1020
1021       // We can specify what kind of shader we need to use now. Update shader.
1022       mNeedUnifiedYuvAndRgb = false;
1023       UpdateShader();
1024     }
1025   }
1026   else
1027   {
1028     resourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
1029     mLoadState     = TextureManager::LoadState::LOAD_FAILED;
1030
1031     // Change renderer as broken.
1032     ShowBrokenImage();
1033   }
1034
1035   mFastTrackLoadingTask.Reset();
1036
1037   // Signal to observers ( control ) that resources are ready. Must be all resources.
1038   ResourceReady(resourceStatus);
1039 }
1040
1041 // From Texture Manager
1042 void ImageVisual::LoadComplete(bool loadingSuccess, TextureInformation textureInformation)
1043 {
1044   Toolkit::Visual::ResourceStatus resourceStatus;
1045   if(mImpl->mRenderer)
1046   {
1047     if(textureInformation.useAtlasing)
1048     {
1049       mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
1050     }
1051
1052     EnablePreMultipliedAlpha(textureInformation.preMultiplied);
1053
1054     Actor actor = mPlacementActor.GetHandle();
1055     if(!loadingSuccess)
1056     {
1057       ShowBrokenImage();
1058       textureInformation.textureSet = mImpl->mRenderer.GetTextures();
1059     }
1060     else
1061     {
1062       if(!textureInformation.useAtlasing)
1063       {
1064         Sampler sampler = Sampler::New();
1065         sampler.SetWrapMode(mWrapModeU, mWrapModeV);
1066         textureInformation.textureSet.SetSampler(0u, sampler);
1067       }
1068
1069       mImpl->mRenderer.SetTextures(textureInformation.textureSet);
1070       ComputeTextureSize();
1071       CheckMaskTexture();
1072
1073       if(textureInformation.textureSet.GetTextureCount() == 3)
1074       {
1075         if(textureInformation.textureSet.GetTexture(0).GetPixelFormat() == Pixel::L8 && textureInformation.textureSet.GetTexture(1).GetPixelFormat() == Pixel::CHROMINANCE_U && textureInformation.textureSet.GetTexture(2).GetPixelFormat() == Pixel::CHROMINANCE_V)
1076         {
1077           mNeedYuvToRgb = true;
1078           UpdateShader();
1079         }
1080       }
1081
1082       if(actor)
1083       {
1084         actor.AddRenderer(mImpl->mRenderer);
1085         mRendererAdded = true;
1086         // reset the weak handle so that the renderer only get added to actor once
1087         mPlacementActor.Reset();
1088       }
1089     }
1090   }
1091
1092   // Storing TextureSet needed when renderer staged.
1093   if(!mImpl->mRenderer)
1094   {
1095     mTextures = textureInformation.textureSet;
1096   }
1097
1098   // Image loaded, set status regardless of staged status.
1099   if(loadingSuccess)
1100   {
1101     resourceStatus = Toolkit::Visual::ResourceStatus::READY;
1102     mLoadState     = TextureManager::LoadState::LOAD_FINISHED;
1103   }
1104   else
1105   {
1106     resourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
1107     mLoadState     = TextureManager::LoadState::LOAD_FAILED;
1108   }
1109
1110   // use geometry if needed
1111   if(loadingSuccess)
1112   {
1113     uint32_t firstElementCount{0u};
1114     uint32_t secondElementCount{0u};
1115     auto     geometry = mFactoryCache.GetTextureManager().GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
1116     if(mImpl->mRenderer && geometry)
1117     {
1118       mImpl->mRenderer.SetGeometry(geometry);
1119       Dali::DevelRenderer::DrawCommand drawCommand{};
1120       drawCommand.drawType = DevelRenderer::DrawType::INDEXED;
1121
1122       if(firstElementCount)
1123       {
1124         drawCommand.firstIndex   = 0;
1125         drawCommand.elementCount = firstElementCount;
1126         drawCommand.queue        = DevelRenderer::RENDER_QUEUE_OPAQUE;
1127         DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
1128       }
1129
1130       if(secondElementCount)
1131       {
1132         drawCommand.firstIndex   = firstElementCount;
1133         drawCommand.elementCount = secondElementCount;
1134         drawCommand.queue        = DevelRenderer::RENDER_QUEUE_TRANSPARENT;
1135         DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
1136       }
1137     }
1138   }
1139
1140   // Signal to observers ( control ) that resources are ready. Must be all resources.
1141   ResourceReady(resourceStatus);
1142 }
1143
1144 void ImageVisual::RemoveTexture()
1145 {
1146   if(mTextureId != TextureManager::INVALID_TEXTURE_ID)
1147   {
1148     mFactoryCache.GetTextureManager().RequestRemove(mTextureId, this);
1149     mTextureId = TextureManager::INVALID_TEXTURE_ID;
1150   }
1151   else
1152   {
1153     ResetFastTrackLoadingTask();
1154
1155     Vector4         atlasRect(0.f, 0.f, 1.f, 1.f);
1156     Property::Index index = mImpl->mRenderer.GetPropertyIndex(ATLAS_RECT_UNIFORM_NAME);
1157     if(index != Property::INVALID_INDEX)
1158     {
1159       Property::Value atlasRectValue = mImpl->mRenderer.GetProperty(index);
1160       atlasRectValue.Get(atlasRect);
1161     }
1162
1163     TextureSet textureSet = mImpl->mRenderer.GetTextures();
1164
1165     if(index != Property::INVALID_INDEX)
1166     {
1167       mFactoryCache.GetAtlasManager()->Remove(textureSet, atlasRect);
1168     }
1169   }
1170 }
1171
1172 void ImageVisual::ComputeTextureSize()
1173 {
1174   if(mImpl->mRenderer)
1175   {
1176     auto textureSet = mImpl->mRenderer.GetTextures();
1177     if(textureSet && textureSet.GetTextureCount())
1178     {
1179       auto texture = textureSet.GetTexture(0);
1180       if(texture)
1181       {
1182         mTextureSize.x = texture.GetWidth();
1183         mTextureSize.y = texture.GetHeight();
1184         if(textureSet.GetTextureCount() > 1u && mMaskingData && !mMaskingData->mPreappliedMasking && mMaskingData->mCropToMask)
1185         {
1186           Texture maskTexture = textureSet.GetTexture(1);
1187           if(maskTexture)
1188           {
1189             mTextureSize.x = std::min(static_cast<uint32_t>(mTextureSize.x * mMaskingData->mContentScaleFactor), maskTexture.GetWidth());
1190             mTextureSize.y = std::min(static_cast<uint32_t>(mTextureSize.y * mMaskingData->mContentScaleFactor), maskTexture.GetHeight());
1191           }
1192         }
1193       }
1194     }
1195   }
1196 }
1197
1198 Vector2 ImageVisual::ComputeMaskTextureRatio()
1199 {
1200   Vector2 maskTextureRatio;
1201   if(mImpl->mRenderer)
1202   {
1203     auto textureSet = mImpl->mRenderer.GetTextures();
1204     if(textureSet && textureSet.GetTextureCount())
1205     {
1206       auto texture = textureSet.GetTexture(0);
1207       if(texture)
1208       {
1209         if(textureSet.GetTextureCount() > 1u && mMaskingData && !mMaskingData->mPreappliedMasking && mMaskingData->mCropToMask)
1210         {
1211           Texture maskTexture = textureSet.GetTexture(1);
1212           if(maskTexture)
1213           {
1214             float textureWidth  = std::max(static_cast<float>(texture.GetWidth() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
1215             float textureHeight = std::max(static_cast<float>(texture.GetHeight() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
1216             maskTextureRatio    = Vector2(std::min(static_cast<float>(maskTexture.GetWidth()), textureWidth) / textureWidth,
1217                                        std::min(static_cast<float>(maskTexture.GetHeight()), textureHeight) / textureHeight);
1218           }
1219         }
1220       }
1221     }
1222   }
1223   return maskTextureRatio;
1224 }
1225
1226 Shader ImageVisual::GenerateShader() const
1227 {
1228   Shader shader;
1229
1230   const bool useStandardShader = !mImpl->mCustomShader;
1231   const bool useNativeImage    = (mTextures && DevelTexture::IsNative(mTextures.GetTexture(0)));
1232
1233   if(useStandardShader)
1234   {
1235     bool requiredAlphaMaskingOnRendering = (mMaskingData && !mMaskingData->mMaskImageLoadingFailed) ? !mMaskingData->mPreappliedMasking : false;
1236     // Create and cache the standard shader
1237     shader = mImageVisualShaderFactory.GetShader(
1238       mFactoryCache,
1239       ImageVisualShaderFeatureBuilder()
1240         .EnableTextureAtlas(mImpl->mFlags & Visual::Base::Impl::IS_ATLASING_APPLIED && !useNativeImage)
1241         .ApplyDefaultTextureWrapMode(mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE)
1242         .EnableRoundedCorner(IsRoundedCornerRequired())
1243         .EnableBorderline(IsBorderlineRequired())
1244         .SetTextureForFragmentShaderCheck(useNativeImage ? mTextures.GetTexture(0) : Dali::Texture())
1245         .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering)
1246         .EnableYuvToRgb(mNeedYuvToRgb, mNeedUnifiedYuvAndRgb));
1247   }
1248   else
1249   {
1250     bool             usesWholeTexture = true;
1251     std::string_view vertexShaderView;
1252     std::string_view fragmentShaderView;
1253
1254     if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty())
1255     {
1256       vertexShaderView = mImpl->mCustomShader->mVertexShader;
1257       usesWholeTexture = false; // Impossible to tell.
1258     }
1259     else
1260     {
1261       vertexShaderView = mImageVisualShaderFactory.GetVertexShaderSource();
1262     }
1263
1264     if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty())
1265     {
1266       fragmentShaderView = mImpl->mCustomShader->mFragmentShader;
1267     }
1268     else
1269     {
1270       fragmentShaderView = mImageVisualShaderFactory.GetFragmentShaderSource();
1271     }
1272
1273     // If the texture is native, we may need to change prefix and sampler in
1274     // the fragment shader
1275     if(useNativeImage)
1276     {
1277       bool        modifiedFragmentShader = false;
1278       Texture     nativeTexture          = mTextures.GetTexture(0);
1279       std::string fragmentShaderString   = std::string(fragmentShaderView);
1280
1281       modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShaderString);
1282       if(modifiedFragmentShader)
1283       {
1284         fragmentShaderView = fragmentShaderString;
1285       }
1286
1287       // Create shader here cause fragmentShaderString scope issue
1288       shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints);
1289     }
1290     else
1291     {
1292       shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints);
1293     }
1294
1295     if(usesWholeTexture)
1296     {
1297       shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
1298     }
1299   }
1300
1301   return shader;
1302 }
1303
1304 Dali::Property ImageVisual::OnGetPropertyObject(Dali::Property::Key key)
1305 {
1306   if((key.type == Property::Key::INDEX && key.indexKey == Toolkit::ImageVisual::Property::PIXEL_AREA) || (key.type == Property::Key::STRING && key.stringKey == PIXEL_AREA_UNIFORM_NAME))
1307   {
1308     if(DALI_LIKELY(mImpl->mRenderer))
1309     {
1310       if(mPixelAreaIndex == Property::INVALID_INDEX)
1311       {
1312         mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
1313       }
1314       return Dali::Property(mImpl->mRenderer, mPixelAreaIndex);
1315     }
1316   }
1317
1318   Handle handle;
1319   return Dali::Property(handle, Property::INVALID_INDEX);
1320 }
1321
1322 void ImageVisual::CheckMaskTexture()
1323 {
1324   if(mMaskingData && !mMaskingData->mPreappliedMasking)
1325   {
1326     bool       maskLoadFailed = true;
1327     TextureSet textures       = mImpl->mRenderer.GetTextures();
1328     if(textures && textures.GetTextureCount() >= TEXTURE_COUNT_FOR_GPU_ALPHA_MASK)
1329     {
1330       if(mMaskingData->mCropToMask)
1331       {
1332         mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, ComputeMaskTextureRatio());
1333       }
1334       maskLoadFailed = false;
1335     }
1336
1337     if(mMaskingData->mMaskImageLoadingFailed != maskLoadFailed)
1338     {
1339       mMaskingData->mMaskImageLoadingFailed = maskLoadFailed;
1340       UpdateShader();
1341     }
1342   }
1343 }
1344
1345 void ImageVisual::ResetRenderer()
1346 {
1347   RemoveTexture(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
1348   mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
1349
1350   TextureSet textureSet = TextureSet::New();
1351   mImpl->mRenderer.SetTextures(textureSet);
1352   ComputeTextureSize();
1353
1354   mLoadState = TextureManager::LoadState::NOT_STARTED;
1355 }
1356
1357 void ImageVisual::ShowBrokenImage()
1358 {
1359   if(mEnableBrokenImage)
1360   {
1361     Actor actor = mPlacementActor.GetHandle();
1362
1363     Vector2 imageSize = Vector2::ZERO;
1364     if(actor)
1365     {
1366       imageSize           = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
1367       mPlacementActorSize = imageSize;
1368
1369       if(mRendererAdded)
1370       {
1371         actor.RemoveRenderer(mImpl->mRenderer);
1372         mRendererAdded = false;
1373       }
1374     }
1375
1376     mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
1377     if(actor)
1378     {
1379       actor.AddRenderer(mImpl->mRenderer);
1380       mRendererAdded = true;
1381       mPlacementActor.Reset();
1382     }
1383   }
1384   else
1385   {
1386     if(mRendererAdded)
1387     {
1388       Actor actor = mPlacementActor.GetHandle();
1389       if(actor)
1390       {
1391         actor.RemoveRenderer(mImpl->mRenderer);
1392         mRendererAdded = false;
1393       }
1394     }
1395     ResetRenderer();
1396   }
1397 }
1398
1399 void ImageVisual::ResetFastTrackLoadingTask()
1400 {
1401   if(mFastTrackLoadingTask)
1402   {
1403     Dali::AsyncTaskManager::Get().RemoveTask(mFastTrackLoadingTask);
1404     mFastTrackLoadingTask.Reset();
1405   }
1406 }
1407
1408 } // namespace Internal
1409
1410 } // namespace Toolkit
1411
1412 } // namespace Dali