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