From: Xiangyin Ma Date: Wed, 24 Aug 2016 16:05:29 +0000 (+0100) Subject: Async image loading X-Git-Tag: dali_1.2.10~13 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=41307f75478fd6c6fb5f9ae7e7de5036647f39b3 Async image loading Change-Id: If5bb546217017a055e70f4a0b55f4d1f6cfa9b85 --- diff --git a/automated-tests/src/dali-toolkit/CMakeLists.txt b/automated-tests/src/dali-toolkit/CMakeLists.txt index 3b39bd0..d6ad543 100644 --- a/automated-tests/src/dali-toolkit/CMakeLists.txt +++ b/automated-tests/src/dali-toolkit/CMakeLists.txt @@ -54,6 +54,7 @@ SET(TC_SOURCES utc-Dali-DebugRendering.cpp utc-Dali-ImageAtlas.cpp utc-Dali-VideoView.cpp + utc-Dali-AsyncImageLoader.cpp ) # Append list of test harness files (Won't get parsed for test cases) diff --git a/automated-tests/src/dali-toolkit/utc-Dali-AsyncImageLoader.cpp b/automated-tests/src/dali-toolkit/utc-Dali-AsyncImageLoader.cpp new file mode 100644 index 0000000..e85d90f --- /dev/null +++ b/automated-tests/src/dali-toolkit/utc-Dali-AsyncImageLoader.cpp @@ -0,0 +1,245 @@ +/* + * 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 +#include +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ +// resolution: 34*34, pixel format: RGBA8888 +static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png"; +// resolution: 50*50, pixel format: RGBA8888 +static const char* gImage_50_RGBA = TEST_RESOURCE_DIR "/icon-delete.png"; +// resolution: 128*128, pixel format: RGB888 +static const char* gImage_128_RGB = TEST_RESOURCE_DIR "/gallery-small-1.jpg"; + +// for testing the ImageLoadedSignal +class ImageLoadedSignalVerifier : public ConnectionTracker +{ +public: + + ImageLoadedSignalVerifier() + : mCount( 0 ) + {} + + virtual ~ImageLoadedSignalVerifier() + {} + + void ImageLoaded( uint32_t id, PixelData pixelData ) + { + mIDs.push_back( id ); + mPixelDataList.push_back( pixelData ); + mCount++; + } + + int LoadedImageCount() + { + return mCount; + } + + bool Verify( uint32_t id, uint32_t width, uint32_t height ) + { + int size = mIDs.size(); + for( int i = 0; i mIDs; + std::vector mPixelDataList; +}; + + +} // anonymous namespace + +void dali_async_image_loader_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void dali_async_image_loader_cleanup(void) +{ + test_return_value = TET_PASS; +} + +int UtcDaliImageAtlasNew01(void) +{ + ToolkitTestApplication application; + + //invoke default handle constructor + AsyncImageLoader loader; + + DALI_TEST_CHECK( !loader ); + + // initialise handle + loader = AsyncImageLoader::New(); + DALI_TEST_CHECK( loader ); + + END_TEST; +} + +int UtcDaliAsyncImageLoaderCopyConstructor(void) +{ + ToolkitTestApplication application; + + AsyncImageLoader loader = AsyncImageLoader::New( ); + DALI_TEST_CHECK( loader ); + + AsyncImageLoader loaderCopy(loader); + DALI_TEST_CHECK( loaderCopy ); + + END_TEST; +} + +int UtcDaliAsyncImageLoaderAssignmentOperator(void) +{ + ToolkitTestApplication application; + + AsyncImageLoader loader = AsyncImageLoader::New(); + DALI_TEST_CHECK( loader ); + + AsyncImageLoader loader2; + DALI_TEST_CHECK( !loader2 ); + + loader2 = loader; + DALI_TEST_CHECK( loader2 ); + DALI_TEST_CHECK( loader == loader2 ); // the two handles are pointing to the same object. + + END_TEST; +} + +int UtcDaliAsyncImageLoaderLoadAndLoadedSignal(void) +{ + ToolkitTestApplication application; + + AsyncImageLoader loader = AsyncImageLoader::New(); + ImageLoadedSignalVerifier loadedSignalVerifier; + + loader.ImageLoadedSignal().Connect( &loadedSignalVerifier, &ImageLoadedSignalVerifier::ImageLoaded ); + + loader.Load( gImage_34_RGBA ); + uint32_t id02 = loader.Load( gImage_50_RGBA, ImageDimensions( 25, 25 ) ); + uint32_t id03 = loader.Load( gImage_128_RGB, ImageDimensions( 100, 100 ), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, true ); + + EventThreadCallback* eventTrigger = EventThreadCallback::Get(); + CallbackBase* callback = eventTrigger->GetCallback(); + + eventTrigger->WaitingForTrigger( 3 );// waiting until all three images are loaded + + CallbackBase::Execute( *callback ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( loadedSignalVerifier.LoadedImageCount() == 3 ); + DALI_TEST_CHECK( loadedSignalVerifier.Verify( id02, 25, 25 ) ); + DALI_TEST_CHECK( loadedSignalVerifier.Verify( id03, 100, 100 ) ); + + END_TEST; +} + +int UtcDaliAsyncImageLoaderCancel(void) +{ + ToolkitTestApplication application; + + AsyncImageLoader loader = AsyncImageLoader::New(); + ImageLoadedSignalVerifier loadedSignalVerifier; + + loader.ImageLoadedSignal().Connect( &loadedSignalVerifier, &ImageLoadedSignalVerifier::ImageLoaded ); + + uint32_t id01 = loader.Load( gImage_34_RGBA, ImageDimensions( 34, 34 ) ); + uint32_t id02 = loader.Load( gImage_50_RGBA, ImageDimensions( 25, 25 ) ); + uint32_t id03 = loader.Load( gImage_128_RGB, ImageDimensions( 100, 100 ), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, true ); + + // cancel the loading of the second image + DALI_TEST_CHECK( loader.Cancel( id02 ) ); + + EventThreadCallback* eventTrigger = EventThreadCallback::Get(); + CallbackBase* callback = eventTrigger->GetCallback(); + + eventTrigger->WaitingForTrigger( 2 );// waiting until first and third images are loaded + + CallbackBase::Execute( *callback ); + + DALI_TEST_CHECK( ! loader.Cancel( id03 ) ); // can not cancel a task that is already implemeted + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( loadedSignalVerifier.LoadedImageCount() == 2 ); + + DALI_TEST_CHECK( loadedSignalVerifier.Verify( id01, 34, 34 ) ); // first image is successfully loaded + DALI_TEST_CHECK( !loadedSignalVerifier.Verify( id02, 25, 25 ) ); // second image is not loaded + DALI_TEST_CHECK( loadedSignalVerifier.Verify( id03, 100, 100 ) ); // third image is successfully loaded + + END_TEST; +} + +int UtcDaliAsyncImageLoaderCancelAll(void) +{ + ToolkitTestApplication application; + + AsyncImageLoader loader = AsyncImageLoader::New(); + ImageLoadedSignalVerifier loadedSignalVerifier; + + loader.ImageLoadedSignal().Connect( &loadedSignalVerifier, &ImageLoadedSignalVerifier::ImageLoaded ); + + uint32_t id01 = loader.Load( gImage_34_RGBA, ImageDimensions( 34, 34 ) ); + uint32_t id02 = loader.Load( gImage_50_RGBA, ImageDimensions( 25, 25 ) ); + + // cancel the loading of the first and second image + loader.CancelAll(); + + uint32_t id03 = loader.Load( gImage_128_RGB, ImageDimensions( 100, 100 ), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, true ); + + EventThreadCallback* eventTrigger = EventThreadCallback::Get(); + CallbackBase* callback = eventTrigger->GetCallback(); + + eventTrigger->WaitingForTrigger( 1 );// waiting until the third images is loaded + + CallbackBase::Execute( *callback ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK( loadedSignalVerifier.LoadedImageCount() == 1 ); + + DALI_TEST_CHECK( !loadedSignalVerifier.Verify( id01, 34, 34 ) ); // first image is not loaded + DALI_TEST_CHECK( !loadedSignalVerifier.Verify( id02, 25, 25 ) ); // second image is not loaded + DALI_TEST_CHECK( loadedSignalVerifier.Verify( id03, 100, 100 ) ); // third image is successfully loaded + + END_TEST; +} + + diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp index a8fa8cc..a945850 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageAtlas.cpp @@ -20,8 +20,7 @@ #include #include #include -#include -#include +#include #include using namespace Dali; @@ -120,15 +119,31 @@ int UtcDaliImageAtlasGetAtlas(void) ToolkitTestApplication application; ImageAtlas atlas = ImageAtlas::New( 32, 32 ); - Image image = atlas.GetAtlas(); + Texture image = atlas.GetAtlas(); // test the atlas created DALI_TEST_EQUALS( (bool)image, true, TEST_LOCATION ); DALI_TEST_CHECK( image.GetHeight() == 32u ); DALI_TEST_CHECK( image.GetWidth() == 32u ); - Atlas coreAtlas = Atlas::DownCast( image ); - DALI_TEST_EQUALS( (bool)coreAtlas, true, TEST_LOCATION ); + END_TEST; +} + +int UtcDaliImageAtlasGetOccupancyRate(void) +{ + ToolkitTestApplication application; + + ImageAtlas atlas = ImageAtlas::New( 100, 100 ); + + DALI_TEST_EQUALS( atlas.GetOccupancyRate(), 0.f, TEST_LOCATION ); + + Vector4 textureRect1; + atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34) ); + DALI_TEST_EQUALS( atlas.GetOccupancyRate(), 34.f*34.f/10000.f, 0.001f, TEST_LOCATION ); + + Vector4 textureRect2; + atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50) ); + DALI_TEST_EQUALS( atlas.GetOccupancyRate(), (34.f*34.f+50.f*50.f)/10000.f, 0.001f, TEST_LOCATION ); END_TEST; } @@ -165,9 +180,6 @@ int UtcDaliImageAtlasUploadP(void) unsigned int size = 200; ImageAtlas atlas = ImageAtlas::New( size, size ); - EventThreadCallback* eventTrigger = EventThreadCallback::Get(); - CallbackBase* callback = eventTrigger->GetCallback(); - TraceCallStack& callStack = application.GetGlAbstraction().GetTextureTrace(); callStack.Reset(); callStack.Enable(true); @@ -179,6 +191,9 @@ int UtcDaliImageAtlasUploadP(void) Vector4 textureRect3; atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128) ); + EventThreadCallback* eventTrigger = EventThreadCallback::Get(); + CallbackBase* callback = eventTrigger->GetCallback(); + eventTrigger->WaitingForTrigger( 3 );// waiting until all three images are loaded CallbackBase::Execute( *callback ); diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp index 424e79d..779259d 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp @@ -417,6 +417,7 @@ int UtcDaliImageViewAsyncLoadingWithAltasing(void) loader.WaitForLoading();// waiting until the image to be loaded DALI_TEST_CHECK( loader.IsLoaded() ); + eventTrigger->WaitingForTrigger( 1 ); CallbackBase* callback = eventTrigger->GetCallback(); CallbackBase::Execute( *callback ); diff --git a/build/tizen/dali-toolkit/Makefile.am b/build/tizen/dali-toolkit/Makefile.am index 5c6d46c..f31838e 100644 --- a/build/tizen/dali-toolkit/Makefile.am +++ b/build/tizen/dali-toolkit/Makefile.am @@ -109,7 +109,7 @@ develapiprogressbardir = $(develapicontrolsdir)/progress-bar develapishadowviewdir = $(develapicontrolsdir)/shadow-view develapisuperblurviewdir = $(develapicontrolsdir)/super-blur-view develapifocusmanagerdir = $(develapidir)/focus-manager -develapiimageatlasdir = $(develapidir)/image-atlas +develapiimageloaderdir = $(develapidir)/image-loader develapiscriptingdir = $(develapidir)/scripting develapishadereffectsdir = $(develapidir)/shader-effects develapitransitioneffectsdir = $(develapidir)/transition-effects @@ -126,7 +126,7 @@ develapibuilder_HEADERS = $(devel_api_builder_header_files) develapieffectsview_HEADERS = $(devel_api_effects_view_header_files) develapifocusmanager_HEADERS = $(devel_api_focus_manager_header_files) develapigaussianblurview_HEADERS = $(devel_api_gaussian_blur_view_header_files) -develapiimageatlas_HEADERS = $(devel_api_image_atlas_header_files) +develapiimageloader_HEADERS = $(devel_api_image_loader_header_files) develapimagnifier_HEADERS = $(devel_api_magnifier_header_files) develapipageturnview_HEADERS = $(devel_api_page_turn_view_header_files) develapipopup_HEADERS = $(devel_api_popup_header_files) diff --git a/dali-toolkit/devel-api/file.list b/dali-toolkit/devel-api/file.list index 80e97ee..7cc6a2c 100755 --- a/dali-toolkit/devel-api/file.list +++ b/dali-toolkit/devel-api/file.list @@ -20,7 +20,8 @@ devel_api_src_files = \ $(devel_api_src_dir)/controls/text-controls/text-selection-toolbar.cpp \ $(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-atlas/image-atlas.cpp \ + $(devel_api_src_dir)/image-loader/async-image-loader.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_src_dir)/transition-effects/cube-transition-effect.cpp \ @@ -75,8 +76,9 @@ devel_api_shadow_view_header_files = \ devel_api_focus_manager_header_files = \ $(devel_api_src_dir)/focus-manager/keyinput-focus-manager.h -devel_api_image_atlas_header_files = \ - $(devel_api_src_dir)/image-atlas/image-atlas.h +devel_api_image_loader_header_files = \ + $(devel_api_src_dir)/image-loader/async-image-loader.h \ + $(devel_api_src_dir)/image-loader/image-atlas.h devel_api_scripting_header_files = \ $(devel_api_src_dir)/scripting/script.h \ diff --git a/dali-toolkit/devel-api/image-loader/async-image-loader.cpp b/dali-toolkit/devel-api/image-loader/async-image-loader.cpp new file mode 100644 index 0000000..a20ece1 --- /dev/null +++ b/dali-toolkit/devel-api/image-loader/async-image-loader.cpp @@ -0,0 +1,93 @@ +/* + * 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 "async-image-loader.h" + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Toolkit +{ + +AsyncImageLoader::AsyncImageLoader() +{ +} + +AsyncImageLoader::~AsyncImageLoader() +{ +} + +AsyncImageLoader::AsyncImageLoader( Internal::AsyncImageLoader* impl ) +: BaseHandle( impl ) +{} + +AsyncImageLoader::AsyncImageLoader( const AsyncImageLoader& handle ) +: BaseHandle( handle ) +{ +} + +AsyncImageLoader& AsyncImageLoader::operator=( const AsyncImageLoader& handle ) +{ + BaseHandle::operator=(handle); + return *this; +} + +AsyncImageLoader AsyncImageLoader::New() +{ + IntrusivePtr internal = Internal::AsyncImageLoader::New(); + return AsyncImageLoader( internal.Get() ); +} + +uint32_t AsyncImageLoader::Load( const std::string& url ) +{ + return GetImplementation(*this).Load( url, ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true ); +} + +uint32_t AsyncImageLoader::Load( const std::string& url, ImageDimensions size ) +{ + return GetImplementation(*this).Load( url, size, FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true ); +} + +uint32_t AsyncImageLoader::Load( const std::string& url, + ImageDimensions size, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection ) +{ + return GetImplementation(*this).Load( url, size, fittingMode, samplingMode, orientationCorrection ); +} + +bool AsyncImageLoader::Cancel( uint32_t loadingTaskId) +{ + return GetImplementation(*this).Cancel( loadingTaskId ); +} + +void AsyncImageLoader::CancelAll() +{ + GetImplementation(*this).CancelAll(); +} + +AsyncImageLoader::ImageLoadedSignalType& AsyncImageLoader::ImageLoadedSignal() +{ + return GetImplementation(*this).ImageLoadedSignal(); +} +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/devel-api/image-loader/async-image-loader.h b/dali-toolkit/devel-api/image-loader/async-image-loader.h new file mode 100644 index 0000000..82a2079 --- /dev/null +++ b/dali-toolkit/devel-api/image-loader/async-image-loader.h @@ -0,0 +1,193 @@ +#ifndef __DALI_TOOLKIT_ASYNC_IMAGE_LOADER_H__ +#define __DALI_TOOLKIT_ASYNC_IMAGE_LOADER_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. + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ +class PixelData; + +namespace Toolkit +{ + +namespace Internal DALI_INTERNAL +{ +class AsyncImageLoader; +} + +/** + *@brief The AysncImageLoader is used to load pixel data from the URL asynchronously. + * + * The images are loaded in a worker thread to avoid blocking the main event thread. + * + * Each load call is assigned with an ID, connect to the ImageLoadedSignal and receive the corresponding pixel data by comparing the ID. + * + * To make sure the signal is always received, the signal should get connected before invoking the load call. + * @code + * class MyClass : public ConnectionTracker + * { + * public: + * + * MyCallback( uint32_t id, PixelData pixelData) + * { + * if(id == mId1) + * { + * // use the loaded pixel data from the first image + * } + * else if( id == mId2 ) + * { + * // use the loaded pixel data from the second image + * } + * } + * + * uint32_t mId1; + * uint32_t mId2; + * }; + * + * MyClass myObject; + * AsyncImageLoader imageLoader = AsyncImageLoader::New(); + * // connect the signal here + * imageLoader.ImageLoadedSignal().Connect( &myObject, &MyClass::MyCallback ); + * // then invoke the load calls + * testCallback.mId1 = imageLoader.Load( "first_image_url.jpg" ); + * testCallback.mId2 = imageLoader.Load( "second_image_url.jpg" ); + * + * @endcode + */ +class DALI_IMPORT_API AsyncImageLoader : public BaseHandle +{ +public: + + /** + * @brief Type of signal for image loading finished. + * + * The signal is emit with the load ID and its corresponding loaded pixel data + */ + typedef Signal< void( uint32_t, PixelData ) > ImageLoadedSignalType; + +public: + + /** + * @brief Constructor which creates an empty AsyncImageLoader handle. + * + * Use AsyncImageLoader::New() to create an initialised object. + */ + AsyncImageLoader(); + + /** + * @brief Destructor + * + * This is non-virtual since derived Handle types must not contain data or virtual methods. + */ + ~AsyncImageLoader(); + + /** + * @brief This copy constructor is required for (smart) pointer semantics. + * + * @param [in] handle A reference to the copied handle + */ + AsyncImageLoader( const AsyncImageLoader& handle ); + + /** + * @brief This assignment operator is required for (smart) pointer semantics. + * + * @param [in] handle A reference to the copied handle + * @return A reference to this + */ + AsyncImageLoader& operator=( const AsyncImageLoader& handle ); + + /* + * @brief Create a new loader to load the image asynchronously in a worker thread. + * + * @return The image loader. + */ + static AsyncImageLoader New(); + + /** + * @brief Start a image loading task. + * + * @param[in] url The URL of the image file to load. + * @return The loading task id. + */ + uint32_t Load( const std::string& url ); + /* + * @brief Start a image loading task. + * + * @param[in] url The URL of the image file to load. + * @param[in] size The width and height to fit the loaded image to. + * @return The loading task id. + */ + uint32_t Load( const std::string& url, ImageDimensions size ); + + /* + * @brief Start a image loading task. + * + * @param[in] url The URL of the image file to load. + * @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] samplingMode The filtering method used when sampling pixels from the input image while fitting it to desired size. + * @param[in] orientationCorrection Reorient the image to respect any orientation metadata in its header. + * @return The loading task id. + */ + uint32_t Load( const std::string& url, + ImageDimensions size, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection ); + + /** + * @brief Cancel a image loading task if it is still queuing in the work thread. + * + * @param[in] loadingTaskId The task id returned when invoking the load call. + * @return If true, the loading task is removed from the queue, otherwise the loading is already implemented and unable to cancel anymore + */ + bool Cancel( uint32_t loadingTaskId); + + /** + * @brief Cancel all the loading tasks in the queue + */ + void CancelAll(); + + /** + * @brief Signal emit for connected callback functions to get access to the loaded pixel data. + * + * A callback of the following type may be connected: + * @code + * void YourCallbackName( uint32_t id, PixelData pixelData ); + * @endcode + * + * @return A signal object to Connect() with. + */ + ImageLoadedSignalType& ImageLoadedSignal(); + +public: // Not intended for developer use + + explicit DALI_INTERNAL AsyncImageLoader( Internal::AsyncImageLoader* impl ); + +}; + +} // namespace Toolkit + +} // namespace Dali + +#endif /* __DALI_TOOLKIT_ASYNC_IMAGE_LOADER_H__ */ diff --git a/dali-toolkit/devel-api/image-atlas/image-atlas.cpp b/dali-toolkit/devel-api/image-loader/image-atlas.cpp similarity index 91% rename from dali-toolkit/devel-api/image-atlas/image-atlas.cpp rename to dali-toolkit/devel-api/image-loader/image-atlas.cpp index 0c4a463..a7174d5 100644 --- a/dali-toolkit/devel-api/image-atlas/image-atlas.cpp +++ b/dali-toolkit/devel-api/image-loader/image-atlas.cpp @@ -19,7 +19,7 @@ #include "image-atlas.h" // INTERNAL INCLUDES -#include +#include namespace Dali { @@ -58,11 +58,17 @@ ImageAtlas ImageAtlas::New(SizeType width, SizeType height, return ImageAtlas( internal.Get() ); } -Image ImageAtlas::GetAtlas() +Texture ImageAtlas::GetAtlas() { return GetImplementation( *this ).GetAtlas(); } +float ImageAtlas::GetOccupancyRate() const +{ + return GetImplementation( *this ).GetOccupancyRate(); +} + + void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl ) { GetImplementation( *this ).SetBrokenImage( brokenImageUrl ); diff --git a/dali-toolkit/devel-api/image-atlas/image-atlas.h b/dali-toolkit/devel-api/image-loader/image-atlas.h similarity index 88% rename from dali-toolkit/devel-api/image-atlas/image-atlas.h rename to dali-toolkit/devel-api/image-loader/image-atlas.h index 2bc97ae..c85194f 100644 --- a/dali-toolkit/devel-api/image-atlas/image-atlas.h +++ b/dali-toolkit/devel-api/image-loader/image-atlas.h @@ -21,10 +21,10 @@ #include #include #include -#include #include #include #include +#include namespace Dali { @@ -37,10 +37,10 @@ class ImageAtlas; } /** - * @brief An ImageAtlas is a large image containing multiple smaller images. + * @brief An ImageAtlas is a large texture containing multiple smaller images. * - * Only images with url provided are supported for uploading. - * The image are loaded by a worker thread to avoid blocking the main event thread. + * Only images with url provided or pixel data are supported for uploading. + * The images are loaded by a worker thread to avoid blocking the main event thread. */ class DALI_IMPORT_API ImageAtlas : public BaseHandle { @@ -91,11 +91,18 @@ public: /** * @brief Get the atlas image. * - * This atlas image is still valid after destroying the ImageAtlas object. + * This atlas texture is still valid after destroying the ImageAtlas object. * - * @return the atlas image with type of Dali::Atlas + * @return The atlas texture */ - Image GetAtlas(); + Texture GetAtlas(); + + /* + * @brief Query what percentage of space is been occupied in the atlas. + * + * @return The occupancy rate of the atlas. + */ + float GetOccupancyRate() const; /** * @brief Set the broken image which is used to replace the image if loading fails. diff --git a/dali-toolkit/internal/file.list b/dali-toolkit/internal/file.list index 0b13dfb..7fb2109 100644 --- a/dali-toolkit/internal/file.list +++ b/dali-toolkit/internal/file.list @@ -85,9 +85,10 @@ toolkit_src_files = \ $(toolkit_src_dir)/filters/emboss-filter.cpp \ $(toolkit_src_dir)/filters/image-filter.cpp \ $(toolkit_src_dir)/filters/spread-filter.cpp \ - $(toolkit_src_dir)/image-atlas/atlas-packer.cpp \ - $(toolkit_src_dir)/image-atlas/image-atlas-impl.cpp \ - $(toolkit_src_dir)/image-atlas/image-load-thread.cpp \ + $(toolkit_src_dir)/image-loader/async-image-loader-impl.cpp \ + $(toolkit_src_dir)/image-loader/atlas-packer.cpp \ + $(toolkit_src_dir)/image-loader/image-atlas-impl.cpp \ + $(toolkit_src_dir)/image-loader/image-load-thread.cpp \ $(toolkit_src_dir)/styling/style-manager-impl.cpp \ $(toolkit_src_dir)/text/bidirectional-support.cpp \ $(toolkit_src_dir)/text/character-set-conversion.cpp \ diff --git a/dali-toolkit/internal/image-loader/async-image-loader-impl.cpp b/dali-toolkit/internal/image-loader/async-image-loader-impl.cpp new file mode 100644 index 0000000..d25ea5c --- /dev/null +++ b/dali-toolkit/internal/image-loader/async-image-loader-impl.cpp @@ -0,0 +1,100 @@ +/* + * 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 "async-image-loader-impl.h" + +// EXTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +AsyncImageLoader::AsyncImageLoader() +: mLoadedSignal(), + mLoadThread( new EventThreadCallback( MakeCallback( this, &AsyncImageLoader::ProcessLoadedImage ) ) ), + mLoadTaskId( 0 ), + mIsLoadThreadStarted( false ) +{ +} + +AsyncImageLoader::~AsyncImageLoader() +{ + mLoadThread.CancelAll(); +} + +IntrusivePtr AsyncImageLoader::New() +{ + IntrusivePtr internal = new AsyncImageLoader(); + return internal; +} + +uint32_t AsyncImageLoader::Load( const std::string& url, + ImageDimensions size, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection ) +{ + if( !mIsLoadThreadStarted ) + { + mLoadThread.Start(); + mIsLoadThreadStarted = true; + } + + BitmapLoader loader = BitmapLoader::New( url, size, fittingMode, samplingMode, orientationCorrection ); + + mLoadThread.AddTask( new LoadingTask( ++mLoadTaskId, loader ) ); + + return mLoadTaskId; +} + +Toolkit::AsyncImageLoader::ImageLoadedSignalType& AsyncImageLoader::ImageLoadedSignal() +{ + return mLoadedSignal; +} + +bool AsyncImageLoader::Cancel( uint32_t loadingTaskId ) +{ + return mLoadThread.CancelTask( loadingTaskId ); +} + +void AsyncImageLoader::CancelAll() +{ + mLoadThread.CancelAll(); +} + +void AsyncImageLoader::ProcessLoadedImage() +{ + while( LoadingTask *next = mLoadThread.NextCompletedTask() ) + { + mLoadedSignal.Emit( next->id, next->loader.GetPixelData() ); + delete next; + } +} + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali diff --git a/dali-toolkit/internal/image-loader/async-image-loader-impl.h b/dali-toolkit/internal/image-loader/async-image-loader-impl.h new file mode 100644 index 0000000..cb0311c --- /dev/null +++ b/dali-toolkit/internal/image-loader/async-image-loader-impl.h @@ -0,0 +1,122 @@ +#ifndef DALI_TOOLKIT_ASYNC_IMAGE_LOADER_IMPL_H__ +#define DALI_TOOLKIT_ASYNC_IMAGE_LOADER_IMPL_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. + */ + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +class AsyncImageLoader : public BaseObject +{ +public: + + /** + * Constructor + */ + AsyncImageLoader(); + + /** + * @copydoc Toolkit::AsyncImageLoader::New() + */ + static IntrusivePtr New(); + + /** + * @copydoc Toolkit::AsyncImageLoader::Load( const std::string&, ImageDimensions,FittingMode::Type, SamplingMode::Type, bool ) + */ + uint32_t Load( const std::string& url, + ImageDimensions size, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection ); + + /** + * @copydoc Toolkit::AsyncImageLoader::ImageLoadedSignal + */ + Toolkit::AsyncImageLoader::ImageLoadedSignalType& ImageLoadedSignal(); + + /** + * @copydoc Toolkit::AsyncImageLoader::Cancel + */ + bool Cancel( uint32_t loadingTaskId ); + + /** + * @copydoc Toolkit::AsyncImageLoader::CancelAll + */ + void CancelAll(); + + /** + * Process the completed loading task from the worker thread. + */ + void ProcessLoadedImage(); + +protected: + + /** + * Destructor + */ + ~AsyncImageLoader(); + +private: + + Toolkit::AsyncImageLoader::ImageLoadedSignalType mLoadedSignal; + + ImageLoadThread mLoadThread; + uint32_t mLoadTaskId; + bool mIsLoadThreadStarted; + + +}; + +} // namespace Internal + +inline const Internal::AsyncImageLoader& GetImplementation( const Toolkit::AsyncImageLoader& loader ) +{ + DALI_ASSERT_ALWAYS( loader && "AsyncImageLoader handle is empty" ); + + const BaseObject& object = loader.GetBaseObject(); + + return static_cast( object ); +} + +inline Internal::AsyncImageLoader& GetImplementation( Toolkit::AsyncImageLoader& loader ) +{ + DALI_ASSERT_ALWAYS( loader && "AsyncImageLoader handle is empty" ); + + BaseObject& object = loader.GetBaseObject(); + + return static_cast( object ); +} + +} // namespace Toolkit + +} // namespace Dali + +#endif /* DALI_TOOLKIT_ASYNC_IMAGE_LOADER_IMPL_H__ */ diff --git a/dali-toolkit/internal/image-atlas/atlas-packer.cpp b/dali-toolkit/internal/image-loader/atlas-packer.cpp similarity index 88% rename from dali-toolkit/internal/image-atlas/atlas-packer.cpp rename to dali-toolkit/internal/image-loader/atlas-packer.cpp index b061ec5..f452399 100644 --- a/dali-toolkit/internal/image-atlas/atlas-packer.cpp +++ b/dali-toolkit/internal/image-loader/atlas-packer.cpp @@ -148,24 +148,22 @@ void AtlasPacker::SplitNode( Node* node, SizeType blockWidth, SizeType blockHeig AtlasPacker::Node* AtlasPacker::SearchNode( Node* node, SizeType packPositionX, SizeType packPositionY, SizeType blockWidth, SizeType blockHeight ) { - if( node == NULL ) - { - return NULL; - } - - if( node->child[0] != NULL) //not a leaf + if( node != NULL ) { - Node* newNode = SearchNode(node->child[0], packPositionX, packPositionY, blockWidth, blockHeight); - if( newNode == NULL )// try search from the second child. + if( node->child[0] != NULL) //not a leaf { - newNode = SearchNode(node->child[1], packPositionX, packPositionY, blockWidth, blockHeight); + Node* newNode = SearchNode(node->child[0], packPositionX, packPositionY, blockWidth, blockHeight); + if( newNode == NULL )// try search from the second child. + { + newNode = SearchNode(node->child[1], packPositionX, packPositionY, blockWidth, blockHeight); + } + return newNode; + } + else if( ApproximatelyEqual(node->rectArea.x, packPositionX) && ApproximatelyEqual(node->rectArea.y, packPositionY ) + && ApproximatelyEqual(node->rectArea.width, blockWidth) && ApproximatelyEqual( node->rectArea.height, blockHeight) ) + { + return node; } - return newNode; - } - else if( ApproximatelyEqual(node->rectArea.x, packPositionX) && ApproximatelyEqual(node->rectArea.y, packPositionY ) - && ApproximatelyEqual(node->rectArea.width, blockWidth) && ApproximatelyEqual( node->rectArea.height, blockHeight) ) - { - return node; } return NULL; diff --git a/dali-toolkit/internal/image-atlas/atlas-packer.h b/dali-toolkit/internal/image-loader/atlas-packer.h similarity index 100% rename from dali-toolkit/internal/image-atlas/atlas-packer.h rename to dali-toolkit/internal/image-loader/atlas-packer.h diff --git a/dali-toolkit/internal/image-atlas/image-atlas-impl.cpp b/dali-toolkit/internal/image-loader/image-atlas-impl.cpp similarity index 69% rename from dali-toolkit/internal/image-atlas/image-atlas-impl.cpp rename to dali-toolkit/internal/image-loader/image-atlas-impl.cpp index 0459de5..67f257c 100644 --- a/dali-toolkit/internal/image-atlas/image-atlas-impl.cpp +++ b/dali-toolkit/internal/image-loader/image-atlas-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * 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. @@ -22,6 +22,7 @@ #include #include #include +#include #include namespace Dali @@ -34,45 +35,40 @@ namespace Internal { ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat ) -: mPacker( width, height ), - mLoadQueue(), - mCompleteQueue( new EventThreadCallback( MakeCallback( this, &ImageAtlas::UploadToAtlas ) ) ), - mLoadingThread( mLoadQueue, mCompleteQueue ), +: mAtlas( Texture::New( Dali::TextureType::TEXTURE_2D, pixelFormat, width, height ) ), + mPacker( width, height ), + mAsyncLoader( Toolkit::AsyncImageLoader::New() ), mBrokenImageUrl(""), mBrokenImageSize(), - mPixelFormat( pixelFormat ), - mLoadingThreadStarted( false ) + mWidth( static_cast(width) ), + mHeight( static_cast( height ) ), + mPixelFormat( pixelFormat ) { - mAtlas = Atlas::New( width, height, pixelFormat ); - mWidth = static_cast(width); - mHeight = static_cast( height ); + mAsyncLoader.ImageLoadedSignal().Connect( this, &ImageAtlas::UploadToAtlas ); } ImageAtlas::~ImageAtlas() { - if( mLoadingThreadStarted ) - { - // add an empty task would stop the loading thread from contional wait. - mLoadQueue.AddTask( NULL ); - // stop the loading thread - mLoadingThread.Join(); - // The atlas can still be used as texture after ImageAtlas has been thrown away, - // so make sure all the loaded bitmap been uploaded to atlas - UploadToAtlas(); - } + mIdRectContainer.Clear(); } IntrusivePtr ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat ) { IntrusivePtr internal = new ImageAtlas( width, height, pixelFormat ); + return internal; } -Image ImageAtlas::GetAtlas() +Texture ImageAtlas::GetAtlas() { return mAtlas; } +float ImageAtlas::GetOccupancyRate() const +{ + return 1.f - static_cast( mPacker.GetAvailableArea() ) / ( mWidth*mHeight ); +} + void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl ) { mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl ); @@ -107,24 +103,12 @@ bool ImageAtlas::Upload( Vector4& textureRect, } } - if( static_cast(dimensions.GetWidth() * dimensions.GetHeight()) > mPacker.GetAvailableArea() ) - { - return false; - } - unsigned int packPositionX = 0; unsigned int packPositionY = 0; if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) ) { - if( !mLoadingThreadStarted ) - { - mLoadingThread.Start(); - mLoadingThreadStarted = true; - } - - LoadingTask* newTask = new LoadingTask(BitmapLoader::New(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection ), - packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight()); - mLoadQueue.AddTask( newTask ); + unsigned short loadId = mAsyncLoader.Load( url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection ); + mIdRectContainer.PushBack( new IdRectPair( loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight() ) ); // apply the half pixel correction textureRect.x = ( static_cast( packPositionX ) +0.5f ) / mWidth; // left @@ -144,7 +128,7 @@ bool ImageAtlas::Upload( Vector4& textureRect, PixelData pixelData ) unsigned int packPositionY = 0; if( mPacker.Pack( pixelData.GetWidth(), pixelData.GetHeight(), packPositionX, packPositionY ) ) { - mAtlas.Upload( pixelData, packPositionX, packPositionY ); + mAtlas.Upload( pixelData, 0u, 0u, packPositionX, packPositionY, pixelData.GetWidth(), pixelData.GetHeight() ); // apply the half pixel correction textureRect.x = ( static_cast( packPositionX ) +0.5f ) / mWidth; // left @@ -166,38 +150,36 @@ void ImageAtlas::Remove( const Vector4& textureRect ) static_cast((textureRect.w-textureRect.y)*mHeight+1.f) ); } -void ImageAtlas::UploadToAtlas() +void ImageAtlas::UploadToAtlas( unsigned int id, PixelData pixelData ) { - while( LoadingTask* next = mCompleteQueue.NextTask() ) + if( mIdRectContainer[0]->loadTaskId == id) { - if( ! next->loader.IsLoaded() ) + if( !pixelData || ( pixelData.GetWidth() ==0 && pixelData.GetHeight() == 0 )) { if(!mBrokenImageUrl.empty()) // replace with the broken image { - UploadBrokenImage( next->packRect ); + UploadBrokenImage( mIdRectContainer[0]->packRect ); } - - DALI_LOG_ERROR( "Failed to load the image: %s\n", (next->loader.GetUrl()).c_str()); } else { - if( next->loader.GetPixelData().GetWidth() < next->packRect.width || next->loader.GetPixelData().GetHeight() < next->packRect.height ) + if( pixelData.GetWidth() < mIdRectContainer[0]->packRect.width || pixelData.GetHeight() < mIdRectContainer[0]->packRect.height ) { DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n", - next->loader.GetPixelData().GetWidth(), - next->loader.GetPixelData().GetHeight(), - next->packRect.width, - next->packRect.height ); + pixelData.GetWidth(), pixelData.GetHeight(), + mIdRectContainer[0]->packRect.width, mIdRectContainer[0]->packRect.height ); } - mAtlas.Upload( next->loader.GetPixelData(), next->packRect.x, next->packRect.y ); + mAtlas.Upload( pixelData, 0u, 0u, + mIdRectContainer[0]->packRect.x, mIdRectContainer[0]->packRect.y, + mIdRectContainer[0]->packRect.width, mIdRectContainer[0]->packRect.height ); } - - delete next; } + + mIdRectContainer.Erase( mIdRectContainer.Begin() ); } -void ImageAtlas::UploadBrokenImage( const Rect& area ) +void ImageAtlas::UploadBrokenImage( const Rect& area ) { BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) ); loader.Load(); @@ -228,10 +210,10 @@ void ImageAtlas::UploadBrokenImage( const Rect& area ) { buffer[idx] = 0x00; } - mAtlas.Upload( background, area.x, area.y ); + mAtlas.Upload( background, 0u, 0u, area.x, area.y, area.width, area.height ); } - mAtlas.Upload( loader.GetPixelData(), packX, packY ); + mAtlas.Upload( loader.GetPixelData(), 0u, 0u, packX, packY, loadedWidth, loadedHeight ); } } // namespace Internal diff --git a/dali-toolkit/internal/image-atlas/image-atlas-impl.h b/dali-toolkit/internal/image-loader/image-atlas-impl.h similarity index 69% rename from dali-toolkit/internal/image-atlas/image-atlas-impl.h rename to dali-toolkit/internal/image-loader/image-atlas-impl.h index 524b257..34924d3 100644 --- a/dali-toolkit/internal/image-atlas/image-atlas-impl.h +++ b/dali-toolkit/internal/image-loader/image-atlas-impl.h @@ -20,12 +20,14 @@ // EXTERNAL INCLUDES #include #include +#include +#include #include // INTERNAL INCLUDES -#include -#include -#include +#include +#include +#include namespace Dali { @@ -37,7 +39,7 @@ namespace Toolkit namespace Internal { -class ImageAtlas : public BaseObject +class ImageAtlas : public BaseObject, public ConnectionTracker { public: @@ -59,7 +61,12 @@ public: /** * @copydoc Toolkit::ImageAtlas::GetAtlas */ - Image GetAtlas(); + Texture GetAtlas(); + + /** + * @copydoc Toolkit::ImageAtlas::GetOccupancyRate + */ + float GetOccupancyRate() const; /** * @copydoc Toolkit::ImageAtlas::SetBrokenImage @@ -95,16 +102,16 @@ protected: private: /** - * Upload the bitmap to atlas when the image is loaded in the worker thread. + * @copydoc PixelDataRequester::ProcessPixels */ - void UploadToAtlas(); + void UploadToAtlas( unsigned int id, PixelData pixelData ); /** * Upload broken image * * @param[in] area The pixel area for uploading. */ - void UploadBrokenImage( const Rect& area ); + void UploadBrokenImage( const Rect& area ); // Undefined ImageAtlas( const ImageAtlas& imageAtlas); @@ -114,19 +121,31 @@ private: private: - Atlas mAtlas; - AtlasPacker mPacker; - - LoadQueue mLoadQueue; - CompleteQueue mCompleteQueue; - ImageLoadThread mLoadingThread; - - std::string mBrokenImageUrl; - ImageDimensions mBrokenImageSize; - float mWidth; - float mHeight; - Pixel::Format mPixelFormat; - bool mLoadingThreadStarted; + struct IdRectPair + { + IdRectPair( unsigned short loadTaskId, + unsigned int packPositionX, + unsigned int packPositionY, + unsigned int width, + unsigned int height ) + : loadTaskId( loadTaskId ), + packRect( packPositionX, packPositionY, width, height ) + {} + + unsigned short loadTaskId; + Rect packRect; + }; + + OwnerContainer mIdRectContainer; + + Texture mAtlas; + AtlasPacker mPacker; + Toolkit::AsyncImageLoader mAsyncLoader; + std::string mBrokenImageUrl; + ImageDimensions mBrokenImageSize; + float mWidth; + float mHeight; + Pixel::Format mPixelFormat; }; diff --git a/dali-toolkit/internal/image-atlas/image-load-thread.cpp b/dali-toolkit/internal/image-loader/image-load-thread.cpp similarity index 52% rename from dali-toolkit/internal/image-atlas/image-load-thread.cpp rename to dali-toolkit/internal/image-loader/image-load-thread.cpp index c7da85b..a27db14 100644 --- a/dali-toolkit/internal/image-atlas/image-load-thread.cpp +++ b/dali-toolkit/internal/image-loader/image-load-thread.cpp @@ -27,46 +27,45 @@ namespace Toolkit namespace Internal { -LoadingTask::LoadingTask(BitmapLoader loader, uint32_t packPositionX, uint32_t packPositionY, uint32_t width, uint32_t height ) +LoadingTask::LoadingTask(uint32_t id, BitmapLoader loader ) : loader( loader ), - packRect( packPositionX, packPositionY, width, height ) + id( id ) { } -LoadQueue::LoadQueue() +ImageLoadThread::ImageLoadThread( EventThreadCallback* trigger ) +: mTrigger( trigger ) { } -LoadQueue::~LoadQueue() +ImageLoadThread::~ImageLoadThread() { + // add an empty task would stop the thread from conditional wait. + AddTask( NULL ); + // stop the thread + Join(); + + delete mTrigger; } -LoadingTask* LoadQueue::NextTask() +void ImageLoadThread::Run() { - // Lock while popping task out from the queue - ConditionalWait::ScopedLock lock( mConditionalWait ); - - while( mTasks.Empty() ) + while( LoadingTask* task = NextTaskToProcess()) { - mConditionalWait.Wait( lock ); + task->loader.Load(); + AddCompletedTask( task ); } - - Vector< LoadingTask* >::Iterator next = mTasks.Begin(); - LoadingTask* nextTask = *next; - mTasks.Erase( next ); - - return nextTask; } -void LoadQueue::AddTask( LoadingTask* task ) +void ImageLoadThread::AddTask( LoadingTask* task ) { bool wasEmpty = false; { // Lock while adding task to the queue ConditionalWait::ScopedLock lock( mConditionalWait ); - wasEmpty = mTasks.Empty(); - mTasks.PushBack( task ); + wasEmpty = mLoadQueue.Empty(); + mLoadQueue.PushBack( task ); } if( wasEmpty) @@ -76,62 +75,83 @@ void LoadQueue::AddTask( LoadingTask* task ) } } -CompleteQueue::CompleteQueue(EventThreadCallback* trigger) -: mTrigger( trigger ) -{} - -CompleteQueue::~CompleteQueue() -{ - delete mTrigger; -} - -LoadingTask* CompleteQueue::NextTask() +LoadingTask* ImageLoadThread::NextCompletedTask() { // Lock while popping task out from the queue Mutex::ScopedLock lock( mMutex ); - if( mTasks.Empty() ) + if( mCompleteQueue.Empty() ) { return NULL; } - Vector< LoadingTask* >::Iterator next = mTasks.Begin(); + Vector< LoadingTask* >::Iterator next = mCompleteQueue.Begin(); LoadingTask* nextTask = *next; - mTasks.Erase( next ); + mCompleteQueue.Erase( next ); return nextTask; } -void CompleteQueue::AddTask( LoadingTask* task ) +bool ImageLoadThread::CancelTask( uint32_t loadingTaskId ) { - // Lock while adding task to the queue - Mutex::ScopedLock lock( mMutex ); - mTasks.PushBack( task ); + // Lock while remove task from the queue + ConditionalWait::ScopedLock lock( mConditionalWait ); - // wake up the main thread - mTrigger->Trigger(); + for( Vector< LoadingTask* >::Iterator iter = mLoadQueue.Begin(); iter != mLoadQueue.End(); iter++ ) + { + if( (*iter)->id == loadingTaskId ) + { + delete (*iter); + mLoadQueue.Erase( iter ); + return true; + } + } + + return false; } -ImageLoadThread::ImageLoadThread( LoadQueue& loadQueue, CompleteQueue& completeQueue ) -: mLoadQueue( loadQueue ), - mCompleteQueue( completeQueue ) +void ImageLoadThread::CancelAll() { -} + // Lock while remove task from the queue + ConditionalWait::ScopedLock lock( mConditionalWait ); -ImageLoadThread::~ImageLoadThread() -{ + for( Vector< LoadingTask* >::Iterator iter = mLoadQueue.Begin(); iter != mLoadQueue.End(); iter++ ) + { + delete (*iter); + } + mLoadQueue.Clear(); } -void ImageLoadThread::Run() +LoadingTask* ImageLoadThread::NextTaskToProcess() { - while( LoadingTask* task = mLoadQueue.NextTask() ) + // Lock while popping task out from the queue + ConditionalWait::ScopedLock lock( mConditionalWait ); + + while( mLoadQueue.Empty() ) { - task->loader.Load(); - mCompleteQueue.AddTask( task ); + mConditionalWait.Wait( lock ); } + + Vector< LoadingTask* >::Iterator next = mLoadQueue.Begin(); + LoadingTask* nextTask = *next; + mLoadQueue.Erase( next ); + + return nextTask; } +void ImageLoadThread::AddCompletedTask( LoadingTask* task ) +{ + // Lock while adding task to the queue + Mutex::ScopedLock lock( mMutex ); + mCompleteQueue.PushBack( task ); + + // wake up the main thread + mTrigger->Trigger(); +} + + + } // namespace Internal } // namespace Toolkit diff --git a/dali-toolkit/internal/image-atlas/image-load-thread.h b/dali-toolkit/internal/image-loader/image-load-thread.h similarity index 59% rename from dali-toolkit/internal/image-atlas/image-load-thread.h rename to dali-toolkit/internal/image-loader/image-load-thread.h index 700815c..4eddce7 100644 --- a/dali-toolkit/internal/image-atlas/image-load-thread.h +++ b/dali-toolkit/internal/image-loader/image-load-thread.h @@ -43,7 +43,7 @@ struct LoadingTask /** * Constructor. */ - LoadingTask( BitmapLoader loader, uint32_t packPositionX, uint32_t packPositionY, uint32_t width, uint32_t height ); + LoadingTask( uint32_t id, BitmapLoader loader ); private: @@ -55,123 +55,69 @@ private: public: - BitmapLoader loader; ///< The loader used to load the bitmap from URL - Rect packRect; ///< The x coordinate of the position to pack the image. - + BitmapLoader loader; ///< The loader used to load the bitmap from URL + uint32_t id; ///< The id associated with this task. }; + /** - * The queue of the tasks waiting to load the bitmap from the URL in the worker thread/ + * The worker thread for image loading. */ -class LoadQueue //: public TaskQueue +class ImageLoadThread : public Thread { public: /** - * Constructor + * Constructor. + * + * @param[in] mTrigger The trigger to wake up the main thread. */ - LoadQueue(); + ImageLoadThread( EventThreadCallback* mTrigger ); /** * Destructor. */ - ~LoadQueue(); - - /** - * Pop the next task out from the queue. - * - * @return The next task to be processed. - */ - LoadingTask* NextTask(); + virtual ~ImageLoadThread(); /** - * Add a task in to the queue + * Add a task in to the loading queue * * @param[in] task The task added to the queue. */ void AddTask( LoadingTask* task ); -private: - - // Undefined - LoadQueue( const LoadQueue& queue ); - - // Undefined - LoadQueue& operator=( const LoadQueue& queue ); - -private: - - Vector< LoadingTask* > mTasks; - ConditionalWait mConditionalWait; -}; - -/** - * The queue of the tasks, with the image loaded, waiting for the main thread to upload the bitmap. - */ -class CompleteQueue //: public TaskQueue -{ -public: - /** - * Constructor + * Pop the next task out from the completed queue. * - * @param[in] mTrigger The trigger to wake up the main thread. - */ - CompleteQueue( EventThreadCallback* mTrigger ); - - /** - * Destructor. + * @return The next task to be processed. */ - ~CompleteQueue(); + LoadingTask* NextCompletedTask(); /** - * Pop the next task out from the queue. - * - * @return The next task to be processed. + * Remove the loading task from the waiting queue. */ - LoadingTask* NextTask(); + bool CancelTask( uint32_t loadingTaskId ); /** - * Add a task in to the queue - * - * @param[in] task The task added to the queue. + * Remove all the loading tasks in the waiting queue. */ - void AddTask( LoadingTask* task ); - -private: - - // Undefined - CompleteQueue( const CompleteQueue& queue ); - - // Undefined - CompleteQueue& operator=( const CompleteQueue& queue ); + void CancelAll(); private: - Vector< LoadingTask* > mTasks; - Dali::Mutex mMutex; - EventThreadCallback* mTrigger; -}; - -/** - * The worker thread for image loading. - */ -class ImageLoadThread : public Thread -{ -public: - /** - * Constructor. + * Pop the next loading task out from the queue to process. * - * @param[in] loadQueue The task queue with images for loading. - * @param[in] completeQurue The task queue with images loaded. + * @return The next task to be processed. */ - ImageLoadThread( LoadQueue& loadQueue, CompleteQueue& completeQueue ); + LoadingTask* NextTaskToProcess(); /** - * Destructor. + * Add a task in to the loading queue + * + * @param[in] task The task added to the queue. */ - virtual ~ImageLoadThread(); + void AddCompletedTask( LoadingTask* task ); protected: @@ -191,8 +137,12 @@ private: private: - LoadQueue& mLoadQueue; /// mLoadQueue; /// mCompleteQueue; /// // INTERNAL INCLUDES -#include +#include namespace Dali {