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>
22 #include <dali/integration-api/debug.h>
35 const Vector2 DEFAULT_ATLAS_SIZE( 512.0f, 512.0f );
36 const Vector2 DEFAULT_BLOCK_SIZE( 32.0f, 32.0f );
39 AtlasManager::AtlasManager()
40 : mNewAtlasSize( DEFAULT_ATLAS_SIZE ),
41 mNewBlockSize( DEFAULT_BLOCK_SIZE ),
42 mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES )
46 AtlasManagerPtr AtlasManager::New()
48 AtlasManagerPtr internal = new AtlasManager();
52 AtlasManager::~AtlasManager()
56 Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( SizeType width,
60 Pixel::Format pixelformat )
62 // Check to see if the atlas is large enough to hold a single block even ?
63 if ( blockWidth > width || blockHeight > height )
65 DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %i x %i\n",
66 width, height, blockWidth, blockHeight );
70 Dali::Atlas atlas = Dali::Atlas::New( width, height, pixelformat );
71 AtlasDescriptor atlasDescriptor;
72 atlasDescriptor.mAtlas = atlas;
73 atlasDescriptor.mWidth = width;
74 atlasDescriptor.mHeight = height;
75 atlasDescriptor.mBlockWidth = blockWidth;
76 atlasDescriptor.mBlockHeight = blockHeight;
77 atlasDescriptor.mPixelFormat = pixelformat;
78 std::stringstream materialLabel;
79 materialLabel << "Atlas Material - ";
80 materialLabel << mAtlasList.size();
81 atlasDescriptor.mMaterial = Material::New( materialLabel.str() );
82 atlasDescriptor.mMaterial.SetDiffuseTexture( atlas );
83 atlasDescriptor.mNextFreeBlock = 1u; // indicate next free block will be the first ( +1 )
84 mAtlasList.push_back( atlasDescriptor );
85 return mAtlasList.size();
88 void AtlasManager::SetAddPolicy( Toolkit::AtlasManager::AddFailPolicy policy )
90 mAddFailPolicy = policy;
93 void AtlasManager::Add( const BufferImage& image,
94 Toolkit::AtlasManager::AtlasSlot& slot,
95 Toolkit::AtlasManager::AtlasId atlas )
97 // See if there's a slot in an atlas that matches the requirements of this image
98 // A bitmap must be sliceable into a single atlas
99 Pixel::Format pixelFormat = image.GetPixelFormat();
100 SizeType width = image.GetWidth();
101 SizeType height = image.GetHeight();
102 SizeType blockArea = 0;
103 SizeType totalBlocks = 0;
104 SizeType foundAtlas = 0;
108 AtlasSlotDescriptor desc;
110 // If there is a preferred atlas then check for room in that first
113 foundAtlas = CheckAtlas( atlas, width, height, pixelFormat, blockArea, totalBlocks );
116 // Search current atlases to see if there is a good match
118 while( !foundAtlas && index < mAtlasList.size() )
120 foundAtlas = CheckAtlas( index, width, height, pixelFormat, blockArea, totalBlocks );
124 // If we can't find a suitable atlas then check the policy to determine action
127 if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy )
129 SizeType newAtlas = CreateAtlas( mNewAtlasSize.x, mNewAtlasSize.y, mNewBlockSize.x, mNewBlockSize.y, pixelFormat );
136 foundAtlas = CheckAtlas( newAtlas, width, height, pixelFormat, blockArea, totalBlocks );
140 if ( Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy || !foundAtlas-- )
142 // Haven't found an atlas for this image!!!!!!
147 // Work out where the blocks are we're going to use
148 for ( SizeType i = 0; i < blockArea; ++i )
150 // Is there currently a next free block available ?
151 if ( mAtlasList[ foundAtlas ].mNextFreeBlock )
153 // Yes, so use this for our next block
154 SizeType selectedBlock = mAtlasList[ foundAtlas ].mNextFreeBlock - 1u;
155 desc.mBlocksList.PushBack( selectedBlock );
157 // Any blocks going to be available after this one (adjust to store +1 )?
160 if ( selectedBlock > totalBlocks )
162 // No so start trying to use free blocks list
165 mAtlasList[ foundAtlas ].mNextFreeBlock = selectedBlock;
169 // Our next block must be from the free list, fetch from the start of the list
170 desc.mBlocksList.PushBack( mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ] );
171 mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
175 desc.mImageWidth = width;
176 desc.mImageHeight = height;
177 desc.mAtlasId = foundAtlas + 1u;
180 // See if there's a previously freed image ID that we can assign to this new image
181 uint32_t imageId = 0;
182 for ( uint32_t i = 0; i < mImageList.size(); ++i )
184 if ( !mImageList[ i ].mCount )
192 mImageList.push_back( desc );
193 slot.mImageId = mImageList.size();
197 mImageList[ imageId - 1u ] = desc;
198 slot.mImageId = imageId;
200 slot.mAtlasId = foundAtlas + 1u;
201 UploadImage( image, desc );
204 AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
207 Pixel::Format pixelFormat,
209 SizeType& totalBlocks )
211 if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
213 // Work out how many blocks wide and high our bitmap is in the atlas' block size
214 SizeType widthInBlocks = width / mAtlasList[ atlas ].mBlockWidth;
215 if ( width % mAtlasList[ atlas ].mBlockWidth )
219 SizeType heightInBlocks = height / mAtlasList[ atlas ].mBlockHeight;
220 if ( height % mAtlasList[ atlas ].mBlockHeight )
224 blockArea = widthInBlocks * heightInBlocks;
226 // Check to see if there are any unused blocks in this atlas to accomodate our image
227 SizeType blocksInX = mAtlasList[ atlas ].mWidth / mAtlasList[ atlas ].mBlockWidth;
228 SizeType blocksInY = mAtlasList[ atlas ].mHeight / mAtlasList[ atlas ].mBlockHeight;
229 totalBlocks = blocksInX * blocksInY;
230 SizeType blocksFree = mAtlasList[ atlas ].mNextFreeBlock ? totalBlocks - mAtlasList[ atlas ].mNextFreeBlock + 1u : 0;
232 // Check to see if there are enough blocks to accomodate our sliced image ?
233 if ( blockArea <= ( mAtlasList[ atlas ].mFreeBlocksList.Size() + blocksFree ) )
235 // Yes, we've found room
236 return ( atlas + 1u );
242 void AtlasManager::CreateMesh( SizeType atlas,
244 SizeType imageHeight,
245 const Vector2& position,
246 SizeType widthInBlocks,
247 SizeType heightInBlocks,
248 Dali::MeshData& meshData,
249 AtlasSlotDescriptor& desc )
251 Dali::MeshData::Vertex vertex;
252 Dali::MeshData::VertexContainer vertices;
253 Dali::MeshData::FaceIndices faces;
254 Dali::MeshData::FaceIndex faceIndex = 0;
255 meshData.SetHasNormals( false );
256 meshData.SetHasColor( true );
257 meshData.SetHasTextureCoords( true );
259 SizeType blockWidth = mAtlasList[ atlas ].mBlockWidth;
260 SizeType blockHeight = mAtlasList[ atlas ].mBlockHeight;
262 float vertexBlockWidth = static_cast< float >( blockWidth );
263 float vertexBlockHeight = static_cast< float >( blockHeight );
265 SizeType width = mAtlasList[ atlas ].mWidth;
266 SizeType height = mAtlasList[ atlas ].mHeight;
268 SizeType atlasWidthInBlocks = width / blockWidth;
270 // Get the normalized size of a texel in both directions
271 // TODO when texture resizing and passing texture size via uniforms is available,
272 // we will encode pixel positions into the vertex data rather than normalized
273 // meaning that geometry needn't be changed on an atlas resize
274 float texelX = 1.0f / static_cast< float >( width );
275 float texelY = 1.0f / static_cast< float >( height );
277 // Get the normalized size of a block in texels
278 float texelBlockWidth = texelX * vertexBlockWidth;
279 float texelBlockHeight = texelY * vertexBlockHeight;
281 // Get partial block space
282 float vertexEdgeWidth = static_cast< float >( imageWidth % blockWidth );
283 float vertexEdgeHeight = static_cast< float >( imageHeight % blockHeight );
286 float texelEdgeWidth = vertexEdgeWidth * texelX;
287 float texelEdgeHeight = vertexEdgeHeight * texelY;
289 // Block by block create the two triangles for the quad
290 SizeType blockIndex = 0;
296 Vector2 topLeft = position;
298 for ( SizeType y = 0; y < heightInBlocks; ++y )
301 float currentX = position.x;
303 if ( ( heightInBlocks - 1u ) == y && vertexEdgeHeight > 0.0f )
305 ndcHeight = texelEdgeHeight;
306 ndcVHeight = vertexEdgeHeight;
310 ndcHeight = texelBlockHeight;
311 ndcVHeight = vertexBlockHeight;
314 for ( SizeType x = 0; x < widthInBlocks; ++x )
316 SizeType block = desc.mBlocksList[ blockIndex++ ];
318 float fBlockX = texelBlockWidth * static_cast< float >( block % atlasWidthInBlocks );
319 float fBlockY = texelBlockHeight * static_cast< float >( block / atlasWidthInBlocks );
321 if ( ( widthInBlocks - 1u ) == x && vertexEdgeWidth > 0.0f )
323 ndcWidth = texelEdgeWidth;
324 ndcVWidth = vertexEdgeWidth;
328 ndcWidth = texelBlockWidth;
329 ndcVWidth = vertexBlockWidth;
333 vertex.x = topLeft.x;
334 vertex.y = topLeft.y;
339 vertices.push_back( vertex );
342 vertex.x = topLeft.x + ndcVWidth;
343 vertex.y = topLeft.y;
345 vertex.u = fBlockX + ndcWidth;
348 vertices.push_back( vertex );
351 vertex.x = topLeft.x;
352 vertex.y = topLeft.y + ndcVHeight;
355 vertex.v = fBlockY + ndcHeight;
357 vertices.push_back( vertex );
360 topLeft.x += ndcVWidth;
361 vertex.x = topLeft.x;
362 vertex.y = topLeft.y + ndcVHeight;
364 vertex.u = fBlockX + ndcWidth;
365 vertex.v = fBlockY + ndcHeight;
367 vertices.push_back( vertex );
369 // Six indices in counter clockwise winding
370 faces.push_back( faceIndex + 1u );
371 faces.push_back( faceIndex );
372 faces.push_back( faceIndex + 2u );
373 faces.push_back( faceIndex + 2u );
374 faces.push_back( faceIndex + 3u );
375 faces.push_back( faceIndex + 1u );
380 topLeft.x = currentX;
381 topLeft.y += vertexBlockHeight;
384 // If there's only one block then skip this next vertex optimisation
385 if ( widthInBlocks * heightInBlocks > 1 )
387 Dali::MeshData::VertexContainer optimizedVertices;
388 OptimizeVertices( vertices, faces, optimizedVertices );
389 meshData.SetVertices( optimizedVertices );
393 meshData.SetVertices( vertices );
396 meshData.SetFaceIndices( faces );
397 meshData.SetMaterial( mAtlasList[ atlas ].mMaterial );
398 //PrintMeshData( meshData );
401 void AtlasManager::PrintMeshData( const MeshData& meshData )
403 std::cout << "\nMesh Data for Image: VertexCount = " << meshData.GetVertexCount();
404 std::cout << ", Triangles = " << meshData.GetFaceCount() << std::endl;
406 Dali::MeshData::VertexContainer vertices = meshData.GetVertices();
407 Dali::MeshData::FaceIndices faces = meshData.GetFaces();
409 for ( SizeType v = 0; v < vertices.size(); ++v )
411 std::cout << " Vertex(" << v << ") x = " << vertices[v].x << ", ";
412 std::cout << "y = " << vertices[v].y << ", " << "z = " << vertices[v].z << ", ";
413 std::cout << "u = " << vertices[v].u << ", " << "v = " << vertices[v].v << std::endl;
416 std::cout << "\n Indices: ";
417 for ( SizeType i = 0; i < faces.size(); ++i )
419 std::cout << " " << faces[ i ];
421 std::cout << std::endl;
424 void AtlasManager::OptimizeVertices( const MeshData::VertexContainer& in,
425 MeshData::FaceIndices& faces,
426 MeshData::VertexContainer& out )
428 unsigned short vertexIndex = 0;
430 // We could check to see if blocks are next to each other, but it's probably just as quick to compare verts
431 for ( SizeType i = 0; i < faces.size(); ++i )
433 // Fetch a vertex, has it already been assigned?
434 bool foundVertex = false;
435 Dali::MeshData::Vertex v = in[ faces [ i ] ];
436 for ( SizeType j = 0; j < vertexIndex; ++j )
438 if ( v.x == out[ j ].x && v.y == out[ j ].y && v.z == out[ j ].z &&
439 v.u == out[ j ].u && v.v == out[ j ].v && v.nX == out[ j ].nX &&
440 v.nY == out[ j ].nY && v.nZ == out[ j ].nZ )
442 // Yes, so store this down as the vertex to use
449 // Did we find a vertex ?
453 faces[ i ] = vertexIndex++;
459 void AtlasManager::StitchMesh( MeshData& first,
460 const MeshData& second,
464 // Would be much quicker to be able to get a non-const reference to these containers and update in situ
465 MeshData::VertexContainer v1 = first.GetVertices();
466 MeshData::VertexContainer v2 = second.GetVertices();
467 MeshData::FaceIndices f1 = first.GetFaces();
468 MeshData::FaceIndices f2 = second.GetFaces();
470 uint32_t vc1 = first.GetVertexCount();
471 uint32_t vc2 = second.GetVertexCount();
473 for ( uint32_t v = 0; v < vc2; ++v )
475 v1.push_back( v2[ v ] );
478 for ( uint32_t f = 0; f < f2.size(); ++f )
480 f1.push_back( f2[ f ] + vc1 );
485 MeshData::VertexContainer optimizedVertices;
486 OptimizeVertices( v1, f1, optimizedVertices );
487 first.SetVertices( optimizedVertices );
491 first.SetVertices( v1 );
494 first.SetFaceIndices( f1 );
496 // TODO rather than set the material to the second, check to see if there's a match and return if not
497 first.SetMaterial( second.GetMaterial() );
500 void AtlasManager::StitchMesh( const MeshData& first,
501 const MeshData& second,
505 // TODO Would be much quicker to be able to get a non-const reference to these containers and update in situ
506 MeshData::VertexContainer v1 = first.GetVertices();
507 MeshData::VertexContainer v2 = second.GetVertices();
508 MeshData::FaceIndices f1 = first.GetFaces();
509 MeshData::FaceIndices f2 = second.GetFaces();
511 uint32_t vc1 = first.GetVertexCount();
512 uint32_t vc2 = second.GetVertexCount();
514 MeshData::VertexContainer vertices;
516 MeshData::FaceIndices faces;
518 MeshData::Vertex vertex;
520 for ( uint32_t v = 0; v < vc1; ++v )
522 vertices.push_back( v1[ v ] );
525 for ( uint32_t v = 0; v < vc2; ++v )
527 vertices.push_back( v2[ v ] );
530 for ( uint32_t f = 0; f < f1.size(); ++f )
532 faces.push_back( f1[ f ] );
535 for ( uint32_t f = 0; f < f2.size(); ++f )
537 faces.push_back( f2[ f ] + vc1 );
542 MeshData::VertexContainer optimizedVertices;
543 OptimizeVertices( vertices, faces, optimizedVertices );
544 out.SetVertices( optimizedVertices );
548 out.SetVertices( vertices );
551 // TODO rather than set the material to the second, check to see if there's a match and return if not
552 out.SetMaterial( second.GetMaterial() );
553 out.SetFaceIndices( faces );
556 void AtlasManager::UploadImage( const BufferImage& image,
557 const AtlasSlotDescriptor& desc )
559 // Get the atlas to upload the image to
560 SizeType atlas = desc.mAtlasId - 1u;
562 // Check to see that the pixel formats are compatible
563 if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat )
565 DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n");
569 SizeType atlasBlockWidth = mAtlasList[ atlas ].mBlockWidth;
570 SizeType atlasBlockHeight = mAtlasList[ atlas ].mBlockHeight;
571 SizeType atlasWidthInBlocks = mAtlasList[ atlas ].mWidth / mAtlasList[ atlas ].mBlockWidth;
573 SizeType block = desc.mBlocksList[ 0 ];
574 SizeType blockX = block % atlasWidthInBlocks;
575 SizeType blockY = block / atlasWidthInBlocks;
576 SizeType blockOffsetX = blockX * atlasBlockWidth;
577 SizeType blockOffsetY = blockY * atlasBlockHeight;
579 if ( !mAtlasList[ atlas ].mAtlas.Upload( image, blockOffsetX, blockOffsetY ) )
581 DALI_LOG_ERROR("Uploading block to Atlas Failed!.\n");
585 void AtlasManager::GenerateMeshData( ImageId id,
586 const Vector2& position,
589 // Read the atlas Id to use for this image
590 SizeType imageId = id - 1u;
591 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
592 SizeType width = mImageList[ imageId ].mImageWidth;
593 SizeType height = mImageList[ imageId ].mImageHeight;
595 SizeType widthInBlocks = width / mAtlasList[ atlas ].mBlockWidth;
596 if ( width % mAtlasList[ atlas ].mBlockWidth )
600 SizeType heightInBlocks = height / mAtlasList[ atlas ].mBlockHeight;
601 if ( height % mAtlasList[ atlas ].mBlockHeight )
606 CreateMesh( atlas, width, height, position, widthInBlocks, heightInBlocks, meshData, mImageList[ imageId ] );
608 // Mesh created so increase the reference count
609 mImageList[ imageId ].mCount++;
612 Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const
615 if ( !atlas || atlas > mAtlasList.size( ) )
618 DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
621 return mAtlasList[ atlas -1u ].mAtlas;
624 bool AtlasManager::Remove( ImageId id )
626 // Decrements the reference count of this image, and removes the blocks if zero.
627 SizeType imageId = id - 1u;
628 bool removed = false;
630 if ( id > mImageList.size() )
632 DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
636 // If we attempt to free an image that is already freed then do nothing, other than log
637 if ( !mImageList[ imageId ].mCount )
639 DALI_LOG_ERROR("Atlas was asked to free an imageID: %i, that has already been freed!\n", id );
643 if ( 1u == --mImageList[ imageId ].mCount )
645 // 'Remove the blocks' from this image and add to the atlas' freelist
647 mImageList[ imageId ].mCount = 0;
648 SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
649 for ( uint32_t i = 0; i < mImageList[ imageId ].mBlocksList.Size(); ++i )
651 mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlocksList[ i ] );
657 AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
659 if ( id && id <= mImageList.size() )
661 return mImageList[ id - 1u ].mAtlasId;
669 void AtlasManager::SetAtlasSize( const Vector2& size,
670 const Vector2& blockSize )
672 mNewAtlasSize = size;
673 mNewBlockSize = blockSize;
676 Vector2 AtlasManager::GetBlockSize( AtlasId atlas )
678 if ( atlas && atlas <= mAtlasList.size() )
680 return Vector2( static_cast< float >( mAtlasList[ atlas - 1u ].mBlockWidth ),
681 static_cast< float >( mAtlasList[ atlas - 1u ].mBlockHeight) );
685 return Vector2( 0.0f, 0.0f );
689 AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
691 if ( atlas && atlas <= mAtlasList.size() )
693 uint32_t index = atlas - 1u;
694 uint32_t width = mAtlasList[ index ].mWidth;
695 uint32_t height = mAtlasList[ index ].mHeight;
696 uint32_t blockWidth = mAtlasList[ index ].mBlockWidth;
697 uint32_t blockHeight = mAtlasList[ index ].mBlockHeight;
699 //Work out how many blocks wide and high our bitmap is in the atlas' block size
700 SizeType widthInBlocks = width / blockWidth;
701 if ( width % blockWidth )
705 SizeType heightInBlocks = height / blockHeight;
706 if ( height % blockHeight )
711 uint32_t blockCount = widthInBlocks * heightInBlocks;
713 // Check free previously unallocated blocks and any free blocks
714 blockCount -= mAtlasList[ index ].mNextFreeBlock - mAtlasList[ index ].mFreeBlocksList.Size();
723 AtlasManager::SizeType AtlasManager::GetAtlasCount() const
725 return mAtlasList.size();
728 Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas )
730 if ( !atlas || atlas > mAtlasList.size( ) )
733 DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
736 return mAtlasList[ atlas -1u ].mPixelFormat;
740 } // namespace Internal
742 } // namespace Toolkit