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