(9 patch) Make ImageActor SetImage handle 9 patch images
[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   ImagePtr imagePtr( image );
173
174   // Automatically convert nine-patch images to cropped bitmap
175   NinePatchImage* ninePatchImage = NinePatchImage::GetNinePatchImage( image );
176   if( ninePatchImage )
177   {
178     imagePtr = ninePatchImage->CreateCroppedBitmapImage();
179   }
180
181   mImageNext.Set( imagePtr.Get(), OnStage() );
182
183   if( ninePatchImage )
184   {
185     SetStyle( Dali::ImageActor::STYLE_NINE_PATCH );
186     SetNinePatchBorder( ninePatchImage->GetStretchBorders(), true );
187   }
188
189   if ( !imagePtr )
190   {
191     mImageAttachment->SetImage( NULL );
192   }
193   else
194   {
195     // don't disconnect currently shown image until we made sure that the new one is loaded
196     OnImageSet( *imagePtr.Get() );
197   }
198 }
199
200 Dali::Image ImageActor::GetImage()
201 {
202   return mImageAttachment->GetImage();
203 }
204
205 void ImageActor::SetToNaturalSize()
206 {
207   mUsingNaturalSize = true;
208   Dali::Image image = mImageAttachment->GetImage();
209
210   if( image )
211   {
212     SetNaturalSize( GetImplementation(image) );
213   }
214 }
215
216 void ImageActor::SetPixelArea( const PixelArea& pixelArea )
217 {
218   mImageAttachment->SetPixelArea( pixelArea );
219
220   if( mUsingNaturalSize )
221   {
222     mInternalSetSize = true;
223     SetSize(pixelArea.width, pixelArea.height);
224     mInternalSetSize = false;
225   }
226 }
227
228 const ImageActor::PixelArea& ImageActor::GetPixelArea() const
229 {
230   return mImageAttachment->GetPixelArea();
231 }
232
233 bool ImageActor::IsPixelAreaSet() const
234 {
235   return mImageAttachment->IsPixelAreaSet();
236 }
237
238 void ImageActor::ClearPixelArea()
239 {
240   mImageAttachment->ClearPixelArea();
241
242   if( mUsingNaturalSize )
243   {
244     Dali::Image image = mImageAttachment->GetImage();
245     if( image )
246     {
247       mInternalSetSize = true;
248       SetSize(GetImplementation(image).GetNaturalSize());
249       mInternalSetSize = false;
250     }
251   }
252 }
253
254 void ImageActor::SetStyle( Style style )
255 {
256   mImageAttachment->SetStyle( style );
257 }
258
259 ImageActor::Style ImageActor::GetStyle() const
260 {
261   return mImageAttachment->GetStyle();
262 }
263
264 void ImageActor::SetNinePatchBorder( const Vector4& border, bool inPixels )
265 {
266   mImageAttachment->SetNinePatchBorder( border, inPixels );
267 }
268
269 Vector4 ImageActor::GetNinePatchBorder() const
270 {
271   return mImageAttachment->GetNinePatchBorder();
272 }
273
274 void ImageActor::SetFadeIn( bool enableFade )
275 {
276   mFadeIn = enableFade;
277 }
278
279 bool ImageActor::GetFadeIn() const
280 {
281   return mFadeIn;
282 }
283
284 void ImageActor::SetFadeInDuration( float durationSeconds )
285 {
286   mFadeInDuration = durationSeconds;
287 }
288
289 float ImageActor::GetFadeInDuration() const
290 {
291   return mFadeInDuration;
292 }
293
294 ImageAttachment& ImageActor::GetImageAttachment()
295 {
296   return *mImageAttachment;
297 }
298
299 Vector2 ImageActor::GetCurrentImageSize() const
300 {
301   Vector3 size;
302   Vector3 currentSize;
303
304   // get the texture size / pixel area if only a subset of it is displayed
305   if( IsPixelAreaSet() )
306   {
307     PixelArea area(GetPixelArea());
308     return Vector2(area.width, area.height );
309   }
310   else
311   {
312     return Vector2( GetCurrentSize() );
313   }
314 }
315
316 RenderableAttachment& ImageActor::GetRenderableAttachment() const
317 {
318   DALI_ASSERT_DEBUG( mImageAttachment && "ImageAttachment missing from ImageActor" );
319   return *mImageAttachment;
320 }
321
322 ImageActor::ImageActor()
323 : RenderableActor(),
324   mUsingNaturalSize(true),
325   mInternalSetSize(false),
326   mFadeIn( false ),
327   mFadeInitial( true ),
328   mLoadedConnection( this ),
329   mFadeInDuration( 1.0f )
330 {
331 }
332
333 ImageActor::~ImageActor()
334 {
335   // ScopedConnection disconnects automatically
336 }
337
338 void ImageActor::OnImageSet( Image& image )
339 {
340   // observe image loaded
341   if( Dali::ResourceLoading == image.GetLoadingState() && ! image.GetFilename().empty() )
342   {
343     image.LoadingFinishedSignal().Connect( mLoadedConnection, &ImageActor::ImageLoaded );
344   }
345   else
346   {
347     // image already loaded, or generated
348     ImageLoaded( Dali::Image(&image) );
349   }
350 }
351
352 void ImageActor::SetNaturalSize( Image& image )
353 {
354   if( mUsingNaturalSize )
355   {
356     Vector2 size;
357     if( IsPixelAreaSet() )
358     {
359       PixelArea area(GetPixelArea());
360       size.width = area.width;
361       size.height = area.height;
362     }
363     else
364     {
365       size = image.GetNaturalSize();
366     }
367
368     mInternalSetSize = true;
369     SetSize( size );
370     mInternalSetSize = false;
371   }
372 }
373
374 void ImageActor::OnSizeSet( const Vector3& targetSize )
375 {
376   if( !mInternalSetSize )
377   {
378     mUsingNaturalSize = false;
379   }
380 }
381
382 void ImageActor::OnSizeAnimation(Animation& animation, const Vector3& targetSize)
383 {
384   mUsingNaturalSize = false;
385 }
386
387 void ImageActor::OnStageConnectionInternal()
388 {
389   FadeIn();
390
391   mImageNext.OnStageConnect();
392 }
393
394 void ImageActor::OnStageDisconnectionInternal()
395 {
396   mImageNext.OnStageDisconnect();
397 }
398
399 void ImageActor::ImageLoaded( Dali::Image image )
400 {
401   DALI_ASSERT_DEBUG (image && "Image handle empty!");
402
403   // TODO: Handle case where image loading failed
404
405   // Need to keep mUploadedConnection connected as image may change later through Reload
406   // Note: Reloaded images may have changed size.
407
408   // Set the attachment's image once we know the image has loaded to prevent
409   // blank frames during load / reload.
410   mImageAttachment->SetImage( &GetImplementation( image ) );
411
412   // If size has never been set by application
413   if( mUsingNaturalSize )
414   {
415     // If a pixel area has been set, use this size
416     if( IsPixelAreaSet() )
417     {
418       const PixelArea& area = GetPixelArea();
419       mInternalSetSize = true;
420       SetSize(area.width, area.height);
421       mInternalSetSize = false;
422     }
423     else
424     {
425       mInternalSetSize = true;
426       SetSize( GetImplementation(image).GetNaturalSize() );
427       mInternalSetSize = false;
428     }
429   }
430
431   // fade in if required
432   FadeIn();
433 }
434
435 void ImageActor::FadeIn()
436 {
437   // only fade in if enabled and newly displayed on screen
438   if( mFadeIn && mFadeInitial && ( mFadeInDuration > 0.0f ) )
439   {
440     // need to set opacity immediately to 0 otherwise child actors might get rendered
441     SetOpacity( 0.0f );
442
443     Dali::Image image = mImageAttachment->GetImage();
444
445     // Fade-in when on-stage & the image is loaded
446     if (OnStage() &&
447         image &&
448         image.GetLoadingState() == Dali::ResourceLoadingSucceeded)
449     {
450       // fire and forget animation; will clean up after it's finished
451       Dali::Animation animation = Dali::Animation::New( mFadeInDuration );
452       animation.OpacityTo( Dali::Actor( this ), 1.0f, AlphaFunctions::EaseOut );
453       animation.Play();
454       mFadeInitial = false;
455     }
456   }
457 }
458
459 unsigned int ImageActor::GetDefaultPropertyCount() const
460 {
461   return RenderableActor::GetDefaultPropertyCount() + DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT;
462 }
463
464 void ImageActor::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
465 {
466   RenderableActor::GetDefaultPropertyIndices( indices ); // RenderableActor class properties
467
468   indices.reserve( indices.size() + DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT );
469
470   int index = DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT;
471   for ( int i = 0; i < DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT; ++i, ++index )
472   {
473     indices.push_back( index );
474   }
475 }
476
477 bool ImageActor::IsDefaultPropertyWritable( Property::Index index ) const
478 {
479   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
480   {
481     return RenderableActor::IsDefaultPropertyWritable(index);
482   }
483   else
484   {
485     return true;
486   }
487 }
488
489 bool ImageActor::IsDefaultPropertyAnimatable( Property::Index index ) const
490 {
491   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
492   {
493     return RenderableActor::IsDefaultPropertyAnimatable(index);
494   }
495   else
496   {
497     return false;
498   }
499 }
500
501 bool ImageActor::IsDefaultPropertyAConstraintInput( Property::Index index ) const
502 {
503   if( index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT )
504   {
505     return RenderableActor::IsDefaultPropertyAConstraintInput(index);
506   }
507   return true; // Our properties can be used as input to constraints.
508 }
509
510 Property::Type ImageActor::GetDefaultPropertyType( Property::Index index ) const
511 {
512   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
513   {
514     return RenderableActor::GetDefaultPropertyType(index);
515   }
516   else
517   {
518     index -= DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT;
519
520     if ( ( index >= 0 ) && ( index < DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT ) )
521     {
522       return DEFAULT_IMAGE_ACTOR_PROPERTY_TYPES[index];
523     }
524     else
525     {
526       // index out-of-bounds
527       return Property::NONE;
528     }
529   }
530 }
531
532 const std::string& ImageActor::GetDefaultPropertyName( Property::Index index ) const
533 {
534   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
535   {
536     return RenderableActor::GetDefaultPropertyName(index);
537   }
538   else
539   {
540     index -= DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT;
541
542     if ( ( index >= 0 ) && ( index < DEFAULT_IMAGE_ACTOR_PROPERTY_COUNT ) )
543     {
544       return DEFAULT_IMAGE_ACTOR_PROPERTY_NAMES[index];
545     }
546     else
547     {
548       // index out-of-bounds
549       static const std::string INVALID_PROPERTY_NAME;
550       return INVALID_PROPERTY_NAME;
551     }
552   }
553 }
554
555 Property::Index ImageActor::GetDefaultPropertyIndex(const std::string& name) const
556 {
557   Property::Index index = Property::INVALID_INDEX;
558
559   DALI_ASSERT_DEBUG( NULL != mDefaultImageActorPropertyLookup );
560
561   // Look for name in current class' default properties
562   DefaultPropertyLookup::const_iterator result = mDefaultImageActorPropertyLookup->find( name );
563   if ( mDefaultImageActorPropertyLookup->end() != result )
564   {
565     index = result->second;
566   }
567   else
568   {
569     // If not found, check in base class
570     index = RenderableActor::GetDefaultPropertyIndex( name );
571   }
572
573   return index;
574 }
575
576 void ImageActor::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
577 {
578   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
579   {
580     RenderableActor::SetDefaultProperty(index, propertyValue);
581   }
582   else
583   {
584     switch(index)
585     {
586       case Dali::ImageActor::PIXEL_AREA:
587       {
588         SetPixelArea(propertyValue.Get<Rect<int> >());
589         break;
590       }
591       case Dali::ImageActor::FADE_IN:
592       {
593         SetFadeIn(propertyValue.Get<bool>());
594         break;
595       }
596       case Dali::ImageActor::FADE_IN_DURATION:
597       {
598         SetFadeInDuration(propertyValue.Get<float>());
599         break;
600       }
601       case Dali::ImageActor::STYLE:
602       {
603         SetStyle(StyleEnum(propertyValue.Get<std::string>()));
604         break;
605       }
606       case Dali::ImageActor::BORDER:
607       {
608         SetNinePatchBorder( propertyValue.Get<Vector4>(), true /*in pixels*/ );
609         break;
610       }
611       case Dali::ImageActor::IMAGE:
612       {
613         Dali::Image img = Scripting::NewImage( propertyValue );
614         if(img)
615         {
616           SetImage( &GetImplementation(img) );
617         }
618         else
619         {
620           DALI_LOG_WARNING("Cannot create image from property value\n");
621         }
622         break;
623       }
624       default:
625       {
626         DALI_LOG_WARNING("Unknown property (%d)\n", index);
627         break;
628       }
629     } // switch(index)
630
631   } // else
632 }
633
634 Property::Value ImageActor::GetDefaultProperty( Property::Index index ) const
635 {
636   Property::Value ret;
637   if(index < DEFAULT_RENDERABLE_ACTOR_PROPERTY_MAX_COUNT)
638   {
639     ret = RenderableActor::GetDefaultProperty(index);
640   }
641   else
642   {
643     switch(index)
644     {
645       case Dali::ImageActor::PIXEL_AREA:
646       {
647         Rect<int> r = GetPixelArea();
648         ret = r;
649         break;
650       }
651       case Dali::ImageActor::FADE_IN:
652       {
653         ret = GetFadeIn();
654         break;
655       }
656       case Dali::ImageActor::FADE_IN_DURATION:
657       {
658         ret = GetFadeInDuration();
659         break;
660       }
661       case Dali::ImageActor::STYLE:
662       {
663         ret = StyleString(GetStyle());
664         break;
665       }
666       case Dali::ImageActor::BORDER:
667       {
668         ret = GetNinePatchBorder();
669         break;
670       }
671       case Dali::ImageActor::IMAGE:
672       {
673         Property::Map map;
674         Scripting::CreatePropertyMap( mImageAttachment->GetImage(), map );
675         ret = Property::Value( map );
676         break;
677       }
678       default:
679       {
680         DALI_LOG_WARNING("Unknown property (%d)\n", index);
681         break;
682       }
683     } // switch(index)
684   }
685
686   return ret;
687 }
688
689
690 } // namespace Internal
691
692 } // namespace Dali