Added metrics logging to TextAtlasRenderer
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / atlas-manager / atlas-manager-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 // CLASS HEADER
18 #include <dali-toolkit/internal/atlas-manager/atlas-manager-impl.h>
19
20 // EXTERNAL INCLUDE
21 #include <iostream>
22 #include <string.h>
23 #include <dali/integration-api/debug.h>
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Internal
32 {
33
34 namespace
35 {
36   const Vector2 DEFAULT_ATLAS_SIZE( 512.0f, 512.0f );
37   const Vector2 DEFAULT_BLOCK_SIZE( 32.0f, 32.0f );
38   const uint32_t PIXEL_PADDING( 2u );
39 }
40
41 AtlasManager::AtlasManager()
42 : mNewAtlasSize( DEFAULT_ATLAS_SIZE ),
43   mNewBlockSize( DEFAULT_BLOCK_SIZE ),
44   mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES ),
45   mEdgeBuffer( NULL ),
46   mEdgeBufferSize( 0 )
47 {
48 }
49
50 AtlasManagerPtr AtlasManager::New()
51 {
52   AtlasManagerPtr internal = new AtlasManager();
53   return internal;
54 }
55
56 AtlasManager::~AtlasManager()
57 {
58   delete[] mEdgeBuffer;
59 }
60
61 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( SizeType width,
62                                                           SizeType height,
63                                                           SizeType blockWidth,
64                                                           SizeType blockHeight,
65                                                           Pixel::Format pixelformat )
66 {
67   // Check to see if the atlas is large enough to hold a single block even ?
68   if ( blockWidth > width || blockHeight > height )
69   {
70     DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %ix%i\n",
71                     width, height, blockWidth, blockHeight );
72     return 0;
73   }
74
75   Dali::Atlas atlas = Dali::Atlas::New( width, height, pixelformat );
76   AtlasDescriptor atlasDescriptor;
77   atlasDescriptor.mAtlas = atlas;
78   atlasDescriptor.mWidth = width;
79   atlasDescriptor.mHeight = height;
80   atlasDescriptor.mBlockWidth = blockWidth;
81   atlasDescriptor.mBlockHeight = blockHeight;
82   atlasDescriptor.mPixelFormat = pixelformat;
83   std::stringstream materialLabel;
84   materialLabel << "Atlas Material - ";
85   materialLabel << mAtlasList.size();
86   atlasDescriptor.mMaterial = Material::New( materialLabel.str() );
87   atlasDescriptor.mMaterial.SetDiffuseTexture( atlas );
88   atlasDescriptor.mNextFreeBlock = 1u; // indicate next free block will be the first ( +1 )
89
90   // What size do we need for this atlas' edge buffer ( assume RGBA pixel format )?
91   uint32_t neededEdgeSize = ( blockWidth > blockHeight ? blockWidth : blockHeight ) << 2;
92
93   // Is the current edge buffer large enough?
94   if ( neededEdgeSize > mEdgeBufferSize )
95   {
96     delete[] mEdgeBuffer;
97     mEdgeBuffer = new PixelBuffer[ neededEdgeSize ];
98     memset( mEdgeBuffer, 0, neededEdgeSize );
99     mEdgeBufferSize = neededEdgeSize;
100   }
101
102   atlasDescriptor.mEdgeX = BufferImage::New( mEdgeBuffer, blockWidth, PIXEL_PADDING, pixelformat );
103   atlasDescriptor.mEdgeY = BufferImage::New( mEdgeBuffer, PIXEL_PADDING, blockHeight, pixelformat );
104
105   mAtlasList.push_back( atlasDescriptor );
106   return mAtlasList.size();
107 }
108
109 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
110 {
111   mAddFailPolicy = policy;
112 }
113
114 void AtlasManager::Add( const BufferImage& image,
115                         Toolkit::AtlasManager::AtlasSlot& slot,
116                         Toolkit::AtlasManager::AtlasId atlas )
117 {
118   // See if there's a slot in an atlas that matches the requirements of this image
119   // A bitmap must be sliceable into a single atlas
120   Pixel::Format pixelFormat = image.GetPixelFormat();
121   SizeType width = image.GetWidth();
122   SizeType height = image.GetHeight();
123   SizeType blockArea = 0;
124   SizeType totalBlocks = 0;
125   SizeType foundAtlas = 0;
126   SizeType index = 0;
127   slot.mImageId = 0;
128
129   AtlasSlotDescriptor desc;
130
131   // If there is a preferred atlas then check for room in that first
132   if ( atlas-- )
133   {
134     foundAtlas = CheckAtlas( atlas, width, height, pixelFormat, blockArea, totalBlocks );
135   }
136
137   // Search current atlases to see if there is a good match
138
139   while( !foundAtlas && index < mAtlasList.size() )
140   {
141     foundAtlas = CheckAtlas( index, width, height, pixelFormat, blockArea, totalBlocks );
142     ++index;
143   }
144
145   // If we can't find a suitable atlas then check the policy to determine action
146   if ( !foundAtlas-- )
147   {
148     if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
149     {
150       SizeType newAtlas = CreateAtlas( mNewAtlasSize.x, mNewAtlasSize.y, mNewBlockSize.x, mNewBlockSize.y, pixelFormat );
151       if ( !newAtlas-- )
152       {
153         return;
154       }
155       else
156       {
157         foundAtlas = CheckAtlas( newAtlas, width, height, pixelFormat, blockArea, totalBlocks );
158       }
159     }
160
161     if ( Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy || !foundAtlas-- )
162     {
163       // Haven't found an atlas for this image!!!!!!
164           return;
165     }
166   }
167
168   // Work out where the blocks are we're going to use
169   for ( SizeType i = 0; i < blockArea; ++i )
170   {
171     // Is there currently a next free block available ?
172     if ( mAtlasList[ foundAtlas ].mNextFreeBlock )
173     {
174       // Yes, so use this for our next block
175       SizeType selectedBlock = mAtlasList[ foundAtlas ].mNextFreeBlock - 1u;
176       desc.mBlocksList.PushBack( selectedBlock );
177
178       // Any blocks going to be available after this one (adjust to store +1 )?
179       selectedBlock++;
180       selectedBlock++;
181       if ( selectedBlock > totalBlocks )
182       {
183         // No so start trying to use free blocks list
184         selectedBlock = 0;
185       }
186       mAtlasList[ foundAtlas ].mNextFreeBlock = selectedBlock;
187     }
188     else
189     {
190       // Our next block must be from the free list, fetch from the start of the list
191       desc.mBlocksList.PushBack( mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ] );
192       mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
193     }
194   }
195
196   desc.mImageWidth = width;
197   desc.mImageHeight = height;
198   desc.mAtlasId = foundAtlas + 1u;
199   desc.mCount = 1u;
200
201   // See if there's a previously freed image ID that we can assign to this new image
202   uint32_t imageId = 0;
203   for ( uint32_t i = 0; i < mImageList.size(); ++i )
204   {
205     if ( !mImageList[ i ].mCount )
206     {
207       imageId = i + 1u;
208       break;
209     }
210   }
211   if ( !imageId )
212   {
213     mImageList.push_back( desc );
214     slot.mImageId = mImageList.size();
215   }
216   else
217   {
218     mImageList[ imageId - 1u ] = desc;
219     slot.mImageId = imageId;
220   }
221   slot.mAtlasId = foundAtlas + 1u;
222   UploadImage( image, desc );
223 }
224
225 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
226                                                  SizeType width,
227                                                  SizeType height,
228                                                  Pixel::Format pixelFormat,
229                                                  SizeType& blockArea,
230                                                  SizeType& totalBlocks )
231 {
232   if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
233   {
234     // Check to see if there are any unused blocks in this atlas to accomodate our image
235     SizeType blocksInX = mAtlasList[ atlas ].mWidth / mAtlasList[ atlas ].mBlockWidth;
236     SizeType blocksInY = mAtlasList[ atlas ].mHeight / mAtlasList[ atlas ].mBlockHeight;
237     totalBlocks = blocksInX * blocksInY;
238     SizeType blocksFree = mAtlasList[ atlas ].mNextFreeBlock ? totalBlocks - mAtlasList[ atlas ].mNextFreeBlock + 1u : 0;
239
240     // Check to see if the image will fit in these blocks, if not we'll need to create a new atlas
241     if ( blocksFree
242          && width + PIXEL_PADDING <= mAtlasList[ atlas ].mBlockWidth
243          && height + PIXEL_PADDING <= mAtlasList[ atlas ].mBlockHeight )
244     {
245       blockArea = 1u;
246       return ( atlas + 1u );
247     }
248   }
249   return 0;
250 }
251
252 void AtlasManager::CreateMesh( SizeType atlas,
253                                SizeType imageWidth,
254                                SizeType imageHeight,
255                                const Vector2& position,
256                                SizeType widthInBlocks,
257                                SizeType heightInBlocks,
258                                Dali::MeshData& meshData,
259                                AtlasSlotDescriptor& desc )
260 {
261   Dali::MeshData::Vertex vertex;
262   Dali::MeshData::VertexContainer vertices;
263   Dali::MeshData::FaceIndices faces;
264   Dali::MeshData::FaceIndex faceIndex = 0;
265   meshData.SetHasNormals( false );
266   meshData.SetHasColor( true );
267   meshData.SetHasTextureCoords( true );
268
269   SizeType blockWidth = mAtlasList[ atlas ].mBlockWidth;
270   SizeType blockHeight = mAtlasList[ atlas ].mBlockHeight;
271
272   float vertexBlockWidth = static_cast< float >( blockWidth );
273   float vertexBlockHeight = static_cast< float >( blockHeight );
274
275   SizeType width = mAtlasList[ atlas ].mWidth;
276   SizeType height = mAtlasList[ atlas ].mHeight;
277
278   SizeType atlasWidthInBlocks = width / blockWidth;
279
280   // Get the normalized size of a texel in both directions
281   // TODO when texture resizing and passing texture size via uniforms is available,
282   //      we will encode pixel positions into the vertex data rather than normalized
283   //      meaning that geometry needn't be changed on an atlas resize
284   float texelX = 1.0f / static_cast< float >( width );
285   float texelY = 1.0f / static_cast< float >( height );
286
287   // Get the normalized size of a block in texels
288   float texelBlockWidth = texelX * vertexBlockWidth;
289   float texelBlockHeight = texelY * vertexBlockHeight;
290
291   // Get partial block space
292   float vertexEdgeWidth = static_cast< float >( imageWidth % blockWidth );
293   float vertexEdgeHeight = static_cast< float >( imageHeight % blockHeight );
294
295   // And in texels
296   float texelEdgeWidth = vertexEdgeWidth * texelX;
297   float texelEdgeHeight = vertexEdgeHeight * texelY;
298
299    // Block by block create the two triangles for the quad
300   SizeType blockIndex = 0;
301   float ndcWidth;
302   float ndcHeight;
303   float ndcVWidth;
304   float ndcVHeight;
305
306   Vector2 topLeft = position;
307
308   for ( SizeType y = 0; y < heightInBlocks; ++y )
309   {
310
311     float currentX = position.x;
312
313     if ( ( heightInBlocks - 1u ) == y && vertexEdgeHeight > 0.0f )
314     {
315       ndcHeight = texelEdgeHeight;
316       ndcVHeight = vertexEdgeHeight;
317     }
318     else
319     {
320       ndcHeight = texelBlockHeight;
321       ndcVHeight = vertexBlockHeight;
322     }
323
324     for ( SizeType x = 0; x < widthInBlocks; ++x )
325     {
326       SizeType block = desc.mBlocksList[ blockIndex++ ];
327
328       float fBlockX = texelBlockWidth * static_cast< float >( block % atlasWidthInBlocks );
329       float fBlockY = texelBlockHeight * static_cast< float >( block / atlasWidthInBlocks );
330
331       if (  ( widthInBlocks - 1u ) == x && vertexEdgeWidth > 0.0f )
332       {
333         ndcWidth = texelEdgeWidth;
334         ndcVWidth = vertexEdgeWidth;
335       }
336       else
337       {
338         ndcWidth = texelBlockWidth;
339         ndcVWidth = vertexBlockWidth;
340       }
341
342       // Top left
343       vertex.x = topLeft.x;
344       vertex.y = topLeft.y;
345       vertex.z = 0.0f;
346       vertex.u = fBlockX;
347       vertex.v = fBlockY;
348
349       vertices.push_back( vertex );
350
351       // Top Right
352       vertex.x = topLeft.x + ndcVWidth;
353       vertex.y = topLeft.y;
354       vertex.z = 0.0f;
355       vertex.u = fBlockX + ndcWidth;
356       vertex.v = fBlockY;
357
358       vertices.push_back( vertex );
359
360       // Bottom Left
361       vertex.x = topLeft.x;
362       vertex.y = topLeft.y + ndcVHeight;
363       vertex.z = 0.0f;
364       vertex.u = fBlockX;
365       vertex.v = fBlockY + ndcHeight;
366
367       vertices.push_back( vertex );
368
369       // Bottom Right
370       topLeft.x += ndcVWidth;
371       vertex.x = topLeft.x;
372       vertex.y = topLeft.y + ndcVHeight;
373       vertex.z = 0.0f;
374       vertex.u = fBlockX + ndcWidth;
375       vertex.v = fBlockY + ndcHeight;
376
377       vertices.push_back( vertex );
378
379       // Six indices in counter clockwise winding
380       faces.push_back( faceIndex + 1u );
381       faces.push_back( faceIndex );
382       faces.push_back( faceIndex + 2u );
383       faces.push_back( faceIndex + 2u );
384       faces.push_back( faceIndex + 3u );
385       faces.push_back( faceIndex + 1u );
386       faceIndex += 4;
387     }
388
389     // Move down a row
390     topLeft.x = currentX;
391     topLeft.y += vertexBlockHeight;
392   }
393
394   // If there's only one block then skip this next vertex optimisation
395   if ( widthInBlocks * heightInBlocks > 1 )
396   {
397     Dali::MeshData::VertexContainer optimizedVertices;
398     OptimizeVertices( vertices, faces, optimizedVertices );
399     meshData.SetVertices( optimizedVertices );
400   }
401   else
402   {
403     meshData.SetVertices( vertices );
404   }
405
406   meshData.SetFaceIndices( faces );
407   meshData.SetMaterial( mAtlasList[ atlas ].mMaterial );
408   //PrintMeshData( meshData );
409 }
410
411 void AtlasManager::PrintMeshData( const MeshData& meshData )
412 {
413   std::cout << "\nMesh Data for Image: VertexCount = " << meshData.GetVertexCount();
414   std::cout << ", Triangles = " << meshData.GetFaceCount() << std::endl;
415
416   Dali::MeshData::VertexContainer vertices = meshData.GetVertices();
417   Dali::MeshData::FaceIndices faces = meshData.GetFaces();
418
419   for ( SizeType v = 0; v < vertices.size(); ++v )
420   {
421     std::cout << " Vertex(" << v << ") x = " << vertices[v].x << ", ";
422     std::cout << "y = " << vertices[v].y << ", " << "z = " << vertices[v].z << ", ";
423     std::cout << "u = " << vertices[v].u << ", " << "v = " << vertices[v].v << std::endl;
424   }
425
426   std::cout << "\n Indices: ";
427   for ( SizeType i = 0; i < faces.size(); ++i )
428   {
429     std::cout << " " << faces[ i ];
430   }
431   std::cout << std::endl;
432 }
433
434 void AtlasManager::OptimizeVertices( const MeshData::VertexContainer& in,
435                                      MeshData::FaceIndices& faces,
436                                      MeshData::VertexContainer& out )
437 {
438   unsigned short vertexIndex = 0;
439
440   // We could check to see if blocks are next to each other, but it's probably just as quick to compare verts
441   for ( SizeType i = 0; i < faces.size(); ++i )
442   {
443     // Fetch a vertex, has it already been assigned?
444     bool foundVertex = false;
445     Dali::MeshData::Vertex v = in[ faces [ i ] ];
446     for ( SizeType j = 0; j < vertexIndex; ++j )
447     {
448       if ( v.x == out[ j ].x && v.y == out[ j ].y && v.z == out[ j ].z &&
449            v.u == out[ j ].u && v.v == out[ j ].v && v.nX == out[ j ].nX &&
450            v.nY == out[ j ].nY && v.nZ == out[ j ].nZ )
451       {
452         // Yes, so store this down as the vertex to use
453         faces[ i ] = j;
454         foundVertex = true;
455         break;
456       }
457     }
458
459     // Did we find a vertex ?
460     if ( !foundVertex )
461     {
462       // Add a new vertex
463       faces[ i ] = vertexIndex++;
464       out.push_back( v );
465     }
466   }
467 }
468
469 void AtlasManager::StitchMesh( MeshData& first,
470                                const MeshData& second,
471                                bool optimize )
472 {
473
474   // Would be much quicker to be able to get a non-const reference to these containers and update in situ
475   MeshData::VertexContainer v1 = first.GetVertices();
476   MeshData::VertexContainer v2 = second.GetVertices();
477   MeshData::FaceIndices f1 = first.GetFaces();
478   MeshData::FaceIndices f2 = second.GetFaces();
479
480   uint32_t vc1 = first.GetVertexCount();
481   uint32_t vc2 = second.GetVertexCount();
482
483   for ( uint32_t v = 0; v < vc2; ++v )
484   {
485     v1.push_back( v2[ v ] );
486   }
487
488   for ( uint32_t f = 0; f < f2.size(); ++f )
489   {
490     f1.push_back( f2[ f ] + vc1 );
491   }
492
493   if ( optimize )
494   {
495     MeshData::VertexContainer optimizedVertices;
496     OptimizeVertices( v1, f1, optimizedVertices );
497     first.SetVertices( optimizedVertices );
498   }
499   else
500   {
501     first.SetVertices( v1 );
502   }
503
504   first.SetFaceIndices( f1 );
505
506   // TODO rather than set the material to the second, check to see if there's a match and return if not
507   first.SetMaterial( second.GetMaterial() );
508 }
509
510 void AtlasManager::StitchMesh( const MeshData& first,
511                                const MeshData& second,
512                                MeshData& out,
513                                bool optimize )
514 {
515   // TODO Would be much quicker to be able to get a non-const reference to these containers and update in situ
516   MeshData::VertexContainer v1 = first.GetVertices();
517   MeshData::VertexContainer v2 = second.GetVertices();
518   MeshData::FaceIndices f1 = first.GetFaces();
519   MeshData::FaceIndices f2 = second.GetFaces();
520
521   uint32_t vc1 = first.GetVertexCount();
522   uint32_t vc2 = second.GetVertexCount();
523
524   MeshData::VertexContainer vertices;
525
526   MeshData::FaceIndices faces;
527
528   MeshData::Vertex vertex;
529
530   for ( uint32_t v = 0; v < vc1; ++v )
531   {
532     vertices.push_back( v1[ v ] );
533   }
534
535   for ( uint32_t v = 0; v < vc2; ++v )
536   {
537     vertices.push_back( v2[ v  ] );
538   }
539
540   for ( uint32_t f = 0; f < f1.size(); ++f )
541   {
542     faces.push_back( f1[ f ] );
543   }
544
545   for ( uint32_t f = 0; f < f2.size(); ++f )
546   {
547     faces.push_back( f2[ f ] + vc1 );
548   }
549
550   if ( optimize )
551   {
552     MeshData::VertexContainer optimizedVertices;
553     OptimizeVertices( vertices, faces, optimizedVertices );
554     out.SetVertices( optimizedVertices );
555   }
556   else
557   {
558     out.SetVertices( vertices );
559   }
560
561   // TODO rather than set the material to the second, check to see if there's a match and return if not
562   out.SetMaterial( second.GetMaterial() );
563   out.SetFaceIndices( faces );
564 }
565
566 void AtlasManager::UploadImage( const BufferImage& image,
567                                 const AtlasSlotDescriptor& desc )
568 {
569   // Get the atlas to upload the image to
570   SizeType atlas = desc.mAtlasId - 1u;
571
572   // Check to see that the pixel formats are compatible
573   if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
574   {
575     DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
576     return;
577   }
578
579   SizeType atlasBlockWidth = mAtlasList[ atlas ].mBlockWidth;
580   SizeType atlasBlockHeight = mAtlasList[ atlas ].mBlockHeight;
581   SizeType atlasWidthInBlocks = mAtlasList[ atlas ].mWidth / mAtlasList[ atlas ].mBlockWidth;
582
583   SizeType block = desc.mBlocksList[ 0 ];
584   SizeType blockX = block % atlasWidthInBlocks;
585   SizeType blockY = block / atlasWidthInBlocks;
586   SizeType blockOffsetX = blockX * atlasBlockWidth;
587   SizeType blockOffsetY = blockY * atlasBlockHeight;
588
589   SizeType width = image.GetWidth();
590   SizeType height = image.GetHeight();
591
592   if ( !mAtlasList[ atlas ].mAtlas.Upload( image, blockOffsetX, blockOffsetY ) )
593   {
594     DALI_LOG_ERROR("Uploading block to Atlas Failed!.\n");
595   }
596   if ( blockOffsetY + height + PIXEL_PADDING <= mAtlasList[ atlas ].mHeight )
597   {
598     if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mEdgeX, blockOffsetX, blockOffsetY + height ) )
599     {
600       DALI_LOG_ERROR("Uploading edgeX to Atlas Failed!.\n");
601     }
602   }
603   if ( blockOffsetX + width + PIXEL_PADDING <= mAtlasList[ atlas ].mWidth )
604   {
605     if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mEdgeY, blockOffsetX + width, blockOffsetY ) )
606     {
607       DALI_LOG_ERROR("Uploading edgeY to Atlas Failed!.\n");
608     }
609   }
610 }
611
612 void AtlasManager::GenerateMeshData( ImageId id,
613                                      const Vector2& position,
614                                      MeshData& meshData )
615 {
616   // Read the atlas Id to use for this image
617   SizeType imageId = id - 1u;
618   SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
619   SizeType width = mImageList[ imageId ].mImageWidth;
620   SizeType height = mImageList[ imageId ].mImageHeight;
621
622   SizeType widthInBlocks = width / mAtlasList[ atlas ].mBlockWidth;
623   if ( width % mAtlasList[ atlas ].mBlockWidth )
624   {
625     widthInBlocks++;
626   }
627   SizeType heightInBlocks = height / mAtlasList[ atlas ].mBlockHeight;
628   if ( height % mAtlasList[ atlas ].mBlockHeight )
629   {
630     heightInBlocks++;
631   }
632
633   CreateMesh( atlas, width, height, position, widthInBlocks, heightInBlocks, meshData, mImageList[ imageId ] );
634
635   // Mesh created so increase the reference count
636   mImageList[ imageId ].mCount++;
637 }
638
639 Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const
640 {
641   Dali::Atlas null;
642   if ( !atlas || atlas > mAtlasList.size( ) )
643   {
644
645     DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
646     return null;
647   }
648   return mAtlasList[ atlas -1u ].mAtlas;
649 }
650
651 bool AtlasManager::Remove( ImageId id )
652 {
653   // Decrements the reference count of this image, and removes the blocks if zero.
654   SizeType imageId = id - 1u;
655   bool removed = false;
656
657   if ( id > mImageList.size() )
658      {
659     DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
660     return false;
661   }
662
663   // If we attempt to free an image that is already freed then do nothing, other than log
664   if ( !mImageList[ imageId ].mCount )
665   {
666     DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
667     return false;
668   }
669
670   if ( 1u == --mImageList[ imageId ].mCount )
671   {
672     // 'Remove the blocks' from this image and add to the atlas' freelist
673     removed = true;
674     mImageList[ imageId ].mCount = 0;
675     SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
676     for ( uint32_t i = 0; i < mImageList[ imageId ].mBlocksList.Size(); ++i )
677     {
678       mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlocksList[ i ] );
679     }
680   }
681   return removed;
682 }
683
684 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
685 {
686   if ( id && id <= mImageList.size() )
687   {
688     return mImageList[ id - 1u ].mAtlasId;
689   }
690   else
691   {
692     return 0;
693   }
694 }
695
696 void AtlasManager::SetNewAtlasSize( const Vector2& size,
697                                     const Vector2& blockSize )
698 {
699   mNewAtlasSize = size;
700   mNewBlockSize = blockSize;
701 }
702
703 Vector2 AtlasManager::GetBlockSize( AtlasId atlas )
704 {
705   if ( atlas && atlas <= mAtlasList.size() )
706   {
707   return Vector2( static_cast< float >( mAtlasList[ atlas - 1u ].mBlockWidth ),
708                   static_cast< float >( mAtlasList[ atlas - 1u ].mBlockHeight) );
709   }
710   else
711   {
712     return Vector2( 0.0f, 0.0f );
713   }
714 }
715
716 Vector2 AtlasManager::GetAtlasSize( AtlasId atlas )
717 {
718   if ( atlas && atlas <= mAtlasList.size() )
719   {
720     return Vector2( static_cast< float >( mAtlasList[ atlas - 1u ].mWidth ),
721                     static_cast< float >( mAtlasList[ atlas - 1u ].mHeight ) );
722   }
723   else
724   {
725     return Vector2( 0.0f, 0.0f );
726   }
727 }
728
729 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
730 {
731   if ( atlas && atlas <= mAtlasList.size() )
732   {
733     uint32_t index = atlas - 1u;
734     uint32_t width = mAtlasList[ index ].mWidth;
735     uint32_t height = mAtlasList[ index ].mHeight;
736     uint32_t blockWidth = mAtlasList[ index ].mBlockWidth;
737     uint32_t blockHeight = mAtlasList[ index ].mBlockHeight;
738
739     SizeType widthInBlocks = width / blockWidth;
740     SizeType heightInBlocks = height / blockHeight;
741     uint32_t blockCount = widthInBlocks * heightInBlocks;
742
743     // Check free previously unallocated blocks and any free blocks
744     blockCount -= mAtlasList[ index ].mNextFreeBlock - mAtlasList[ index ].mFreeBlocksList.Size();
745     return blockCount;
746   }
747   else
748   {
749     return 0;
750   }
751 }
752
753 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
754 {
755   return mAtlasList.size();
756 }
757
758 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas )
759 {
760   if ( !atlas || atlas > mAtlasList.size( ) )
761   {
762
763     DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
764     return Pixel::L8;
765   }
766   return mAtlasList[ atlas -1u ].mPixelFormat;
767 }
768
769 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
770 {
771   Toolkit::AtlasManager::AtlasMetricsEntry entry;
772   uint32_t textureMemoryUsed = 0;
773   uint32_t atlasCount = mAtlasList.size();
774   metrics.mAtlasCount = atlasCount;
775   metrics.mAtlasMetrics.Resize(0);
776
777   for ( uint32_t i = 0; i < atlasCount; ++i )
778   {
779     SizeType width = mAtlasList[ i ].mWidth;
780     SizeType height = mAtlasList[ i ].mHeight;
781     SizeType blockWidth = mAtlasList[ i ].mBlockWidth;
782     SizeType blockHeight = mAtlasList[ i ].mBlockHeight;
783
784     entry.mWidth = width;
785     entry.mHeight = height;
786     entry.mBlockWidth = blockWidth;
787     entry.mBlockHeight = blockHeight;
788     entry.mTotalBlocks = ( width / blockWidth ) * ( height / blockHeight );
789     entry.mBlocksUsed = mAtlasList[ i ].mNextFreeBlock ? mAtlasList[ i ].mNextFreeBlock : entry.mTotalBlocks - mAtlasList[ i ].mFreeBlocksList.Size();
790     entry.mPixelFormat = GetPixelFormat( i + 1 );
791
792       metrics.mAtlasMetrics.PushBack( entry );
793
794     uint32_t size = width * height;
795     if ( entry.mPixelFormat == Pixel::BGRA8888 )
796     {
797       size <<= 2;
798     }
799
800     textureMemoryUsed += size;
801
802   }
803   metrics.mTextureMemoryUsed = textureMemoryUsed;
804 }
805
806
807 } // namespace Internal
808
809 } // namespace Toolkit
810
811 } // namespace Dali
812
813