X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fatlas-manager%2Fatlas-manager-impl.cpp;h=832e5d9010d0e740af07f0fa6f699a65224de8aa;hp=da6ae94a8131130f1dadd83ee0ddc61b67c554fb;hb=3e39b3bd20678fc2aba9618f782a830014f2062a;hpb=7132bfee764dc2c7c68feb8c9e9c4e81c9a7d883 diff --git a/dali-toolkit/internal/atlas-manager/atlas-manager-impl.cpp b/dali-toolkit/internal/atlas-manager/atlas-manager-impl.cpp index da6ae94..832e5d9 100644 --- a/dali-toolkit/internal/atlas-manager/atlas-manager-impl.cpp +++ b/dali-toolkit/internal/atlas-manager/atlas-manager-impl.cpp @@ -17,8 +17,11 @@ // CLASS HEADER #include -// EXTERNAL INCLUDES +// EXTERNAL INCLUDE #include +#include +#include +#include #include namespace Dali @@ -32,15 +35,64 @@ namespace Internal namespace { - const Vector2 DEFAULT_ATLAS_SIZE( 512.0f, 512.0f ); - const Vector2 DEFAULT_BLOCK_SIZE( 32.0f, 32.0f ); + const uint32_t DEFAULT_ATLAS_WIDTH( 512u ); + const uint32_t DEFAULT_ATLAS_HEIGHT( 512u ); + const uint32_t DEFAULT_BLOCK_WIDTH( 16u ); + const uint32_t DEFAULT_BLOCK_HEIGHT( 16u ); + const uint32_t SINGLE_PIXEL_PADDING( 1u ); + const uint32_t DOUBLE_PIXEL_PADDING( SINGLE_PIXEL_PADDING << 1 ); + const uint32_t FILLED_PIXEL( -1 ); + Toolkit::AtlasManager::AtlasSize EMPTY_SIZE; + + #define MAKE_SHADER(A)#A + + const char* VERTEX_SHADER = MAKE_SHADER( + attribute mediump vec2 aPosition; + attribute mediump vec2 aTexCoord; + uniform mediump mat4 uMvpMatrix; + varying mediump vec2 vTexCoord; + + void main() + { + mediump vec4 position = vec4( aPosition, 0.0, 1.0 ); + gl_Position = uMvpMatrix * position; + vTexCoord = aTexCoord; + } + ); + + const char* FRAGMENT_SHADER_L8 = MAKE_SHADER( + uniform lowp vec4 uColor; + uniform sampler2D sTexture; + varying mediump vec2 vTexCoord; + + void main() + { + mediump vec4 color = texture2D( sTexture, vTexCoord ); + gl_FragColor = vec4( uColor.rgb, uColor.a * color.r ); + } + ); + + const char* FRAGMENT_SHADER_RGBA = MAKE_SHADER( + uniform sampler2D sTexture; + varying mediump vec2 vTexCoord; + + void main() + { + gl_FragColor = texture2D( sTexture, vTexCoord ); + } + ); + } AtlasManager::AtlasManager() -: mNewAtlasSize( DEFAULT_ATLAS_SIZE ), - mNewBlockSize( DEFAULT_BLOCK_SIZE ), - mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES ) +: mAddFailPolicy( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES ) { + mNewAtlasSize.mWidth = DEFAULT_ATLAS_WIDTH; + mNewAtlasSize.mHeight = DEFAULT_ATLAS_HEIGHT; + mNewAtlasSize.mBlockWidth = DEFAULT_BLOCK_WIDTH; + mNewAtlasSize.mBlockHeight = DEFAULT_BLOCK_HEIGHT; + mShaderL8 = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_L8 ); + mShaderRgba = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_RGBA ); } AtlasManagerPtr AtlasManager::New() @@ -53,34 +105,66 @@ AtlasManager::~AtlasManager() { } -Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( SizeType width, - SizeType height, - SizeType blockWidth, - SizeType blockHeight, - Pixel::Format pixelformat ) +Toolkit::AtlasManager::AtlasId AtlasManager::CreateAtlas( const Toolkit::AtlasManager::AtlasSize& size, Pixel::Format pixelformat ) { + SizeType width = size.mWidth; + SizeType height = size.mHeight; + SizeType blockWidth = size.mBlockWidth; + SizeType blockHeight = size.mBlockHeight; + // Check to see if the atlas is large enough to hold a single block even ? - if ( blockWidth > width || blockHeight > height ) + if ( blockWidth + DOUBLE_PIXEL_PADDING + 1u > width || blockHeight + DOUBLE_PIXEL_PADDING + 1u > height ) { - DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %i x %i\n", - width, height, blockWidth, blockHeight ); + DALI_LOG_ERROR("Atlas %i x %i too small. Dimensions need to be at least %ix%i\n", + width, height, blockWidth + DOUBLE_PIXEL_PADDING + 1u, blockHeight + DOUBLE_PIXEL_PADDING + 1u ); return 0; } Dali::Atlas atlas = Dali::Atlas::New( width, height, pixelformat ); + atlas.Clear( Vector4::ZERO ); AtlasDescriptor atlasDescriptor; atlasDescriptor.mAtlas = atlas; - atlasDescriptor.mWidth = width; - atlasDescriptor.mHeight = height; - atlasDescriptor.mBlockWidth = blockWidth; - atlasDescriptor.mBlockHeight = blockHeight; + atlasDescriptor.mSize = size; atlasDescriptor.mPixelFormat = pixelformat; - std::stringstream materialLabel; - materialLabel << "Atlas Material - "; - materialLabel << mAtlasList.size(); - atlasDescriptor.mMaterial = Material::New( materialLabel.str() ); - atlasDescriptor.mMaterial.SetDiffuseTexture( atlas ); - atlasDescriptor.mNextFreeBlock = 1u; // indicate next free block will be the first ( +1 ) + atlasDescriptor.mTotalBlocks = ( ( width - 1u ) / blockWidth ) * ( ( height - 1u ) / blockHeight ); + atlasDescriptor.mAvailableBlocks = atlasDescriptor.mTotalBlocks; + + atlasDescriptor.mHorizontalStrip = BufferImage::New( blockWidth, SINGLE_PIXEL_PADDING, pixelformat ); + atlasDescriptor.mVerticalStrip = BufferImage::New( SINGLE_PIXEL_PADDING, blockHeight - DOUBLE_PIXEL_PADDING, pixelformat ); + + PixelBuffer* buffer = atlasDescriptor.mHorizontalStrip.GetBuffer(); + if( buffer == NULL ) + { + DALI_LOG_ERROR("atlasDescriptor.mHorizontalStrip.GetBuffer() returns NULL\n"); + return 0; + } + memset( buffer, 0, atlasDescriptor.mHorizontalStrip.GetBufferSize() ); + + buffer = atlasDescriptor.mVerticalStrip.GetBuffer(); + if( buffer == NULL ) + { + DALI_LOG_ERROR("atlasDescriptor.mVerticalStrip.GetBuffer() returns NULL\n"); + return 0; + } + memset( buffer, 0, atlasDescriptor.mVerticalStrip.GetBufferSize() ); + + BufferImage filledPixelImage = BufferImage::New( 1u, 1u, pixelformat ); + buffer = filledPixelImage.GetBuffer(); + if( buffer == NULL) + { + DALI_LOG_ERROR("filledPixelImage.GetBuffer() returns NULL\n"); + return 0; + } + + memset( buffer, 0xFF, filledPixelImage.GetBufferSize() ); + atlas.Upload( filledPixelImage, 0, 0 ); + + Sampler sampler = Sampler::New( atlas, "sTexture" ); + sampler.SetProperty( Sampler::Property::AFFECTS_TRANSPARENCY, true ); + atlasDescriptor.mMaterial = Material::New( pixelformat == Pixel::L8 ? mShaderL8 : mShaderRgba ); + atlasDescriptor.mMaterial.AddSampler( sampler ); + atlasDescriptor.mSampler = sampler; + atlasDescriptor.mMaterial.SetBlendMode( BlendingMode::ON ); mAtlasList.push_back( atlasDescriptor ); return mAtlasList.size(); } @@ -100,7 +184,6 @@ void AtlasManager::Add( const BufferImage& image, SizeType width = image.GetWidth(); SizeType height = image.GetHeight(); SizeType blockArea = 0; - SizeType totalBlocks = 0; SizeType foundAtlas = 0; SizeType index = 0; slot.mImageId = 0; @@ -110,14 +193,13 @@ void AtlasManager::Add( const BufferImage& image, // If there is a preferred atlas then check for room in that first if ( atlas-- ) { - foundAtlas = CheckAtlas( atlas, width, height, pixelFormat, blockArea, totalBlocks ); + foundAtlas = CheckAtlas( atlas, width, height, pixelFormat, blockArea ); } // Search current atlases to see if there is a good match - while( !foundAtlas && index < mAtlasList.size() ) { - foundAtlas = CheckAtlas( index, width, height, pixelFormat, blockArea, totalBlocks ); + foundAtlas = CheckAtlas( index, width, height, pixelFormat, blockArea ); ++index; } @@ -126,21 +208,25 @@ void AtlasManager::Add( const BufferImage& image, { if ( Toolkit::AtlasManager::FAIL_ON_ADD_CREATES == mAddFailPolicy ) { - SizeType newAtlas = CreateAtlas( mNewAtlasSize.x, mNewAtlasSize.y, mNewBlockSize.x, mNewBlockSize.y, pixelFormat ); - if ( !newAtlas-- ) + foundAtlas = CreateAtlas( mNewAtlasSize, pixelFormat ); + if ( !foundAtlas-- ) { + DALI_LOG_ERROR("Failed to create an atlas of %i x %i blocksize: %i x %i.\n", + mNewAtlasSize.mWidth, + mNewAtlasSize.mHeight, + mNewAtlasSize.mBlockWidth, + mNewAtlasSize.mBlockHeight ); return; } - else - { - foundAtlas = CheckAtlas( newAtlas, width, height, pixelFormat, blockArea, totalBlocks ); - } + + foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat, blockArea ); } - if ( Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy || !foundAtlas-- ) + if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy ) { // Haven't found an atlas for this image!!!!!! - return; + DALI_LOG_ERROR("Failed to create an atlas under current policy.\n"); + return; } } @@ -148,21 +234,10 @@ void AtlasManager::Add( const BufferImage& image, for ( SizeType i = 0; i < blockArea; ++i ) { // Is there currently a next free block available ? - if ( mAtlasList[ foundAtlas ].mNextFreeBlock ) + if ( mAtlasList[ foundAtlas ].mAvailableBlocks ) { - // Yes, so use this for our next block - SizeType selectedBlock = mAtlasList[ foundAtlas ].mNextFreeBlock - 1u; - desc.mBlocksList.PushBack( selectedBlock ); - - // Any blocks going to be available after this one (adjust to store +1 )? - selectedBlock++; - selectedBlock++; - if ( selectedBlock > totalBlocks ) - { - // No so start trying to use free blocks list - selectedBlock = 0; - } - mAtlasList[ foundAtlas ].mNextFreeBlock = selectedBlock; + // Yes, so select our next block + desc.mBlocksList.PushBack( mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks-- ); } else { @@ -178,8 +253,8 @@ void AtlasManager::Add( const BufferImage& image, desc.mCount = 1u; // See if there's a previously freed image ID that we can assign to this new image - uint32_t imageId = 0; - for ( uint32_t i = 0; i < mImageList.size(); ++i ) + uint32_t imageId = 0u; + for ( uint32_t i = 0u; i < mImageList.size(); ++i ) { if ( !mImageList[ i ].mCount ) { @@ -205,38 +280,20 @@ AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas, SizeType width, SizeType height, Pixel::Format pixelFormat, - SizeType& blockArea, - SizeType& totalBlocks ) + SizeType& blockArea ) { if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat ) { - // Work out how many blocks wide and high our bitmap is in the atlas' block size - SizeType widthInBlocks = width / mAtlasList[ atlas ].mBlockWidth; - if ( width % mAtlasList[ atlas ].mBlockWidth ) - { - widthInBlocks++; - } - SizeType heightInBlocks = height / mAtlasList[ atlas ].mBlockHeight; - if ( height % mAtlasList[ atlas ].mBlockHeight ) + // Check to see if the image will fit in these blocks, if not we'll need to create a new atlas + if ( ( mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size() ) + && width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockWidth + && height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockHeight ) { - heightInBlocks++; - } - blockArea = widthInBlocks * heightInBlocks; - - // Check to see if there are any unused blocks in this atlas to accomodate our image - SizeType blocksInX = mAtlasList[ atlas ].mWidth / mAtlasList[ atlas ].mBlockWidth; - SizeType blocksInY = mAtlasList[ atlas ].mHeight / mAtlasList[ atlas ].mBlockHeight; - totalBlocks = blocksInX * blocksInY; - SizeType blocksFree = mAtlasList[ atlas ].mNextFreeBlock ? totalBlocks - mAtlasList[ atlas ].mNextFreeBlock + 1u : 0; - - // Check to see if there are enough blocks to accomodate our sliced image ? - if ( blockArea <= ( mAtlasList[ atlas ].mFreeBlocksList.Size() + blocksFree ) ) - { - // Yes, we've found room + blockArea = 1u; return ( atlas + 1u ); } } - return 0; + return 0u; } void AtlasManager::CreateMesh( SizeType atlas, @@ -245,27 +302,22 @@ void AtlasManager::CreateMesh( SizeType atlas, const Vector2& position, SizeType widthInBlocks, SizeType heightInBlocks, - Dali::MeshData& meshData, + Toolkit::AtlasManager::Mesh2D& mesh, AtlasSlotDescriptor& desc ) { - Dali::MeshData::Vertex vertex; - Dali::MeshData::VertexContainer vertices; - Dali::MeshData::FaceIndices faces; - Dali::MeshData::FaceIndex faceIndex = 0; - meshData.SetHasNormals( false ); - meshData.SetHasColor( true ); - meshData.SetHasTextureCoords( true ); + Toolkit::AtlasManager::Vertex2D vertex; + uint32_t faceIndex = 0; // TODO change to unsigned short when property type is available - SizeType blockWidth = mAtlasList[ atlas ].mBlockWidth; - SizeType blockHeight = mAtlasList[ atlas ].mBlockHeight; + SizeType blockWidth = mAtlasList[ atlas ].mSize.mBlockWidth; + SizeType blockHeight = mAtlasList[ atlas ].mSize.mBlockHeight; float vertexBlockWidth = static_cast< float >( blockWidth ); float vertexBlockHeight = static_cast< float >( blockHeight ); - SizeType width = mAtlasList[ atlas ].mWidth; - SizeType height = mAtlasList[ atlas ].mHeight; + SizeType width = mAtlasList[ atlas ].mSize.mWidth; + SizeType height = mAtlasList[ atlas ].mSize.mHeight; - SizeType atlasWidthInBlocks = width / blockWidth; + SizeType atlasWidthInBlocks = ( width - 1u ) / blockWidth; // Get the normalized size of a texel in both directions // TODO when texture resizing and passing texture size via uniforms is available, @@ -274,6 +326,9 @@ void AtlasManager::CreateMesh( SizeType atlas, float texelX = 1.0f / static_cast< float >( width ); float texelY = 1.0f / static_cast< float >( height ); + float oneAndAHalfTexelX = texelX + ( texelX * 0.5f ); + float oneAndAHalfTexelY = texelY + ( texelY * 0.5f ); + // Get the normalized size of a block in texels float texelBlockWidth = texelX * vertexBlockWidth; float texelBlockHeight = texelY * vertexBlockHeight; @@ -283,8 +338,14 @@ void AtlasManager::CreateMesh( SizeType atlas, float vertexEdgeHeight = static_cast< float >( imageHeight % blockHeight ); // And in texels - float texelEdgeWidth = vertexEdgeWidth * texelX; - float texelEdgeHeight = vertexEdgeHeight * texelY; + float texelEdgeWidth = texelX * vertexEdgeWidth; + float texelEdgeHeight = texelY * vertexEdgeHeight; + + // We're going to 'blit' half a pixel more on each edge + vertexBlockWidth++; + vertexEdgeWidth++; + vertexBlockHeight++; + vertexEdgeHeight++; // Block by block create the two triangles for the quad SizeType blockIndex = 0; @@ -293,7 +354,8 @@ void AtlasManager::CreateMesh( SizeType atlas, float ndcVWidth; float ndcVHeight; - Vector2 topLeft = position; + // Move back half a pixel + Vector2 topLeft = Vector2( position.x - 0.5f, position.y - 0.5f ); for ( SizeType y = 0; y < heightInBlocks; ++y ) { @@ -302,12 +364,12 @@ void AtlasManager::CreateMesh( SizeType atlas, if ( ( heightInBlocks - 1u ) == y && vertexEdgeHeight > 0.0f ) { - ndcHeight = texelEdgeHeight; + ndcHeight = texelEdgeHeight + texelY; ndcVHeight = vertexEdgeHeight; } else { - ndcHeight = texelBlockHeight; + ndcHeight = texelBlockHeight + texelY; ndcVHeight = vertexBlockHeight; } @@ -318,61 +380,61 @@ void AtlasManager::CreateMesh( SizeType atlas, float fBlockX = texelBlockWidth * static_cast< float >( block % atlasWidthInBlocks ); float fBlockY = texelBlockHeight * static_cast< float >( block / atlasWidthInBlocks ); + // Add on texture filtering compensation ( half a texel plus compensation for filled pixel in top left corner ) + fBlockX += oneAndAHalfTexelX; + fBlockY += oneAndAHalfTexelY; + if ( ( widthInBlocks - 1u ) == x && vertexEdgeWidth > 0.0f ) { - ndcWidth = texelEdgeWidth; + ndcWidth = texelEdgeWidth + texelX; ndcVWidth = vertexEdgeWidth; } else { - ndcWidth = texelBlockWidth; + ndcWidth = texelBlockWidth + texelX; ndcVWidth = vertexBlockWidth; } // Top left - vertex.x = topLeft.x; - vertex.y = topLeft.y; - vertex.z = 0.0f; - vertex.u = fBlockX; - vertex.v = fBlockY; + vertex.mPosition.x = topLeft.x; + vertex.mPosition.y = topLeft.y; + vertex.mTexCoords.x = fBlockX; + vertex.mTexCoords.y = fBlockY; - vertices.push_back( vertex ); + mesh.mVertices.PushBack( vertex ); // Top Right - vertex.x = topLeft.x + ndcVWidth; - vertex.y = topLeft.y; - vertex.z = 0.0f; - vertex.u = fBlockX + ndcWidth; - vertex.v = fBlockY; + vertex.mPosition.x = topLeft.x + ndcVWidth; + vertex.mPosition.y = topLeft.y; + vertex.mTexCoords.x = fBlockX + ndcWidth; + vertex.mTexCoords.y = fBlockY; - vertices.push_back( vertex ); + mesh.mVertices.PushBack( vertex ); // Bottom Left - vertex.x = topLeft.x; - vertex.y = topLeft.y + ndcVHeight; - vertex.z = 0.0f; - vertex.u = fBlockX; - vertex.v = fBlockY + ndcHeight; + vertex.mPosition.x = topLeft.x; + vertex.mPosition.y = topLeft.y + ndcVHeight; + vertex.mTexCoords.x = fBlockX; + vertex.mTexCoords.y = fBlockY + ndcHeight; - vertices.push_back( vertex ); + mesh.mVertices.PushBack( vertex ); // Bottom Right topLeft.x += ndcVWidth; - vertex.x = topLeft.x; - vertex.y = topLeft.y + ndcVHeight; - vertex.z = 0.0f; - vertex.u = fBlockX + ndcWidth; - vertex.v = fBlockY + ndcHeight; + vertex.mPosition.x = topLeft.x; + vertex.mPosition.y = topLeft.y + ndcVHeight; + vertex.mTexCoords.x = fBlockX + ndcWidth; + vertex.mTexCoords.y = fBlockY + ndcHeight; - vertices.push_back( vertex ); + mesh.mVertices.PushBack( vertex ); // Six indices in counter clockwise winding - faces.push_back( faceIndex + 1u ); - faces.push_back( faceIndex ); - faces.push_back( faceIndex + 2u ); - faces.push_back( faceIndex + 2u ); - faces.push_back( faceIndex + 3u ); - faces.push_back( faceIndex + 1u ); + mesh.mIndices.PushBack( faceIndex + 1u ); + mesh.mIndices.PushBack( faceIndex ); + mesh.mIndices.PushBack( faceIndex + 2u ); + mesh.mIndices.PushBack( faceIndex + 2u ); + mesh.mIndices.PushBack( faceIndex + 3u ); + mesh.mIndices.PushBack( faceIndex + 1u ); faceIndex += 4; } @@ -384,63 +446,54 @@ void AtlasManager::CreateMesh( SizeType atlas, // If there's only one block then skip this next vertex optimisation if ( widthInBlocks * heightInBlocks > 1 ) { - Dali::MeshData::VertexContainer optimizedVertices; - OptimizeVertices( vertices, faces, optimizedVertices ); - meshData.SetVertices( optimizedVertices ); - } - else - { - meshData.SetVertices( vertices ); + Toolkit::AtlasManager::Mesh2D optimizedMesh; + OptimizeMesh( mesh, optimizedMesh ); } - - meshData.SetFaceIndices( faces ); - meshData.SetMaterial( mAtlasList[ atlas ].mMaterial ); - //PrintMeshData( meshData ); } -void AtlasManager::PrintMeshData( const MeshData& meshData ) +void AtlasManager::PrintMeshData( const Toolkit::AtlasManager::Mesh2D& mesh ) { - std::cout << "\nMesh Data for Image: VertexCount = " << meshData.GetVertexCount(); - std::cout << ", Triangles = " << meshData.GetFaceCount() << std::endl; - - Dali::MeshData::VertexContainer vertices = meshData.GetVertices(); - Dali::MeshData::FaceIndices faces = meshData.GetFaces(); + uint32_t vertexCount = mesh.mVertices.Size(); + uint32_t indexCount = mesh.mIndices.Size(); + std::cout << "\nMesh Data for Image: VertexCount = " << vertexCount; + std::cout << ", Triangles = " << indexCount / 3 << std::endl; - for ( SizeType v = 0; v < vertices.size(); ++v ) + for ( SizeType v = 0; v < vertexCount; ++v ) { - std::cout << " Vertex(" << v << ") x = " << vertices[v].x << ", "; - std::cout << "y = " << vertices[v].y << ", " << "z = " << vertices[v].z << ", "; - std::cout << "u = " << vertices[v].u << ", " << "v = " << vertices[v].v << std::endl; + std::cout << " Vertex(" << v << ") x = " << mesh.mVertices[v].mPosition.x << ", "; + std::cout << "y = " << mesh.mVertices[v].mPosition.y << ", "; + std::cout << "u = " << mesh.mVertices[v].mTexCoords.x << ", "; + std::cout << "v = " << mesh.mVertices[v].mTexCoords.y << std::endl; } std::cout << "\n Indices: "; - for ( SizeType i = 0; i < faces.size(); ++i ) + for ( SizeType i = 0; i < indexCount; ++i ) { - std::cout << " " << faces[ i ]; + std::cout << " " << mesh.mIndices[ i ]; } std::cout << std::endl; } -void AtlasManager::OptimizeVertices( const MeshData::VertexContainer& in, - MeshData::FaceIndices& faces, - MeshData::VertexContainer& out ) +void AtlasManager::OptimizeMesh( const Toolkit::AtlasManager::Mesh2D& in, + Toolkit::AtlasManager::Mesh2D& out ) { unsigned short vertexIndex = 0; // We could check to see if blocks are next to each other, but it's probably just as quick to compare verts - for ( SizeType i = 0; i < faces.size(); ++i ) + for ( SizeType i = 0; i < in.mIndices.Size(); ++i ) { // Fetch a vertex, has it already been assigned? bool foundVertex = false; - Dali::MeshData::Vertex v = in[ faces [ i ] ]; - for ( SizeType j = 0; j < vertexIndex; ++j ) + Toolkit::AtlasManager::Vertex2D v = in.mVertices[ in.mIndices[ i ] ]; + for ( SizeType j = 0; j < out.mVertices.Size(); ++j ) { - if ( v.x == out[ j ].x && v.y == out[ j ].y && v.z == out[ j ].z && - v.u == out[ j ].u && v.v == out[ j ].v && v.nX == out[ j ].nX && - v.nY == out[ j ].nY && v.nZ == out[ j ].nZ ) + if ( ( fabsf( v.mPosition.x - out.mVertices[ j ].mPosition.x ) < Math::MACHINE_EPSILON_1000 ) && + ( fabsf( v.mPosition.y - out.mVertices[ j ].mPosition.y ) < Math::MACHINE_EPSILON_1000 ) && + ( fabsf( v.mTexCoords.x - out.mVertices[ j ].mTexCoords.x ) < Math::MACHINE_EPSILON_1000 ) && + ( fabsf( v.mTexCoords.y - out.mVertices[ j ].mTexCoords.y ) < Math::MACHINE_EPSILON_1000 ) ) { // Yes, so store this down as the vertex to use - faces[ i ] = j; + out.mIndices.PushBack( j ); foundVertex = true; break; } @@ -449,164 +502,152 @@ void AtlasManager::OptimizeVertices( const MeshData::VertexContainer& in, // Did we find a vertex ? if ( !foundVertex ) { - // Add a new vertex - faces[ i ] = vertexIndex++; - out.push_back( v ); + // No so add a new one + out.mVertices.PushBack( v ); + vertexIndex++; } } } -void AtlasManager::StitchMesh( MeshData& first, - const MeshData& second, +void AtlasManager::StitchMesh( Toolkit::AtlasManager::Mesh2D& first, + const Toolkit::AtlasManager::Mesh2D& second, bool optimize ) { + const uint32_t verticesCount = first.mVertices.Size(); + first.mVertices.Insert( first.mVertices.End(), + second.mVertices.Begin(), + second.mVertices.End() ); - // Would be much quicker to be able to get a non-const reference to these containers and update in situ - MeshData::VertexContainer v1 = first.GetVertices(); - MeshData::VertexContainer v2 = second.GetVertices(); - MeshData::FaceIndices f1 = first.GetFaces(); - MeshData::FaceIndices f2 = second.GetFaces(); - - uint32_t vc1 = first.GetVertexCount(); - uint32_t vc2 = second.GetVertexCount(); - - for ( uint32_t v = 0; v < vc2; ++v ) - { - v1.push_back( v2[ v ] ); - } + const uint32_t indicesCount = first.mIndices.Size(); + first.mIndices.Insert( first.mIndices.End(), + second.mIndices.Begin(), + second.mIndices.End() ); - for ( uint32_t f = 0; f < f2.size(); ++f ) + for( Vector::Iterator it = first.mIndices.Begin() + indicesCount, + endIt = first.mIndices.End(); + it != endIt; + ++it ) { - f1.push_back( f2[ f ] + vc1 ); + *it += verticesCount; } if ( optimize ) { - MeshData::VertexContainer optimizedVertices; - OptimizeVertices( v1, f1, optimizedVertices ); - first.SetVertices( optimizedVertices ); - } - else - { - first.SetVertices( v1 ); + Toolkit::AtlasManager::Mesh2D optimizedMesh; + OptimizeMesh( first, optimizedMesh ); + first = optimizedMesh; } - - first.SetFaceIndices( f1 ); - - // TODO rather than set the material to the second, check to see if there's a match and return if not - first.SetMaterial( second.GetMaterial() ); } -void AtlasManager::StitchMesh( const MeshData& first, - const MeshData& second, - MeshData& out, - bool optimize ) +void AtlasManager::UploadImage( const BufferImage& image, + const AtlasSlotDescriptor& desc ) { - // TODO Would be much quicker to be able to get a non-const reference to these containers and update in situ - MeshData::VertexContainer v1 = first.GetVertices(); - MeshData::VertexContainer v2 = second.GetVertices(); - MeshData::FaceIndices f1 = first.GetFaces(); - MeshData::FaceIndices f2 = second.GetFaces(); + // Get the atlas to upload the image to + SizeType atlas = desc.mAtlasId - 1u; - uint32_t vc1 = first.GetVertexCount(); - uint32_t vc2 = second.GetVertexCount(); + // Check to see that the pixel formats are compatible + if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat ) + { + DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n"); + return; + } - MeshData::VertexContainer vertices; + SizeType atlasBlockWidth = mAtlasList[ atlas ].mSize.mBlockWidth; + SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight; + SizeType atlasWidthInBlocks = ( mAtlasList[ atlas ].mSize.mWidth - 1u ) / mAtlasList[ atlas ].mSize.mBlockWidth; - MeshData::FaceIndices faces; + SizeType block = desc.mBlocksList[ 0 ]; + SizeType blockX = block % atlasWidthInBlocks; + SizeType blockY = block / atlasWidthInBlocks; + SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u; + SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u; - MeshData::Vertex vertex; + SizeType width = image.GetWidth(); + SizeType height = image.GetHeight(); - for ( uint32_t v = 0; v < vc1; ++v ) + // Blit image 1 pixel to the right and down into the block to compensate for texture filtering + if ( !mAtlasList[ atlas ].mAtlas.Upload( image, + blockOffsetX + SINGLE_PIXEL_PADDING, + blockOffsetY + SINGLE_PIXEL_PADDING ) ) { - vertices.push_back( v1[ v ] ); + DALI_LOG_ERROR("Uploading image to Atlas Failed!.\n"); } - for ( uint32_t v = 0; v < vc2; ++v ) + // Blit top strip + if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, + blockOffsetX, + blockOffsetY ) ) { - vertices.push_back( v2[ v ] ); + DALI_LOG_ERROR("Uploading top strip to Atlas Failed!\n"); } - for ( uint32_t f = 0; f < f1.size(); ++f ) + // Blit left strip + if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, + blockOffsetX, + blockOffsetY + SINGLE_PIXEL_PADDING ) ) { - faces.push_back( f1[ f ] ); + DALI_LOG_ERROR("Uploading left strip to Atlas Failed!\n"); } - for ( uint32_t f = 0; f < f2.size(); ++f ) + // Blit bottom strip + if ( blockOffsetY + height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mHeight ) { - faces.push_back( f2[ f ] + vc1 ); + if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mHorizontalStrip, + blockOffsetX, + blockOffsetY + height + SINGLE_PIXEL_PADDING ) ) + { + DALI_LOG_ERROR("Uploading bottom strip to Atlas Failed!.\n"); + } } - if ( optimize ) - { - MeshData::VertexContainer optimizedVertices; - OptimizeVertices( vertices, faces, optimizedVertices ); - out.SetVertices( optimizedVertices ); - } - else + // Blit right strip + if ( blockOffsetX + width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mWidth ) { - out.SetVertices( vertices ); + if ( !mAtlasList[ atlas ].mAtlas.Upload( mAtlasList[ atlas ].mVerticalStrip, + blockOffsetX + width + SINGLE_PIXEL_PADDING, + blockOffsetY + SINGLE_PIXEL_PADDING ) ) + { + DALI_LOG_ERROR("Uploading right strip to Atlas Failed!.\n"); + } } - - // TODO rather than set the material to the second, check to see if there's a match and return if not - out.SetMaterial( second.GetMaterial() ); - out.SetFaceIndices( faces ); } -void AtlasManager::UploadImage( const BufferImage& image, - const AtlasSlotDescriptor& desc ) +void AtlasManager::GenerateMeshData( ImageId id, + const Vector2& position, + Toolkit::AtlasManager::Mesh2D& meshData, + bool addReference ) { - // Get the atlas to upload the image to - SizeType atlas = desc.mAtlasId - 1u; - - // Check to see that the pixel formats are compatible - if ( image.GetPixelFormat() != mAtlasList[ atlas ].mPixelFormat ) + if ( id ) { - DALI_LOG_ERROR("Cannot upload an image with a different PixelFormat to the Atlas.\n"); - return; - } - - SizeType atlasBlockWidth = mAtlasList[ atlas ].mBlockWidth; - SizeType atlasBlockHeight = mAtlasList[ atlas ].mBlockHeight; - SizeType atlasWidthInBlocks = mAtlasList[ atlas ].mWidth / mAtlasList[ atlas ].mBlockWidth; - - SizeType block = desc.mBlocksList[ 0 ]; - SizeType blockX = block % atlasWidthInBlocks; - SizeType blockY = block / atlasWidthInBlocks; - SizeType blockOffsetX = blockX * atlasBlockWidth; - SizeType blockOffsetY = blockY * atlasBlockHeight; + // Read the atlas Id to use for this image + SizeType imageId = id - 1u; + SizeType atlas = mImageList[ imageId ].mAtlasId - 1u; + SizeType width = mImageList[ imageId ].mImageWidth; + SizeType height = mImageList[ imageId ].mImageHeight; - if ( !mAtlasList[ atlas ].mAtlas.Upload( image, blockOffsetX, blockOffsetY ) ) - { - DALI_LOG_ERROR("Uploading block to Atlas Failed!.\n"); - } -} + SizeType widthInBlocks = width / mAtlasList[ atlas ].mSize.mBlockWidth; + if ( width % mAtlasList[ atlas ].mSize.mBlockWidth ) + { + widthInBlocks++; + } + SizeType heightInBlocks = height / mAtlasList[ atlas ].mSize.mBlockHeight; + if ( height % mAtlasList[ atlas ].mSize.mBlockHeight ) + { + heightInBlocks++; + } -void AtlasManager::GenerateMeshData( ImageId id, - const Vector2& position, - MeshData& meshData ) -{ - // Read the atlas Id to use for this image - SizeType imageId = id - 1u; - SizeType atlas = mImageList[ imageId ].mAtlasId - 1u; - SizeType width = mImageList[ imageId ].mImageWidth; - SizeType height = mImageList[ imageId ].mImageHeight; + CreateMesh( atlas, width, height, position, widthInBlocks, heightInBlocks, meshData, mImageList[ imageId ] ); - SizeType widthInBlocks = width / mAtlasList[ atlas ].mBlockWidth; - if ( width % mAtlasList[ atlas ].mBlockWidth ) - { - widthInBlocks++; + // Mesh created so increase the reference count, if we're asked to + if ( addReference ) + { + mImageList[ imageId ].mCount++; + } } - SizeType heightInBlocks = height / mAtlasList[ atlas ].mBlockHeight; - if ( height % mAtlasList[ atlas ].mBlockHeight ) + else { - heightInBlocks++; + DALI_LOG_ERROR("Cannot generate mesh with invalid AtlasId\n"); } - - CreateMesh( atlas, width, height, position, widthInBlocks, heightInBlocks, meshData, mImageList[ imageId ] ); - - // Mesh created so increase the reference count - mImageList[ imageId ].mCount++; } Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const @@ -640,7 +681,7 @@ bool AtlasManager::Remove( ImageId id ) return false; } - if ( 1u == --mImageList[ imageId ].mCount ) + if ( 2u > --mImageList[ imageId ].mCount ) { // 'Remove the blocks' from this image and add to the atlas' freelist removed = true; @@ -666,53 +707,29 @@ AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const } } -void AtlasManager::SetAtlasSize( const Vector2& size, - const Vector2& blockSize ) +void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size ) { mNewAtlasSize = size; - mNewBlockSize = blockSize; + + // Add on padding for borders around atlas entries + mNewAtlasSize.mBlockWidth += DOUBLE_PIXEL_PADDING; + mNewAtlasSize.mBlockHeight += DOUBLE_PIXEL_PADDING; } -Vector2 AtlasManager::GetBlockSize( AtlasId atlas ) +const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas ) { - if ( atlas && atlas <= mAtlasList.size() ) + if ( atlas && atlas-- <= mAtlasList.size() ) { - return Vector2( static_cast< float >( mAtlasList[ atlas - 1u ].mBlockWidth ), - static_cast< float >( mAtlasList[ atlas - 1u ].mBlockHeight) ); - } - else - { - return Vector2( 0.0f, 0.0f ); + return mAtlasList[ atlas ].mSize; } + return EMPTY_SIZE; } AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const { - if ( atlas && atlas <= mAtlasList.size() ) + if ( atlas && atlas-- <= mAtlasList.size() ) { - uint32_t index = atlas - 1u; - uint32_t width = mAtlasList[ index ].mWidth; - uint32_t height = mAtlasList[ index ].mHeight; - uint32_t blockWidth = mAtlasList[ index ].mBlockWidth; - uint32_t blockHeight = mAtlasList[ index ].mBlockHeight; - - //Work out how many blocks wide and high our bitmap is in the atlas' block size - SizeType widthInBlocks = width / blockWidth; - if ( width % blockWidth ) - { - widthInBlocks++; - } - SizeType heightInBlocks = height / blockHeight; - if ( height % blockHeight ) - { - heightInBlocks++; - } - - uint32_t blockCount = widthInBlocks * heightInBlocks; - - // Check free previously unallocated blocks and any free blocks - blockCount -= mAtlasList[ index ].mNextFreeBlock - mAtlasList[ index ].mFreeBlocksList.Size(); - return blockCount; + return ( mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size() ); } else { @@ -733,9 +750,57 @@ Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas ) DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n"); return Pixel::L8; } - return mAtlasList[ atlas -1u ].mPixelFormat; + return mAtlasList[ --atlas].mPixelFormat; } +void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics ) +{ + Toolkit::AtlasManager::AtlasMetricsEntry entry; + uint32_t textureMemoryUsed = 0; + uint32_t atlasCount = mAtlasList.size(); + metrics.mAtlasCount = atlasCount; + metrics.mAtlasMetrics.Resize(0); + + for ( uint32_t i = 0; i < atlasCount; ++i ) + { + entry.mSize = mAtlasList[ i ].mSize; + entry.mTotalBlocks = mAtlasList[ i ].mTotalBlocks; + entry.mBlocksUsed = entry.mTotalBlocks - mAtlasList[ i ].mAvailableBlocks + mAtlasList[ i ].mFreeBlocksList.Size(); + entry.mPixelFormat = GetPixelFormat( i + 1 ); + + metrics.mAtlasMetrics.PushBack( entry ); + + uint32_t size = entry.mSize.mWidth * entry.mSize.mHeight; + if ( entry.mPixelFormat == Pixel::BGRA8888 ) + { + size <<= 2; + } + + textureMemoryUsed += size; + + } + metrics.mTextureMemoryUsed = textureMemoryUsed; +} + +Material AtlasManager::GetMaterial( AtlasId atlas ) const +{ + if ( atlas && atlas-- <= mAtlasList.size() ) + { + return mAtlasList[ atlas ].mMaterial; + } + Material null; + return null; +} + +Sampler AtlasManager::GetSampler( AtlasId atlas ) const +{ + if ( atlas && atlas-- <= mAtlasList.size() ) + { + return mAtlasList[ atlas ].mSampler; + } + Sampler null; + return null; +} } // namespace Internal