Merge "Fix the framebuffer texture context loss handling" into tizen
[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   ImagePtr image;
77   if( IsNinePatchFileName(filename) )
78   {
79     image = NinePatchImage::New( filename, attributes, loadPol, releasePol );
80   }
81   else
82   {
83     image = new Image( loadPol, releasePol );
84     image->Initialize();
85
86     // if the attributes have a size, use that as natural size, otherwise get the size
87     const unsigned int width = attributes.GetWidth();
88     const unsigned int height = attributes.GetHeight();
89     // if one is zero, then there is some scaling calculation so we have to ask the loading logic what it will do
90     if( width > 0 && height > 0 )
91     {
92       image->mWidth  = width;
93       image->mHeight = height;
94       image->mRequest = image->mImageFactory.RegisterRequest( filename, &attributes );
95     }
96     else
97     {
98       // TODO do this query only if/when its needed (if someone is calling GetSize)
99       Integration::PlatformAbstraction& platformAbstraction = Internal::ThreadLocalStorage::Get().GetPlatformAbstraction();
100       Vector2 closestSize;
101       platformAbstraction.GetClosestImageSize( filename, attributes, closestSize );
102       image->mWidth = closestSize.width;
103       image->mHeight = closestSize.height;
104       // need a new request object as we will request for the closest size
105       Dali::ImageAttributes newAttributes( attributes );
106       newAttributes.SetSize( closestSize );
107       image->mRequest = image->mImageFactory.RegisterRequest( filename, &newAttributes );
108     }
109
110     if( Dali::Image::Immediate == loadPol )
111     {
112       // Trigger loading of the image on a seperate resource thread as soon as it
113       // can be scheduled:
114       image->mTicket = image->mImageFactory.Load( image->mRequest.Get() );
115       image->mTicket->AddObserver( *image );
116     }
117   }
118   DALI_LOG_SET_OBJECT_STRING( image, filename );
119
120   return image;
121 }
122
123 ImagePtr Image::New( NativeImage& nativeImg, LoadPolicy loadPol, ReleasePolicy releasePol )
124 {
125   ImagePtr image = new Image;
126   image->Initialize();
127
128   ResourceClient &resourceClient = ThreadLocalStorage::Get().GetResourceClient();
129
130   image->mWidth  = nativeImg.GetWidth();
131   image->mHeight = nativeImg.GetHeight();
132
133   const ResourceTicketPtr& ticket = resourceClient.AddNativeImage( nativeImg );
134   DALI_ASSERT_DEBUG( dynamic_cast<ImageTicket*>( ticket.Get() ) && "Resource ticket not ImageTicket subclass for image resource.\n" );
135   image->mTicket = static_cast<ImageTicket*>(ticket.Get());
136   image->mTicket->AddObserver( *image );
137
138   return image;
139 }
140
141 Image::~Image()
142 {
143   if( mTicket )
144   {
145     mTicket->RemoveObserver( *this );
146     if( Stage::IsInstalled() )
147     {
148       mImageFactory.ReleaseTicket( mTicket.Get() );
149     }
150   }
151
152   if( Stage::IsInstalled() )
153   {
154     UnregisterObject();
155   }
156 }
157
158 bool Image::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
159 {
160   bool connected( true );
161   DALI_ASSERT_DEBUG( dynamic_cast<Image*>( object ) && "Resource ticket not ImageTicket subclass for image resource.\n" );
162   Image* image = static_cast<Image*>(object);
163
164   if( Dali::Image::SIGNAL_IMAGE_LOADING_FINISHED == signalName )
165   {
166     image->LoadingFinishedSignal().Connect( tracker, functor );
167   }
168   else if(Dali::Image::SIGNAL_IMAGE_UPLOADED == signalName)
169   {
170     image->UploadedSignal().Connect( tracker, functor );
171   }
172   else
173   {
174     // signalName does not match any signal
175     connected = false;
176   }
177
178   return connected;
179 }
180
181 ResourceId Image::GetResourceId() const
182 {
183   ResourceId ret = mTicket ? mTicket->GetId() : 0;
184
185   return ret;
186 }
187
188 const Dali::ImageAttributes& Image::GetAttributes() const
189 {
190   if( mTicket )
191   {
192     return mImageFactory.GetActualAttributes( mTicket->GetId() );
193   }
194   else
195   {
196     return mImageFactory.GetRequestAttributes( mRequest.Get() );
197   }
198 }
199
200 const std::string& Image::GetFilename() const
201 {
202   return mImageFactory.GetRequestPath( mRequest.Get() );
203 }
204
205 void Image::Reload()
206 {
207   if ( mRequest )
208   {
209     ResourceTicketPtr ticket = mImageFactory.Reload( mRequest.Get() );
210     SetTicket( ticket.Get() );
211   }
212 }
213
214 void Image::ResourceLoadingFailed(const ResourceTicket& ticket)
215 {
216   mLoadingFinishedV2.Emit( Dali::Image( this ) );
217 }
218
219 void Image::ResourceLoadingSucceeded(const ResourceTicket& ticket)
220 {
221   mLoadingFinishedV2.Emit( Dali::Image( this ) );
222 }
223
224 void Image::ResourceUploaded(const ResourceTicket& ticket)
225 {
226   mUploadedV2.Emit( Dali::Image( this ) );
227 }
228
229 void Image::ResourceSavingSucceeded( const ResourceTicket& ticket )
230 {
231   // do nothing
232 }
233
234 void Image::ResourceSavingFailed( const ResourceTicket& ticket )
235 {
236   // do nothing
237 }
238
239 unsigned int Image::GetWidth() const
240 {
241   return mWidth;
242 }
243
244 unsigned int Image::GetHeight() const
245 {
246   return mHeight;
247 }
248
249 Vector2 Image::GetNaturalSize() const
250 {
251   Vector2 naturalSize(mWidth, mHeight);
252   return naturalSize;
253 }
254
255 void Image::Connect()
256 {
257   ++mConnectionCount;
258
259   if( mConnectionCount == 1 )
260   {
261     // ticket was thrown away when related actors went offstage or image loading on demand
262     if( !mTicket )
263     {
264       ResourceTicketPtr newTicket = mImageFactory.Load( mRequest.Get() );
265       SetTicket( newTicket.Get() );
266     }
267   }
268 }
269
270 void Image::Disconnect()
271 {
272   if( !mTicket )
273   {
274     return;
275   }
276
277   DALI_ASSERT_DEBUG( mConnectionCount > 0 );
278   --mConnectionCount;
279   if( mConnectionCount == 0 && mReleasePolicy == Dali::Image::Unused )
280   {
281     // release image memory when it's not visible anymore (decrease ref. count of texture)
282     SetTicket( NULL );
283   }
284 }
285
286 void Image::Initialize()
287 {
288   RegisterObject();
289 }
290
291 void Image::SetTicket( ResourceTicket* ticket )
292 {
293   if( ticket == mTicket.Get() )
294   {
295     return;
296   }
297
298   if( mTicket )
299   {
300     mTicket->RemoveObserver( *this );
301     mImageFactory.ReleaseTicket( mTicket.Get() );
302   }
303
304   if( ticket )
305   {
306     mTicket.Reset( ticket );
307     mTicket->AddObserver( *this );
308   }
309   else
310   {
311     mTicket.Reset();
312   }
313 }
314
315 bool Image::IsNinePatchFileName( std::string filename )
316 {
317   bool match = false;
318
319   std::string::const_reverse_iterator iter = filename.rbegin();
320   enum { SUFFIX, HASH, HASH_DOT, DONE } state = SUFFIX;
321   while(iter < filename.rend())
322   {
323     switch(state)
324     {
325       case SUFFIX:
326       {
327         if(*iter == '.')
328         {
329           state = HASH;
330         }
331         else if(!isalnum(*iter))
332         {
333           state = DONE;
334         }
335       }
336       break;
337       case HASH:
338       {
339         if( *iter == '#' || *iter == '9' )
340         {
341           state = HASH_DOT;
342         }
343         else
344         {
345           state = DONE;
346         }
347       }
348       break;
349       case HASH_DOT:
350       {
351         if(*iter == '.')
352         {
353           match = true;
354         }
355         state = DONE; // Stop testing characters
356       }
357       break;
358       case DONE:
359       {
360       }
361       break;
362     }
363
364     // Satisfy prevent
365     if( state == DONE )
366     {
367       break;
368     }
369
370     iter++;
371   }
372   return match;
373 }
374
375
376 } // namespace Internal
377
378 } // namespace Dali