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