2668f9ae78d74203b146c07c551591aff086a8a2
[platform/core/uifw/dali-core.git] / dali / internal / event / images / image-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/images/image-impl.h>
19
20 // INTERNAL INCLUDES
21 #include <dali/public-api/common/dali-common.h>
22
23 #include <dali/integration-api/platform-abstraction.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/internal/event/resources/resource-ticket.h>
26 #include <dali/internal/event/common/thread-local-storage.h>
27 #include <dali/internal/event/resources/resource-client.h>
28 #include <dali/internal/event/images/image-factory.h>
29 #include <dali/internal/event/images/nine-patch-image-impl.h>
30 #include <dali/internal/event/common/stage-impl.h>
31
32 using namespace Dali::Integration;
33
34 namespace Dali
35 {
36
37 namespace Internal
38 {
39
40 Image::Image( LoadPolicy loadPol, ReleasePolicy releasePol )
41 : mWidth(0),
42   mHeight(0),
43   mLoadPolicy(loadPol),
44   mReleasePolicy(releasePol),
45   mConnectionCount(0),
46   mImageFactory(ThreadLocalStorage::Get().GetImageFactory())
47 {
48 }
49
50 Image* Image::New()
51 {
52   return new Image;
53 }
54
55 Image* Image::New( const std::string& filename, const Dali::ImageAttributes& attributes, LoadPolicy loadPol, ReleasePolicy releasePol )
56 {
57   if( IsNinePatchFileName(filename) )
58   {
59     NinePatchImage* image = new NinePatchImage( filename, attributes, loadPol, releasePol );
60     return image;
61   }
62   else
63   {
64     Image* image = new Image( loadPol, releasePol );
65
66     if( ! filename.empty() )
67     {
68       Vector2 closestSize;
69
70       Internal::ThreadLocalStorage::Get().GetPlatformAbstraction().GetClosestImageSize( filename, attributes, closestSize );
71       image->mWidth = closestSize.width;
72       image->mHeight = closestSize.height;
73     }
74
75     image->mRequest = image->mImageFactory.RegisterRequest( filename, &attributes );
76
77     if( Dali::Image::Immediate == loadPol )
78     {
79       // Trigger loading of the image on a seperate resource thread as soon as it
80       // can be scheduled:
81       image->mTicket = image->mImageFactory.Load( image->mRequest.Get() );
82       image->mTicket->AddObserver( *image );
83     }
84     // else lazily load image data later, only when it is needed to draw something:
85
86     DALI_LOG_SET_OBJECT_STRING( image, filename );
87
88     return image;
89   }
90 }
91
92 Image* Image::New( NativeImage& nativeImg, LoadPolicy loadPol, ReleasePolicy releasePol )
93 {
94   Image* image = new Image;
95   ResourceClient &resourceClient = ThreadLocalStorage::Get().GetResourceClient();
96
97
98   image->mWidth  = nativeImg.GetWidth();
99   image->mHeight = nativeImg.GetHeight();
100
101   const ResourceTicketPtr& ticket = resourceClient.AddNativeImage( nativeImg );
102   DALI_ASSERT_DEBUG( dynamic_cast<ImageTicket*>( ticket.Get() ) && "Resource ticket not ImageTicket subclass for image resource.\n" );
103   image->mTicket = static_cast<ImageTicket*>(ticket.Get());
104   image->mTicket->AddObserver( *image );
105
106   return image;
107 }
108
109 Image::~Image()
110 {
111   if( mTicket )
112   {
113     mTicket->RemoveObserver( *this );
114     if( Stage::IsInstalled() )
115     {
116       mImageFactory.ReleaseTicket( mTicket.Get() );
117     }
118   }
119 }
120
121 bool Image::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
122 {
123   bool connected( true );
124   DALI_ASSERT_DEBUG( dynamic_cast<Image*>( object ) && "Resource ticket not ImageTicket subclass for image resource.\n" );
125   Image* image = static_cast<Image*>(object);
126
127   if( Dali::Image::SIGNAL_IMAGE_LOADING_FINISHED == signalName )
128   {
129     image->LoadingFinishedSignal().Connect( tracker, functor );
130   }
131   else if(Dali::Image::SIGNAL_IMAGE_UPLOADED == signalName)
132   {
133     image->UploadedSignal().Connect( tracker, functor );
134   }
135   else
136   {
137     // signalName does not match any signal
138     connected = false;
139   }
140
141   return connected;
142 }
143
144 ResourceId Image::GetResourceId() const
145 {
146   ResourceId ret = mTicket ? mTicket->GetId() : 0;
147
148   return ret;
149 }
150
151 const Dali::ImageAttributes& Image::GetAttributes() const
152 {
153   if( mTicket )
154   {
155     return mImageFactory.GetActualAttributes( mTicket->GetId() );
156   }
157   else
158   {
159     return mImageFactory.GetRequestAttributes( mRequest.Get() );
160   }
161 }
162
163 const std::string& Image::GetFilename() const
164 {
165   return mImageFactory.GetRequestPath( mRequest.Get() );
166 }
167
168 void Image::Reload()
169 {
170   if ( mRequest )
171   {
172     ResourceTicketPtr ticket = mImageFactory.Reload( mRequest.Get() );
173     SetTicket( ticket.Get() );
174   }
175 }
176
177 void Image::ResourceLoadingFailed(const ResourceTicket& ticket)
178 {
179   mLoadingFinishedV2.Emit( Dali::Image( this ) );
180 }
181
182 void Image::ResourceLoadingSucceeded(const ResourceTicket& ticket)
183 {
184   // Update size with actual loaded size
185   const ImageTicket* imageTicket = static_cast<const ImageTicket*>(&ticket);
186   mWidth = imageTicket->GetWidth();
187   mHeight = imageTicket->GetHeight();
188   mLoadingFinishedV2.Emit( Dali::Image( this ) );
189 }
190
191 void Image::ResourceUploaded(const ResourceTicket& ticket)
192 {
193   mUploadedV2.Emit( Dali::Image( this ) );
194 }
195
196 void Image::ResourceSavingSucceeded( const ResourceTicket& ticket )
197 {
198   // do nothing
199 }
200
201 void Image::ResourceSavingFailed( const ResourceTicket& ticket )
202 {
203   // do nothing
204 }
205
206 unsigned int Image::GetWidth() const
207 {
208   // Width has already been calculated - just return that
209   return mWidth;
210 }
211
212 unsigned int Image::GetHeight() const
213 {
214   // Height has already been calculated - just return that
215   return mHeight;
216 }
217
218 Vector2 Image::GetNaturalSize() const
219 {
220   return Vector2( mWidth, mHeight );
221 }
222
223 void Image::Connect()
224 {
225   ++mConnectionCount;
226
227   if( mConnectionCount == 1 )
228   {
229     // ticket was thrown away when related actors went offstage or image loading on demand
230     if( !mTicket )
231     {
232       ResourceTicketPtr newTicket = mImageFactory.Load( mRequest.Get() );
233       SetTicket( newTicket.Get() );
234     }
235   }
236 }
237
238 void Image::Disconnect()
239 {
240   if( !mTicket )
241   {
242     return;
243   }
244
245   DALI_ASSERT_DEBUG( mConnectionCount > 0 );
246   --mConnectionCount;
247   if( mConnectionCount == 0 && mReleasePolicy == Dali::Image::Unused )
248   {
249     // release image memory when it's not visible anymore (decrease ref. count of texture)
250     SetTicket( NULL );
251   }
252 }
253
254 void Image::SetTicket( ResourceTicket* ticket )
255 {
256   if( ticket == mTicket.Get() )
257   {
258     return;
259   }
260
261   if( mTicket )
262   {
263     mTicket->RemoveObserver( *this );
264     mImageFactory.ReleaseTicket( mTicket.Get() );
265   }
266
267   if( ticket )
268   {
269     mTicket.Reset( ticket );
270     mTicket->AddObserver( *this );
271   }
272   else
273   {
274     mTicket.Reset();
275   }
276 }
277
278 bool Image::IsNinePatchFileName( std::string filename )
279 {
280   bool match = false;
281
282   std::string::const_reverse_iterator iter = filename.rbegin();
283   enum { SUFFIX, HASH, HASH_DOT, DONE } state = SUFFIX;
284   while(iter < filename.rend())
285   {
286     switch(state)
287     {
288       case SUFFIX:
289       {
290         if(*iter == '.')
291         {
292           state = HASH;
293         }
294         else if(!isalnum(*iter))
295         {
296           state = DONE;
297         }
298       }
299       break;
300       case HASH:
301       {
302         if( *iter == '#' || *iter == '9' )
303         {
304           state = HASH_DOT;
305         }
306         else
307         {
308           state = DONE;
309         }
310       }
311       break;
312       case HASH_DOT:
313       {
314         if(*iter == '.')
315         {
316           match = true;
317         }
318         state = DONE; // Stop testing characters
319       }
320       break;
321       case DONE:
322       {
323       }
324       break;
325     }
326
327     // Satisfy prevnet
328     if( state == DONE )
329     {
330       break;
331     }
332
333     iter++;
334   }
335   return match;
336 }
337
338
339 } // namespace Internal
340
341 } // namespace Dali