const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS)
+PixelData CreatePixelData( unsigned int width, unsigned int height )
+{
+ unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::RGBA8888 );
+
+ unsigned char* buffer= reinterpret_cast<unsigned char*>( malloc( bufferSize ) );
+ PixelData pixelData = PixelData::New( buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE );
+
+ return pixelData;
+}
+
Rect<int> TextureCoordinateToPixelArea( const Vector4& textureCoordinate, float size )
{
Vector4 temp = textureCoordinate * size;
Rect<int> pixelArea;
pixelArea.x = static_cast<int>( temp.x );
pixelArea.y = static_cast<int>( temp.y );
- pixelArea.width = static_cast<int>( temp.z-temp.x+1.f );
- pixelArea.height = static_cast<int>( temp.w-temp.y+1.f );
+ pixelArea.width = static_cast<int>( temp.z-temp.x+1.01f );
+ pixelArea.height = static_cast<int>( temp.w-temp.y+1.01f );
+
+ return pixelArea;
+}
+
+Rect<int> TextureCoordinateToPixelArea( const Vector4& textureCoordinate, float width, float height )
+{
+ Rect<int> pixelArea;
+ pixelArea.x = static_cast<int>( textureCoordinate.x*width );
+ pixelArea.y = static_cast<int>( textureCoordinate.y*height);
+ pixelArea.width = static_cast<int>( (textureCoordinate.z-textureCoordinate.x)*width+1.01f );
+ pixelArea.height = static_cast<int>( (textureCoordinate.w-textureCoordinate.y)*height+1.01f );
return pixelArea;
}
END_TEST;
}
+
+int UtcDaliImageAtlasPackToAtlas(void)
+{
+ ToolkitTestApplication application;
+
+ std::vector<PixelData> pixelDataContainer;
+ pixelDataContainer.push_back( CreatePixelData( 20, 30 ) );
+ pixelDataContainer.push_back( CreatePixelData( 10, 10 ) );
+ pixelDataContainer.push_back( CreatePixelData( 45, 30 ) );
+ pixelDataContainer.push_back( CreatePixelData( 20, 20 ) );
+
+ Dali::Vector<Vector4> textureRects;
+ Texture texture = ImageAtlas::PackToAtlas( pixelDataContainer, textureRects );
+
+ // --------------
+ // | |
+ // | 45*30 |
+// | |
+// --------------
+// | 20 | | 20*20
+// | * |____|
+// | 30 | | 10*10
+// --------
+
+ DALI_TEST_EQUALS( texture.GetWidth(), 45, TEST_LOCATION );
+ DALI_TEST_EQUALS( texture.GetHeight(), 60, TEST_LOCATION );
+
+ Rect<int> pixelArea = TextureCoordinateToPixelArea(textureRects[0], texture.GetWidth(), texture.GetHeight());
+ DALI_TEST_EQUALS( pixelArea.x, 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.y, 30, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.width, 20, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.height, 30, TEST_LOCATION );
+
+ pixelArea = TextureCoordinateToPixelArea(textureRects[1], texture.GetWidth(), texture.GetHeight());
+ DALI_TEST_EQUALS( pixelArea.x, 20, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.y, 50, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.width, 10, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.height, 10, TEST_LOCATION );
+
+ pixelArea = TextureCoordinateToPixelArea(textureRects[2], texture.GetWidth(), texture.GetHeight());
+ DALI_TEST_EQUALS( pixelArea.x, 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.y, 0, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.width, 45, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.height, 30, TEST_LOCATION );
+
+ pixelArea = TextureCoordinateToPixelArea(textureRects[3], texture.GetWidth(), texture.GetHeight());
+ DALI_TEST_EQUALS( pixelArea.x, 20, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.y, 30, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.width, 20, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.height, 20, TEST_LOCATION );
+
+ END_TEST;
+}
{
}
+Texture ImageAtlas::PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects )
+{
+ return Internal::ImageAtlas::PackToAtlas( pixelData, textureRects );
+}
+
ImageAtlas::ImageAtlas(Internal::ImageAtlas* internal)
: BaseHandle( internal )
{
// EXTERNAL INCLUDES
#include <string>
#include <stdint.h>
+#include <dali/public-api/common/vector-wrapper.h>
#include <dali/public-api/object/base-handle.h>
#include <dali/public-api/images/image-operations.h>
#include <dali/public-api/images/pixel.h>
namespace Toolkit
{
+
namespace Internal DALI_INTERNAL
{
class ImageAtlas;
public:
/**
+ * @brief Pack a group of pixel data into atlas.
+ * @param[in] pixelData The group of the pixel data to be packed into the atlas.
+ * @param[out] textureRects The list of texture areas where each frame is located inside the atlas.
+ * @return The atlas texture.
+ */
+ static Texture PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects );
+
+ /**
* @brief Create a new ImageAtlas.
*
* @param [in] width The atlas width in pixels.
// EXTERNAL HEADER
#include <stdlib.h> // For abs()
+#include <dali/integration-api/debug.h>
namespace Dali
{
return abs( a-b ) <= 1;
}
+uint16_t MaxDimension( const Uint16Pair& dimensions )
+{
+ return dimensions.GetWidth() >= dimensions.GetHeight() ? dimensions.GetWidth() : dimensions.GetHeight();
+}
+
+void Swap( Uint16Pair& first, Uint16Pair& second )
+{
+ Uint16Pair temp = first;
+ first = second;
+ second = temp;
+}
+
}
AtlasPacker::Node::Node( Node* parent, SizeType x, SizeType y, SizeType width, SizeType height )
}
}
+Uint16Pair AtlasPacker::GroupPack( const Dali::Vector<Uint16Pair>& blockSizes, Dali::Vector<Uint16Pair>& packPositions )
+{
+ uint16_t count = blockSizes.Count();
+ packPositions.Resize( count );
+
+ // Sort the blocks according to its maximum dimension. The bigger blocks are packed first.
+ Dali::Vector<Uint16Pair> packOrder;
+ packOrder.Resize( count );
+ for( uint16_t i = 0; i < count; i++ )
+ {
+ packOrder[i].SetX( MaxDimension( blockSizes[i] ) );
+ packOrder[i].SetY( i );
+ }
+ for( uint16_t i = 0; i < count-1; i++ )
+ for( uint16_t j = 0; j < count-i-1; j++ )
+ {
+ if( packOrder[j].GetX() < packOrder[j+1].GetX() )
+ {
+ Swap( packOrder[j], packOrder[j+1] );
+ }
+ }
+
+ int index = packOrder[0].GetY();
+ AtlasPacker packer( blockSizes[index].GetWidth(), blockSizes[index].GetHeight() );
+
+ SizeType packPositionX, packPositionY;
+ // pack the blocks one by one with descending size, grows as necessary to accommodate each subsequent block.
+ for( uint16_t i = 0; i < count; i++ )
+ {
+ index = packOrder[i].GetY();
+ packer.GrowPack( blockSizes[index].GetWidth(), blockSizes[index].GetHeight(),
+ packPositionX, packPositionY );
+ packPositions[index].SetX( packPositionX );
+ packPositions[index].SetY( packPositionY );
+ }
+
+ return Uint16Pair( packer.mRoot->rectArea.width, packer.mRoot->rectArea.height );
+}
+
+void AtlasPacker::GrowPack( SizeType blockWidth, SizeType blockHeight,
+ SizeType& packPositionX, SizeType& packPositionY )
+{
+ Node* firstFit = InsertNode( mRoot, blockWidth, blockHeight );
+ if( firstFit == NULL )
+ {
+ // Could fit in the current left space, grow the partition tree to get more space.
+ GrowNode( blockWidth, blockHeight );
+ firstFit = InsertNode( mRoot->child[1], blockWidth, blockHeight );
+ }
+
+ if( firstFit != NULL )
+ {
+ firstFit->occupied = true;
+ packPositionX = firstFit->rectArea.x;
+ packPositionY = firstFit->rectArea.y;
+ }
+}
+
+void AtlasPacker::GrowNode( SizeType blockWidth, SizeType blockHeight )
+{
+ // Attempts to maintain a roughly square ratio when choosing the growing direction: right or down
+ bool canGrowRight = blockWidth <= mRoot->rectArea.width;
+ bool canGrowDown = blockHeight <= mRoot->rectArea.height;
+
+ bool shouldGrowRight = canGrowRight && mRoot->rectArea.height >= mRoot->rectArea.width+blockWidth;
+ bool shouldGrowDown = canGrowDown && mRoot->rectArea.width >= mRoot->rectArea.height+blockHeight;
+
+ if( canGrowRight && canGrowRight )
+ {
+ shouldGrowRight = mRoot->rectArea.width+blockWidth <= mRoot->rectArea.height+blockHeight;
+ shouldGrowDown = !shouldGrowRight;
+ }
+
+ if( shouldGrowRight || ( canGrowRight && !shouldGrowDown ) )
+ {
+ Node* newRoot = new Node( NULL, 0u, 0u, mRoot->rectArea.width+blockWidth, mRoot->rectArea.height );
+ newRoot->occupied = true;
+ newRoot->child[0] = mRoot;
+ newRoot->child[1] = new Node( newRoot, mRoot->rectArea.width, 0u, blockWidth, mRoot->rectArea.height );
+
+ mRoot = newRoot;
+ }
+ else if( shouldGrowDown || ( canGrowDown && !shouldGrowRight ) )
+ {
+ Node* newRoot = new Node( NULL, 0u, 0u, mRoot->rectArea.width, mRoot->rectArea.height+blockHeight );
+ newRoot->occupied = true;
+ newRoot->child[0] = mRoot;
+ newRoot->child[1] = new Node( newRoot, 0u, mRoot->rectArea.height, mRoot->rectArea.width, blockHeight );
+
+ mRoot = newRoot;
+ }
+ else
+ {
+ DALI_LOG_ERROR( " Atlas Packer failed to grow: make sure the packing order is sorted with the block size to avoid this happening");
+ }
+}
+
} // namespace Internal
} // namespace Toolkit
*/
#include <stdint.h>
+#include <dali/public-api/common/dali-vector.h>
#include <dali/public-api/math/rect.h>
+#include <dali/public-api/math/uint-16-pair.h>
namespace Dali
{
*/
unsigned int GetAvailableArea() const;
+ /**
+ * Pack a group of blocks with different sizes, calculate the required packing size and the position of each block.
+ * @param[in] blockSizes The size list of the blocks .
+ * @param[out] packPositions The packing position of each block.
+ * @return The required size to accommodate all the blocks.
+ */
+ static Uint16Pair GroupPack( const Dali::Vector<Uint16Pair>& blockSizes, Dali::Vector<Uint16Pair>& packPositions );
+
private:
/*
*/
void DeleteNode( Node* node );
+ /**
+ * Pack a block into the atlas. If there is no enough room, grow the partition tree.
+ *
+ * @param[in] blockWidth The width of the block to pack.
+ * @param[in] blockHeight The height of the block to pack.
+ * @param[out] packPositionX The x coordinate of the position to pack the block.
+ * @param[out] packPositionY The y coordinate of the position to pack the block.
+ */
+ void GrowPack( SizeType blockWidth, SizeType blockHeight,
+ SizeType& packPositionX, SizeType& packPositionY );
+
+ /**
+ * Add extra node into the partition tree to accommodate the given block.
+ *
+ * @param[in] blockWidth The width of the block to pack.
+ * @param[in] blockHeight The height of the block to pack.
+ */
+ void GrowNode( SizeType blockWidth, SizeType blockHeight );
+
// Undefined
- AtlasPacker( const AtlasPacker& imageAtlas);
+ AtlasPacker( const AtlasPacker& atlasPacker);
// Undefined
- AtlasPacker& operator=( const AtlasPacker& imageAtlas );
+ AtlasPacker& operator=( const AtlasPacker& atlasPacker );
private:
namespace Internal
{
+Texture ImageAtlas::PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects )
+{
+ // Record each block size
+ Dali::Vector<Uint16Pair> blockSizes;
+ SizeType count = pixelData.size();
+ for( SizeType index = 0; index < count; index++ )
+ {
+ blockSizes.PushBack( ImageDimensions( pixelData[index].GetWidth(), pixelData[index].GetHeight() ) );
+ }
+
+ // Ask atlasPacker for packing position of each block
+ Dali::Vector<Uint16Pair> packPositions;
+ ImageDimensions atlasSize = AtlasPacker::GroupPack( blockSizes, packPositions );
+
+ // Prepare for outout texture rect array
+ textureRects.Clear();
+ textureRects.Resize( count );
+
+ // create the texture for uploading the multiple pixel data
+ Texture atlasTexture = Texture::New( Dali::TextureType::TEXTURE_2D, Pixel::RGBA8888, atlasSize.GetWidth(), atlasSize.GetHeight() );
+
+ float atlasWidth = static_cast<float>( atlasTexture.GetWidth() );
+ float atlasHeight = static_cast<float>( atlasTexture.GetHeight() );
+ int packPositionX, packPositionY;
+ // Upload the pixel data one by one to its packing position, and record the texture rects
+ for( SizeType index = 0; index < count; index++ )
+ {
+ packPositionX = packPositions[index].GetX();
+ packPositionY = packPositions[index].GetY();
+ atlasTexture.Upload( pixelData[index], 0u, 0u,
+ packPositionX, packPositionY,
+ pixelData[index].GetWidth(), pixelData[index].GetHeight() );
+
+ // Apply the half pixel correction to avoid the color bleeding between neighbour blocks
+ textureRects[index].x = ( static_cast<float>( packPositionX ) +0.5f ) / atlasWidth; // left
+ textureRects[index].y = ( static_cast<float>( packPositionY ) +0.5f ) / atlasHeight; // right
+ textureRects[index].z = ( static_cast<float>( packPositionX + pixelData[index].GetWidth() )-0.5f ) / atlasWidth; // right
+ textureRects[index].w = ( static_cast<float>( packPositionY + pixelData[index].GetHeight() )-0.5f ) / atlasHeight;// bottom
+ }
+
+ return atlasTexture;
+}
+
ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat )
: mAtlas( Texture::New( Dali::TextureType::TEXTURE_2D, pixelFormat, width, height ) ),
mPacker( width, height ),
typedef Toolkit::ImageAtlas::SizeType SizeType;
/**
+ * @copydoc ImageAtlas::PackToAtlas( const std::vector<PixelData>&, Dali::Vector<Vector4>& )
+ */
+ static Texture PackToAtlas( const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects );
+
+ /**
* Constructor
* @param [in] width The atlas width in pixels.
* @param [in] height The atlas height in pixels.