// EXTERNAL INCLUDES
#include <string.h>
#include <dali/public-api/signals/callback.h>
-#include <dali/public-api/images/resource-image.h>
-#include <dali/devel-api/adaptor-framework/bitmap-loader.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
#include <dali/integration-api/debug.h>
namespace Dali
namespace Internal
{
+typedef unsigned char PixelBuffer;
+
+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 ) ),
ImageAtlas::~ImageAtlas()
{
- mIdRectContainer.Clear();
+ const std::size_t count = mLoadingTaskInfoContainer.Count();
+ for( std::size_t i=0; i < count; ++i )
+ {
+ // Call unregister to every observer in the list.
+ // Note that, the Atlas can be registered to same observer multiple times, and the Unregister method only remove one item each time.
+ // In this way, the atlas is actually detached from a observer either every upload call invoked by this observer is completed or atlas is destroyed.
+ if( mLoadingTaskInfoContainer[i]->observer )
+ {
+ mLoadingTaskInfoContainer[i]->observer->Unregister( *this );
+ }
+ }
+
+ mLoadingTaskInfoContainer.Clear();
}
IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
{
- mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl );
+ mBrokenImageSize = Dali::GetClosestImageSize( brokenImageUrl );
if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0 ) // check the url is valid
{
mBrokenImageUrl = brokenImageUrl;
const std::string& url,
ImageDimensions size,
FittingMode::Type fittingMode,
- bool orientationCorrection )
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver )
{
ImageDimensions dimensions = size;
ImageDimensions zero;
if( size == zero ) // image size not provided
{
- dimensions = ResourceImage::GetImageSize( url );
+ dimensions = Dali::GetClosestImageSize( url );
if( dimensions == zero ) // Fail to read the image & broken image file exists
{
if( !mBrokenImageUrl.empty() )
{
- return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true );
+ return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver );
}
else
{
unsigned int packPositionY = 0;
if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) )
{
- unsigned short loadId = mAsyncLoader.Load( url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection );
- mIdRectContainer.PushBack( new IdRectPair( loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight() ) );
-
+ unsigned short loadId = mAsyncLoader.Load( url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection);
+ mLoadingTaskInfoContainer.PushBack( new LoadingTaskInfo( loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight(), atlasUploadObserver ) );
// apply the half pixel correction
textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
textureRect.z = ( static_cast<float>( packPositionX + dimensions.GetX() )-0.5f ) / mWidth; // right
textureRect.w = ( static_cast<float>( packPositionY + dimensions.GetY() )-0.5f ) / mHeight;// bottom
+ if( atlasUploadObserver )
+ {
+ // register to the observer,
+ // Not that a matching unregister call should be invoked in UploadToAtlas if the observer is still alive by then.
+ atlasUploadObserver->Register( *this );
+ }
+
return true;
}
static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
}
-void ImageAtlas::UploadToAtlas( unsigned int id, PixelData pixelData )
+void ImageAtlas::ObserverDestroyed( AtlasUploadObserver* observer )
+{
+ const std::size_t count = mLoadingTaskInfoContainer.Count();
+ for( std::size_t i=0; i < count; ++i )
+ {
+ if( mLoadingTaskInfoContainer[i]->observer == observer )
+ {
+ // the observer is destructing, so its member function should not be called anymore
+ mLoadingTaskInfoContainer[i]->observer = NULL;
+ }
+ }
+}
+
+void ImageAtlas::UploadToAtlas( uint32_t id, PixelData pixelData )
{
- if( mIdRectContainer[0]->loadTaskId == id)
+ if( mLoadingTaskInfoContainer[0]->loadTaskId == id)
{
+ Rect<unsigned int> packRect( mLoadingTaskInfoContainer[0]->packRect );
if( !pixelData || ( pixelData.GetWidth() ==0 && pixelData.GetHeight() == 0 ))
{
if(!mBrokenImageUrl.empty()) // replace with the broken image
{
- UploadBrokenImage( mIdRectContainer[0]->packRect );
+ UploadBrokenImage( packRect );
}
}
else
{
- if( pixelData.GetWidth() < mIdRectContainer[0]->packRect.width || pixelData.GetHeight() < mIdRectContainer[0]->packRect.height )
+ if( pixelData.GetWidth() < packRect.width || pixelData.GetHeight() < packRect.height )
{
DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
- pixelData.GetWidth(), pixelData.GetHeight(),
- mIdRectContainer[0]->packRect.width, mIdRectContainer[0]->packRect.height );
+ pixelData.GetWidth(), pixelData.GetHeight(),
+ packRect.width, packRect.height );
}
- mAtlas.Upload( pixelData, 0u, 0u,
- mIdRectContainer[0]->packRect.x, mIdRectContainer[0]->packRect.y,
- mIdRectContainer[0]->packRect.width, mIdRectContainer[0]->packRect.height );
+ mAtlas.Upload( pixelData, 0u, 0u, packRect.x, packRect.y, packRect.width, packRect.height );
}
- }
- mIdRectContainer.Erase( mIdRectContainer.Begin() );
+ if( mLoadingTaskInfoContainer[0]->observer )
+ {
+ mLoadingTaskInfoContainer[0]->observer->UploadCompleted();
+ mLoadingTaskInfoContainer[0]->observer->Unregister( *this );
+ }
+
+ mLoadingTaskInfoContainer.Erase( mLoadingTaskInfoContainer.Begin() );
+ }
}
void ImageAtlas::UploadBrokenImage( const Rect<unsigned int>& area )
{
- BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
- loader.Load();
- SizeType loadedWidth = loader.GetPixelData().GetWidth();
- SizeType loadedHeight = loader.GetPixelData().GetHeight();
+ Devel::PixelBuffer brokenBuffer = LoadImageFromFile( mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
+ SizeType loadedWidth = brokenBuffer.GetWidth();
+ SizeType loadedHeight = brokenBuffer.GetHeight();
bool needBackgroundClear = false;
SizeType packX = area.x;
if( needBackgroundClear )
{
SizeType size = area.width * area.height * Pixel::GetBytesPerPixel( mPixelFormat );
- PixelBuffer* buffer = new PixelBuffer [size];
- PixelData background = PixelData::New( buffer, size, area.width, area.height, mPixelFormat, PixelData::DELETE_ARRAY );
+ Devel::PixelBuffer background = Devel::PixelBuffer::New( area.width, area.height, mPixelFormat );
+ unsigned char* buffer = background.GetBuffer();
for( SizeType idx = 0; idx < size; idx++ )
{
buffer[idx] = 0x00;
}
- mAtlas.Upload( background, 0u, 0u, area.x, area.y, area.width, area.height );
+ PixelData pixelData = Devel::PixelBuffer::Convert( background );
+ mAtlas.Upload( pixelData, 0u, 0u, area.x, area.y, area.width, area.height );
}
- mAtlas.Upload( loader.GetPixelData(), 0u, 0u, packX, packY, loadedWidth, loadedHeight );
+ PixelData brokenPixelData = Devel::PixelBuffer::Convert( brokenBuffer );
+ mAtlas.Upload( brokenPixelData, 0u, 0u, packX, packY, loadedWidth, loadedHeight );
}
} // namespace Internal