(Properties) Create a new actor using a Property::Map & event-thread-only default...
[platform/core/uifw/dali-core.git] / dali / internal / event / actors / image-actor-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // CLASS HEADER
18 #include <dali/internal/event/actors/image-actor-impl.h>
19
20 // INTERNAL INCLUDES
21 #include <dali/public-api/object/type-registry.h>
22 #include <dali/internal/event/common/property-index-ranges.h>
23 #include <dali/internal/event/images/image-connector.h>
24 #include <dali/public-api/scripting/scripting.h>
25
26 namespace Dali
27 {
28
29 const Property::Index ImageActor::PIXEL_AREA           = Internal::DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT;
30 const Property::Index ImageActor::FADE_IN              = Internal::DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT + 1;
31 const Property::Index ImageActor::FADE_IN_DURATION     = Internal::DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT + 2;
32 const Property::Index ImageActor::STYLE                = Internal::DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT + 3;
33 const Property::Index ImageActor::BORDER               = Internal::DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT + 4;
34 const Property::Index ImageActor::IMAGE                = Internal::DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT + 5;
35
36 namespace Internal
37 {
38 bool ImageActor::mFirstInstance = true;
39 Actor::DefaultPropertyLookup* ImageActor::mDefaultImageActorPropertyLookup = NULL;
40
41 namespace
42 {
43
44 BaseHandle Create()
45 {
46   return Dali::ImageActor::New();
47 }
48
49 TypeRegistration mType( typeid(Dali::ImageActor), typeid(Dali::RenderableActor), Create );
50
51 const std::string DEFAULT_IMAGE_ACTOR_PROPERTY_NAMES[] =
52 {
53   "pixel-area",
54   "fade-in",
55   "fade-in-duration",
56   "style",
57   "border",
58   "image"
59 };
60 const int DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT = sizeof( DEFAULT_IMAGE_ACTOR_PROPERTY_NAMES ) / sizeof( std::string );
61
62 const Property::Type DEFAULT_IMAGE_ACTOR_PROPERTY_TYPES[DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT] =
63 {
64   Property::RECTANGLE,  // "pixel-area",
65   Property::BOOLEAN,    // "fade-in",
66   Property::FLOAT,      // "fade-in-duration",
67   Property::STRING,     // "style",
68   Property::VECTOR4,    // "border",
69   Property::MAP,        // "image",
70 };
71
72 ImageActor::Style StyleEnum(const std::string &s)
73 {
74   if(s == "STYLE_NINE_PATCH")
75   {
76     return Dali::ImageActor::STYLE_NINE_PATCH;
77   }
78   else // if(s == "QUAD")
79   {
80     return Dali::ImageActor::STYLE_QUAD;
81   }
82 }
83
84 std::string StyleString(const ImageActor::Style style)
85 {
86   if(style == Dali::ImageActor::STYLE_NINE_PATCH)
87   {
88     return "STYLE_NINE_PATCH";
89   }
90   else // if(s == "QUAD")
91   {
92     return "STYLE_QUAD";
93   }
94 }
95 }
96
97 ImageActorPtr ImageActor::New( Image* image )
98 {
99   ImageActorPtr actor( new ImageActor() );
100
101   // Second-phase construction
102   actor->Initialize();
103
104   // Create the attachment
105   actor->mImageAttachment = ImageAttachment::New( *actor->mNode, image );
106   actor->Attach( *actor->mImageAttachment );
107
108   // Adjust the actor's size
109   if( image )
110   {
111     actor->mImageNext.Set( image, false );
112     actor->OnImageSet( *image );
113     actor->SetNaturalSize( *image );
114   }
115
116   return actor;
117 }
118
119 ImageActorPtr ImageActor::New( Image* image, const PixelArea& pixelArea )
120 {
121   // re-use basic New
122   ImageActorPtr actor = New( image );
123   // then set the pixel area
124   actor->mImageAttachment->SetPixelArea( pixelArea );
125   return actor;
126 }
127
128 void ImageActor::OnInitialize()
129 {
130   if(ImageActor::mFirstInstance)
131   {
132     mDefaultImageActorPropertyLookup = new DefaultPropertyLookup();
133     const int start = DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT;
134     for ( int i = 0; i < DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT; ++i )
135     {
136       (*mDefaultImageActorPropertyLookup)[DEFAULT_IMAGE_ACTOR_PROPERTY_NAMES[i]] = i + start;
137     }
138     ImageActor::mFirstInstance = false;
139   }
140 }
141
142 void ImageActor::SetImage( Image* image )
143 {
144   Image* currentImage = static_cast<Image*>(mImageAttachment->GetImage().GetObjectPtr());
145   // early exit if it's the same image
146   if ( currentImage == image || mImageNext.Get() == image )
147   {
148     return;
149   }
150
151   mLoadedConnection.DisconnectAll();
152
153   mImageNext.Set( image, OnStage() );
154
155   if ( image == NULL )
156   {
157     mImageAttachment->SetImage( NULL );
158   }
159   else
160   {
161     // don't disconnect currently shown image until we made sure that the new one is loaded
162     OnImageSet( *image );
163   }
164 }
165
166 Dali::Image ImageActor::GetImage()
167 {
168   return mImageAttachment->GetImage();
169 }
170
171 void ImageActor::SetToNaturalSize()
172 {
173   mUsingNaturalSize = true;
174   Dali::Image image = mImageAttachment->GetImage();
175
176   if( image )
177   {
178     SetNaturalSize( GetImplementation(image) );
179   }
180 }
181
182 void ImageActor::SetPixelArea( const PixelArea& pixelArea )
183 {
184   mImageAttachment->SetPixelArea( pixelArea );
185
186   if( mUsingNaturalSize )
187   {
188     mInternalSetSize = true;
189     SetSize(pixelArea.width, pixelArea.height);
190     mInternalSetSize = false;
191   }
192 }
193
194 const ImageActor::PixelArea& ImageActor::GetPixelArea() const
195 {
196   return mImageAttachment->GetPixelArea();
197 }
198
199 bool ImageActor::IsPixelAreaSet() const
200 {
201   return mImageAttachment->IsPixelAreaSet();
202 }
203
204 void ImageActor::ClearPixelArea()
205 {
206   mImageAttachment->ClearPixelArea();
207
208   if( mUsingNaturalSize )
209   {
210     Dali::Image image = mImageAttachment->GetImage();
211     if( image )
212     {
213       mInternalSetSize = true;
214       SetSize(GetImplementation(image).GetNaturalSize());
215       mInternalSetSize = false;
216     }
217   }
218 }
219
220 void ImageActor::SetStyle( Style style )
221 {
222   mImageAttachment->SetStyle( style );
223 }
224
225 ImageActor::Style ImageActor::GetStyle() const
226 {
227   return mImageAttachment->GetStyle();
228 }
229
230 void ImageActor::SetNinePatchBorder( const Vector4& border, bool inPixels )
231 {
232   mImageAttachment->SetNinePatchBorder( border, inPixels );
233 }
234
235 Vector4 ImageActor::GetNinePatchBorder() const
236 {
237   return mImageAttachment->GetNinePatchBorder();
238 }
239
240 void ImageActor::SetFadeIn( bool enableFade )
241 {
242   mFadeIn = enableFade;
243 }
244
245 bool ImageActor::GetFadeIn() const
246 {
247   return mFadeIn;
248 }
249
250 void ImageActor::SetFadeInDuration( float durationSeconds )
251 {
252   mFadeInDuration = durationSeconds;
253 }
254
255 float ImageActor::GetFadeInDuration() const
256 {
257   return mFadeInDuration;
258 }
259
260 ImageAttachment& ImageActor::GetImageAttachment()
261 {
262   return *mImageAttachment;
263 }
264
265 Vector2 ImageActor::GetCurrentImageSize() const
266 {
267   Vector3 size;
268   Vector3 currentSize;
269
270   // get the texture size / pixel area if only a subset of it is displayed
271   if( IsPixelAreaSet() )
272   {
273     PixelArea area(GetPixelArea());
274     return Vector2(area.width, area.height );
275   }
276   else
277   {
278     return Vector2( GetCurrentSize() );
279   }
280 }
281
282 RenderableAttachment& ImageActor::GetRenderableAttachment() const
283 {
284   DALI_ASSERT_DEBUG( mImageAttachment && "ImageAttachment missing from ImageActor" );
285   return *mImageAttachment;
286 }
287
288 ImageActor::ImageActor()
289 : RenderableActor(),
290   mUsingNaturalSize(true),
291   mInternalSetSize(false),
292   mFadeIn( false ),
293   mFadeInitial( true ),
294   mLoadedConnection( this ),
295   mFadeInDuration( 1.0f )
296 {
297 }
298
299 ImageActor::~ImageActor()
300 {
301   // ScopedConnection disconnects automatically
302 }
303
304 void ImageActor::OnImageSet( Image& image )
305 {
306   // observe image loaded
307   if( Dali::ResourceLoading == image.GetLoadingState() && ! image.GetFilename().empty() )
308   {
309     image.LoadingFinishedSignal().Connect( mLoadedConnection, &ImageActor::ImageLoaded );
310   }
311   else
312   {
313     // image already loaded, or generated
314     ImageLoaded( Dali::Image(&image) );
315   }
316 }
317
318 void ImageActor::SetNaturalSize( Image& image )
319 {
320   if( mUsingNaturalSize )
321   {
322     Vector2 size;
323     if( IsPixelAreaSet() )
324     {
325       PixelArea area(GetPixelArea());
326       size.width = area.width;
327       size.height = area.height;
328     }
329     else
330     {
331       size = image.GetNaturalSize();
332     }
333
334     mInternalSetSize = true;
335     SetSize( size );
336     mInternalSetSize = false;
337   }
338 }
339
340 void ImageActor::OnSizeSet( const Vector3& targetSize )
341 {
342   if( !mInternalSetSize )
343   {
344     mUsingNaturalSize = false;
345   }
346 }
347
348 void ImageActor::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
349 {
350   mUsingNaturalSize = false;
351 }
352
353 void ImageActor::OnStageConnectionInternal()
354 {
355   FadeIn();
356
357   mImageNext.OnStageConnect();
358 }
359
360 void ImageActor::OnStageDisconnectionInternal()
361 {
362   mImageNext.OnStageDisconnect();
363 }
364
365 void ImageActor::ImageLoaded( Dali::Image image )
366 {
367   DALI_ASSERT_DEBUG (image && "Image handle empty!");
368
369   // TODO: Handle case where image loading failed
370
371   // Need to keep mUploadedConnection connected as image may change later through Reload
372   // Note: Reloaded images may have changed size.
373
374   // Set the attachment's image once we know the image has loaded to prevent
375   // blank frames during load / reload.
376   mImageAttachment->SetImage( &GetImplementation( image ) );
377
378   // If size has never been set by application
379   if( mUsingNaturalSize )
380   {
381     // If a pixel area has been set, use this size
382     if( IsPixelAreaSet() )
383     {
384       const PixelArea& area = GetPixelArea();
385       mInternalSetSize = true;
386       SetSize(area.width, area.height);
387       mInternalSetSize = false;
388     }
389     else
390     {
391       mInternalSetSize = true;
392       SetSize( GetImplementation(image).GetNaturalSize() );
393       mInternalSetSize = false;
394     }
395   }
396
397   // fade in if required
398   FadeIn();
399 }
400
401 void ImageActor::FadeIn()
402 {
403   // only fade in if enabled and newly displayed on screen
404   if( mFadeIn && mFadeInitial && ( mFadeInDuration > 0.0f ) )
405   {
406     // need to set opacity immediately to 0 otherwise child actors might get rendered
407     SetOpacity( 0.0f );
408
409     Dali::Image image = mImageAttachment->GetImage();
410
411     // Fade-in when on-stage & the image is loaded
412     if (OnStage() &&
413         image &&
414         image.GetLoadingState() == Dali::ResourceLoadingSucceeded)
415     {
416       // fire and forget animation; will clean up after it's finished
417       Dali::Animation animation = Dali::Animation::New( mFadeInDuration );
418       animation.OpacityTo( Dali::Actor( this ), 1.0f, AlphaFunctions::EaseOut );
419       animation.Play();
420       mFadeInitial = false;
421     }
422   }
423 }
424
425 unsigned int ImageActor::GetDefaultPropertyCount() const
426 {
427   return RenderableActor::GetDefaultPropertyCount() + DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT;
428 }
429
430 void ImageActor::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
431 {
432   RenderableActor::GetDefaultPropertyIndices( indices ); // RenderableActor class properties
433
434   indices.reserve( indices.size() + DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT );
435
436   int index = DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT;
437   for ( int i = 0; i < DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT; ++i, ++index )
438   {
439     indices.push_back( index );
440   }
441 }
442
443 bool ImageActor::IsDefaultPropertyWritable( Property::Index index ) const
444 {
445   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
446   {
447     return RenderableActor::IsDefaultPropertyWritable(index);
448   }
449   else
450   {
451     return true;
452   }
453 }
454
455 bool ImageActor::IsDefaultPropertyAnimatable( Property::Index index ) const
456 {
457   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
458   {
459     return RenderableActor::IsDefaultPropertyAnimatable(index);
460   }
461   else
462   {
463     return false;
464   }
465 }
466
467 bool ImageActor::IsDefaultPropertyAConstraintInput( Property::Index index ) const
468 {
469   if( index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT )
470   {
471     return RenderableActor::IsDefaultPropertyAConstraintInput(index);
472   }
473   return true; // Our properties can be used as input to constraints.
474 }
475
476 Property::Type ImageActor::GetDefaultPropertyType( Property::Index index ) const
477 {
478   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
479   {
480     return RenderableActor::GetDefaultPropertyType(index);
481   }
482   else
483   {
484     index -= DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT;
485
486     if ( ( index >= 0 ) && ( index < DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT ) )
487     {
488       return DEFAULT_IMAGE_ACTOR_PROPERTY_TYPES[index];
489     }
490     else
491     {
492       // index out-of-bounds
493       return Property::NONE;
494     }
495   }
496 }
497
498 const std::string& ImageActor::GetDefaultPropertyName( Property::Index index ) const
499 {
500   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
501   {
502     return RenderableActor::GetDefaultPropertyName(index);
503   }
504   else
505   {
506     index -= DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT;
507
508     if ( ( index >= 0 ) && ( index < DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT ) )
509     {
510       return DEFAULT_IMAGE_ACTOR_PROPERTY_NAMES[index];
511     }
512     else
513     {
514       // index out-of-bounds
515       static const std::string INVALID_PROPERTY_NAME;
516       return INVALID_PROPERTY_NAME;
517     }
518   }
519 }
520
521 Property::Index ImageActor::GetDefaultPropertyIndex(const std::string& name) const
522 {
523   Property::Index index = Property::INVALID_INDEX;
524
525   DALI_ASSERT_DEBUG( NULL != mDefaultImageActorPropertyLookup );
526
527   // Look for name in current class' default properties
528   DefaultPropertyLookup::const_iterator result = mDefaultImageActorPropertyLookup->find( name );
529   if ( mDefaultImageActorPropertyLookup->end() != result )
530   {
531     index = result->second;
532   }
533   else
534   {
535     // If not found, check in base class
536     index = RenderableActor::GetDefaultPropertyIndex( name );
537   }
538
539   return index;
540 }
541
542 void ImageActor::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
543 {
544   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
545   {
546     RenderableActor::SetDefaultProperty(index, propertyValue);
547   }
548   else
549   {
550     switch(index)
551     {
552       case Dali::ImageActor::PIXEL_AREA:
553       {
554         SetPixelArea(propertyValue.Get<Rect<int> >());
555         break;
556       }
557       case Dali::ImageActor::FADE_IN:
558       {
559         SetFadeIn(propertyValue.Get<bool>());
560         break;
561       }
562       case Dali::ImageActor::FADE_IN_DURATION:
563       {
564         SetFadeInDuration(propertyValue.Get<float>());
565         break;
566       }
567       case Dali::ImageActor::STYLE:
568       {
569         SetStyle(StyleEnum(propertyValue.Get<std::string>()));
570         break;
571       }
572       case Dali::ImageActor::BORDER:
573       {
574         SetNinePatchBorder( propertyValue.Get<Vector4>(), true /*in pixels*/ );
575         break;
576       }
577       case Dali::ImageActor::IMAGE:
578       {
579         Dali::Image img = Scripting::NewImage( propertyValue );
580         if(img)
581         {
582           SetImage( &GetImplementation(img) );
583         }
584         else
585         {
586           DALI_LOG_WARNING("Cannot create image from property value\n");
587         }
588         break;
589       }
590       default:
591       {
592         DALI_LOG_WARNING("Unknown property (%d)\n", index);
593         break;
594       }
595     } // switch(index)
596
597   } // else
598 }
599
600 Property::Value ImageActor::GetDefaultProperty( Property::Index index ) const
601 {
602   Property::Value ret;
603   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
604   {
605     ret = RenderableActor::GetDefaultProperty(index);
606   }
607   else
608   {
609     switch(index)
610     {
611       case Dali::ImageActor::PIXEL_AREA:
612       {
613         Rect<int> r = GetPixelArea();
614         ret = r;
615         break;
616       }
617       case Dali::ImageActor::FADE_IN:
618       {
619         ret = GetFadeIn();
620         break;
621       }
622       case Dali::ImageActor::FADE_IN_DURATION:
623       {
624         ret = GetFadeInDuration();
625         break;
626       }
627       case Dali::ImageActor::STYLE:
628       {
629         ret = StyleString(GetStyle());
630         break;
631       }
632       case Dali::ImageActor::BORDER:
633       {
634         ret = GetNinePatchBorder();
635         break;
636       }
637       case Dali::ImageActor::IMAGE:
638       {
639         Property::Map map;
640         Scripting::CreatePropertyMap( mImageAttachment->GetImage(), map );
641         ret = Property::Value( map );
642         break;
643       }
644       default:
645       {
646         DALI_LOG_WARNING("Unknown property (%d)\n", index);
647         break;
648       }
649     } // switch(index)
650   }
651
652   return ret;
653 }
654
655
656 } // namespace Internal
657
658 } // namespace Dali