(ImageSize) Add natural image size handling
[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           = RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT;
30 const Property::Index ImageActor::FADE_IN              = RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT + 1;
31 const Property::Index ImageActor::FADE_IN_DURATION     = RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT + 2;
32 const Property::Index ImageActor::STYLE                = RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT + 3;
33 const Property::Index ImageActor::BORDER               = RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT + 4;
34 const Property::Index ImageActor::IMAGE                = RENDERABLE_ACTOR_DEFAULT_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 == "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 "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 = RENDERABLE_ACTOR_DEFAULT_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 bool ImageActor::IsDefaultPropertyWritable( Property::Index index ) const
431 {
432   if(static_cast<unsigned int>(index) < RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT)
433   {
434     return RenderableActor::IsDefaultPropertyWritable(index);
435   }
436   else
437   {
438     return true;
439   }
440 }
441
442 bool ImageActor::IsDefaultPropertyAnimatable( Property::Index index ) const
443 {
444   if(static_cast<unsigned int>(index) < RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT)
445   {
446     return RenderableActor::IsDefaultPropertyAnimatable(index);
447   }
448   else
449   {
450     return false;
451   }
452 }
453
454 Property::Type ImageActor::GetDefaultPropertyType( Property::Index index ) const
455 {
456   if(static_cast<unsigned int>(index) < RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT)
457   {
458     return RenderableActor::GetDefaultPropertyType(index);
459   }
460   else
461   {
462     // ProxyObject guarantees that index is within range
463     return DEFAULT_IMAGE_ACTOR_PROPERTY_TYPES[index - RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT];
464   }
465 }
466
467 const std::string& ImageActor::GetDefaultPropertyName( Property::Index index ) const
468 {
469   if(static_cast<unsigned int>(index) < RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT)
470   {
471     return RenderableActor::GetDefaultPropertyName(index);
472   }
473   else
474   {
475     // ProxyObject guarantees that index is within range
476     return DEFAULT_IMAGE_ACTOR_PROPERTY_NAMES[index - RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT];
477   }
478 }
479
480 Property::Index ImageActor::GetDefaultPropertyIndex(const std::string& name) const
481 {
482   Property::Index index = Property::INVALID_INDEX;
483
484   DALI_ASSERT_DEBUG( NULL != mDefaultImageActorPropertyLookup );
485
486   // Look for name in current class' default properties
487   DefaultPropertyLookup::const_iterator result = mDefaultImageActorPropertyLookup->find( name );
488   if ( mDefaultImageActorPropertyLookup->end() != result )
489   {
490     index = result->second;
491   }
492   else
493   {
494     // If not found, check in base class
495     index = RenderableActor::GetDefaultPropertyIndex( name );
496   }
497
498   return index;
499 }
500
501 void ImageActor::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
502 {
503   // ProxyObject guarantees the property is writable and index is in range
504
505   if(index < RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT)
506   {
507     RenderableActor::SetDefaultProperty(index, propertyValue);
508   }
509   else
510   {
511     switch(index)
512     {
513       case Dali::ImageActor::PIXEL_AREA:
514       {
515         SetPixelArea(propertyValue.Get<Rect<int> >());
516         break;
517       }
518       case Dali::ImageActor::FADE_IN:
519       {
520         SetFadeIn(propertyValue.Get<bool>());
521         break;
522       }
523       case Dali::ImageActor::FADE_IN_DURATION:
524       {
525         SetFadeInDuration(propertyValue.Get<float>());
526         break;
527       }
528       case Dali::ImageActor::STYLE:
529       {
530         SetStyle(StyleEnum(propertyValue.Get<std::string>()));
531         break;
532       }
533       case Dali::ImageActor::BORDER:
534       {
535         SetNinePatchBorder(propertyValue.Get<Vector4>());
536         break;
537       }
538       case Dali::ImageActor::IMAGE:
539       {
540         Dali::Image img = Scripting::NewImage( propertyValue );
541         if(img)
542         {
543           SetImage( &GetImplementation(img) );
544         }
545         else
546         {
547           DALI_LOG_WARNING("Cannot create image from property value\n");
548         }
549         break;
550       }
551       default:
552       {
553         DALI_LOG_WARNING("Unknown property (%d)\n", index);
554         break;
555       }
556     } // switch(index)
557
558   } // else
559 }
560
561 Property::Value ImageActor::GetDefaultProperty( Property::Index index ) const
562 {
563   Property::Value ret;
564   if(index < RENDERABLE_ACTOR_DEFAULT_PROPERTY_MAX_COUNT)
565   {
566     ret = RenderableActor::GetDefaultProperty(index);
567   }
568   else
569   {
570     switch(index)
571     {
572       case Dali::ImageActor::PIXEL_AREA:
573       {
574         Rect<int> r = GetPixelArea();
575         ret = r;
576         break;
577       }
578       case Dali::ImageActor::FADE_IN:
579       {
580         ret = GetFadeIn();
581         break;
582       }
583       case Dali::ImageActor::FADE_IN_DURATION:
584       {
585         ret = GetFadeInDuration();
586         break;
587       }
588       case Dali::ImageActor::STYLE:
589       {
590         ret = StyleString(GetStyle());
591         break;
592       }
593       case Dali::ImageActor::BORDER:
594       {
595         ret = GetNinePatchBorder();
596         break;
597       }
598       case Dali::ImageActor::IMAGE:
599       {
600         ret = Property::Value(Property::MAP);
601         break;
602       }
603       default:
604       {
605         DALI_LOG_WARNING("Unknown property (%d)\n", index);
606         break;
607       }
608     } // switch(index)
609   }
610
611   return ret;
612 }
613
614
615 } // namespace Internal
616
617 } // namespace Dali