Move MultiplyColorByAlpha() from main thread to resource thread
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / image-loader / image-atlas-impl.cpp
index 67f257c..d167499 100644 (file)
@@ -21,8 +21,7 @@
 // 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
@@ -33,6 +32,50 @@ namespace Toolkit
 
 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 ) ),
@@ -49,7 +92,19 @@ ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelForm
 
 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 )
@@ -71,7 +126,7 @@ float ImageAtlas::GetOccupancyRate() const
 
 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;
@@ -82,18 +137,19 @@ bool ImageAtlas::Upload( Vector4& textureRect,
                          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
       {
@@ -107,15 +163,21 @@ bool ImageAtlas::Upload( Vector4& textureRect,
   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;
   }
 
@@ -150,41 +212,58 @@ void ImageAtlas::Remove( const Vector4& textureRect )
                        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;
@@ -204,16 +283,18 @@ void ImageAtlas::UploadBrokenImage( const Rect<unsigned int>& area )
   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