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