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