2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <dali-toolkit/internal/atlas-manager/atlas-manager-impl.h>
23 #include <dali/devel-api/rendering/sampler.h>
24 #include <dali/devel-api/rendering/shader.h>
25 #include <dali/integration-api/debug.h>
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;
47 #define MAKE_SHADER(A)#A
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;
58 mediump vec4 position = vec4( aPosition, 0.0, 1.0 );
59 position.xyz *= uSize;
60 gl_Position = uMvpMatrix * position;
61 vTexCoord = aTexCoord;
65 const char* FRAGMENT_SHADER_L8 = MAKE_SHADER(
66 uniform lowp vec4 uColor;
67 uniform sampler2D sTexture;
68 varying mediump vec2 vTexCoord;
72 mediump vec4 color = texture2D( sTexture, vTexCoord );
73 gl_FragColor = vec4( uColor.rgb, uColor.a * color.r );
77 const char* FRAGMENT_SHADER_RGBA = MAKE_SHADER(
78 uniform sampler2D sTexture;
79 varying mediump vec2 vTexCoord;
83 gl_FragColor = texture2D( sTexture, vTexCoord );
89 AtlasManager::AtlasManager()
90 : mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES ),
91 mFilledPixel( FILLED_PIXEL )
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 mShaderL8 = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_L8 );
98 mShaderRgba = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_RGBA );
101 AtlasManagerPtr AtlasManager::New()
103 AtlasManagerPtr internal = new AtlasManager();
107 AtlasManager::~AtlasManager()
109 for ( SizeType i = 0; i < mAtlasList.size(); ++i )
111 mAtlasList[ i ].mAtlas.UploadedSignal().Disconnect( this, &AtlasManager::OnUpload );
112 delete[] mAtlasList[ i ].mStripBuffer;
115 // Are there any upload signals pending? Free up those buffer images now.
116 for ( SizeType i = 0; i < mUploadedImages.Size(); ++i )
118 delete[] mUploadedImages[ i ];
122 void AtlasManager::OnUpload( Image image )
124 if ( mUploadedImages.Size() )
126 delete[] mUploadedImages[ 0 ];
127 mUploadedImages.Erase( mUploadedImages.Begin() );
131 DALI_LOG_ERROR("Atlas Image Upload List should not be empty\n");
135 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat )
137 SizeType width = size.mWidth;
138 SizeType height = size.mHeight;
139 SizeType blockWidth = size.mBlockWidth;
140 SizeType blockHeight = size.mBlockHeight;
142 // Check to see if the atlas is large enough to hold a single block even ?
143 if ( blockWidth > width || blockHeight > height )
145 DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %ix%i\n",
146 width, height, blockWidth, blockHeight );
150 Dali::Atlas atlas = Dali::Atlas::New( width, height, pixelformat );
151 AtlasDescriptor atlasDescriptor;
152 atlasDescriptor.mAtlas = atlas;
153 atlasDescriptor.mSize = size;
154 atlasDescriptor.mPixelFormat = pixelformat;
155 atlasDescriptor.mNextFreeBlock = 1u; // indicate next free block will be the first ( +1 )
156 atlas.UploadedSignal().Connect( this, &AtlasManager::OnUpload );
158 // What size do we need for this atlas' strip buffer ( assume 32bit pixel format )?
159 SizeType neededStripSize =( blockWidth > blockHeight - DOUBLE_PIXEL_PADDING ? blockWidth : blockHeight - DOUBLE_PIXEL_PADDING ) << 2;
160 atlasDescriptor.mStripBuffer = new PixelBuffer[ neededStripSize ];
161 memset( atlasDescriptor.mStripBuffer, 0, neededStripSize );
163 atlasDescriptor.mHorizontalStrip = BufferImage::New( atlasDescriptor.mStripBuffer,
165 SINGLE_PIXEL_PADDING,
168 atlasDescriptor.mVerticalStrip = BufferImage::New( atlasDescriptor.mStripBuffer,
169 SINGLE_PIXEL_PADDING,
170 blockHeight - DOUBLE_PIXEL_PADDING,
172 mUploadedImages.PushBack( NULL );
173 atlasDescriptor.mFilledPixelImage = BufferImage::New( reinterpret_cast< PixelBuffer* >( &mFilledPixel ), 1, 1, pixelformat );
174 atlas.Upload( atlasDescriptor.mFilledPixelImage, 0, 0 );
176 Sampler sampler = Sampler::New( atlas, "sTexture" );
177 sampler.SetProperty( Sampler::Property::AFFECTS_TRANSPARENCY, true );
178 atlasDescriptor.mMaterial = Material::New( pixelformat == Pixel::L8 ? mShaderL8 : mShaderRgba );
179 atlasDescriptor.mMaterial.AddSampler( sampler );
180 atlasDescriptor.mSampler = sampler;
181 atlasDescriptor.mMaterial.SetBlendMode( BlendingMode::ON );
182 mAtlasList.push_back( atlasDescriptor );
183 return mAtlasList.size();
186 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
188 mAddFailPolicy = policy;
191 void AtlasManager::Add( const BufferImage& image,
192 Toolkit::AtlasManager::AtlasSlot& slot,
193 Toolkit::AtlasManager::AtlasId atlas )
195 // See if there's a slot in an atlas that matches the requirements of this image
196 // A bitmap must be sliceable into a single atlas
197 Pixel::Format pixelFormat = image.GetPixelFormat();
198 SizeType width = image.GetWidth();
199 SizeType height = image.GetHeight();
200 SizeType blockArea = 0;
201 SizeType totalBlocks = 0;
202 SizeType foundAtlas = 0;
206 AtlasSlotDescriptor desc;
208 // If there is a preferred atlas then check for room in that first
211 foundAtlas = CheckAtlas( atlas, width, height, pixelFormat, blockArea, totalBlocks );
214 // Search current atlases to see if there is a good match
216 while( !foundAtlas && index < mAtlasList.size() )
218 foundAtlas = CheckAtlas( index, width, height, pixelFormat, blockArea, totalBlocks );
222 // If we can't find a suitable atlas then check the policy to determine action
225 if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
227 SizeType newAtlas = CreateAtlas( mNewAtlasSize, pixelFormat );
234 foundAtlas = CheckAtlas( newAtlas, width, height, pixelFormat, blockArea, totalBlocks );
238 if ( Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy || !foundAtlas-- )
240 // Haven't found an atlas for this image!!!!!!
245 // Work out where the blocks are we're going to use
246 for ( SizeType i = 0; i < blockArea; ++i )
248 // Is there currently a next free block available ?
249 if ( mAtlasList[ foundAtlas ].mNextFreeBlock )
251 // Yes, so use this for our next block
252 SizeType selectedBlock = mAtlasList[ foundAtlas ].mNextFreeBlock - 1u;
253 desc.mBlocksList.PushBack( selectedBlock );
255 // Any blocks going to be available after this one (adjust to store +1 )?
258 if ( selectedBlock > totalBlocks )
260 // No so start trying to use free blocks list
263 mAtlasList[ foundAtlas ].mNextFreeBlock = selectedBlock;
267 // Our next block must be from the free list, fetch from the start of the list
268 desc.mBlocksList.PushBack( mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ] );
269 mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
273 desc.mImageWidth = width;
274 desc.mImageHeight = height;
275 desc.mAtlasId = foundAtlas + 1u;
278 // See if there's a previously freed image ID that we can assign to this new image
279 uint32_t imageId = 0;
280 for ( uint32_t i = 0; i < mImageList.size(); ++i )
282 if ( !mImageList[ i ].mCount )
290 mImageList.push_back( desc );
291 slot.mImageId = mImageList.size();
295 mImageList[ imageId - 1u ] = desc;
296 slot.mImageId = imageId;
298 slot.mAtlasId = foundAtlas + 1u;
299 UploadImage( image, desc );
302 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
305 Pixel::Format pixelFormat,
307 SizeType& totalBlocks )
309 if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
311 // Check to see if there are any unused blocks in this atlas to accomodate our image
312 SizeType blocksInX = mAtlasList[ atlas ].mSize.mWidth / mAtlasList[ atlas ].mSize.mBlockWidth;
313 SizeType blocksInY = mAtlasList[ atlas ].mSize.mHeight / mAtlasList[ atlas ].mSize.mBlockHeight;
314 totalBlocks = blocksInX * blocksInY;
315 SizeType blocksFree = mAtlasList[ atlas ].mNextFreeBlock ?
316 totalBlocks - mAtlasList[ atlas ].mNextFreeBlock + 1u :
317 mAtlasList[ atlas ].mFreeBlocksList.Size();
319 // Check to see if the image will fit in these blocks, if not we'll need to create a new atlas
321 && width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockWidth
322 && height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockHeight )
325 return ( atlas + 1u );
331 void AtlasManager::CreateMesh( SizeType atlas,
333 SizeType imageHeight,
334 const Vector2& position,
335 SizeType widthInBlocks,
336 SizeType heightInBlocks,
337 Toolkit::AtlasManager::Mesh2D& mesh,
338 AtlasSlotDescriptor& desc )
341 Toolkit::AtlasManager::Vertex2D vertex;
342 uint32_t faceIndex = 0; // TODO change to unsigned short when property type is available
344 SizeType blockWidth = mAtlasList[ atlas ].mSize.mBlockWidth;
345 SizeType blockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
347 float vertexBlockWidth = static_cast< float >( blockWidth );
348 float vertexBlockHeight = static_cast< float >( blockHeight );
350 SizeType width = mAtlasList[ atlas ].mSize.mWidth;
351 SizeType height = mAtlasList[ atlas ].mSize.mHeight;
353 SizeType atlasWidthInBlocks = width / blockWidth;
355 // Get the normalized size of a texel in both directions
356 // TODO when texture resizing and passing texture size via uniforms is available,
357 // we will encode pixel positions into the vertex data rather than normalized
358 // meaning that geometry needn't be changed on an atlas resize
359 float texelX = 1.0f / static_cast< float >( width );
360 float texelY = 1.0f / static_cast< float >( height );
362 // Get the normalized size of a block in texels
363 float texelBlockWidth = texelX * vertexBlockWidth;
364 float texelBlockHeight = texelY * vertexBlockHeight;
366 // Get partial block space
367 float vertexEdgeWidth = static_cast< float >( imageWidth % blockWidth );
368 float vertexEdgeHeight = static_cast< float >( imageHeight % blockHeight );
371 float texelEdgeWidth = vertexEdgeWidth * texelX;
372 float texelEdgeHeight = vertexEdgeHeight * texelY;
374 // Block by block create the two triangles for the quad
375 SizeType blockIndex = 0;
381 Vector2 topLeft = position;
383 for ( SizeType y = 0; y < heightInBlocks; ++y )
386 float currentX = position.x;
388 if ( ( heightInBlocks - 1u ) == y && vertexEdgeHeight > 0.0f )
390 ndcHeight = texelEdgeHeight;
391 ndcVHeight = vertexEdgeHeight;
395 ndcHeight = texelBlockHeight;
396 ndcVHeight = vertexBlockHeight;
399 for ( SizeType x = 0; x < widthInBlocks; ++x )
401 SizeType block = desc.mBlocksList[ blockIndex++ ];
403 float fBlockX = texelBlockWidth * static_cast< float >( block % atlasWidthInBlocks );
404 float fBlockY = texelBlockHeight * static_cast< float >( block / atlasWidthInBlocks );
406 // Add on texture filtering compensation
410 if ( ( widthInBlocks - 1u ) == x && vertexEdgeWidth > 0.0f )
412 ndcWidth = texelEdgeWidth;
413 ndcVWidth = vertexEdgeWidth;
417 ndcWidth = texelBlockWidth;
418 ndcVWidth = vertexBlockWidth;
422 vertex.mPosition.x = topLeft.x;
423 vertex.mPosition.y = topLeft.y;
424 vertex.mTexCoords.x = fBlockX;
425 vertex.mTexCoords.y = fBlockY;
427 mesh.mVertices.PushBack( vertex );
430 vertex.mPosition.x = topLeft.x + ndcVWidth;
431 vertex.mPosition.y = topLeft.y;
432 vertex.mTexCoords.x = fBlockX + ndcWidth;
433 vertex.mTexCoords.y = fBlockY;
435 mesh.mVertices.PushBack( vertex );
438 vertex.mPosition.x = topLeft.x;
439 vertex.mPosition.y = topLeft.y + ndcVHeight;
440 vertex.mTexCoords.x = fBlockX;
441 vertex.mTexCoords.y = fBlockY + ndcHeight;
443 mesh.mVertices.PushBack( vertex );
446 topLeft.x += ndcVWidth;
447 vertex.mPosition.x = topLeft.x;
448 vertex.mPosition.y = topLeft.y + ndcVHeight;
449 vertex.mTexCoords.x = fBlockX + ndcWidth;
450 vertex.mTexCoords.y = fBlockY + ndcHeight;
452 mesh.mVertices.PushBack( vertex );
454 // Six indices in counter clockwise winding
455 mesh.mIndices.PushBack( faceIndex + 1u );
456 mesh.mIndices.PushBack( faceIndex );
457 mesh.mIndices.PushBack( faceIndex + 2u );
458 mesh.mIndices.PushBack( faceIndex + 2u );
459 mesh.mIndices.PushBack( faceIndex + 3u );
460 mesh.mIndices.PushBack( faceIndex + 1u );
465 topLeft.x = currentX;
466 topLeft.y += vertexBlockHeight;
469 // If there's only one block then skip this next vertex optimisation
470 if ( widthInBlocks * heightInBlocks > 1 )
472 Toolkit::AtlasManager::Mesh2D optimizedMesh;
473 OptimizeMesh( mesh, optimizedMesh );
475 //PrintMeshData( mesh );
478 void AtlasManager::PrintMeshData( const Toolkit::AtlasManager::Mesh2D& mesh )
480 uint32_t vertexCount = mesh.mVertices.Size();
481 uint32_t indexCount = mesh.mIndices.Size();
482 std::cout << "\nMesh Data for Image: VertexCount = " << vertexCount;
483 std::cout << ", Triangles = " << indexCount / 3 << std::endl;
485 for ( SizeType v = 0; v < vertexCount; ++v )
487 std::cout << " Vertex(" << v << ") x = " << mesh.mVertices[v].mPosition.x << ", ";
488 std::cout << "y = " << mesh.mVertices[v].mPosition.y << ", ";
489 std::cout << "u = " << mesh.mVertices[v].mTexCoords.x << ", ";
490 std::cout << "v = " << mesh.mVertices[v].mTexCoords.y << std::endl;
493 std::cout << "\n Indices: ";
494 for ( SizeType i = 0; i < indexCount; ++i )
496 std::cout << " " << mesh.mIndices[ i ];
498 std::cout << std::endl;
501 void AtlasManager::OptimizeMesh( const Toolkit::AtlasManager::Mesh2D& in,
502 Toolkit::AtlasManager::Mesh2D& out )
504 unsigned short vertexIndex = 0;
506 // We could check to see if blocks are next to each other, but it's probably just as quick to compare verts
507 for ( SizeType i = 0; i < in.mIndices.Size(); ++i )
509 // Fetch a vertex, has it already been assigned?
510 bool foundVertex = false;
511 Toolkit::AtlasManager::Vertex2D v = in.mVertices[ in.mIndices[ i ] ];
512 for ( SizeType j = 0; j < out.mVertices.Size(); ++j )
514 if ( ( fabsf( v.mPosition.x - out.mVertices[ j ].mPosition.x ) < Math::MACHINE_EPSILON_1000 ) &&
515 ( fabsf( v.mPosition.y - out.mVertices[ j ].mPosition.y ) < Math::MACHINE_EPSILON_1000 ) &&
516 ( fabsf( v.mTexCoords.x - out.mVertices[ j ].mTexCoords.x ) < Math::MACHINE_EPSILON_1000 ) &&
517 ( fabsf( v.mTexCoords.y - out.mVertices[ j ].mTexCoords.y ) < Math::MACHINE_EPSILON_1000 ) )
519 // Yes, so store this down as the vertex to use
520 out.mIndices.PushBack( j );
526 // Did we find a vertex ?
529 // No so add a new one
530 out.mVertices.PushBack( v );
536 void AtlasManager::StitchMesh( Toolkit::AtlasManager::Mesh2D& first,
537 const Toolkit::AtlasManager::Mesh2D& second,
540 uint32_t vc = first.mVertices.Size();
542 for ( uint32_t v = 0; v < second.mVertices.Size(); ++v )
544 first.mVertices.PushBack( second.mVertices[ v ] );
547 for ( uint32_t i = 0; i < second.mIndices.Size(); ++i )
549 first.mIndices.PushBack( second.mIndices[ i ] + vc );
554 Toolkit::AtlasManager::Mesh2D optimizedMesh;
555 OptimizeMesh( first, optimizedMesh );
556 first = optimizedMesh;
560 void AtlasManager::StitchMesh( const Toolkit::AtlasManager::Mesh2D& first,
561 const Toolkit::AtlasManager::Mesh2D& second,
562 Toolkit::AtlasManager::Mesh2D& out,
565 uint32_t vc = first.mVertices.Size();
567 for ( uint32_t v = 0; v < vc; ++v )
569 out.mVertices.PushBack( first.mVertices[ v ] );
572 for ( uint32_t v = 0; v < second.mVertices.Size(); ++v )
574 out.mVertices.PushBack( second.mVertices[ v ] );
577 for ( uint32_t i = 0; i < first.mIndices.Size(); ++i )
579 out.mIndices.PushBack( first.mIndices[ i ] );
582 for ( uint32_t i = 0; i < second.mIndices.Size(); ++i )
584 out.mIndices.PushBack( second.mIndices[ i ] + vc );
589 Toolkit::AtlasManager::Mesh2D optimizedMesh;
590 OptimizeMesh( out, optimizedMesh );
595 void AtlasManager::UploadImage( const BufferImage& image,
596 const AtlasSlotDescriptor& desc )
598 // Get the atlas to upload the image to
599 SizeType atlas = desc.mAtlasId - 1u;
601 // Check to see that the pixel formats are compatible
602 if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
604 DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
608 SizeType atlasBlockWidth = mAtlasList[ atlas ].mSize.mBlockWidth;
609 SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
610 SizeType atlasWidthInBlocks = mAtlasList[ atlas ].mSize.mWidth / mAtlasList[ atlas ].mSize.mBlockWidth;
612 SizeType block = desc.mBlocksList[ 0 ];
613 SizeType blockX = block % atlasWidthInBlocks;
614 SizeType blockY = block / atlasWidthInBlocks;
615 SizeType blockOffsetX = blockX * atlasBlockWidth;
616 SizeType blockOffsetY = blockY * atlasBlockHeight;
618 SizeType width = image.GetWidth();
619 SizeType height = image.GetHeight();
621 // Blit image 1 pixel to the right and down into the block to compensate for texture filtering
622 if ( !mAtlasList[ atlas ].mAtlas.Upload( image,
623 blockOffsetX + SINGLE_PIXEL_PADDING,
624 blockOffsetY + SINGLE_PIXEL_PADDING ) )
626 DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n");
630 mUploadedImages.PushBack( const_cast< BufferImage& >( image ).GetBuffer() );
633 // If this is the first block then we need to keep the first pixel free for underline texture
638 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip,
642 DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n");
646 mUploadedImages.PushBack( NULL );
650 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip,
652 blockOffsetY + SINGLE_PIXEL_PADDING ) )
654 DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n");
658 mUploadedImages.PushBack( NULL );
663 if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight )
665 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip,
667 blockOffsetY + height + SINGLE_PIXEL_PADDING ) )
669 DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n");
673 mUploadedImages.PushBack( NULL );
678 if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth )
680 if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip,
681 blockOffsetX + width + SINGLE_PIXEL_PADDING,
682 blockOffsetY + SINGLE_PIXEL_PADDING ) )
684 DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n");
688 mUploadedImages.PushBack( NULL );
693 void AtlasManager::GenerateMeshData( ImageId id,
694 const Vector2& position,
695 Toolkit::AtlasManager::Mesh2D& meshData,
698 // Read the atlas Id to use for this image
699 SizeType imageId = id - 1u;
700 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
701 SizeType width = mImageList[ imageId ].mImageWidth;
702 SizeType height = mImageList[ imageId ].mImageHeight;
704 SizeType widthInBlocks = width / mAtlasList[ atlas ].mSize.mBlockWidth;
705 if ( width % mAtlasList[ atlas ].mSize.mBlockWidth )
709 SizeType heightInBlocks = height / mAtlasList[ atlas ].mSize.mBlockHeight;
710 if ( height % mAtlasList[ atlas ].mSize.mBlockHeight )
715 CreateMesh( atlas, width, height, position, widthInBlocks, heightInBlocks, meshData, mImageList[ imageId ] );
717 // Mesh created so increase the reference count, if we're asked to
720 mImageList[ imageId ].mCount++;
724 Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const
727 if ( !atlas || atlas > mAtlasList.size( ) )
730 DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
733 return mAtlasList[ atlas -1u ].mAtlas;
736 bool AtlasManager::Remove( ImageId id )
738 // Decrements the reference count of this image, and removes the blocks if zero.
739 SizeType imageId = id - 1u;
740 bool removed = false;
742 if ( id > mImageList.size() )
744 DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
748 // If we attempt to free an image that is already freed then do nothing, other than log
749 if ( !mImageList[ imageId ].mCount )
751 DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
755 if ( 2u > --mImageList[ imageId ].mCount )
757 // 'Remove the blocks' from this image and add to the atlas' freelist
759 mImageList[ imageId ].mCount = 0;
760 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
761 for ( uint32_t i = 0; i < mImageList[ imageId ].mBlocksList.Size(); ++i )
763 mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlocksList[ i ] );
769 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
771 if ( id && id <= mImageList.size() )
773 return mImageList[ id - 1u ].mAtlasId;
781 void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
783 mNewAtlasSize = size;
784 mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING;
785 mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING;
788 const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
790 if ( atlas && atlas-- <= mAtlasList.size() )
792 return mAtlasList[ atlas ].mSize;
797 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
799 if ( atlas && atlas <= mAtlasList.size() )
801 uint32_t index = atlas - 1u;
802 uint32_t width = mAtlasList[ index ].mSize.mWidth;
803 uint32_t height = mAtlasList[ index ].mSize.mHeight;
804 uint32_t blockWidth = mAtlasList[ index ].mSize.mBlockWidth;
805 uint32_t blockHeight = mAtlasList[ index ].mSize.mBlockHeight;
807 SizeType widthInBlocks = width / blockWidth;
808 SizeType heightInBlocks = height / blockHeight;
809 uint32_t blockCount = widthInBlocks * heightInBlocks;
811 // Check free previously unallocated blocks and any free blocks
812 if ( mAtlasList[ index ].mNextFreeBlock )
814 blockCount -= mAtlasList[ index ].mNextFreeBlock -1u - mAtlasList[ index ].mFreeBlocksList.Size();
818 blockCount = mAtlasList[ index ].mFreeBlocksList.Size();
828 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
830 return mAtlasList.size();
833 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas )
835 if ( !atlas || atlas > mAtlasList.size( ) )
838 DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
841 return mAtlasList[ atlas -1u ].mPixelFormat;
844 void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
846 Toolkit::AtlasManager::AtlasMetricsEntry entry;
847 uint32_t textureMemoryUsed = 0;
848 uint32_t atlasCount = mAtlasList.size();
849 metrics.mAtlasCount = atlasCount;
850 metrics.mAtlasMetrics.Resize(0);
852 for ( uint32_t i = 0; i < atlasCount; ++i )
854 entry.mSize = mAtlasList[ i ].mSize;
855 entry.mTotalBlocks = ( entry.mSize.mWidth / entry.mSize.mBlockWidth ) * ( entry.mSize.mHeight / entry.mSize.mBlockHeight );
856 uint32_t reuseBlocks = mAtlasList[ i ].mFreeBlocksList.Size();
857 entry.mBlocksUsed = mAtlasList[ i ].mNextFreeBlock ? mAtlasList[ i ].mNextFreeBlock - reuseBlocks - 1u: entry.mTotalBlocks - reuseBlocks;
858 entry.mPixelFormat = GetPixelFormat( i + 1 );
860 metrics.mAtlasMetrics.PushBack( entry );
862 uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight;
863 if ( entry.mPixelFormat == Pixel::BGRA8888 )
868 textureMemoryUsed += size;
871 metrics.mTextureMemoryUsed = textureMemoryUsed;
874 Material AtlasManager::GetMaterial( AtlasId atlas ) const
876 if ( atlas && atlas <= mAtlasList.size() )
878 return mAtlasList[ atlas -1u ].mMaterial;
884 Sampler AtlasManager::GetSampler( AtlasId atlas ) const
886 if ( atlas && atlas <= mAtlasList.size() )
888 return mAtlasList[ atlas -1u ].mSampler;
894 } // namespace Internal
896 } // namespace Toolkit