Merge changes I2df640e0,Ia1188305,I7fae506e,I7967a7cc,Ib0fdcdf4, ... into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / image-view / image-view-impl.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 "image-view-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/object/type-registry.h>
23 #include <dali/public-api/object/type-registry-helper.h>
24 #include <dali/devel-api/scripting/scripting.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
28 #include <dali-toolkit/devel-api/controls/control-devel.h>
29 #include <dali-toolkit/public-api/visuals/visual-properties.h>
30 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
31 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
32 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
33 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
34 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
35
36 namespace Dali
37 {
38
39 namespace Toolkit
40 {
41
42 namespace Internal
43 {
44
45 namespace
46 {
47
48 const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
49
50 BaseHandle Create()
51 {
52   return Toolkit::ImageView::New();
53 }
54
55 // Setup properties, signals and actions using the type-registry.
56 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ImageView, Toolkit::Control, Create );
57 DALI_PROPERTY_REGISTRATION( Toolkit, ImageView, "image", MAP, IMAGE )
58 DALI_PROPERTY_REGISTRATION( Toolkit, ImageView, "preMultipliedAlpha", BOOLEAN, PRE_MULTIPLIED_ALPHA )
59
60 DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT( Toolkit, ImageView, "pixelArea", Vector4(0.f, 0.f, 1.f, 1.f), PIXEL_AREA )
61 DALI_TYPE_REGISTRATION_END()
62
63 } // anonymous namespace
64
65 using namespace Dali;
66
67 ImageView::ImageView()
68 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
69   mImageSize(),
70   mImageVisualPaddingSetByTransform( false ),
71   mImageViewPixelAreaSetByFittingMode( false )
72 {
73 }
74
75 ImageView::~ImageView()
76 {
77 }
78
79 Toolkit::ImageView ImageView::New()
80 {
81   ImageView* impl = new ImageView();
82
83   Toolkit::ImageView handle = Toolkit::ImageView( *impl );
84
85   // Second-phase init of the implementation
86   // This can only be done after the CustomActor connection has been made...
87   impl->Initialize();
88
89   return handle;
90 }
91
92 /////////////////////////////////////////////////////////////
93
94 void ImageView::OnInitialize()
95 {
96   // ImageView can relayout in the OnImageReady, alternative to a signal would be to have a upcall from the Control to ImageView
97   Dali::Toolkit::Control handle( GetOwner() );
98   handle.ResourceReadySignal().Connect( this, &ImageView::OnResourceReady );
99
100   DevelControl::SetAccessibilityConstructor( Self(), []( Dali::Actor actor ) {
101     return std::unique_ptr< Dali::Accessibility::Accessible >(
102       new Control::Impl::AccessibleImpl( actor, Dali::Accessibility::Role::IMAGE ) );
103   } );
104
105   //Enable highightability
106   Self().SetProperty( Toolkit::DevelControl::Property::ACCESSIBILITY_HIGHLIGHTABLE, true );
107
108 }
109
110 void ImageView::SetImage( const Property::Map& map )
111 {
112   // Comparing a property map is too expensive so just creating a new visual
113   mPropertyMap = map;
114   mUrl.clear();
115
116   Toolkit::Visual::Base visual =  Toolkit::VisualFactory::Get().CreateVisual( mPropertyMap );
117   if( visual )
118   {
119     // Don't set mVisual until it is ready and shown. Getters will still use current visual.
120     if( !mVisual )
121     {
122       mVisual = visual;
123     }
124
125     if( !mShaderMap.Empty() )
126     {
127       Internal::Visual::Base& visualImpl = Toolkit::GetImplementation( visual );
128       visualImpl.SetCustomShader( mShaderMap );
129     }
130
131     DevelControl::RegisterVisual( *this, Toolkit::ImageView::Property::IMAGE, visual  );
132   }
133   else
134   {
135     // Unregister the exsiting visual
136     DevelControl::UnregisterVisual( *this, Toolkit::ImageView::Property::IMAGE );
137
138     // Trigger a size negotiation request that may be needed when unregistering a visual.
139     RelayoutRequest();
140   }
141
142   // Signal that a Relayout may be needed
143 }
144
145 void ImageView::SetImage( const std::string& url, ImageDimensions size )
146 {
147   // Don't bother comparing if we had a visual previously, just drop old visual and create new one
148   mUrl = url;
149   mImageSize = size;
150   mPropertyMap.Clear();
151
152   // Don't set mVisual until it is ready and shown. Getters will still use current visual.
153   Toolkit::Visual::Base visual =  Toolkit::VisualFactory::Get().CreateVisual( url, size );
154   if( visual )
155   {
156     if( !mVisual )
157     {
158       mVisual = visual;
159     }
160
161     if( !mShaderMap.Empty() )
162     {
163       Internal::Visual::Base& visualImpl = Toolkit::GetImplementation( visual );
164       visualImpl.SetCustomShader( mShaderMap );
165     }
166
167     DevelControl::RegisterVisual( *this, Toolkit::ImageView::Property::IMAGE, visual );
168   }
169   else
170   {
171     // Unregister the exsiting visual
172     DevelControl::UnregisterVisual( *this, Toolkit::ImageView::Property::IMAGE );
173
174     // Trigger a size negotiation request that may be needed when unregistering a visual.
175     RelayoutRequest();
176   }
177
178   // Signal that a Relayout may be needed
179 }
180
181 void ImageView::EnablePreMultipliedAlpha( bool preMultipled )
182 {
183   if( mVisual )
184   {
185     Toolkit::GetImplementation( mVisual ).EnablePreMultipliedAlpha( preMultipled );
186   }
187 }
188
189 bool ImageView::IsPreMultipliedAlphaEnabled() const
190 {
191   if( mVisual )
192   {
193     return Toolkit::GetImplementation( mVisual ).IsPreMultipliedAlphaEnabled();
194   }
195   return false;
196 }
197
198 void ImageView::SetDepthIndex( int depthIndex )
199 {
200   if( mVisual )
201   {
202     mVisual.SetDepthIndex( depthIndex );
203   }
204 }
205
206 Vector3 ImageView::GetNaturalSize()
207 {
208   if( mVisual )
209   {
210     Vector2 rendererNaturalSize;
211     mVisual.GetNaturalSize( rendererNaturalSize );
212
213     Extents padding;
214     padding = Self().GetProperty<Extents>( Toolkit::Control::Property::PADDING );
215
216     rendererNaturalSize.width += ( padding.start + padding.end );
217     rendererNaturalSize.height += ( padding.top + padding.bottom );
218     return Vector3( rendererNaturalSize );
219   }
220
221   // if no visual then use Control's natural size
222   return Control::GetNaturalSize();
223 }
224
225 float ImageView::GetHeightForWidth( float width )
226 {
227   Extents padding;
228   padding = Self().GetProperty<Extents>( Toolkit::Control::Property::PADDING );
229
230   if( mVisual )
231   {
232     return mVisual.GetHeightForWidth( width ) + padding.top + padding.bottom;
233   }
234   else
235   {
236     return Control::GetHeightForWidth( width ) + padding.top + padding.bottom;
237   }
238 }
239
240 float ImageView::GetWidthForHeight( float height )
241 {
242   Extents padding;
243   padding = Self().GetProperty<Extents>( Toolkit::Control::Property::PADDING );
244
245   if( mVisual )
246   {
247     return mVisual.GetWidthForHeight( height ) + padding.start + padding.end;
248   }
249   else
250   {
251     return Control::GetWidthForHeight( height ) + padding.start + padding.end;
252   }
253 }
254
255 void ImageView::OnRelayout( const Vector2& size, RelayoutContainer& container )
256 {
257   Control::OnRelayout( size, container );
258   if( mVisual )
259   {
260     Property::Map transformMap = Property::Map();
261
262     Extents padding = Self().GetProperty<Extents>( Toolkit::Control::Property::PADDING );
263
264     bool zeroPadding = ( padding == Extents() );
265
266     Vector2 naturalSize;
267     mVisual.GetNaturalSize( naturalSize );
268
269     Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>(
270           Self().GetProperty( Dali::Actor::Property::LAYOUT_DIRECTION ).Get<int>() );
271     if( Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection )
272     {
273       std::swap( padding.start, padding.end );
274     }
275
276     // remove padding from the size to know how much is left for the visual
277     Vector2 finalSize = size - Vector2( padding.start + padding.end, padding.top + padding.bottom );
278     Vector2 finalOffset = Vector2( padding.start, padding.top );
279
280     ApplyFittingMode( finalSize, naturalSize, finalOffset, zeroPadding , transformMap );
281
282     mVisual.SetTransformAndSize( transformMap, size );
283
284     // mVisual is not updated util the resource is ready in the case of visual replacement.
285     // in this case, the Property Map must be initialized so that the previous value is not reused.
286     // after mVisual is updated, the correct value will be reset.
287     Toolkit::Visual::Base visual = DevelControl::GetVisual( *this, Toolkit::ImageView::Property::IMAGE );
288     if( visual && visual != mVisual )
289     {
290       visual.SetTransformAndSize( Property::Map(), size );
291     }
292   }
293 }
294
295 void ImageView::OnResourceReady( Toolkit::Control control )
296 {
297   // Visual ready so update visual attached to this ImageView, following call to RelayoutRequest will use this visual.
298   mVisual = DevelControl::GetVisual( *this, Toolkit::ImageView::Property::IMAGE );
299   // Signal that a Relayout may be needed
300 }
301
302 void ImageView::SetTransformMapForFittingMode( Vector2 finalSize, Vector2 naturalSize, Vector2 finalOffset, Visual::FittingMode fittingMode, Property::Map& transformMap )
303 {
304   switch(fittingMode)
305   {
306     case Visual::FittingMode::FIT_KEEP_ASPECT_RATIO:
307     {
308       auto availableVisualSize = finalSize;
309
310       // scale to fit the padded area
311       finalSize = naturalSize * std::min( ( naturalSize.width  ? ( availableVisualSize.width  / naturalSize.width  ) : 0 ),
312                                             ( naturalSize.height ? ( availableVisualSize.height / naturalSize.height ) : 0 ) );
313
314       // calculate final offset within the padded area
315       finalOffset += ( availableVisualSize - finalSize ) * .5f;
316
317       // populate the transform map
318       transformMap.Add( Toolkit::Visual::Transform::Property::OFFSET, finalOffset )
319                   .Add( Toolkit::Visual::Transform::Property::SIZE, finalSize );
320       break;
321     }
322     case Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO:
323     {
324       mImageViewPixelAreaSetByFittingMode = true;
325       auto availableVisualSize = finalSize;
326       finalSize = naturalSize * std::max( ( naturalSize.width  ? ( availableVisualSize.width  / naturalSize.width  ) : 0 ),
327                                           ( naturalSize.height ? ( availableVisualSize.height / naturalSize.height ) : 0 ) );
328
329       auto originalOffset = finalOffset;
330       finalOffset += ( availableVisualSize - finalSize ) * .5f;
331
332       float x = abs( (availableVisualSize.width - finalSize.width ) / finalSize.width )* .5f;
333       float y = abs( (availableVisualSize.height - finalSize.height ) / finalSize.height ) * .5f;
334       float widthRatio = 1.f - abs( (availableVisualSize.width - finalSize.width ) / finalSize.width );
335       float heightRatio = 1.f - abs( (availableVisualSize.height - finalSize.height ) / finalSize.height );
336       Vector4 pixelArea = Vector4( x, y, widthRatio, heightRatio);
337       Self().SetProperty( Toolkit::ImageView::Property::PIXEL_AREA, pixelArea );
338
339       // populate the transform map
340       transformMap.Add( Toolkit::Visual::Transform::Property::OFFSET, originalOffset )
341                   .Add( Toolkit::Visual::Transform::Property::SIZE, availableVisualSize );
342       break;
343     }
344     case Visual::FittingMode::CENTER:
345     {
346       auto availableVisualSize = finalSize;
347       if( availableVisualSize.width > naturalSize.width && availableVisualSize.height > naturalSize.height )
348       {
349         finalSize = naturalSize;
350       }
351       else
352       {
353         finalSize = naturalSize * std::min( ( naturalSize.width  ? ( availableVisualSize.width  / naturalSize.width  ) : 0 ),
354                                           ( naturalSize.height ? ( availableVisualSize.height / naturalSize.height ) : 0 ) );
355       }
356
357       finalOffset += ( availableVisualSize - finalSize ) * .5f;
358
359       // populate the transform map
360       transformMap.Add( Toolkit::Visual::Transform::Property::OFFSET, finalOffset )
361                   .Add( Toolkit::Visual::Transform::Property::SIZE, finalSize );
362       break;
363     }
364     case Visual::FittingMode::FILL:
365     {
366       transformMap.Add( Toolkit::Visual::Transform::Property::OFFSET, finalOffset )
367                   .Add( Toolkit::Visual::Transform::Property::SIZE, finalSize );
368       break;
369     }
370     case Visual::FittingMode::FIT_WIDTH:
371     case Visual::FittingMode::FIT_HEIGHT:
372     {
373       // This FittingMode already converted
374       break;
375     }
376   }
377 }
378
379 void ImageView::ApplyFittingMode( Vector2 finalSize, Vector2 naturalSize, Vector2 finalOffset, bool zeroPadding , Property::Map& transformMap )
380 {
381     Visual::FittingMode fittingMode = Toolkit::GetImplementation(mVisual).GetFittingMode();
382
383     // Reset PIXEL_AREA after using OVER_FIT_KEEP_ASPECT_RATIO
384     if( mImageViewPixelAreaSetByFittingMode )
385     {
386       Self().SetProperty( Toolkit::ImageView::Property::PIXEL_AREA, FULL_TEXTURE_RECT );
387       mImageViewPixelAreaSetByFittingMode = false;
388     }
389
390     if( ( !zeroPadding ) || // If padding is not zero
391         ( fittingMode != Visual::FittingMode::FILL ) )
392     {
393       mImageVisualPaddingSetByTransform = true;
394
395       // If FittingMode use FIT_WIDTH or FIT_HEIGTH, it need to change proper fittingMode
396       if( fittingMode == Visual::FittingMode::FIT_WIDTH )
397       {
398         fittingMode = ( finalSize.height  / naturalSize.height ) < ( finalSize.width / naturalSize.width ) ? Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::FIT_KEEP_ASPECT_RATIO;
399       }
400       else if( fittingMode == Visual::FittingMode::FIT_HEIGHT )
401       {
402         fittingMode = ( finalSize.height  / naturalSize.height ) < ( finalSize.width / naturalSize.width ) ? Visual::FittingMode::FIT_KEEP_ASPECT_RATIO : Visual::FittingMode::OVER_FIT_KEEP_ASPECT_RATIO;
403       }
404
405       SetTransformMapForFittingMode( finalSize, naturalSize, finalOffset, fittingMode, transformMap );
406
407       // Set extra value for applying transformMap
408       transformMap.Add( Toolkit::Visual::Transform::Property::OFFSET_POLICY,
409                         Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
410                   .Add( Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN )
411                   .Add( Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN )
412                   .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY,
413                         Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) );
414     }
415     else if ( mImageVisualPaddingSetByTransform && zeroPadding )  // Reset offset to zero only if padding applied previously
416     {
417       mImageVisualPaddingSetByTransform = false;
418       // Reset the transform map
419       transformMap.Add( Toolkit::Visual::Transform::Property::OFFSET, Vector2::ZERO )
420                   .Add( Toolkit::Visual::Transform::Property::OFFSET_POLICY,
421                         Vector2( Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE ) )
422                   .Add( Toolkit::Visual::Transform::Property::SIZE, Vector2::ONE )
423                   .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY,
424                         Vector2( Toolkit::Visual::Transform::Policy::RELATIVE, Toolkit::Visual::Transform::Policy::RELATIVE ) );
425     }
426 }
427
428 ///////////////////////////////////////////////////////////
429 //
430 // Properties
431 //
432
433 void ImageView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
434 {
435   Toolkit::ImageView imageView = Toolkit::ImageView::DownCast( Dali::BaseHandle( object ) );
436
437   if ( imageView )
438   {
439     ImageView& impl = GetImpl( imageView );
440     switch ( index )
441     {
442       case Toolkit::ImageView::Property::IMAGE:
443       {
444         std::string imageUrl;
445         const Property::Map* map;
446         if( value.Get( imageUrl ) )
447         {
448           impl.SetImage( imageUrl, ImageDimensions() );
449         }
450         // if its not a string then get a Property::Map from the property if possible.
451         else
452         {
453           map = value.GetMap();
454           if( map )
455           {
456             Property::Value* shaderValue = map->Find( Toolkit::Visual::Property::SHADER, CUSTOM_SHADER );
457             // set image only if property map contains image information other than custom shader
458             if( map->Count() > 1u ||  !shaderValue )
459             {
460               impl.SetImage( *map );
461             }
462             // the property map contains only the custom shader
463             else if( ( map->Count() == 1u )&&( shaderValue ) )
464             {
465               Property::Map* shaderMap = shaderValue->GetMap();
466               if( shaderMap )
467               {
468                 impl.mShaderMap = *shaderMap;
469
470                 if( !impl.mUrl.empty() )
471                 {
472                   impl.SetImage( impl.mUrl, impl.mImageSize );
473                 }
474                 else if( !impl.mPropertyMap.Empty() )
475                 {
476                   impl.SetImage( impl.mPropertyMap );
477                 }
478               }
479             }
480           }
481         }
482         break;
483       }
484
485       case Toolkit::ImageView::Property::PRE_MULTIPLIED_ALPHA:
486       {
487         bool isPre;
488         if( value.Get( isPre ) )
489         {
490           impl.EnablePreMultipliedAlpha( isPre );
491         }
492         break;
493       }
494     }
495   }
496 }
497
498 Property::Value ImageView::GetProperty( BaseObject* object, Property::Index propertyIndex )
499 {
500   Property::Value value;
501
502   Toolkit::ImageView imageview = Toolkit::ImageView::DownCast( Dali::BaseHandle( object ) );
503
504   if ( imageview )
505   {
506     ImageView& impl = GetImpl( imageview );
507     switch ( propertyIndex )
508     {
509       case Toolkit::ImageView::Property::IMAGE:
510       {
511         if ( !impl.mUrl.empty() )
512         {
513           value = impl.mUrl;
514         }
515         else
516         {
517           Property::Map map;
518           Toolkit::Visual::Base visual = DevelControl::GetVisual( impl, Toolkit::ImageView::Property::IMAGE );
519           if( visual )
520           {
521             visual.CreatePropertyMap( map );
522           }
523           value = map;
524         }
525         break;
526       }
527
528       case Toolkit::ImageView::Property::PRE_MULTIPLIED_ALPHA:
529       {
530         value = impl.IsPreMultipliedAlphaEnabled();
531         break;
532       }
533     }
534   }
535
536   return value;
537 }
538
539 } // namespace Internal
540 } // namespace Toolkit
541 } // namespace Dali