License conversion from Flora to Apache 2.0
[platform/core/uifw/dali-core.git] / dali / internal / event / resources / resource-client.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 // INTERNAL INCLUDES
19 #include <dali/internal/event/resources/resource-client.h>
20 #include <dali/public-api/common/map-wrapper.h>
21
22 #include <dali/integration-api/glyph-set.h>
23 #include <dali/integration-api/resource-request.h>
24 #include <dali/integration-api/debug.h>
25
26 #include <dali/internal/event/common/stage-impl.h>
27 #include <dali/internal/event/text/resource/glyph-load-observer.h>
28 #include <dali/internal/event/images/image-impl.h>
29 #include <dali/internal/update/resources/resource-manager.h>
30 #include <dali/internal/update/manager/update-manager.h>
31
32
33 namespace Dali
34 {
35 namespace Internal
36 {
37 using namespace Dali::Integration;
38
39 typedef std::map<ResourceId, ResourceTicket*>        TicketContainer;
40 typedef TicketContainer::iterator                    TicketContainerIter;
41 typedef TicketContainer::size_type                   TicketContainerSize;
42 typedef std::pair<ResourceId, ResourceTicket*>       TicketPair;
43
44 typedef std::map<ResourceId, Bitmap*>                BitmapCache;
45 typedef BitmapCache::iterator                        BitmapCacheIter;
46
47 struct ResourceClient::Impl
48 {
49   Impl()
50   : mNextId(0),
51     mGlyphLoadObserver( NULL)
52   {
53   }
54
55   ResourceId       mNextId;
56   TicketContainer  mTickets;
57   BitmapCache      mBitmaps;
58   GlyphLoadObserver* mGlyphLoadObserver;
59 };
60
61 ResourceClient::ResourceClient( ResourceManager& resourceManager,
62                                 SceneGraph::UpdateManager& updateManager )
63 : mResourceManager(resourceManager),
64   mUpdateManager(updateManager)
65 {
66   mImpl = new ResourceClient::Impl();
67   mResourceManager.SetClient(*this);
68 }
69
70 ResourceClient::~ResourceClient()
71 {
72   // Guard to allow handle destruction after Core has been destroyed
73   if ( Stage::IsInstalled() )
74   {
75     for (TicketContainerIter iter = mImpl->mTickets.begin(); iter != mImpl->mTickets.end(); ++iter)
76     {
77       (*iter).second->StopLifetimeObservation();
78     }
79   }
80   delete mImpl;
81 }
82
83 ResourceTicketPtr ResourceClient::RequestResource(
84   const ResourceType& type,
85   const std::string& path,
86   LoadResourcePriority priority )
87 {
88   ResourceTicketPtr newTicket;
89   ResourceTypePath typePath(type, path);
90   ResourceId newId = 0;
91
92   // Create the ticket first
93   // NOTE: pre-increment, otherwise we get 0 for first one.
94   newId = ++(mImpl->mNextId);
95
96   switch (type.id)
97   {
98     case ResourceBitmap:
99     {
100       const BitmapResourceType& bitmapResource = static_cast <const BitmapResourceType&> (type);
101       // image tickets will cache the requested parameters, which are updated on successful loading
102       ImageTicket* imageTicket = new ImageTicket(*this, newId, typePath);
103       imageTicket->mAttributes = bitmapResource.imageAttributes;
104       newTicket = imageTicket;
105       break;
106     }
107
108     case ResourceNativeImage:
109     {
110       const NativeImageResourceType& nativeResource = static_cast <const NativeImageResourceType&> (type);
111       // image tickets will cache the requested parameters, which are updated on successful loading
112       ImageTicket* imageTicket = new ImageTicket(*this, newId, typePath);
113       imageTicket->mAttributes = nativeResource.imageAttributes;
114       newTicket = imageTicket;
115       break;
116     }
117     case ResourceModel:       // FALLTHROUGH
118     case ResourceTargetImage:
119     case ResourceShader:
120     case ResourceMesh:
121     case ResourceText:
122     {
123       newTicket = new ResourceTicket(*this, newId, typePath);
124       break;
125     }
126   }
127
128   mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
129
130   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: RequestResource(path:%s type.id:%d) newId:%u\n", path.c_str(), type.id, newId);
131
132   RequestLoadResourceMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, typePath, priority );
133   return newTicket;
134 }
135
136 ResourceTicketPtr ResourceClient::DecodeResource(
137   const ResourceType& type,
138   RequestBufferPtr buffer,
139   LoadResourcePriority priority )
140 {
141   DALI_ASSERT_DEBUG( type.id == ResourceBitmap && "Only bitmap resources are currently decoded from memory buffers. It should be easy to expand to other resource types though. The public API function at the front and the resource thread at the back end are all that should need to be changed. The code in the middle should be agnostic to the the resource type it is conveying.\n" );
142   DALI_ASSERT_DEBUG( buffer.Get() && "Null resource buffer passed for decoding." );
143   ResourceTicketPtr newTicket;
144   if( buffer.Get() ) //< Check to avoid SEGV on a null pointer.
145   {
146     ResourceTypePath typePath( type, "" );
147     ResourceId newId = 0;
148
149     // Create the correct ticket type for the resource:
150     switch (type.id)
151     {
152       case ResourceBitmap:
153       {
154         // NOTE: pre-increment, otherwise we get 0 for first one.
155         newId = ++(mImpl->mNextId);
156         const BitmapResourceType& bitmapResource = static_cast <const BitmapResourceType&> ( type );
157         // Image tickets will cache the requested parameters, which are updated on successful loading
158         ImageTicket* imageTicket = new ImageTicket( *this, newId, typePath );
159         imageTicket->mAttributes = bitmapResource.imageAttributes;
160         newTicket = imageTicket;
161         break;
162       }
163
164       // FALLTHROUGH:
165       case ResourceNativeImage:
166       case ResourceModel:
167       case ResourceTargetImage:
168       case ResourceShader:
169       case ResourceMesh:
170       case ResourceText:
171       {
172         DALI_LOG_ERROR( "Unsupported resource type passed for decoding from a memory buffer." );
173       }
174     }
175
176     if( newTicket )
177     {
178       mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
179       DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: DecodeResource( type.id:%d ) newId:%u\n", type.id, newId);
180
181       RequestDecodeResourceMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, typePath, buffer, priority );
182     }
183   }
184   return newTicket;
185 }
186
187 ResourceTicketPtr ResourceClient::LoadShader( ShaderResourceType& type,
188                                               const std::string& path )
189 {
190   ResourceTicketPtr newTicket;
191
192   const ResourceId newId = ++(mImpl->mNextId);
193
194   ResourceTypePath typePath(type, path);
195   newTicket = new ResourceTicket(*this, newId, typePath);
196
197   mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
198
199   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: LoadShader(path:%s) newId:%u\n", path.c_str(), newId);
200
201   RequestLoadShaderMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, typePath );
202   return newTicket;
203 }
204
205 bool ResourceClient::ReloadResource( ResourceId id, LoadResourcePriority priority )
206 {
207   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: ReloadResource(Id: %u)\n", id);
208
209   bool resourceExists = false;
210   TicketContainerIter ticketIter;
211   ticketIter = mImpl->mTickets.find(id);
212
213   if(ticketIter != mImpl->mTickets.end())
214   {
215     resourceExists = true;
216     // The ticket is already being observed
217     ResourceTicket* ticket = ticketIter->second;
218     DALI_ASSERT_DEBUG(ticket && "Null ticket for tracked resource request." );
219     const ResourceTypePath * const typePathPtr = &ticket->GetTypePath();
220     DALI_ASSERT_DEBUG( typePathPtr );
221     RequestReloadResourceMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, id, *typePathPtr, priority );
222   }
223   else
224   {
225     DALI_LOG_ERROR ("Resource %d does not exist\n", id);
226   }
227   return resourceExists;
228 }
229
230 void ResourceClient::SaveResource( ResourceTicketPtr ticket, const std::string& url )
231 {
232   DALI_ASSERT_DEBUG( ticket );
233
234   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: SaveResource(Id: %u, path:%s)\n", ticket->GetId(), url.c_str());
235
236   const ResourceTypePath * const typePathPtr = &ticket->GetTypePath();
237   if( typePathPtr )
238   {
239     if( 0 != url.length() )
240     {
241       ResourceTypePath typePath( *(typePathPtr->type), url );
242       RequestSaveResourceMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, ticket->GetId(), typePath );
243     }
244     else
245     {
246       ResourceTypePath typePath( *typePathPtr );
247       RequestSaveResourceMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, ticket->GetId(), typePath );
248     }
249   }
250 }
251
252 ResourceTicketPtr ResourceClient::RequestResourceTicket( ResourceId id )
253 {
254   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: RequestResourceTicket(Id: %u)\n", id);
255
256   ResourceTicketPtr ticket;
257
258   TicketContainerIter ticketIter = mImpl->mTickets.find( id );
259
260   if ( mImpl->mTickets.end() != ticketIter )
261   {
262     ticket = ticketIter->second;
263   }
264
265   return ticket;
266 }
267
268 ImageTicketPtr ResourceClient::AllocateBitmapImage( unsigned int width,
269                                                     unsigned int height,
270                                                     unsigned int bufferWidth,
271                                                     unsigned int bufferHeight,
272                                                     Pixel::Format pixelformat )
273 {
274   Bitmap* const bitmap = Bitmap::New( Bitmap::BITMAP_2D_PACKED_PIXELS, false/*buffer is available via public-api, therefore not discardable*/ );
275   Bitmap::PackedPixelsProfile* const packedBitmap = bitmap->GetPackedPixelsProfile();
276   DALI_ASSERT_DEBUG(packedBitmap);
277
278   packedBitmap->ReserveBuffer(pixelformat, width, height, bufferWidth, bufferHeight);
279   DALI_ASSERT_DEBUG(bitmap->GetBuffer() != 0);
280   DALI_ASSERT_DEBUG(bitmap->GetBufferSize() >= width * height);
281
282   ImageTicketPtr ticket = AddBitmapImage(bitmap);
283
284   DALI_ASSERT_DEBUG(bitmap->GetBuffer() != 0);
285   DALI_ASSERT_DEBUG(bitmap->GetBufferSize() >= width * height);
286   return ticket;
287 }
288
289 ImageTicketPtr ResourceClient::AddBitmapImage(Bitmap* bitmap)
290 {
291   DALI_ASSERT_DEBUG( bitmap != NULL );
292
293   ImageTicketPtr newTicket;
294
295   const ResourceId newId = ++(mImpl->mNextId);
296
297   Dali::ImageAttributes imageAttributes = Dali::ImageAttributes::New(bitmap->GetImageWidth(), bitmap->GetImageHeight(), bitmap->GetPixelFormat());
298   BitmapResourceType bitmapResourceType(imageAttributes); // construct first as no copy ctor (needed to bind ref to object)
299   ResourceTypePath typePath(bitmapResourceType, "");
300   newTicket = new ImageTicket(*this, newId, typePath);
301   newTicket->mAttributes = imageAttributes;
302   newTicket->LoadingSucceeded();
303
304   mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
305
306   // Store bitmap for immediate access.
307   mImpl->mBitmaps[newId] = bitmap;
308
309   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: AddBitmapImage() New id = %u\n", newId);
310   RequestAddBitmapImageMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, bitmap );
311
312   return newTicket;
313 }
314
315 ResourceTicketPtr ResourceClient::AddNativeImage ( NativeImage& resourceData )
316 {
317   ImageTicketPtr newTicket;
318
319   const ResourceId newId = ++(mImpl->mNextId);
320   NativeImageResourceType nativeImageResourceType; // construct first as no copy ctor (needed to bind ref to object)
321   ResourceTypePath typePath(nativeImageResourceType, "");
322   newTicket = new ImageTicket(*this, newId, typePath);
323   newTicket->mAttributes = ImageAttributes::New(resourceData.GetWidth(),
324                                                 resourceData.GetHeight(),
325                                                 resourceData.GetPixelFormat());
326   newTicket->LoadingSucceeded();
327
328   mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
329
330   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: AddNativeImage() New id = %u\n", newId);
331
332   RequestAddNativeImageMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, &resourceData );
333
334   return newTicket;
335 }
336
337 ImageTicketPtr ResourceClient::AddFrameBufferImage ( unsigned int width, unsigned int height, Pixel::Format pixelFormat )
338 {
339   ImageTicketPtr newTicket;
340
341   const ResourceId newId = ++(mImpl->mNextId);
342
343   Dali::ImageAttributes imageAttributes = Dali::ImageAttributes::New(width, height, pixelFormat );
344   RenderTargetResourceType renderTargetResourceType(imageAttributes) ; // construct first as no copy ctor (needed to bind ref to object)
345   ResourceTypePath typePath(renderTargetResourceType, "");
346   newTicket = new ImageTicket(*this, newId, typePath);
347   newTicket->mAttributes = imageAttributes;
348   newTicket->LoadingSucceeded();
349
350   mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
351
352   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: AddFrameBufferImage() New id = %u\n", newId);
353   RequestAddFrameBufferImageMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, width, height, pixelFormat );
354
355   return newTicket;
356 }
357
358 ImageTicketPtr ResourceClient::AddFrameBufferImage ( NativeImage& nativeImage )
359 {
360   ImageTicketPtr newTicket;
361
362   const ResourceId newId = ++(mImpl->mNextId);
363
364   Dali::ImageAttributes imageAttributes = Dali::ImageAttributes::New(nativeImage.GetWidth(), nativeImage.GetHeight(), nativeImage.GetPixelFormat() );
365   RenderTargetResourceType renderTargetResourceType(imageAttributes); // construct first as no copy ctor (needed to bind ref to object)
366   ResourceTypePath typePath(renderTargetResourceType, "");
367   newTicket = new ImageTicket(*this, newId, typePath);
368   newTicket->mAttributes = imageAttributes;
369   newTicket->LoadingSucceeded();
370
371   mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
372
373   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: AddFrameBufferImage() New id = %u\n", newId);
374   RequestAddFrameBufferImageMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, &nativeImage );
375
376   return newTicket;
377 }
378
379
380 ResourceTicketPtr ResourceClient::AllocateTexture( unsigned int width,
381                                                    unsigned int height,
382                                                    Pixel::Format pixelformat )
383 {
384   ImageTicketPtr newTicket;
385   const ResourceId newId = ++(mImpl->mNextId);
386
387   Dali::ImageAttributes imageAttributes = Dali::ImageAttributes::New( width, height, pixelformat);
388   BitmapResourceType bitmapResourceType(imageAttributes); // construct first as no copy ctor (needed to bind ref to object)
389   ResourceTypePath typePath(bitmapResourceType, "");
390   newTicket = new ImageTicket(*this, newId, typePath);
391
392   mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
393   newTicket->mAttributes = imageAttributes;
394   newTicket->LoadingSucceeded();
395
396   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: AllocateTexture() New id = %u\n", newId);
397
398   RequestAllocateTextureMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, width, height, pixelformat );
399
400   return newTicket;
401 }
402
403 void ResourceClient::UpdateTexture(  ResourceId id,
404                                      BitmapUploadArray uploadArray )
405 {
406   RequestUpdateTextureMessage(  mUpdateManager.GetEventToUpdate(), mResourceManager, id, uploadArray );
407 }
408
409 ResourceTicketPtr ResourceClient::AllocateMesh( OwnerPointer<MeshData>& meshData )
410 {
411   ResourceTicketPtr newTicket;
412   const ResourceId newId = ++(mImpl->mNextId);
413   MeshResourceType meshResourceType; // construct first as no copy ctor (needed to bind ref to object)
414   ResourceTypePath typePath(meshResourceType, "");
415   newTicket = new ResourceTicket(*this, newId, typePath);
416   mImpl->mTickets.insert(TicketPair(newId, newTicket.Get()));
417
418   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: AllocateMesh() New id = %u\n", newId);
419   RequestAllocateMeshMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, newId, meshData );
420
421   return newTicket;
422 }
423
424 void ResourceClient::UpdateBitmapArea( ResourceTicketPtr ticket, RectArea& updateArea )
425 {
426   DALI_ASSERT_DEBUG( ticket );
427
428   RequestUpdateBitmapAreaMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, ticket->GetId(), updateArea );
429 }
430
431 void ResourceClient::UpdateMesh( ResourceTicketPtr ticket, const Dali::MeshData& meshData )
432 {
433   DALI_ASSERT_DEBUG( ticket );
434
435   RequestUpdateMeshMessage( mUpdateManager.GetEventToUpdate(),
436                             mResourceManager,
437                             ticket->GetId(),
438                             meshData );
439 }
440
441 Bitmap* ResourceClient::GetBitmap(ResourceTicketPtr ticket)
442 {
443   DALI_ASSERT_DEBUG( ticket );
444
445   Bitmap* bitmap = NULL;
446   BitmapCacheIter iter = mImpl->mBitmaps.find(ticket->GetId());
447
448   if( iter != mImpl->mBitmaps.end() )
449   {
450     bitmap = iter->second;
451   }
452   return bitmap;
453 }
454
455 void ResourceClient::SetGlyphLoadObserver( GlyphLoadObserver* glyphLoadedInterface )
456 {
457   mImpl->mGlyphLoadObserver = glyphLoadedInterface;
458 }
459
460 void ResourceClient::UpdateAtlasStatus( ResourceId id, ResourceId atlasId, Integration::LoadStatus loadStatus )
461 {
462   RequestAtlasUpdateMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, id, atlasId, loadStatus );
463 }
464
465 /********************************************************************************
466  ********************   ResourceTicketLifetimeObserver methods   ****************
467  ********************************************************************************/
468
469 void ResourceClient::ResourceTicketDiscarded(const ResourceTicket& ticket)
470 {
471   const ResourceId deadId = ticket.GetId();
472   const ResourceTypePath& typePath = ticket.GetTypePath();
473
474   // Ensure associated event owned resources are also removed
475   mImpl->mBitmaps.erase(ticket.GetId());
476
477   // The ticket object is dead, remove from tickets container
478   TicketContainerSize erased = mImpl->mTickets.erase(deadId);
479   DALI_ASSERT_DEBUG(erased != 0);
480   (void)erased; // Avoid "unused variable erased" in release builds
481
482   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: ResourceTicketDiscarded() deadId = %u\n", deadId);
483   RequestDiscardResourceMessage( mUpdateManager.GetEventToUpdate(), mResourceManager, deadId, typePath.type->id );
484 }
485
486 /********************************************************************************
487  ***********************   Notifications from ResourceManager  ******************
488  ********************************************************************************/
489
490 void ResourceClient::NotifyUploaded( ResourceId id )
491 {
492   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: NotifyUpdated(id:%u)\n", id);
493
494   TicketContainerIter ticketIter = mImpl->mTickets.find(id);
495   if(ticketIter != mImpl->mTickets.end())
496   {
497     ResourceTicket* ticket = ticketIter->second;
498     ticket->Uploaded();
499   }
500 }
501
502 void ResourceClient::NotifySaveRequested( ResourceId id )
503 {
504   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: NotifySaveRequested(id:%u)\n", id);
505
506   TicketContainerIter ticketIter = mImpl->mTickets.find(id);
507   if(ticketIter != mImpl->mTickets.end())
508   {
509     ResourceTicket* ticket = ticketIter->second;
510     SaveResource( ticket, "" );
511   }
512 }
513
514
515 void ResourceClient::NotifyLoading( ResourceId id )
516 {
517   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: NotifyLoading(id:%u)\n", id);
518
519   TicketContainerIter ticketIter = mImpl->mTickets.find(id);
520   if(ticketIter != mImpl->mTickets.end())
521   {
522     ResourceTicket* ticket = ticketIter->second;
523     ticket->Loading();
524   }
525 }
526
527 void ResourceClient::NotifyLoadingSucceeded( ResourceId id )
528 {
529   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: NotifyLoadingSucceeded(id:%u)\n", id);
530
531   TicketContainerIter ticketIter = mImpl->mTickets.find(id);
532   if(ticketIter != mImpl->mTickets.end())
533   {
534     ResourceTicket* ticket = ticketIter->second;
535     ticket->LoadingSucceeded();
536   }
537 }
538
539 void ResourceClient::NotifyLoadingFailed( ResourceId id )
540 {
541   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: NotifyLoadingFailed(id:%u)\n", id);
542
543   TicketContainerIter ticketIter = mImpl->mTickets.find(id);
544   if(ticketIter != mImpl->mTickets.end())
545   {
546     ResourceTicket* ticket = ticketIter->second;
547     ticket->LoadingFailed();
548   }
549 }
550
551 void ResourceClient::NotifySavingSucceeded( ResourceId id )
552 {
553   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: NotifySavingSucceeded(id:%u)\n", id);
554
555   TicketContainerIter ticketIter = mImpl->mTickets.find(id);
556   if(ticketIter != mImpl->mTickets.end())
557   {
558     ResourceTicket* ticket = ticketIter->second;
559     ticket->SavingSucceeded();
560   }
561 }
562
563 void ResourceClient::NotifySavingFailed( ResourceId id )
564 {
565   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: NotifySavingFailed(id:%u)\n", id);
566
567   TicketContainerIter ticketIter = mImpl->mTickets.find(id);
568   if(ticketIter != mImpl->mTickets.end())
569   {
570     ResourceTicket* ticket = ticketIter->second;
571     ticket->SavingFailed();
572   }
573 }
574
575 void ResourceClient::NotifyGlyphSetLoaded( ResourceId id, const GlyphSet& glyphSet, LoadStatus loadStatus )
576 {
577   if( mImpl->mGlyphLoadObserver == NULL)
578   {
579     // should not happen.
580     DALI_ASSERT_DEBUG( !"GlyphLoadObserver == NULL ");
581     return;
582   }
583
584   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: NotifyGlyphSetLoaded(hash:%u)\n", glyphSet.mFontHash);
585
586   mImpl->mGlyphLoadObserver->GlyphsLoaded( id, glyphSet, loadStatus );
587 }
588
589 void ResourceClient::UpdateImageTicket( ResourceId id, const Dali::ImageAttributes& imageAttributes ) ///!< Issue #AHC01
590 {
591   DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceClient: UpdateImageTicket(id:%u)\n", id);
592
593   TicketContainerIter ticketIter = mImpl->mTickets.find(id);
594   if(ticketIter != mImpl->mTickets.end())
595   {
596     ResourceTicket* ticket = ticketIter->second;
597     ImageTicketPtr imageTicket = dynamic_cast<ImageTicket*>(ticket);
598     if(imageTicket)
599     {
600       imageTicket->mAttributes = imageAttributes;
601     }
602   }
603 }
604
605 } // Internal
606
607 } // Dali