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