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