Merge "added FONT_SIZE_SCALE("fontSizeScale") DevelProperty into TextLabel, TextField...
[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   Shader shader;
517
518   // Get the geometry
519   if( mImpl->mCustomShader )
520   {
521     geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
522   }
523   else // Get any geometry associated with the texture
524   {
525     TextureManager& textureManager = mFactoryCache.GetTextureManager();
526
527     uint32_t firstElementCount {0u};
528     uint32_t secondElementCount {0u};
529     geometry = textureManager.GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
530
531     if(!firstElementCount && !secondElementCount) // Otherwise use quad
532     {
533       geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
534     }
535   }
536
537   std::string vertexShader;
538   bool usesWholeTexture = true;
539   if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty())
540   {
541     vertexShader = mImpl->mCustomShader->mVertexShader;
542     usesWholeTexture = false; // Impossible to tell.
543   }
544   else
545   {
546     vertexShader = mImageVisualShaderFactory.GetVertexShaderSource().data();
547   }
548
549   std::string fragmentShader;
550   if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty())
551   {
552     fragmentShader = mImpl->mCustomShader->mFragmentShader;
553   }
554   else
555   {
556     fragmentShader = mImageVisualShaderFactory.GetFragmentShaderSource().data();
557   }
558
559   // If the texture is native, we may need to change prefix and sampler in
560   // the fragment shader
561   bool modifiedFragmentShader = false;
562   if(mTextures && DevelTexture::IsNative(mTextures.GetTexture(0)))
563   {
564     Texture nativeTexture = mTextures.GetTexture(0);
565     modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShader);
566   }
567
568   const bool useStandardShader = !mImpl->mCustomShader && !modifiedFragmentShader;
569   if(useStandardShader)
570   {
571     // Create and cache the standard shader
572     shader = mImageVisualShaderFactory.GetShader(
573       mFactoryCache,
574       mImpl->mFlags & Impl::IS_ATLASING_APPLIED,
575       mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE,
576       IsRoundedCornerRequired() );
577   }
578   else if(mImpl->mCustomShader)
579   {
580     shader = Shader::New(vertexShader, fragmentShader, mImpl->mCustomShader->mHints);
581   }
582   else
583   {
584     shader = Shader::New(vertexShader, fragmentShader);
585   }
586
587   if(usesWholeTexture)
588   {
589     shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
590   }
591
592   // Set pixel align off as default.
593   // ToDo: Pixel align causes issues such as rattling image animation.
594   // We should trun it off until issues are resolved
595   shader.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF );
596
597   // Create the renderer
598   mImpl->mRenderer = Renderer::New( geometry, shader );
599
600   if( textureSet )
601   {
602     mImpl->mRenderer.SetTextures( textureSet );
603   }
604   // else still waiting for texture load to finish.
605
606   //Register transform properties
607   mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
608
609   EnablePreMultipliedAlpha( IsPreMultipliedAlphaEnabled() );
610 }
611
612
613 void ImageVisual::LoadTexture( bool& atlasing, Vector4& atlasRect, TextureSet& textures, bool orientationCorrection,
614                                TextureManager::ReloadPolicy forceReload )
615 {
616   TextureManager& textureManager = mFactoryCache.GetTextureManager();
617
618   ImageAtlasManagerPtr atlasManager = nullptr;
619   AtlasUploadObserver* atlasUploadObserver = nullptr;
620   auto textureObserver = this;
621
622   if( atlasing )
623   {
624     atlasManager = mFactoryCache.GetAtlasManager();
625     atlasUploadObserver = this;
626   }
627
628   auto preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader
629     ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
630     : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
631
632   textures = textureManager.LoadTexture( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode,
633                                          mMaskingData, IsSynchronousLoadingRequired(), mTextureId,
634                                          atlasRect, mAtlasRectSize, atlasing, mLoading, mWrapModeU,
635                                          mWrapModeV, textureObserver, atlasUploadObserver, atlasManager,
636                                          mOrientationCorrection, forceReload, preMultiplyOnLoad);
637
638   if( textures )
639   {
640     EnablePreMultipliedAlpha( preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD );
641   }
642
643   if( atlasing ) // Flag needs to be set before creating renderer
644   {
645     mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
646   }
647   else
648   {
649     mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
650   }
651 }
652
653 bool ImageVisual::AttemptAtlasing()
654 {
655   return ( ! mImpl->mCustomShader && mImageUrl.GetProtocolType() == VisualUrl::LOCAL && mAttemptAtlasing );
656 }
657
658 void ImageVisual::InitializeRenderer()
659 {
660   auto attemptAtlasing = AttemptAtlasing();
661
662   // Load Texture if mTextures is empty.
663   // mTextures is already set, the mTexture can be used to create Renderer.
664   // There are two cases mTextures is empty.
665   // 1. mTextureId == TextureManager::INVALID_TEXTURE_ID
666   //  - Visual is on stage with LoadPolicy::ATTACHED
667   // 2. mTextureId != TextureManager::INVALID_TEXTURE_ID
668   //  - If ReleasePolicy is DESTROYED, InitializeRenderer called every on stage called.
669   //  - Then every resources those contained in Visual are Reset but mTextureId is remained when the Off stage time,
670   //  - So, mTextures needed to be get from texture manager to created resources like mImpl->mRenderer.
671   if( ! mTextures )
672   {
673     if( mTextureId == TextureManager::INVALID_TEXTURE_ID )
674     {
675       LoadTexture( attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection,
676                    TextureManager::ReloadPolicy::CACHED );
677     }
678     else
679     {
680       mTextures = mFactoryCache.GetTextureManager().GetTextureSet( mTextureId );
681     }
682   }
683
684   CreateRenderer( mTextures );
685   mTextures.Reset(); // Visual should not keep a handle to the texture after this point.
686
687   if( attemptAtlasing ) // the texture is packed inside atlas
688   {
689     mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mAtlasRect );
690
691     bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
692
693     if( !defaultWrapMode ) // custom wrap mode
694     {
695       Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
696       wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
697       mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
698     }
699   }
700 }
701
702 void ImageVisual::DoSetOnScene( Actor& actor )
703 {
704   if( mImageUrl.IsValid() )
705   {
706     InitializeRenderer();
707   }
708
709   if( !mImpl->mRenderer )
710   {
711     return;
712   }
713
714   mPlacementActor = actor;
715   // Search the Actor tree to find if Layer UI behaviour set.
716   Layer layer = actor.GetLayer();
717   if( layer && layer.GetProperty<Layer::Behavior>( Layer::Property::BEHAVIOR ) == Layer::LAYER_3D )
718   {
719      // Layer 3D set, do not align pixels
720      mImpl->mRenderer.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF );
721   }
722
723   if( mPixelArea != FULL_TEXTURE_RECT )
724   {
725     mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
726   }
727
728   if( mLoading == false )
729   {
730     actor.AddRenderer( mImpl->mRenderer );
731     mPlacementActor.Reset();
732
733     // Image loaded and ready to display
734     ResourceReady( Toolkit::Visual::ResourceStatus::READY );
735   }
736 }
737
738 void ImageVisual::DoSetOffScene( Actor& actor )
739 {
740   // Visual::Base::SetOffScene only calls DoSetOffScene if mRenderer exists (is on onstage)
741
742   // Image release is dependent on the ReleasePolicy, renderer is destroyed.
743   actor.RemoveRenderer( mImpl->mRenderer);
744   if( mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED )
745   {
746     RemoveTexture(); // If INVALID_TEXTURE_ID then removal will be attempted on atlas
747     mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
748   }
749
750   mLoading = false;
751   mImpl->mRenderer.Reset();
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( 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 actionName, const Dali::Property::Value& attributes )
802 {
803   // Check if action is valid for this visual type and perform action if possible
804
805   switch ( actionName )
806   {
807     case DevelImageVisual::Action::RELOAD:
808     {
809       auto attemptAtlasing = AttemptAtlasing();
810       LoadTexture( attemptAtlasing, mAtlasRect, mTextures, mOrientationCorrection,
811                    TextureManager::ReloadPolicy::FORCED );
812       break;
813     }
814   }
815 }
816
817 void ImageVisual::OnSetTransform()
818 {
819   if( mImpl->mRenderer )
820   {
821     mImpl->mTransform.RegisterUniforms( mImpl->mRenderer, Direction::LEFT_TO_RIGHT );
822   }
823 }
824
825 bool ImageVisual::IsResourceReady() const
826 {
827   return ( mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::READY ||
828            mImpl->mResourceStatus == Toolkit::Visual::ResourceStatus::FAILED );
829 }
830
831 // From existing atlas manager
832 void ImageVisual::UploadCompleted()
833 {
834   // Texture has been uploaded. If weak handle is holding a placement actor,
835   // it is the time to add the renderer to actor.
836   Actor actor = mPlacementActor.GetHandle();
837   if( actor )
838   {
839     mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mAtlasRect );
840     actor.AddRenderer( mImpl->mRenderer );
841     // reset the weak handle so that the renderer only get added to actor once
842     mPlacementActor.Reset();
843   }
844   // Image loaded
845   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
846   mLoading = false;
847 }
848
849 // From Texture Manager
850 void ImageVisual::UploadComplete( bool loadingSuccess, int32_t textureId, TextureSet textureSet, bool usingAtlas,
851                                   const Vector4& atlasRectangle, bool preMultiplied )
852 {
853   Toolkit::Visual::ResourceStatus resourceStatus;
854   if( mImpl->mRenderer )
855   {
856     if( usingAtlas )
857     {
858       mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mAtlasRect );
859     }
860
861     EnablePreMultipliedAlpha( preMultiplied );
862
863     Actor actor = mPlacementActor.GetHandle();
864     if( actor )
865     {
866       actor.AddRenderer( mImpl->mRenderer );
867       // reset the weak handle so that the renderer only get added to actor once
868       mPlacementActor.Reset();
869     }
870
871     if( !loadingSuccess )
872     {
873       Texture brokenImage = mFactoryCache.GetBrokenVisualImage();
874
875       textureSet = TextureSet::New();
876       textureSet.SetTexture(0u, brokenImage);
877       mImpl->mRenderer.SetTextures( textureSet );
878     }
879
880     Sampler sampler = Sampler::New();
881     sampler.SetWrapMode(  mWrapModeU, mWrapModeV  );
882     textureSet.SetSampler( 0u, sampler );
883     mImpl->mRenderer.SetTextures(textureSet);
884
885   }
886
887   // Storing TextureSet needed when renderer staged.
888   if( ! mImpl->mRenderer )
889   {
890     mTextures = textureSet;
891   }
892
893   // Image loaded, set status regardless of staged status.
894   if( loadingSuccess )
895   {
896     resourceStatus = Toolkit::Visual::ResourceStatus::READY;
897   }
898   else
899   {
900     resourceStatus = Toolkit::Visual::ResourceStatus::FAILED;
901   }
902
903   // use geometry if needed
904   if( loadingSuccess )
905   {
906     uint32_t firstElementCount{0u};
907     uint32_t secondElementCount{0u};
908     auto geometry = mFactoryCache.GetTextureManager().GetRenderGeometry(mTextureId, firstElementCount, secondElementCount);
909     if (mImpl->mRenderer && geometry)
910     {
911       mImpl->mRenderer.SetGeometry(geometry);
912       Dali::DevelRenderer::DrawCommand drawCommand{};
913       drawCommand.drawType = DevelRenderer::DrawType::INDEXED;
914
915       if (firstElementCount)
916       {
917         drawCommand.firstIndex = 0;
918         drawCommand.elementCount = firstElementCount;
919         drawCommand.queue = DevelRenderer::RENDER_QUEUE_OPAQUE;
920         DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
921       }
922
923       if (secondElementCount)
924       {
925         drawCommand.firstIndex = firstElementCount;
926         drawCommand.elementCount = secondElementCount;
927         drawCommand.queue = DevelRenderer::RENDER_QUEUE_TRANSPARENT;
928         DevelRenderer::AddDrawCommand(mImpl->mRenderer, drawCommand);
929       }
930     }
931   }
932
933   // Signal to observers ( control ) that resources are ready. Must be all resources.
934   ResourceReady( resourceStatus );
935   mLoading = false;
936 }
937
938 void ImageVisual::RemoveTexture()
939 {
940   if( mTextureId != TextureManager::INVALID_TEXTURE_ID )
941   {
942     mFactoryCache.GetTextureManager().Remove( mTextureId, this );
943     mTextureId = TextureManager::INVALID_TEXTURE_ID;
944   }
945   else
946   {
947     Vector4 atlasRect( 0.f, 0.f, 1.f, 1.f );
948     Property::Index index = mImpl->mRenderer.GetPropertyIndex( ATLAS_RECT_UNIFORM_NAME );
949     if( index != Property::INVALID_INDEX )
950     {
951       Property::Value atlasRectValue = mImpl->mRenderer.GetProperty( index );
952       atlasRectValue.Get( atlasRect );
953     }
954
955     TextureSet textureSet = mImpl->mRenderer.GetTextures();
956     mImpl->mRenderer.Reset();
957
958     if( index != Property::INVALID_INDEX )
959     {
960       mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
961     }
962   }
963 }
964
965 } // namespace Internal
966
967 } // namespace Toolkit
968
969 } // namespace Dali