Merge "Add cmake to the required system packages list in dali_env" into tizen
[platform/core/uifw/dali-core.git] / dali / internal / event / images / resource-image-impl.cpp
1 /*
2  * Copyright (c) 2015 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/resource-image-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/common/dali-common.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/integration-api/debug.h>
28 #include <dali/internal/event/common/thread-local-storage.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 namespace
42 {
43
44 // Signals
45
46 const char* const SIGNAL_IMAGE_LOADING_FINISHED = "image-loading-finished";
47
48 BaseHandle CreateImage()
49 {
50   ImagePtr image = ResourceImage::New();
51   return Dali::Image(image.Get());
52 }
53
54 TypeRegistration mType( typeid( Dali::ResourceImage ), typeid( Dali::Image ), CreateImage );
55
56 Dali::SignalConnectorType signalConnector1( mType, SIGNAL_IMAGE_LOADING_FINISHED, &ResourceImage::DoConnectSignal );
57
58 }
59
60 ResourceImage::ResourceImage( LoadPolicy loadPol, ReleasePolicy releasePol )
61 : Image( releasePol ),
62   mImageFactory( ThreadLocalStorage::Get().GetImageFactory() ),
63   mLoadPolicy(loadPol)
64 {
65 }
66
67 ResourceImagePtr ResourceImage::New()
68 {
69   ResourceImagePtr image = new ResourceImage;
70   image->Initialize();
71   return image;
72 }
73
74 ResourceImagePtr ResourceImage::New( const std::string& url, const Dali::ImageAttributes& attributes, LoadPolicy loadPol, ReleasePolicy releasePol )
75 {
76   ResourceImagePtr image;
77   if( IsNinePatch( url ) )
78   {
79     image = NinePatchImage::New( url, attributes, releasePol );
80   }
81   else
82   {
83     image = new ResourceImage( loadPol, releasePol );
84     image->Initialize();
85
86     // consider the requested size as natural size, 0 means we don't (yet) know it
87     image->mWidth = attributes.GetWidth();
88     image->mHeight = attributes.GetHeight();
89     image->mRequest = image->mImageFactory.RegisterRequest( url, &attributes );
90
91     if( Dali::ResourceImage::IMMEDIATE == loadPol )
92     {
93       // Trigger loading of the image on a as soon as it can be done
94       image->mTicket = image->mImageFactory.Load( *image->mRequest.Get() );
95       image->mTicket->AddObserver( *image );
96     }
97   }
98   DALI_LOG_SET_OBJECT_STRING( image, url );
99
100   return image;
101 }
102
103 ResourceImage::~ResourceImage()
104 {
105   if( mTicket )
106   {
107     mTicket->RemoveObserver( *this );
108     if( Stage::IsInstalled() )
109     {
110       mImageFactory.ReleaseTicket( mTicket.Get() );
111     }
112     mTicket.Reset();
113   }
114 }
115
116 bool ResourceImage::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
117 {
118   bool connected( true );
119   DALI_ASSERT_DEBUG( dynamic_cast<ResourceImage*>( object ) && "Resource ticket not ImageTicket subclass for image resource.\n" );
120   ResourceImage* image = static_cast<ResourceImage*>(object);
121
122   if( 0 == strcmp( signalName.c_str(), SIGNAL_IMAGE_LOADING_FINISHED ) )
123   {
124     image->LoadingFinishedSignal().Connect( tracker, functor );
125   }
126   else
127   {
128     // signalName does not match any signal
129     connected = false;
130   }
131
132   return connected;
133 }
134
135 const Dali::ImageAttributes& ResourceImage::GetAttributes() const
136 {
137   if( mTicket )
138   {
139     return mImageFactory.GetActualAttributes( mTicket );
140   }
141   else
142   {
143     return mImageFactory.GetRequestAttributes( mRequest );
144   }
145 }
146
147 const std::string& ResourceImage::GetUrl() const
148 {
149   return mImageFactory.GetRequestPath( mRequest );
150 }
151
152 void ResourceImage::Reload()
153 {
154   if ( mRequest )
155   {
156     ResourceTicketPtr ticket = mImageFactory.Reload( *mRequest.Get() );
157     SetTicket( ticket.Get() );
158   }
159 }
160
161 unsigned int ResourceImage::GetWidth() const
162 {
163   // if width is 0, it means we've not yet loaded the image
164   if( 0u == mWidth )
165   {
166     Size size;
167     mImageFactory.GetImageSize( mRequest, mTicket, size );
168     mWidth = size.width;
169     // The app will probably ask for the height immediately, so don't waste the synchronous file IO that ImageFactory may have just done:
170     DALI_ASSERT_DEBUG( 0 == mHeight || unsigned(size.height) == mHeight );
171     if( 0 == mHeight )
172     {
173       mHeight = size.height;
174     }
175   }
176   return mWidth;
177 }
178
179 unsigned int ResourceImage::GetHeight() const
180 {
181   if( 0u == mHeight )
182   {
183     Size size;
184     mImageFactory.GetImageSize( mRequest, mTicket, size );
185     mHeight = size.height;
186     DALI_ASSERT_DEBUG( 0 == mWidth || unsigned(size.width) == mWidth );
187     if( 0 == mWidth )
188     {
189       mWidth = size.width;
190     }
191   }
192   return mHeight;
193 }
194
195 Vector2 ResourceImage::GetNaturalSize() const
196 {
197   Vector2 naturalSize(mWidth, mHeight);
198   if( 0u == mWidth || 0u == mHeight )
199   {
200     mImageFactory.GetImageSize( mRequest, mTicket, naturalSize );
201     mWidth = naturalSize.width;
202     mHeight = naturalSize.height;
203   }
204   return naturalSize;
205 }
206
207 void ResourceImage::ResourceLoadingFailed(const ResourceTicket& ticket)
208 {
209   mLoadingFinished.Emit( Dali::ResourceImage( this ) );
210 }
211
212 void ResourceImage::ResourceLoadingSucceeded(const ResourceTicket& ticket)
213 {
214   mLoadingFinished.Emit( Dali::ResourceImage( this ) );
215 }
216
217 void ResourceImage::Connect()
218 {
219   ++mConnectionCount;
220
221   if( mConnectionCount == 1 )
222   {
223     // ticket was thrown away when related actors went offstage or image loading on demand
224     if( !mTicket )
225     {
226       DALI_ASSERT_DEBUG( mRequest.Get() );
227       ResourceTicketPtr newTicket = mImageFactory.Load( *mRequest.Get() );
228       SetTicket( newTicket.Get() );
229     }
230   }
231 }
232
233 void ResourceImage::Disconnect()
234 {
235   if( !mTicket )
236   {
237     return;
238   }
239
240   DALI_ASSERT_DEBUG( mConnectionCount > 0 );
241   --mConnectionCount;
242   if( mConnectionCount == 0 && mReleasePolicy == Dali::ResourceImage::UNUSED )
243   {
244     // release image memory when it's not visible anymore (decrease ref. count of texture)
245     SetTicket( NULL );
246   }
247 }
248
249 bool ResourceImage::IsNinePatch( const std::string& url )
250 {
251   bool match = false;
252
253   std::string::const_reverse_iterator iter = url.rbegin();
254   enum { SUFFIX, HASH, HASH_DOT, DONE } state = SUFFIX;
255   while(iter < url.rend())
256   {
257     switch(state)
258     {
259       case SUFFIX:
260       {
261         if(*iter == '.')
262         {
263           state = HASH;
264         }
265         else if(!isalnum(*iter))
266         {
267           state = DONE;
268         }
269       }
270       break;
271       case HASH:
272       {
273         if( *iter == '#' || *iter == '9' )
274         {
275           state = HASH_DOT;
276         }
277         else
278         {
279           state = DONE;
280         }
281       }
282       break;
283       case HASH_DOT:
284       {
285         if(*iter == '.')
286         {
287           match = true;
288         }
289         state = DONE; // Stop testing characters
290       }
291       break;
292       case DONE:
293       {
294       }
295       break;
296     }
297
298     // Satisfy prevent
299     if( state == DONE )
300     {
301       break;
302     }
303
304     ++iter;
305   }
306   return match;
307 }
308
309 void ResourceImage::SetTicket( ResourceTicket* ticket )
310 {
311   if( ticket == mTicket.Get() )
312   {
313     return;
314   }
315
316   if( mTicket )
317   {
318     mTicket->RemoveObserver( *this );
319     mImageFactory.ReleaseTicket( mTicket.Get() );
320   }
321
322   if( ticket )
323   {
324     mTicket.Reset( ticket );
325     mTicket->AddObserver( *this );
326   }
327   else
328   {
329     mTicket.Reset();
330   }
331 }
332
333 } // namespace Internal
334
335 } // namespace Dali