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