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