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