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