*/
// CLASS HEADER
-#include <dali-toolkit/internal/atlas-manager/atlas-manager-impl.h>
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-manager-impl.h>
-// EXTERNAL INCLUDE
-#include <iostream>
+// EXTERNAL INCLUDES
#include <string.h>
-#include <dali/devel-api/rendering/sampler.h>
-#include <dali/devel-api/rendering/shader.h>
#include <dali/integration-api/debug.h>
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/rendering/atlas/atlas-mesh-factory.h>
+
namespace Dali
{
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.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()
memset( buffer, 0xFF, filledPixelImage.GetBufferSize() );
atlas.Upload( filledPixelImage, 0, 0 );
-
- atlasDescriptor.mMaterial = Material::New( pixelformat == Pixel::L8 ? mShaderL8 : mShaderRgba );
- atlasDescriptor.mMaterial.AddTexture(atlas, "sTexture" );
- atlasDescriptor.mImage = atlas;
- atlasDescriptor.mMaterial.SetBlendMode( BlendingMode::ON );
mAtlasList.push_back( atlasDescriptor );
return mAtlasList.size();
}
mAddFailPolicy = policy;
}
-void AtlasManager::Add( const BufferImage& image,
+bool AtlasManager::Add( const BufferImage& image,
Toolkit::AtlasManager::AtlasSlot& slot,
Toolkit::AtlasManager::AtlasId atlas )
{
- // See if there's a slot in an atlas that matches the requirements of this image
- // A bitmap must be sliceable into a single atlas
+ bool created = false;
Pixel::Format pixelFormat = image.GetPixelFormat();
SizeType width = image.GetWidth();
SizeType height = image.GetHeight();
- SizeType blockArea = 0;
SizeType foundAtlas = 0;
SizeType index = 0;
slot.mImageId = 0;
// If there is a preferred atlas then check for room in that first
if ( atlas-- )
{
- foundAtlas = CheckAtlas( atlas, width, height, pixelFormat, blockArea );
+ foundAtlas = CheckAtlas( atlas, width, height, pixelFormat );
}
// Search current atlases to see if there is a good match
while( !foundAtlas && index < mAtlasList.size() )
{
- foundAtlas = CheckAtlas( index, width, height, pixelFormat, blockArea );
+ foundAtlas = CheckAtlas( index, width, height, pixelFormat );
++index;
}
mNewAtlasSize.mHeight,
mNewAtlasSize.mBlockWidth,
mNewAtlasSize.mBlockHeight );
- return;
+ return created;
}
-
- foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat, blockArea );
+ created = true;
+ foundAtlas = CheckAtlas( foundAtlas, width, height, pixelFormat );
}
if ( !foundAtlas-- || Toolkit::AtlasManager::FAIL_ON_ADD_FAILS == mAddFailPolicy )
{
// Haven't found an atlas for this image!!!!!!
DALI_LOG_ERROR("Failed to create an atlas under current policy.\n");
- return;
+ return created;
}
}
- // Work out where the blocks are we're going to use
- for ( SizeType i = 0; i < blockArea; ++i )
+ // Work out which the block we're going to use
+ // Is there currently a next free block available ?
+ if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
{
- // Is there currently a next free block available ?
- if ( mAtlasList[ foundAtlas ].mAvailableBlocks )
- {
- // Yes, so select our next block
- desc.mBlocksList.PushBack( mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks-- );
- }
- else
- {
- // Our next block must be from the free list, fetch from the start of the list
- desc.mBlocksList.PushBack( mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ] );
- mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
- }
+ // Yes, so select our next block
+ desc.mBlock = mAtlasList[ foundAtlas ].mTotalBlocks - mAtlasList[ foundAtlas ].mAvailableBlocks--;
+ }
+ else
+ {
+ // Our next block must be from the free list, fetch from the start of the list
+ desc.mBlock = mAtlasList[ foundAtlas ].mFreeBlocksList[ 0 ];
+ mAtlasList[ foundAtlas ].mFreeBlocksList.Remove( mAtlasList[ foundAtlas ].mFreeBlocksList.Begin() );
}
desc.mImageWidth = width;
// See if there's a previously freed image ID that we can assign to this new image
uint32_t imageId = 0u;
- for ( uint32_t i = 0u; i < mImageList.size(); ++i )
+ for ( uint32_t i = 0u; i < mImageList.Size(); ++i )
{
if ( !mImageList[ i ].mCount )
{
}
if ( !imageId )
{
- mImageList.push_back( desc );
- slot.mImageId = mImageList.size();
+ mImageList.PushBack( desc );
+ slot.mImageId = mImageList.Size();
}
else
{
slot.mImageId = imageId;
}
slot.mAtlasId = foundAtlas + 1u;
+
+ // Upload the buffer image into the atlas
UploadImage( image, desc );
+ return created;
}
AtlasManager::SizeType AtlasManager::CheckAtlas( SizeType atlas,
SizeType width,
SizeType height,
- Pixel::Format pixelFormat,
- SizeType& blockArea )
+ Pixel::Format pixelFormat )
{
+ AtlasManager::SizeType result = 0u;
if ( pixelFormat == mAtlasList[ atlas ].mPixelFormat )
{
// Check to see if the image will fit in these blocks, if not we'll need to create a new atlas
&& width + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockWidth
&& height + DOUBLE_PIXEL_PADDING <= mAtlasList[ atlas ].mSize.mBlockHeight )
{
- blockArea = 1u;
- return ( atlas + 1u );
+ result = atlas + 1u;
}
}
- return 0u;
-}
-
-void AtlasManager::CreateMesh( SizeType atlas,
- SizeType imageWidth,
- SizeType imageHeight,
- const Vector2& position,
- SizeType widthInBlocks,
- SizeType heightInBlocks,
- Toolkit::AtlasManager::Mesh2D& mesh,
- AtlasSlotDescriptor& desc )
-{
- Toolkit::AtlasManager::Vertex2D vertex;
- uint32_t faceIndex = 0; // TODO change to unsigned short when property type is available
-
- 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 ].mSize.mWidth;
- SizeType height = mAtlasList[ atlas ].mSize.mHeight;
-
- 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,
- // we will encode pixel positions into the vertex data rather than normalized
- // meaning that geometry needn't be changed on an atlas resize
- 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;
-
- // Get partial block space
- float vertexEdgeWidth = static_cast< float >( imageWidth % blockWidth );
- float vertexEdgeHeight = static_cast< float >( imageHeight % blockHeight );
-
- // And in texels
- 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;
- float ndcWidth;
- float ndcHeight;
- float ndcVWidth;
- float ndcVHeight;
-
- // Move back half a pixel
- Vector2 topLeft = Vector2( position.x - 0.5f, position.y - 0.5f );
-
- for ( SizeType y = 0; y < heightInBlocks; ++y )
- {
-
- float currentX = position.x;
-
- if ( ( heightInBlocks - 1u ) == y && vertexEdgeHeight > 0.0f )
- {
- ndcHeight = texelEdgeHeight + texelY;
- ndcVHeight = vertexEdgeHeight;
- }
- else
- {
- ndcHeight = texelBlockHeight + texelY;
- ndcVHeight = vertexBlockHeight;
- }
-
- for ( SizeType x = 0; x < widthInBlocks; ++x )
- {
- SizeType block = desc.mBlocksList[ blockIndex++ ];
-
- 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 + texelX;
- ndcVWidth = vertexEdgeWidth;
- }
- else
- {
- ndcWidth = texelBlockWidth + texelX;
- ndcVWidth = vertexBlockWidth;
- }
-
- // Top left
- vertex.mPosition.x = topLeft.x;
- vertex.mPosition.y = topLeft.y;
- vertex.mTexCoords.x = fBlockX;
- vertex.mTexCoords.y = fBlockY;
-
- mesh.mVertices.PushBack( vertex );
-
- // Top Right
- vertex.mPosition.x = topLeft.x + ndcVWidth;
- vertex.mPosition.y = topLeft.y;
- vertex.mTexCoords.x = fBlockX + ndcWidth;
- vertex.mTexCoords.y = fBlockY;
-
- mesh.mVertices.PushBack( vertex );
-
- // Bottom Left
- vertex.mPosition.x = topLeft.x;
- vertex.mPosition.y = topLeft.y + ndcVHeight;
- vertex.mTexCoords.x = fBlockX;
- vertex.mTexCoords.y = fBlockY + ndcHeight;
-
- mesh.mVertices.PushBack( vertex );
-
- // Bottom Right
- topLeft.x += ndcVWidth;
- vertex.mPosition.x = topLeft.x;
- vertex.mPosition.y = topLeft.y + ndcVHeight;
- vertex.mTexCoords.x = fBlockX + ndcWidth;
- vertex.mTexCoords.y = fBlockY + ndcHeight;
-
- mesh.mVertices.PushBack( vertex );
-
- // Six indices in counter clockwise winding
- 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;
- }
-
- // Move down a row
- topLeft.x = currentX;
- topLeft.y += vertexBlockHeight;
- }
-
- // If there's only one block then skip this next vertex optimisation
- if ( widthInBlocks * heightInBlocks > 1 )
- {
- Toolkit::AtlasManager::Mesh2D optimizedMesh;
- OptimizeMesh( mesh, optimizedMesh );
- }
-}
-
-void AtlasManager::PrintMeshData( const Toolkit::AtlasManager::Mesh2D& mesh )
-{
- 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 < vertexCount; ++v )
- {
- 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 < indexCount; ++i )
- {
- std::cout << " " << mesh.mIndices[ i ];
- }
- std::cout << std::endl;
-}
-
-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 < in.mIndices.Size(); ++i )
- {
- // Fetch a vertex, has it already been assigned?
- bool foundVertex = false;
- Toolkit::AtlasManager::Vertex2D v = in.mVertices[ in.mIndices[ i ] ];
- for ( SizeType j = 0; j < out.mVertices.Size(); ++j )
- {
- 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
- out.mIndices.PushBack( j );
- foundVertex = true;
- break;
- }
- }
-
- // Did we find a vertex ?
- if ( !foundVertex )
- {
- // No so add a new one
- out.mVertices.PushBack( v );
- vertexIndex++;
- }
- }
-}
-
-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() );
-
- const uint32_t indicesCount = first.mIndices.Size();
- first.mIndices.Insert( first.mIndices.End(),
- second.mIndices.Begin(),
- second.mIndices.End() );
-
- for( Vector<unsigned int>::Iterator it = first.mIndices.Begin() + indicesCount,
- endIt = first.mIndices.End();
- it != endIt;
- ++it )
- {
- *it += verticesCount;
- }
-
- if ( optimize )
- {
- Toolkit::AtlasManager::Mesh2D optimizedMesh;
- OptimizeMesh( first, optimizedMesh );
- first = optimizedMesh;
- }
+ return result;
}
void AtlasManager::UploadImage( const BufferImage& image,
SizeType atlasBlockHeight = mAtlasList[ atlas ].mSize.mBlockHeight;
SizeType atlasWidthInBlocks = ( mAtlasList[ atlas ].mSize.mWidth - 1u ) / mAtlasList[ atlas ].mSize.mBlockWidth;
- SizeType block = desc.mBlocksList[ 0 ];
- SizeType blockX = block % atlasWidthInBlocks;
- SizeType blockY = block / atlasWidthInBlocks;
+ SizeType blockX = desc.mBlock % atlasWidthInBlocks;
+ SizeType blockY = desc.mBlock / atlasWidthInBlocks;
SizeType blockOffsetX = ( blockX * atlasBlockWidth ) + 1u;
SizeType blockOffsetY = ( blockY * atlasBlockHeight) + 1u;
SizeType width = mImageList[ imageId ].mImageWidth;
SizeType height = mImageList[ imageId ].mImageHeight;
- 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++;
- }
-
- CreateMesh( atlas, width, height, position, widthInBlocks, heightInBlocks, meshData, mImageList[ imageId ] );
+ AtlasMeshFactory::CreateQuad( width,
+ height,
+ mImageList[ imageId ].mBlock,
+ mAtlasList[ atlas ].mSize,
+ position,
+ meshData );
// Mesh created so increase the reference count, if we're asked to
if ( addReference )
Dali::Atlas AtlasManager::GetAtlasContainer( AtlasId atlas ) const
{
- Dali::Atlas null;
- if ( !atlas || atlas > mAtlasList.size( ) )
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
+ Dali::Atlas atlasContainer;
+ if ( atlas && atlas-- <= mAtlasList.size() )
{
-
- DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
- return null;
+ atlasContainer = mAtlasList[ atlas ].mAtlas;
}
- return mAtlasList[ atlas -1u ].mAtlas;
+ return atlasContainer;
}
bool AtlasManager::Remove( ImageId id )
SizeType imageId = id - 1u;
bool removed = false;
- if ( id > mImageList.size() )
+ if ( id > mImageList.Size() )
{
DALI_LOG_ERROR("Atlas was asked to free an invalid imageID: %i\n", id );
return false;
removed = true;
mImageList[ imageId ].mCount = 0;
SizeType atlas = mImageList[ imageId ].mAtlasId - 1u;
- for ( uint32_t i = 0; i < mImageList[ imageId ].mBlocksList.Size(); ++i )
- {
- mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlocksList[ i ] );
- }
+ mAtlasList[ atlas ].mFreeBlocksList.PushBack( mImageList[ imageId ].mBlock );
}
return removed;
}
AtlasManager::AtlasId AtlasManager::GetAtlas( ImageId id ) const
{
- if ( id && id <= mImageList.size() )
- {
- return mImageList[ id - 1u ].mAtlasId;
- }
- else
+ DALI_ASSERT_DEBUG( id && id <= mImageList.Size() );
+ AtlasManager::AtlasId atlasId = 0u;
+ if ( id && id-- <= mImageList.Size() )
{
- return 0;
+ atlasId = mImageList[ id ].mAtlasId;
}
+ return atlasId;
}
void AtlasManager::SetNewAtlasSize( const Toolkit::AtlasManager::AtlasSize& size )
const Toolkit::AtlasManager::AtlasSize& AtlasManager::GetAtlasSize( AtlasId atlas )
{
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
if ( atlas && atlas-- <= mAtlasList.size() )
{
return mAtlasList[ atlas ].mSize;
AtlasManager::SizeType AtlasManager::GetFreeBlocks( AtlasId atlas ) const
{
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
+ AtlasManager::SizeType freeBlocks = 0u;
if ( atlas && atlas-- <= mAtlasList.size() )
{
- return ( mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size() );
- }
- else
- {
- return 0;
+ freeBlocks = mAtlasList[ atlas ].mAvailableBlocks + mAtlasList[ atlas ].mFreeBlocksList.Size();
}
+ return freeBlocks;
}
AtlasManager::SizeType AtlasManager::GetAtlasCount() const
Pixel::Format AtlasManager::GetPixelFormat( AtlasId atlas )
{
- if ( !atlas || atlas > mAtlasList.size( ) )
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
+ Pixel::Format pixelFormat = Pixel::RGBA8888;
+ if ( atlas && atlas-- <= mAtlasList.size() )
{
-
- DALI_LOG_ERROR("Cannot get Atlas from AtlasID ( doesn't exist ).\n");
- return Pixel::L8;
+ pixelFormat = mAtlasList[ atlas ].mPixelFormat;
}
- return mAtlasList[ --atlas].mPixelFormat;
+ return pixelFormat;
}
void AtlasManager::GetMetrics( Toolkit::AtlasManager::Metrics& metrics )
Material AtlasManager::GetMaterial( AtlasId atlas ) const
{
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
+ Material material;
if ( atlas && atlas-- <= mAtlasList.size() )
{
- return mAtlasList[ atlas ].mMaterial;
+ material = mAtlasList[ atlas ].mMaterial;
}
- Material null;
- return null;
+ return material;
}
-Image AtlasManager::GetImage( AtlasId atlas ) const
+void AtlasManager::SetMaterial( AtlasId atlas, Material& material )
{
+ DALI_ASSERT_DEBUG( atlas && atlas <= mAtlasList.size() );
if ( atlas && atlas-- <= mAtlasList.size() )
{
- return mAtlasList[ atlas ].mImage;
+ mAtlasList[ atlas ].mMaterial = material;
}
- Image null;
- return null;
}
} // namespace Internal