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