From: Xiangyin Ma Date: Mon, 19 Sep 2016 17:09:31 +0000 (+0100) Subject: ImageVisual postpones adding renderer to actor until image is loaded X-Git-Tag: dali_1.2.10~1 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=193fa3dcbfa6b46bb9d3d1c079f2ca4eb25747a1 ImageVisual postpones adding renderer to actor until image is loaded Change-Id: Id219a0f0ca7bc67f11f8dcda9126d97f3bb07d51 --- diff --git a/automated-tests/src/dali-toolkit/utc-Dali-AsyncImageLoader.cpp b/automated-tests/src/dali-toolkit/utc-Dali-AsyncImageLoader.cpp index 21603f3..3ee9a77 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-AsyncImageLoader.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-AsyncImageLoader.cpp @@ -206,7 +206,30 @@ int UtcDaliAsyncImageLoaderCancel(void) 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; diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp index a945850..5fe744f 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp @@ -60,6 +60,22 @@ bool IsOverlap( Rect rect1, Rect rect2 ) && 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) @@ -241,6 +257,100 @@ int UtcDaliImageAtlasUploadP(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; diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp index 779259d..12c3f17 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp @@ -393,6 +393,7 @@ int UtcDaliImageViewAsyncLoadingWithAltasing(void) callStack.Reset(); callStack.Enable(true); + BitmapLoader::ResetLatestCreated(); ImageView imageView = ImageView::New( gImage_34_RGBA, ImageDimensions( 34, 34 ) ); // By default, Aysnc loading is used diff --git a/dali-toolkit/devel-api/file.list b/dali-toolkit/devel-api/file.list index 188ed1b..509f22e 100755 --- a/dali-toolkit/devel-api/file.list +++ b/dali-toolkit/devel-api/file.list @@ -22,6 +22,7 @@ devel_api_src_files = \ $(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 \ @@ -82,6 +83,7 @@ devel_api_focus_manager_header_files = \ 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 = \ diff --git a/dali-toolkit/devel-api/image-loader/atlas-upload-observer.cpp b/dali-toolkit/devel-api/image-loader/atlas-upload-observer.cpp new file mode 100644 index 0000000..f362edc --- /dev/null +++ b/dali-toolkit/devel-api/image-loader/atlas-upload-observer.cpp @@ -0,0 +1,71 @@ +/* + * 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 + +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; + } + } +} + +} + +} diff --git a/dali-toolkit/devel-api/image-loader/atlas-upload-observer.h b/dali-toolkit/devel-api/image-loader/atlas-upload-observer.h new file mode 100644 index 0000000..2688c1a --- /dev/null +++ b/dali-toolkit/devel-api/image-loader/atlas-upload-observer.h @@ -0,0 +1,85 @@ +#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 +#include +#include + + +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 mAtlasList; ///< The list of the registered ImageAtlas object + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif /* DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H */ diff --git a/dali-toolkit/devel-api/image-loader/image-atlas.cpp b/dali-toolkit/devel-api/image-loader/image-atlas.cpp index a7174d5..fd6825e 100644 --- a/dali-toolkit/devel-api/image-loader/image-atlas.cpp +++ b/dali-toolkit/devel-api/image-loader/image-atlas.cpp @@ -80,7 +80,17 @@ bool ImageAtlas::Upload( Vector4& textureRect, 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 ) diff --git a/dali-toolkit/devel-api/image-loader/image-atlas.h b/dali-toolkit/devel-api/image-loader/image-atlas.h index c85194f..98796bd 100644 --- a/dali-toolkit/devel-api/image-loader/image-atlas.h +++ b/dali-toolkit/devel-api/image-loader/image-atlas.h @@ -26,6 +26,9 @@ #include #include +// INTERNAL INCLUDES +#include + namespace Dali { @@ -114,7 +117,7 @@ public: /** * @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. @@ -135,6 +138,32 @@ public: 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 * * @param [out] textureRect The texture area of the resource image in the atlas. diff --git a/dali-toolkit/internal/image-loader/image-atlas-impl.cpp b/dali-toolkit/internal/image-loader/image-atlas-impl.cpp index 67f257c..bdd2780 100644 --- a/dali-toolkit/internal/image-loader/image-atlas-impl.cpp +++ b/dali-toolkit/internal/image-loader/image-atlas-impl.cpp @@ -49,7 +49,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::New( SizeType width, SizeType height, Pixel::Format pixelFormat ) @@ -82,7 +94,8 @@ 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; @@ -93,7 +106,7 @@ bool ImageAtlas::Upload( Vector4& textureRect, { if( !mBrokenImageUrl.empty() ) { - return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true ); + return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver ); } else { @@ -108,14 +121,20 @@ bool ImageAtlas::Upload( Vector4& textureRect, 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( packPositionX ) +0.5f ) / mWidth; // left textureRect.y = ( static_cast( packPositionY ) +0.5f ) / mHeight; // right textureRect.z = ( static_cast( packPositionX + dimensions.GetX() )-0.5f ) / mWidth; // right textureRect.w = ( static_cast( 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,33 +169,51 @@ void ImageAtlas::Remove( const Vector4& textureRect ) static_cast((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 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& area ) diff --git a/dali-toolkit/internal/image-loader/image-atlas-impl.h b/dali-toolkit/internal/image-loader/image-atlas-impl.h index 34924d3..a773ea7 100644 --- a/dali-toolkit/internal/image-loader/image-atlas-impl.h +++ b/dali-toolkit/internal/image-loader/image-atlas-impl.h @@ -23,6 +23,7 @@ #include #include #include +#include // INTERNAL INCLUDES #include @@ -80,7 +81,8 @@ public: const std::string& url, ImageDimensions size, FittingMode::Type fittingMode, - bool orientationCorrection); + bool orientationCorrection, + AtlasUploadObserver* atlasUploadObserver ); /** * @copydoc Toolkit::ImageAtlas::Upload( Vector4&, PixelData ) @@ -92,6 +94,11 @@ public: */ 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: /** @@ -104,7 +111,7 @@ private: /** * @copydoc PixelDataRequester::ProcessPixels */ - void UploadToAtlas( unsigned int id, PixelData pixelData ); + void UploadToAtlas( uint32_t id, PixelData pixelData ); /** * Upload broken image @@ -121,22 +128,29 @@ private: 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 packRect; + AtlasUploadObserver* observer; }; - OwnerContainer mIdRectContainer; + OwnerContainer mLoadingTaskInfoContainer; Texture mAtlas; AtlasPacker mPacker; @@ -147,6 +161,7 @@ private: float mHeight; Pixel::Format mPixelFormat; + }; } // namespace Internal diff --git a/dali-toolkit/internal/visuals/image-atlas-manager.cpp b/dali-toolkit/internal/visuals/image-atlas-manager.cpp index 66897e4..fe01571 100644 --- a/dali-toolkit/internal/visuals/image-atlas-manager.cpp +++ b/dali-toolkit/internal/visuals/image-atlas-manager.cpp @@ -51,7 +51,8 @@ TextureSet ImageAtlasManager::Add( Vector4& textureRect, const std::string& url, ImageDimensions size, FittingMode::Type fittingMode, - bool orientationCorrection ) + bool orientationCorrection, + AtlasUploadObserver* atlasUploadObserver ) { ImageDimensions dimensions = size; ImageDimensions zero; @@ -71,7 +72,7 @@ TextureSet ImageAtlasManager::Add( Vector4& textureRect, 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]; } @@ -79,12 +80,12 @@ TextureSet ImageAtlasManager::Add( Vector4& textureRect, } 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 diff --git a/dali-toolkit/internal/visuals/image-atlas-manager.h b/dali-toolkit/internal/visuals/image-atlas-manager.h index 4586236..5907ca2 100644 --- a/dali-toolkit/internal/visuals/image-atlas-manager.h +++ b/dali-toolkit/internal/visuals/image-atlas-manager.h @@ -32,6 +32,8 @@ namespace Dali namespace Toolkit { +class AtlasUploadObserver; + namespace Internal { @@ -65,14 +67,15 @@ public: * @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 * diff --git a/dali-toolkit/internal/visuals/image/image-visual.cpp b/dali-toolkit/internal/visuals/image/image-visual.cpp index 4e545ab..f4652dd 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.cpp +++ b/dali-toolkit/internal/visuals/image/image-visual.cpp @@ -478,7 +478,7 @@ TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, const std::strin } 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 { @@ -509,6 +509,7 @@ void ImageVisual::InitializeRenderer( const std::string& imageUrl ) 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 @@ -573,8 +574,22 @@ void ImageVisual::InitializeRenderer( const Image& image ) } } +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 ); @@ -589,7 +604,11 @@ void ImageVisual::DoSetOnStage( Actor& actor ) 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 ) @@ -606,6 +625,7 @@ void ImageVisual::DoSetOffStage( Actor& actor ) actor.RemoveRenderer( mImpl->mRenderer ); mImpl->mRenderer.Reset(); } + mPlacementActor.Reset(); } void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const diff --git a/dali-toolkit/internal/visuals/image/image-visual.h b/dali-toolkit/internal/visuals/image/image-visual.h index c3f2217..3079c93 100644 --- a/dali-toolkit/internal/visuals/image/image-visual.h +++ b/dali-toolkit/internal/visuals/image/image-visual.h @@ -20,11 +20,13 @@ // INTERNAL INCLUDES #include +#include // EXTERNAL INCLUDES #include #include #include +#include namespace Dali { @@ -72,7 +74,7 @@ typedef IntrusivePtr< ImageVisual > ImageVisualPtr; * "DEFAULT" * */ -class ImageVisual: public Visual::Base, public ConnectionTracker +class ImageVisual: public Visual::Base, public ConnectionTracker, public AtlasUploadObserver { public: @@ -160,6 +162,14 @@ 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: /** @@ -249,6 +259,7 @@ private: Image mImage; PixelData mPixels; Vector4 mPixelArea; + WeakHandle mPlacementActor; std::string mImageUrl; Dali::ImageDimensions mDesiredSize;