Merge "Test harness sync" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / image / image-visual.cpp
1 /*
2  * Copyright (c) 2024 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         mUseBrokenImageRenderer = true;
538         mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
539         Texture brokenImage = mImpl->mRenderer.GetTextures().GetTexture(0);
540         naturalSize.x       = brokenImage.GetWidth();
541         naturalSize.y       = brokenImage.GetWidth();
542       }
543       return;
544     }
545   }
546   naturalSize = Vector2::ZERO;
547 }
548
549 void ImageVisual::OnInitialize()
550 {
551   // Increase reference count of External Resources :
552   // EncodedImageBuffer or ExternalTextures.
553   // Reference count will be decreased at destructor of the visual.
554   if(mImageUrl.IsValid() && (mImageUrl.IsBufferResource() || mImageUrl.GetProtocolType() == VisualUrl::TEXTURE))
555   {
556     TextureManager& textureManager = mFactoryCache.GetTextureManager();
557     textureManager.UseExternalResource(mImageUrl.GetUrl());
558   }
559
560   // Generate geometry and shader. Note that we should check AddOn when generate geometry, due to LoadPolicy::IMMEDIATE case
561   Geometry geometry = GenerateGeometry(mTextureId, true);
562   Shader   shader   = GenerateShader();
563
564   // Create the renderer
565   mImpl->mRenderer = DecoratedVisualRenderer::New(geometry, shader);
566   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
567
568   //Register transform properties
569   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
570
571   EnablePreMultipliedAlpha(IsPreMultipliedAlphaEnabled());
572
573   if(mMaskingData)
574   {
575     mImpl->mRenderer.RegisterProperty(CROP_TO_MASK_NAME, static_cast<float>(mMaskingData->mCropToMask));
576   }
577 }
578
579 void ImageVisual::LoadTexture(bool& atlasing, Vector4& atlasRect, TextureSet& textures, bool orientationCorrection, TextureManager::ReloadPolicy forceReload)
580 {
581   TextureManager& textureManager = mFactoryCache.GetTextureManager();
582
583   ImageAtlasManagerPtr atlasManager        = nullptr;
584   AtlasUploadObserver* atlasUploadObserver = nullptr;
585   auto                 textureObserver     = this;
586
587   if(atlasing)
588   {
589     atlasManager        = mFactoryCache.GetAtlasManager();
590     atlasUploadObserver = this;
591   }
592
593   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
594                              ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
595                              : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
596
597   bool synchronousLoading = IsSynchronousLoadingRequired();
598   bool loadingStatus      = false;
599
600   // Remove previous loading task.
601   ResetFastTrackLoadingTask();
602
603   // Rare case. If someone call LoadTexture during fast track loading task running, (Ex : Action::RELOAD)
604   // we should remove previously added renderer now.
605   if(mRendererAdded)
606   {
607     Actor actor = mPlacementActor.GetHandle();
608     if(actor)
609     {
610       actor.RemoveRenderer(mImpl->mRenderer);
611       mRendererAdded = false;
612     }
613   }
614
615   /**
616    * @brief Check whether FastTrackUploading is avaliable or not.
617    * @return True if we can use fast track uploading feature. False otherwise.
618    */
619   auto IsFastTrackUploadingAvailable = [&]() {
620     if(mUseFastTrackUploading &&
621        mLoadPolicy == Toolkit::ImageVisual::LoadPolicy::ATTACHED &&
622        mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED &&
623        forceReload == TextureManager::ReloadPolicy::CACHED &&
624        (mImageUrl.GetProtocolType() == VisualUrl::LOCAL || mImageUrl.GetProtocolType() == VisualUrl::REMOTE) &&
625        !synchronousLoading &&
626        !atlasing &&
627        !mImpl->mCustomShader &&
628        !(mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid()))
629     {
630       return true;
631     }
632     else if(mUseFastTrackUploading)
633     {
634       DALI_LOG_DEBUG_INFO("FastTrack : Fail to load fast track. mUrl : [%s]%s%s%s%s%s%s%s%s\n",
635                           mImageUrl.GetEllipsedUrl().c_str(),
636                           (mLoadPolicy != Toolkit::ImageVisual::LoadPolicy::ATTACHED) ? "/ mLoadPolicy != ATTACHED" : "",
637                           (mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::DETACHED) ? "/ mReleasePolicy != DETACHED" : "",
638                           (forceReload != TextureManager::ReloadPolicy::CACHED) ? "/ forceReload != CACHED" : "",
639                           (!(mImageUrl.GetProtocolType() == VisualUrl::LOCAL || mImageUrl.GetProtocolType() == VisualUrl::REMOTE)) ? "/ url is not image" : "",
640                           (synchronousLoading) ? "/ synchronousLoading" : "",
641                           (atlasing) ? "/ atlasing" : "",
642                           (mImpl->mCustomShader) ? "/ use customs shader" : "",
643                           (mMaskingData && mMaskingData->mAlphaMaskUrl.IsValid()) ? "/ use masking url" : "");
644     }
645     return false;
646   };
647
648   if(IsFastTrackUploadingAvailable())
649   {
650     // Enable PremultipliedAlpha first.
651     EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
652
653     // Set new TextureSet with fast track loading task
654     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));
655
656     TextureSet textureSet = TextureSet::New();
657     if(!mFastTrackLoadingTask->mLoadPlanesAvaliable)
658     {
659       DALI_ASSERT_ALWAYS(mFastTrackLoadingTask->mTextures.size() >= 1u);
660       textureSet.SetTexture(0u, mFastTrackLoadingTask->mTextures[0]);
661     }
662     else
663     {
664       DALI_ASSERT_ALWAYS(mFastTrackLoadingTask->mTextures.size() >= 3u);
665       textureSet.SetTexture(2u, mFastTrackLoadingTask->mTextures[2]);
666       textureSet.SetTexture(1u, mFastTrackLoadingTask->mTextures[1]);
667       textureSet.SetTexture(0u, mFastTrackLoadingTask->mTextures[0]);
668
669       // We cannot determine what kind of shader will be used.
670       // Just use unified shader, and then change shader after load completed.
671       mNeedUnifiedYuvAndRgb = true;
672       UpdateShader();
673     }
674     mImpl->mRenderer.SetTextures(textureSet);
675
676     Dali::AsyncTaskManager::Get().AddTask(mFastTrackLoadingTask);
677
678     mLoadState = TextureManager::LoadState::LOADING;
679   }
680   else
681   {
682     textures = textureManager.LoadTexture(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode, mMaskingData, synchronousLoading, mTextureId, atlasRect, mAtlasRectSize, atlasing, loadingStatus, textureObserver, atlasUploadObserver, atlasManager, mOrientationCorrection, forceReload, preMultiplyOnLoad);
683   }
684
685   if(textures)
686   {
687     if(loadingStatus)
688     {
689       mLoadState = TextureManager::LoadState::LOADING;
690     }
691     else
692     {
693       mLoadState = TextureManager::LoadState::LOAD_FINISHED;
694     }
695
696     EnablePreMultipliedAlpha(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
697     if(!atlasing)
698     {
699       Sampler sampler = Sampler::New();
700       sampler.SetWrapMode(mWrapModeU, mWrapModeV);
701       textures.SetSampler(0u, sampler);
702     }
703   }
704   else if(synchronousLoading)
705   {
706     // Synchronous loading is failed
707     mLoadState = TextureManager::LoadState::LOAD_FAILED;
708   }
709
710   if(atlasing) // Flag needs to be set before creating renderer
711   {
712     mImpl->mFlags |= Visual::Base::Impl::IS_ATLASING_APPLIED;
713   }
714   else
715   {
716     mImpl->mFlags &= ~Visual::Base::Impl::IS_ATLASING_APPLIED;
717   }
718 }
719
720 bool ImageVisual::AttemptAtlasing()
721 {
722   return (!mImpl->mCustomShader && (mImageUrl.IsLocalResource() || mImageUrl.IsBufferResource()) && mAttemptAtlasing);
723 }
724
725 void ImageVisual::InitializeRenderer()
726 {
727   auto attemptAtlasing = AttemptAtlasing();
728
729   // Load Texture if mTextures is empty.
730   // mTextures is already set, the mTexture can be used to create Renderer.
731   // There are two cases mTextures is empty.
732   // 1. mTextureId == TextureManager::INVALID_TEXTURE_ID
733   //  - Visual is on stage with LoadPolicy::ATTACHED
734   // 2. mTextureId != TextureManager::INVALID_TEXTURE_ID
735   //  - If ReleasePolicy is DESTROYED, InitializeRenderer called every on stage called.
736   //  - Then every resources those contained in Visual are Reset but mTextureId is remained when the Off stage time,
737   //  - So, mTextures needed to be get from texture manager to created resources like mImpl->mRenderer.
738   if(!mTextures)
739   {
740     if(mTextureId == TextureManager::INVALID_TEXTURE_ID)
741     {
742       LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::CACHED);
743     }
744     else
745     {
746       mTextures = mFactoryCache.GetTextureManager().GetTextureSet(mTextureId);
747       if(!(mImpl->mFlags & Visual::Base::Impl::IS_ATLASING_APPLIED) && mTextures)
748       {
749         Sampler sampler = Sampler::New();
750         sampler.SetWrapMode(mWrapModeU, mWrapModeV);
751         mTextures.SetSampler(0u, sampler);
752       }
753     }
754   }
755
756   if(mTextures)
757   {
758     mImpl->mRenderer.SetTextures(mTextures);
759     ComputeTextureSize();
760     CheckMaskTexture();
761
762     bool needToUpdateShader = DevelTexture::IsNative(mTextures.GetTexture(0)) || mUseBrokenImageRenderer;
763
764     if(mTextures.GetTextureCount() == 3)
765     {
766       if(mTextures.GetTexture(0).GetPixelFormat() == Pixel::L8 && mTextures.GetTexture(1).GetPixelFormat() == Pixel::CHROMINANCE_U && mTextures.GetTexture(2).GetPixelFormat() == Pixel::CHROMINANCE_V)
767       {
768         mNeedYuvToRgb      = true;
769         needToUpdateShader = true;
770       }
771     }
772
773     if(needToUpdateShader)
774     {
775       UpdateShader();
776     }
777     mTextures.Reset(); // Visual should not keep a handle to the texture after this point.
778
779     if(DALI_UNLIKELY(mUseBrokenImageRenderer))
780     {
781       // We need to re-generate geometry only if it was broken image before, and result changed after Reload.
782       auto geometry = GenerateGeometry(mTextureId, true);
783
784       // Update geometry only if we need.
785       if(geometry)
786       {
787         mImpl->mRenderer.SetGeometry(geometry);
788       }
789     }
790
791     // We don't use broken image anymore.
792     mUseBrokenImageRenderer = false;
793   }
794
795   if(attemptAtlasing) // the texture is packed inside atlas
796   {
797     mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
798
799     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
800
801     if(!defaultWrapMode) // custom wrap mode
802     {
803       Vector2 wrapMode(mWrapModeU - WrapMode::CLAMP_TO_EDGE, mWrapModeV - WrapMode::CLAMP_TO_EDGE);
804       wrapMode.Clamp(Vector2::ZERO, Vector2(2.f, 2.f));
805       mImpl->mRenderer.RegisterProperty(WRAP_MODE_UNIFORM_NAME, wrapMode);
806     }
807   }
808 }
809
810 void ImageVisual::DoSetOnScene(Actor& actor)
811 {
812   if(mImageUrl.IsValid())
813   {
814     InitializeRenderer();
815   }
816
817   if(!mImpl->mRenderer)
818   {
819     return;
820   }
821
822   mPlacementActor = actor;
823
824   if(mPixelArea != FULL_TEXTURE_RECT)
825   {
826     mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
827   }
828
829   if(mLoadState == TextureManager::LoadState::LOAD_FINISHED)
830   {
831     actor.AddRenderer(mImpl->mRenderer);
832     mRendererAdded = true;
833     mPlacementActor.Reset();
834
835     // Image loaded and ready to display
836     ResourceReady(Toolkit::Visual::ResourceStatus::READY);
837   }
838   else if(mLoadState == TextureManager::LoadState::LOAD_FAILED)
839   {
840     ShowBrokenImage();
841     ResourceReady(Toolkit::Visual::ResourceStatus::FAILED);
842   }
843   else
844   {
845     if(mFastTrackLoadingTask)
846     {
847       actor.AddRenderer(mImpl->mRenderer);
848       mRendererAdded = true;
849     }
850   }
851 }
852
853 void ImageVisual::DoSetOffScene(Actor& actor)
854 {
855   // Visual::Base::SetOffScene only calls DoSetOffScene if mRenderer exists (is on onstage)
856
857   // Image release is dependent on the ReleasePolicy, renderer is removed.
858   actor.RemoveRenderer(mImpl->mRenderer);
859   mRendererAdded = false;
860
861   if(mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
862   {
863     ResetRenderer();
864   }
865
866   mPlacementActor.Reset();
867 }
868
869 void ImageVisual::DoCreatePropertyMap(Property::Map& map) const
870 {
871   map.Clear();
872   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
873
874   bool sync = IsSynchronousLoadingRequired();
875   map.Insert(Toolkit::ImageVisual::Property::SYNCHRONOUS_LOADING, sync);
876   if(mImageUrl.IsValid())
877   {
878     map.Insert(Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl());
879     map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
880     map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
881   }
882
883   map.Insert(Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode);
884   map.Insert(Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode);
885
886   if(mImpl->mRenderer && mPixelAreaIndex != Property::INVALID_INDEX)
887   {
888     // Update values from Renderer
889     Vector4 pixelArea = mImpl->mRenderer.GetProperty<Vector4>(mPixelAreaIndex);
890     map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, pixelArea);
891   }
892   else
893   {
894     map.Insert(Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea);
895   }
896
897   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU);
898   map.Insert(Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV);
899
900   map.Insert(Toolkit::ImageVisual::Property::ATLASING, mAttemptAtlasing);
901
902   if(mMaskingData != NULL)
903   {
904     map.Insert(Toolkit::ImageVisual::Property::ALPHA_MASK_URL, mMaskingData->mAlphaMaskUrl.GetUrl());
905     map.Insert(Toolkit::ImageVisual::Property::MASK_CONTENT_SCALE, mMaskingData->mContentScaleFactor);
906     map.Insert(Toolkit::ImageVisual::Property::CROP_TO_MASK, mMaskingData->mCropToMask);
907     map.Insert(Toolkit::DevelImageVisual::Property::MASKING_TYPE, mMaskingData->mPreappliedMasking ? DevelImageVisual::MaskingType::MASKING_ON_LOADING : DevelImageVisual::MaskingType::MASKING_ON_RENDERING);
908   }
909
910   map.Insert(Toolkit::ImageVisual::Property::LOAD_POLICY, mLoadPolicy);
911   map.Insert(Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy);
912   map.Insert(Toolkit::ImageVisual::Property::ORIENTATION_CORRECTION, mOrientationCorrection);
913
914   map.Insert(Toolkit::DevelImageVisual::Property::FAST_TRACK_UPLOADING, mUseFastTrackUploading);
915 }
916
917 void ImageVisual::DoCreateInstancePropertyMap(Property::Map& map) const
918 {
919   map.Clear();
920   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE);
921   if(mImageUrl.IsValid())
922   {
923     map.Insert(Toolkit::ImageVisual::Property::DESIRED_WIDTH, mDesiredSize.GetWidth());
924     map.Insert(Toolkit::ImageVisual::Property::DESIRED_HEIGHT, mDesiredSize.GetHeight());
925   }
926 }
927
928 void ImageVisual::OnDoAction(const Dali::Property::Index actionId, const Dali::Property::Value& attributes)
929 {
930   // Check if action is valid for this visual type and perform action if possible
931
932   switch(actionId)
933   {
934     case DevelImageVisual::Action::RELOAD:
935     {
936       auto attemptAtlasing = AttemptAtlasing();
937       LoadTexture(attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection, TextureManager::ReloadPolicy::FORCED);
938       break;
939     }
940   }
941 }
942
943 void ImageVisual::OnSetTransform()
944 {
945   if(mImpl->mRenderer)
946   {
947     mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
948   }
949 }
950
951 void ImageVisual::UpdateShader()
952 {
953   if(mImpl->mRenderer)
954   {
955     Shader shader = GenerateShader();
956     mImpl->mRenderer.SetShader(shader);
957   }
958 }
959
960 // From existing atlas manager
961 void ImageVisual::UploadCompleted()
962 {
963   // Texture has been uploaded. If weak handle is holding a placement actor,
964   // it is the time to add the renderer to actor.
965   Actor actor = mPlacementActor.GetHandle();
966   if(actor)
967   {
968     mImpl->mRenderer.RegisterProperty(ATLAS_RECT_UNIFORM_NAME, mAtlasRect);
969     actor.AddRenderer(mImpl->mRenderer);
970     mRendererAdded = true;
971     // reset the weak handle so that the renderer only get added to actor once
972     mPlacementActor.Reset();
973   }
974
975   // Image loaded
976   ResourceReady(Toolkit::Visual::ResourceStatus::READY);
977   mLoadState = TextureManager::LoadState::LOAD_FINISHED;
978 }
979
980 // From FastTrackLoadingTask
981 void ImageVisual::FastLoadComplete(FastTrackLoadingTaskPtr task)
982 {
983   Toolkit::Visual::ResourceStatus resourceStatus;
984
985   DALI_ASSERT_ALWAYS(mFastTrackLoadingTask == task && "Task was not canceled successfully!");
986   DALI_ASSERT_ALWAYS(mRendererAdded && "Some FastTrack logic missed!");
987
988   Actor actor = mPlacementActor.GetHandle();
989
990   if(mFastTrackLoadingTask && mFastTrackLoadingTask->mLoadSuccess)
991   {
992     resourceStatus = Toolkit::Visual::ResourceStatus::READY;
993     mLoadState     = TextureManager::LoadState::LOAD_FINISHED;
994
995     // Change premultiplied alpha flag after change renderer.
996     EnablePreMultipliedAlpha(mFastTrackLoadingTask->mPremultiplied);
997
998     if(mFastTrackLoadingTask->mLoadPlanesAvaliable)
999     {
1000       if(mFastTrackLoadingTask->mPlanesLoaded)
1001       {
1002         // Let we use regular yuv cases.
1003         mNeedYuvToRgb = true;
1004       }
1005       else
1006       {
1007         // Let we use regular image cases.
1008         mNeedYuvToRgb = false;
1009
1010         auto textureSet = mImpl->mRenderer.GetTextures();
1011         DALI_ASSERT_ALWAYS(textureSet && textureSet.GetTextureCount() > 0u && "Previous texture set must exist!");
1012
1013         Dali::TextureSet newTextureSet = TextureSet::New();
1014         newTextureSet.SetTexture(0u, textureSet.GetTexture(0u));
1015         mImpl->mRenderer.SetTextures(newTextureSet);
1016       }
1017
1018       // We can specify what kind of shader we need to use now. Update shader.
1019       mNeedUnifiedYuvAndRgb = false;
1020       UpdateShader();
1021     }
1022   }
1023   else
1024   {
1025     resourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
1026     mLoadState     = TextureManager::LoadState::LOAD_FAILED;
1027
1028     // Change renderer as broken.
1029     ShowBrokenImage();
1030   }
1031
1032   mFastTrackLoadingTask.Reset();
1033
1034   // Signal to observers ( control ) that resources are ready. Must be all resources.
1035   ResourceReady(resourceStatus);
1036 }
1037
1038 // From Texture Manager
1039 void ImageVisual::LoadComplete(bool loadingSuccess, TextureInformation textureInformation)
1040 {
1041   Toolkit::Visual::ResourceStatus resourceStatus;
1042
1043   if(mImpl->mRenderer)
1044   {
1045     EnablePreMultipliedAlpha(textureInformation.preMultiplied);
1046
1047     Actor actor = mPlacementActor.GetHandle();
1048     if(!loadingSuccess)
1049     {
1050       ShowBrokenImage();
1051       textureInformation.textureSet = mImpl->mRenderer.GetTextures();
1052     }
1053     else
1054     {
1055       Sampler sampler = Sampler::New();
1056       sampler.SetWrapMode(mWrapModeU, mWrapModeV);
1057       textureInformation.textureSet.SetSampler(0u, sampler);
1058
1059       mImpl->mRenderer.SetTextures(textureInformation.textureSet);
1060       ComputeTextureSize();
1061       CheckMaskTexture();
1062
1063       bool needToUpdateShader = mUseBrokenImageRenderer;
1064
1065       if(textureInformation.textureSet.GetTextureCount() == 3)
1066       {
1067         if(textureInformation.textureSet.GetTexture(0).GetPixelFormat() == Pixel::L8 && textureInformation.textureSet.GetTexture(1).GetPixelFormat() == Pixel::CHROMINANCE_U && textureInformation.textureSet.GetTexture(2).GetPixelFormat() == Pixel::CHROMINANCE_V)
1068         {
1069           mNeedYuvToRgb      = true;
1070           needToUpdateShader = true;
1071         }
1072       }
1073
1074       if(needToUpdateShader)
1075       {
1076         UpdateShader();
1077       }
1078
1079       if(actor)
1080       {
1081         actor.AddRenderer(mImpl->mRenderer);
1082         mRendererAdded = true;
1083         // reset the weak handle so that the renderer only get added to actor once
1084         mPlacementActor.Reset();
1085       }
1086
1087       auto geometry = GenerateGeometry(textureInformation.textureId, mUseBrokenImageRenderer);
1088
1089       if(DALI_UNLIKELY(geometry))
1090       {
1091         // Rare cases. If load successed image don't use quad geometry (i.e. Show some n-patch broken image, and call Reload(), and success)
1092         // or If given texture use AddOn,
1093         // then we need to make to use quad geometry and update shader agian.
1094         mImpl->mRenderer.SetGeometry(geometry);
1095       }
1096
1097       // We don't use broken image anymore.
1098       mUseBrokenImageRenderer = false;
1099     }
1100   }
1101
1102   // Storing TextureSet needed when renderer staged.
1103   if(!mImpl->mRenderer)
1104   {
1105     mTextures = textureInformation.textureSet;
1106   }
1107
1108   // Image loaded, set status regardless of staged status.
1109   if(loadingSuccess)
1110   {
1111     resourceStatus = Toolkit::Visual::ResourceStatus::READY;
1112     mLoadState     = TextureManager::LoadState::LOAD_FINISHED;
1113   }
1114   else
1115   {
1116     resourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
1117     mLoadState     = TextureManager::LoadState::LOAD_FAILED;
1118   }
1119
1120   // Signal to observers ( control ) that resources are ready. Must be all resources.
1121   ResourceReady(resourceStatus);
1122 }
1123
1124 void ImageVisual::RemoveTexture()
1125 {
1126   if(mTextureId != TextureManager::INVALID_TEXTURE_ID)
1127   {
1128     mFactoryCache.GetTextureManager().RequestRemove(mTextureId, this);
1129     mTextureId = TextureManager::INVALID_TEXTURE_ID;
1130   }
1131   else
1132   {
1133     ResetFastTrackLoadingTask();
1134
1135     Vector4         atlasRect(0.f, 0.f, 1.f, 1.f);
1136     Property::Index index = mImpl->mRenderer.GetPropertyIndex(ATLAS_RECT_UNIFORM_NAME);
1137     if(index != Property::INVALID_INDEX)
1138     {
1139       Property::Value atlasRectValue = mImpl->mRenderer.GetProperty(index);
1140       atlasRectValue.Get(atlasRect);
1141     }
1142
1143     TextureSet textureSet = mImpl->mRenderer.GetTextures();
1144
1145     if(index != Property::INVALID_INDEX)
1146     {
1147       mFactoryCache.GetAtlasManager()->Remove(textureSet, atlasRect);
1148     }
1149   }
1150 }
1151
1152 void ImageVisual::ComputeTextureSize()
1153 {
1154   if(mImpl->mRenderer)
1155   {
1156     auto textureSet = mImpl->mRenderer.GetTextures();
1157     if(textureSet && textureSet.GetTextureCount())
1158     {
1159       auto texture = textureSet.GetTexture(0);
1160       if(texture)
1161       {
1162         mTextureSize.x = texture.GetWidth();
1163         mTextureSize.y = texture.GetHeight();
1164         if(textureSet.GetTextureCount() > 1u && mMaskingData && !mMaskingData->mPreappliedMasking && mMaskingData->mCropToMask)
1165         {
1166           Texture maskTexture = textureSet.GetTexture(1);
1167           if(maskTexture)
1168           {
1169             mTextureSize.x = std::min(static_cast<uint32_t>(mTextureSize.x * mMaskingData->mContentScaleFactor), maskTexture.GetWidth());
1170             mTextureSize.y = std::min(static_cast<uint32_t>(mTextureSize.y * mMaskingData->mContentScaleFactor), maskTexture.GetHeight());
1171           }
1172         }
1173       }
1174     }
1175   }
1176 }
1177
1178 Vector2 ImageVisual::ComputeMaskTextureRatio()
1179 {
1180   Vector2 maskTextureRatio;
1181   if(mImpl->mRenderer)
1182   {
1183     auto textureSet = mImpl->mRenderer.GetTextures();
1184     if(textureSet && textureSet.GetTextureCount())
1185     {
1186       auto texture = textureSet.GetTexture(0);
1187       if(texture)
1188       {
1189         if(textureSet.GetTextureCount() > 1u && mMaskingData && !mMaskingData->mPreappliedMasking && mMaskingData->mCropToMask)
1190         {
1191           Texture maskTexture = textureSet.GetTexture(1);
1192           if(maskTexture)
1193           {
1194             float textureWidth  = std::max(static_cast<float>(texture.GetWidth() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
1195             float textureHeight = std::max(static_cast<float>(texture.GetHeight() * mMaskingData->mContentScaleFactor), Dali::Math::MACHINE_EPSILON_1);
1196             maskTextureRatio    = Vector2(std::min(static_cast<float>(maskTexture.GetWidth()), textureWidth) / textureWidth,
1197                                        std::min(static_cast<float>(maskTexture.GetHeight()), textureHeight) / textureHeight);
1198           }
1199         }
1200       }
1201     }
1202   }
1203   return maskTextureRatio;
1204 }
1205
1206 Shader ImageVisual::GenerateShader() const
1207 {
1208   Shader shader;
1209
1210   const bool useStandardShader = !mImpl->mCustomShader;
1211   const bool useNativeImage    = (mTextures && DevelTexture::IsNative(mTextures.GetTexture(0)));
1212
1213   if(useStandardShader)
1214   {
1215     bool requiredAlphaMaskingOnRendering = (mMaskingData && !mMaskingData->mMaskImageLoadingFailed) ? !mMaskingData->mPreappliedMasking : false;
1216     // Create and cache the standard shader
1217     shader = mImageVisualShaderFactory.GetShader(
1218       mFactoryCache,
1219       ImageVisualShaderFeatureBuilder()
1220         .EnableTextureAtlas(mImpl->mFlags & Visual::Base::Impl::IS_ATLASING_APPLIED && !useNativeImage)
1221         .ApplyDefaultTextureWrapMode(mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE)
1222         .EnableRoundedCorner(IsRoundedCornerRequired())
1223         .EnableBorderline(IsBorderlineRequired())
1224         .SetTextureForFragmentShaderCheck(useNativeImage ? mTextures.GetTexture(0) : Dali::Texture())
1225         .EnableAlphaMaskingOnRendering(requiredAlphaMaskingOnRendering)
1226         .EnableYuvToRgb(mNeedYuvToRgb, mNeedUnifiedYuvAndRgb));
1227   }
1228   else
1229   {
1230     bool             usesWholeTexture = true;
1231     std::string_view vertexShaderView;
1232     std::string_view fragmentShaderView;
1233
1234     if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty())
1235     {
1236       vertexShaderView = mImpl->mCustomShader->mVertexShader;
1237       usesWholeTexture = false; // Impossible to tell.
1238     }
1239     else
1240     {
1241       vertexShaderView = mImageVisualShaderFactory.GetVertexShaderSource();
1242     }
1243
1244     if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty())
1245     {
1246       fragmentShaderView = mImpl->mCustomShader->mFragmentShader;
1247     }
1248     else
1249     {
1250       fragmentShaderView = mImageVisualShaderFactory.GetFragmentShaderSource();
1251     }
1252
1253     // If the texture is native, we may need to change prefix and sampler in
1254     // the fragment shader
1255     if(useNativeImage)
1256     {
1257       bool        modifiedFragmentShader = false;
1258       Texture     nativeTexture          = mTextures.GetTexture(0);
1259       std::string fragmentShaderString   = std::string(fragmentShaderView);
1260
1261       modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShaderString);
1262       if(modifiedFragmentShader)
1263       {
1264         fragmentShaderView = fragmentShaderString;
1265       }
1266
1267       // Create shader here cause fragmentShaderString scope issue
1268       shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints);
1269     }
1270     else
1271     {
1272       shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints);
1273     }
1274
1275     if(usesWholeTexture)
1276     {
1277       shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
1278     }
1279   }
1280
1281   return shader;
1282 }
1283
1284 Dali::Property ImageVisual::OnGetPropertyObject(Dali::Property::Key key)
1285 {
1286   if((key.type == Property::Key::INDEX && key.indexKey == Toolkit::ImageVisual::Property::PIXEL_AREA) || (key.type == Property::Key::STRING && key.stringKey == PIXEL_AREA_UNIFORM_NAME))
1287   {
1288     if(DALI_LIKELY(mImpl->mRenderer))
1289     {
1290       if(mPixelAreaIndex == Property::INVALID_INDEX)
1291       {
1292         mPixelAreaIndex = mImpl->mRenderer.RegisterProperty(mPixelAreaIndex, PIXEL_AREA_UNIFORM_NAME, mPixelArea);
1293       }
1294       return Dali::Property(mImpl->mRenderer, mPixelAreaIndex);
1295     }
1296   }
1297
1298   Handle handle;
1299   return Dali::Property(handle, Property::INVALID_INDEX);
1300 }
1301
1302 void ImageVisual::CheckMaskTexture()
1303 {
1304   if(mMaskingData && !mMaskingData->mPreappliedMasking)
1305   {
1306     bool       maskLoadFailed = true;
1307     TextureSet textures       = mImpl->mRenderer.GetTextures();
1308     if(textures && textures.GetTextureCount() >= TEXTURE_COUNT_FOR_GPU_ALPHA_MASK)
1309     {
1310       if(mMaskingData->mCropToMask)
1311       {
1312         mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, ComputeMaskTextureRatio());
1313       }
1314       else
1315       {
1316         mImpl->mRenderer.RegisterProperty(MASK_TEXTURE_RATIO_NAME, Vector2::ONE);
1317       }
1318       maskLoadFailed = false;
1319     }
1320
1321     if(mMaskingData->mMaskImageLoadingFailed != maskLoadFailed)
1322     {
1323       mMaskingData->mMaskImageLoadingFailed = maskLoadFailed;
1324       UpdateShader();
1325     }
1326   }
1327 }
1328
1329 void ImageVisual::ResetRenderer()
1330 {
1331   RemoveTexture(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
1332   mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
1333
1334   TextureSet textureSet = TextureSet::New();
1335   mImpl->mRenderer.SetTextures(textureSet);
1336   ComputeTextureSize();
1337
1338   mLoadState = TextureManager::LoadState::NOT_STARTED;
1339 }
1340
1341 void ImageVisual::ShowBrokenImage()
1342 {
1343   if(mEnableBrokenImage)
1344   {
1345     Actor actor = mPlacementActor.GetHandle();
1346
1347     Vector2 imageSize = Vector2::ZERO;
1348     if(actor)
1349     {
1350       imageSize           = actor.GetProperty(Actor::Property::SIZE).Get<Vector2>();
1351       mPlacementActorSize = imageSize;
1352
1353       if(mRendererAdded)
1354       {
1355         actor.RemoveRenderer(mImpl->mRenderer);
1356         mRendererAdded = false;
1357       }
1358     }
1359
1360     mUseBrokenImageRenderer = true;
1361     mFactoryCache.UpdateBrokenImageRenderer(mImpl->mRenderer, imageSize);
1362     if(actor)
1363     {
1364       actor.AddRenderer(mImpl->mRenderer);
1365       mRendererAdded = true;
1366       mPlacementActor.Reset();
1367     }
1368   }
1369   else
1370   {
1371     if(mRendererAdded)
1372     {
1373       Actor actor = mPlacementActor.GetHandle();
1374       if(actor)
1375       {
1376         actor.RemoveRenderer(mImpl->mRenderer);
1377         mRendererAdded = false;
1378       }
1379     }
1380     ResetRenderer();
1381   }
1382 }
1383
1384 void ImageVisual::ResetFastTrackLoadingTask()
1385 {
1386   if(mFastTrackLoadingTask)
1387   {
1388     Dali::AsyncTaskManager::Get().RemoveTask(mFastTrackLoadingTask);
1389     mFastTrackLoadingTask.Reset();
1390   }
1391 }
1392
1393 Geometry ImageVisual::GenerateGeometry(TextureManager::TextureId textureId, bool createForce)
1394 {
1395   Geometry geometry;
1396   if(Stage::IsInstalled())
1397   {
1398     if(mImpl->mCustomShader)
1399     {
1400       if(createForce)
1401       {
1402         geometry = CreateGeometry(mFactoryCache, mImpl->mCustomShader->mGridSize);
1403       }
1404     }
1405     else
1406     {
1407       uint32_t firstElementCount{0u};
1408       uint32_t secondElementCount{0u};
1409
1410       geometry = mFactoryCache.GetTextureManager().GetRenderGeometry(textureId, firstElementCount, secondElementCount);
1411       if(geometry)
1412       {
1413         if(mImpl->mRenderer)
1414         {
1415           Dali::DevelRenderer::DrawCommand drawCommand{};
1416           drawCommand.drawType = DevelRenderer::DrawType::INDEXED;
1417
1418           if(firstElementCount)
1419           {
1420             drawCommand.firstIndex   = 0;
1421             drawCommand.elementCount = firstElementCount;
1422             drawCommand.queue        = DevelRenderer::RENDER_QUEUE_OPAQUE;
1423             DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
1424           }
1425
1426           if(secondElementCount)
1427           {
1428             drawCommand.firstIndex   = firstElementCount;
1429             drawCommand.elementCount = secondElementCount;
1430             drawCommand.queue        = DevelRenderer::RENDER_QUEUE_TRANSPARENT;
1431             DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
1432           }
1433         }
1434       }
1435       else if(createForce)
1436       {
1437         // Create default quad geometry now
1438         geometry = CreateGeometry(mFactoryCache, ImageDimensions(1, 1));
1439       }
1440     }
1441   }
1442
1443   return geometry;
1444 }
1445
1446 } // namespace Internal
1447
1448 } // namespace Toolkit
1449
1450 } // namespace Dali