END_TEST;
}
-int UtcDaliAsyncImageLoaderCancelAll(void)
+int UtcDaliAsncImageLoaderCancelAll01(void)
+{
+ ToolkitTestApplication application;
+
+ AsyncImageLoader loader = AsyncImageLoader::New();
+
+ // Test that it is safe to call CancelAll even there is no loading task requested.
+ try
+ {
+ loader.CancelAll();
+ }
+ catch(Dali::DaliException& e)
+ {
+ DALI_TEST_ASSERT(e, "AsyncImageLoader::LoadAll", TEST_LOCATION);
+ }
+
+ // Test that cancelling a non-existing loading task will return false
+ uint32_t id = 1;
+ DALI_TEST_CHECK( !(loader.Cancel( id )) );
+
+ END_TEST;
+}
+
+int UtcDaliAsyncImageLoaderCancelAll02(void)
{
ToolkitTestApplication application;
&& rect2.y < rect1.y+rect1.height;
}
+static unsigned int gCountOfTestFuncCall;
+class TestUploadObserver : public AtlasUploadObserver
+{
+public:
+ TestUploadObserver()
+ {}
+
+ virtual ~TestUploadObserver()
+ {}
+
+ void UploadCompleted()
+ {
+ gCountOfTestFuncCall++;
+ }
+};
+
} // anonymous namespace
void dali_image_atlas_startup(void)
END_TEST;
}
+int UtcDaliImageAtlasUploadWithObserver01(void)
+{
+ TestApplication application;
+ ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+ EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+ CallbackBase* callback = eventTrigger->GetCallback();
+
+ gCountOfTestFuncCall = 0;
+ TestUploadObserver uploadObserver;
+
+ Vector4 textureRect1;
+ atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, &uploadObserver );
+ Vector4 textureRect2;
+ atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, NULL );
+ Vector4 textureRect3;
+ atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, &uploadObserver );
+
+ // waiting until all three images are loaded and uploaded to atlas
+ eventTrigger->WaitingForTrigger( 3 );
+ CallbackBase::Execute( *callback );
+ application.SendNotification();
+ application.Render(RENDER_FRAME_INTERVAL);
+
+ // Check that TestFunc is called twice
+ DALI_TEST_EQUALS( gCountOfTestFuncCall, 2, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliImageAtlasUploadWithObserver02(void)
+{
+ TestApplication application;
+ ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+ EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+ CallbackBase* callback = eventTrigger->GetCallback();
+
+ gCountOfTestFuncCall = 0;
+ TestUploadObserver* uploadObserver = new TestUploadObserver;
+
+ Vector4 textureRect1;
+ atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, uploadObserver );
+ Vector4 textureRect2;
+ atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, uploadObserver );
+ Vector4 textureRect3;
+ atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, uploadObserver );
+
+ // destroy the object.
+ delete uploadObserver;
+
+ // waiting until all three images are loaded and uploaded to atlas
+ eventTrigger->WaitingForTrigger( 3 );
+ CallbackBase::Execute( *callback );
+ application.Render(RENDER_FRAME_INTERVAL);
+ application.SendNotification();
+
+ // Check that TestFunc is called twice
+ DALI_TEST_EQUALS( gCountOfTestFuncCall, 0, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliImageAtlasUploadWithObserver03(void)
+{
+ TestApplication application;
+
+ gCountOfTestFuncCall = 0;
+ TestUploadObserver* uploadObserver = new TestUploadObserver;
+
+ {
+ ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+ Vector4 textureRect1;
+ atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, uploadObserver );
+ Vector4 textureRect2;
+ atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, uploadObserver );
+ Vector4 textureRect3;
+ atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, uploadObserver );
+ }
+
+ //ImageAtlas is out of scope, so it will get destroyed
+
+ application.Render(RENDER_FRAME_INTERVAL);
+ application.SendNotification();
+ application.SendNotification();
+ application.Render(RENDER_FRAME_INTERVAL);
+
+ // Check that TestFunc is called twice
+ DALI_TEST_EQUALS( gCountOfTestFuncCall, 0, TEST_LOCATION );
+
+ END_TEST;
+}
+
int UtcDaliImageAtlasRemove(void)
{
TestApplication application;
callStack.Reset();
callStack.Enable(true);
+ BitmapLoader::ResetLatestCreated();
ImageView imageView = ImageView::New( gImage_34_RGBA, ImageDimensions( 34, 34 ) );
// By default, Aysnc loading is used
$(devel_api_src_dir)/controls/tool-bar/tool-bar.cpp \
$(devel_api_src_dir)/focus-manager/keyinput-focus-manager.cpp \
$(devel_api_src_dir)/image-loader/async-image-loader.cpp \
+ $(devel_api_src_dir)/image-loader/atlas-upload-observer.cpp \
$(devel_api_src_dir)/image-loader/image-atlas.cpp \
$(devel_api_src_dir)/scripting/script.cpp \
$(devel_api_src_dir)/transition-effects/cube-transition-cross-effect.cpp \
devel_api_image_loader_header_files = \
$(devel_api_src_dir)/image-loader/async-image-loader.h \
+ $(devel_api_src_dir)/image-loader/atlas-upload-observer.h \
$(devel_api_src_dir)/image-loader/image-atlas.h
devel_api_scripting_header_files = \
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "atlas-upload-observer.h"
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+AtlasUploadObserver::AtlasUploadObserver()
+{}
+
+AtlasUploadObserver::~AtlasUploadObserver()
+{
+ // Notify the registerd ImageAtlas object about the destruction of observer.
+ const std::size_t size( mAtlasList.Count() );
+ for( std::size_t i = 0; i < size; ++i )
+ {
+ if( mAtlasList[i] )
+ {
+ mAtlasList[i]->ObserverDestroyed( this );
+ }
+ }
+ mAtlasList.Clear();
+}
+
+void AtlasUploadObserver::Register( Internal::ImageAtlas& imageAtlas )
+{
+ // Add to the list so that the ImageAtlas could get notified in the destructor.
+ // If the same atlas is exist in the list already, we would still save the duplicated copy.
+ mAtlasList.PushBack( &imageAtlas );
+}
+
+void AtlasUploadObserver::Unregister( Internal::ImageAtlas& imageAtlas )
+{
+ const std::size_t size( mAtlasList.Count() );
+ for( std::size_t i = 0; i < size; i++ )
+ {
+ if( mAtlasList[i] == &imageAtlas )
+ {
+ // Remove from list
+ mAtlasList.Erase( mAtlasList.Begin() + i );
+ // If there are duplicated copies of same pointer, only the first one is removed
+ return;
+ }
+ }
+}
+
+}
+
+}
--- /dev/null
+#ifndef DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H
+#define DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H
+
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/signals/callback.h>
+
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+class ImageAtlas;
+}
+
+/**
+ * @brief Base class used to observe the upload status of the ImageAtlas when requesting an image atlasing.
+ *
+ * Derived class should implement the UploadCompleted method which would get executed once the texture is ready.
+ */
+class DALI_IMPORT_API AtlasUploadObserver
+{
+public:
+
+ /**
+ * @brief Constructor.
+ */
+ AtlasUploadObserver();
+
+ /**
+ * @brief Virtual destructor.
+ */
+ virtual ~AtlasUploadObserver();
+
+ /**
+ * The action to be taken once the upload is completed.
+ */
+ virtual void UploadCompleted() = 0;
+
+public: // not intended for developer, called by ImageAtlas internally to get notified when this observer dies
+
+ /**
+ * @brief Register an ImageAtlas which be notified when the observer is destructing.
+ * @param[in] imageAtlas The ImageAtlas object to get notification about the destruction of the observer.
+ */
+ void Register( Internal::ImageAtlas& imageAtlas );
+
+ /**
+ * @brief Unregister an ImageAtlas which be notified when the observer is destructing.
+ * @param[in] imageAtlas The ImageAtlas object to get notification about the destruction of the observer.
+ */
+ void Unregister( Internal::ImageAtlas& imageAtlas );
+
+private:
+
+ Vector<Internal::ImageAtlas*> mAtlasList; ///< The list of the registered ImageAtlas object
+
+};
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H */
FittingMode::Type fittingMode,
bool orientationCorrection )
{
- return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection );
+ return Upload( textureRect, url, size, fittingMode, orientationCorrection, NULL );
+}
+
+bool ImageAtlas::Upload( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver )
+{
+ return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver );
}
bool ImageAtlas::Upload( Vector4& textureRect, PixelData pixelData )
#include <dali/public-api/images/pixel-data.h>
#include <dali/public-api/rendering/texture.h>
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
+
namespace Dali
{
/**
* @brief Upload a resource image to the atlas.
*
- * @note To make the atlasing efficient, an valid size should be provided.
+ * @note To make the atlasing efficient, a valid size should be provided.
* If size is not provided, then the image file will be opened to read the actual size for loading.
* Do not set a size that is bigger than the actual image size, as the up-scaling is not available,
* the content of the area not covered by actual image is undefined, it will not be cleared.
FittingMode::Type fittingMode = FittingMode::DEFAULT,
bool orientationCorrection = true );
+ /**
+ * @brief Upload a resource image to the atlas.
+ *
+ * @note To make the atlasing efficient, a valid size should be provided.
+ * If size is not provided, then the image file will be opened to read the actual size for loading.
+ * Do not set a size that is bigger than the actual image size, as the up-scaling is not available,
+ * the content of the area not covered by actual image is undefined, it will not be cleared.
+ *
+ * SamplingMode::BOX_THEN_LINEAR is used to sampling pixels from the input image while fitting it to desired size.
+ *
+ * @param [out] textureRect The texture area of the resource image in the atlas.
+ * @param [in] url The URL of the resource image file to use.
+ * @param [in] size The width and height to fit the loaded image to.
+ * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
+ * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ * @param[in] atlasUploadObserver The observer to observe the upload state inside the ImageAtlas.
+ * @return True if there is enough space to fit this image in,false otherwise.
+ * @note The valid callback function here is required to have the signature of void( void ).
+ */
+ bool Upload( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver );
+
/**
* @brief Upload a pixel buffer to atlas
*
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 )
const std::string& url,
ImageDimensions size,
FittingMode::Type fittingMode,
- bool orientationCorrection )
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver )
{
ImageDimensions dimensions = size;
ImageDimensions zero;
{
if( !mBrokenImageUrl.empty() )
{
- return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true );
+ return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver );
}
else
{
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() ) );
-
+ 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 )
{
- if( mIdRectContainer[0]->loadTaskId == id)
+ 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( 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 )
#include <dali/public-api/signals/connection-tracker.h>
#include <dali/devel-api/common/owner-container.h>
#include <dali/devel-api/images/atlas.h>
+#include <dali/devel-api/common/owner-container.h>
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
const std::string& url,
ImageDimensions size,
FittingMode::Type fittingMode,
- bool orientationCorrection);
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver );
/**
* @copydoc Toolkit::ImageAtlas::Upload( Vector4&, PixelData )
*/
void Remove( const Vector4& textureRect );
+ /**
+ * Resets the destroying observer pointer so that we know not to call methods of this object any more.
+ */
+ void ObserverDestroyed( AtlasUploadObserver* observer );
+
protected:
/**
/**
* @copydoc PixelDataRequester::ProcessPixels
*/
- void UploadToAtlas( unsigned int id, PixelData pixelData );
+ void UploadToAtlas( uint32_t id, PixelData pixelData );
/**
* Upload broken image
private:
- struct IdRectPair
+ /**
+ * Each loading task( identified with an ID ) is associated with a rect region for packing the loaded pixel data into the atlas,
+ * and an AtlasUploadObserver whose UploadCompleted method should get executed once the sub texture is ready.
+ */
+ struct LoadingTaskInfo
{
- IdRectPair( unsigned short loadTaskId,
- unsigned int packPositionX,
- unsigned int packPositionY,
- unsigned int width,
- unsigned int height )
+ LoadingTaskInfo( unsigned short loadTaskId,
+ unsigned int packPositionX,
+ unsigned int packPositionY,
+ unsigned int width,
+ unsigned int height,
+ AtlasUploadObserver* observer )
: loadTaskId( loadTaskId ),
- packRect( packPositionX, packPositionY, width, height )
+ packRect( packPositionX, packPositionY, width, height ),
+ observer( observer )
{}
unsigned short loadTaskId;
Rect<unsigned int> packRect;
+ AtlasUploadObserver* observer;
};
- OwnerContainer<IdRectPair*> mIdRectContainer;
+ OwnerContainer<LoadingTaskInfo*> mLoadingTaskInfoContainer;
Texture mAtlas;
AtlasPacker mPacker;
float mHeight;
Pixel::Format mPixelFormat;
+
};
} // namespace Internal
const std::string& url,
ImageDimensions size,
FittingMode::Type fittingMode,
- bool orientationCorrection )
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver )
{
ImageDimensions dimensions = size;
ImageDimensions zero;
unsigned int i = 0;
for( AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
{
- if( (*iter).Upload( textureRect, url, size, fittingMode, orientationCorrection ) )
+ if( (*iter).Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver ) )
{
return mTextureSetList[i];
}
}
CreateNewAtlas();
- mAtlasList.back().Upload( textureRect, url, size, fittingMode, orientationCorrection );
+ mAtlasList.back().Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver );
return mTextureSetList.back();
}
TextureSet ImageAtlasManager::Add( Vector4& textureRect,
- PixelData pixelData )
+ PixelData pixelData )
{
// big buffer, atlasing is not applied
namespace Toolkit
{
+class AtlasUploadObserver;
+
namespace Internal
{
* @param [in] size The width and height to fit the loaded image to.
* @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
* @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ * @param [in] atlasUploadObserver The object to observe the uploading state inside ImageAtlas.
* @return The texture set containing the image.
*/
TextureSet Add( Vector4& textureRect,
- const std::string& url,
- ImageDimensions size = ImageDimensions(),
- FittingMode::Type fittingMode = FittingMode::DEFAULT,
- bool orientationCorrection = true );
-
+ const std::string& url,
+ ImageDimensions size = ImageDimensions(),
+ FittingMode::Type fittingMode = FittingMode::DEFAULT,
+ bool orientationCorrection = true,
+ AtlasUploadObserver* atlasUploadObserver = NULL );
/**
* @brief Add a pixel buffer to the atlas
*
}
else
{
- textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, mSamplingMode );
+ textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, true, this );
mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
if( !textureSet ) // big image, no atlasing
{
mImageUrl = imageUrl;
mImpl->mRenderer.Reset();
+ mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
if( !mImpl->mCustomShader &&
( strncasecmp( imageUrl.c_str(), HTTP_URL, sizeof(HTTP_URL) -1 ) != 0 ) && // ignore remote images
}
}
+void ImageVisual::UploadCompleted()
+{
+ // Resource image is loaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
+ Actor actor = mPlacementActor.GetHandle();
+ if( actor )
+ {
+ actor.AddRenderer( mImpl->mRenderer );
+ // reset the weak handle so that the renderer only get added to actor once
+ mPlacementActor.Reset();
+ }
+}
+
void ImageVisual::DoSetOnStage( Actor& actor )
{
+ mPlacementActor = actor;
+
if( !mImageUrl.empty() )
{
InitializeRenderer( mImageUrl );
mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
}
- actor.AddRenderer( mImpl->mRenderer );
+ if( IsSynchronousResourceLoading() || !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
+ {
+ actor.AddRenderer( mImpl->mRenderer );
+ mPlacementActor.Reset();
+ }
}
void ImageVisual::DoSetOffStage( Actor& actor )
actor.RemoveRenderer( mImpl->mRenderer );
mImpl->mRenderer.Reset();
}
+ mPlacementActor.Reset();
}
void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
// INTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/visual-base-impl.h>
+#include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
// EXTERNAL INCLUDES
#include <dali/public-api/images/image.h>
#include <dali/public-api/images/image-operations.h>
#include <dali/public-api/images/resource-image.h>
+#include <dali/devel-api/object/weak-handle.h>
namespace Dali
{
* "DEFAULT"
*
*/
-class ImageVisual: public Visual::Base, public ConnectionTracker
+class ImageVisual: public Visual::Base, public ConnectionTracker, public AtlasUploadObserver
{
public:
*/
void SetImage( Actor& actor, const Image& image );
+ /**
+ * @copydoc AtlasUploadObserver::UploadCompleted
+ *
+ * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+ * This callback is the place to add the renderer as it would be called once the loading is finished.
+ */
+ virtual void UploadCompleted();
+
private:
/**
Image mImage;
PixelData mPixels;
Vector4 mPixelArea;
+ WeakHandle<Actor> mPlacementActor;
std::string mImageUrl;
Dali::ImageDimensions mDesiredSize;