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