utc-Dali-Model3dView.cpp
utc-Dali-ControlRenderer.cpp
utc-Dali-RendererFactory.cpp
+ utc-Dali-ImageAtlas.cpp
)
# Append list of test harness files (Won't get parsed for test cases)
dali-toolkit-test-utils/toolkit-accessibility-adaptor.cpp
dali-toolkit-test-utils/toolkit-application.cpp
dali-toolkit-test-utils/toolkit-clipboard.cpp
+ dali-toolkit-test-utils/toolkit-event-thread-callback.cpp
dali-toolkit-test-utils/toolkit-imf-manager.cpp
dali-toolkit-test-utils/toolkit-physical-keyboard.cpp
dali-toolkit-test-utils/toolkit-style-monitor.cpp
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -ggdb --coverage -Wall -Werror=return-type")
+ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" )
+
FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS})
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}")
ENDFOREACH(directory ${CAPI_LIB_LIBRARY_DIRS})
ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.cpp ${TC_SOURCES})
TARGET_LINK_LIBRARIES(${EXEC_NAME}
${${CAPI_LIB}_LIBRARIES}
+ -lpthread
)
INSTALL(PROGRAMS ${EXEC_NAME}
--- /dev/null
+/*
+ * Copyright (c) 2015 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 "toolkit-event-thread-callback.h"
+
+// EXTERNAL INCLUDES
+#include <cstddef>
+#include <semaphore.h>
+#include <math.h>
+
+namespace Dali
+{
+
+namespace
+{
+EventThreadCallback* gEventThreadCallback = NULL;
+}
+
+struct EventThreadCallback::Impl
+{
+ CallbackBase* callback;
+ unsigned int triggeredCount;
+ unsigned int expectedCount;
+ sem_t mySemaphore;
+};
+
+EventThreadCallback::EventThreadCallback( CallbackBase* callback )
+: mImpl( new Impl() )
+{
+ mImpl->callback = callback;
+ mImpl->triggeredCount = 0u;
+ mImpl->expectedCount = INFINITY;
+ sem_init( &(mImpl->mySemaphore), 0, 0 );
+ gEventThreadCallback = this;
+}
+
+EventThreadCallback::~EventThreadCallback()
+{
+ delete mImpl;
+}
+
+void EventThreadCallback::Trigger()
+{
+ mImpl->triggeredCount++;
+ if( mImpl->triggeredCount >= mImpl->expectedCount )
+ {
+ sem_post( &(mImpl->mySemaphore) );
+ }
+}
+
+void EventThreadCallback::WaitingForTrigger(unsigned int count)
+{
+ if( mImpl->triggeredCount >= count )
+ {
+ return;
+ }
+ mImpl->expectedCount = count;
+ sem_wait( &(mImpl->mySemaphore) );
+}
+
+CallbackBase* EventThreadCallback::GetCallback()
+{
+ return mImpl->callback;
+}
+
+EventThreadCallback* EventThreadCallback::Get()
+{
+ return gEventThreadCallback;
+}
+
+}
--- /dev/null
+#ifndef __DALI_TOOLKIT_EVENT_THREAD_CALLBACK_H__
+#define __DALI_TOOLKIT_EVENT_THREAD_CALLBACK_H__
+
+/*
+ * Copyright (c) 2015 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.
+ *
+ */
+#define __DALI_EVENT_THREAD_CALLBACK_H_
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/signals/callback.h>
+
+namespace Dali
+{
+
+class DALI_IMPORT_API EventThreadCallback
+{
+public:
+
+ EventThreadCallback( CallbackBase* callback );
+
+ ~EventThreadCallback();
+
+ void Trigger();
+
+ void WaitingForTrigger(unsigned int count);
+
+ CallbackBase* GetCallback();
+
+ static EventThreadCallback* Get();
+
+private:
+
+ // undefined copy constructor.
+ EventThreadCallback( const EventThreadCallback& );
+
+ // undefined assignment operator
+ EventThreadCallback& operator=( const EventThreadCallback& );
+
+private:
+
+ struct Impl;
+ Impl* mImpl;
+};
+
+}
+
+#endif /* __DALI_TOOLKIT_EVENT_THREAD_CALLBACK_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2015 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 <stdlib.h>
+#include <unistd.h>
+#include <dali/dali.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-event-thread-callback.h>
+#include <dali/devel-api/images/atlas.h>
+#include <dali-toolkit/devel-api/image-atlas/image-atlas.h>
+#include <dali-toolkit/public-api/controls/image-view/image-view.h>
+
+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";
+
+// this is image is not exist, for negative test
+static const char* gImageNonExist = "non-exist.jpg";
+
+const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS)
+
+Rect<int> TextureCoordinateToPixelArea( const Vector4& textureCoordinate, float size )
+{
+ Vector4 temp = textureCoordinate * size;
+ Rect<int> pixelArea;
+ pixelArea.x = static_cast<int>( temp.x );
+ pixelArea.y = static_cast<int>( temp.y );
+ pixelArea.width = static_cast<int>( temp.z-temp.x+1.f );
+ pixelArea.height = static_cast<int>( temp.w-temp.y+1.f );
+
+ return pixelArea;
+}
+
+bool IsOverlap( Rect<int> rect1, Rect<int> rect2 )
+{
+ return rect1.x < rect2.x+rect2.width
+ && rect2.x < rect1.x+rect1.width
+ && rect1.y < rect2.y+rect2.height
+ && rect2.y < rect1.y+rect1.height;
+}
+
+}
+
+void dali_image_atlas_startup(void)
+{
+ test_return_value = TET_UNDEF;
+}
+
+void dali_image_atlas_cleanup(void)
+{
+ test_return_value = TET_PASS;
+}
+
+int UtcDaliImageAtlasNew(void)
+{
+ ToolkitTestApplication application;
+
+ // invoke default handle constructor
+ ImageAtlas atlas;
+
+ DALI_TEST_CHECK( !atlas );
+
+ // initialise handle
+ atlas = ImageAtlas::New( 32, 32 );
+
+ DALI_TEST_CHECK( atlas );
+ END_TEST;
+}
+
+int UtcDaliImageAtlasCopyConstructor(void)
+{
+ ToolkitTestApplication application;
+
+ ImageAtlas atlas = ImageAtlas::New( 32, 32);
+ ImageAtlas atlasCopy(atlas);
+
+ DALI_TEST_EQUALS( (bool)atlasCopy, true, TEST_LOCATION );
+ END_TEST;
+}
+
+int UtcDaliImageAtlasAssignmentOperator(void)
+{
+ ToolkitTestApplication application;
+
+ ImageAtlas atlas = ImageAtlas::New( 32, 32 );
+
+ ImageAtlas atlas2;
+ DALI_TEST_EQUALS( (bool)atlas2, false, TEST_LOCATION );
+
+ atlas2 = atlas;
+ DALI_TEST_EQUALS( (bool)atlas2, true, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliImageAtlasGetAtlas(void)
+{
+ ToolkitTestApplication application;
+
+ ImageAtlas atlas = ImageAtlas::New( 32, 32 );
+ Image 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 UtcDaliImageAtlasSetBrokenImage(void)
+{
+ ToolkitTestApplication application;
+ unsigned int size = 200;
+ ImageAtlas atlas = ImageAtlas::New( size, size );
+
+ Vector4 textureRect;
+ atlas.Upload( textureRect, gImageNonExist );
+ DALI_TEST_EQUALS( textureRect, Vector4::ZERO, TEST_LOCATION );
+
+ // Set broken image
+ TestPlatformAbstraction& platform = application.GetPlatform();
+ platform.SetClosestImageSize(Vector2( 34, 34));
+ atlas.SetBrokenImage( gImage_34_RGBA );
+
+ // the non-exit image will be replaced with the broken image
+ platform.SetClosestImageSize(Vector2( 0, 0));
+ atlas.Upload( textureRect, gImageNonExist );
+
+ Rect<int> pixelArea = TextureCoordinateToPixelArea(textureRect, size);
+ DALI_TEST_EQUALS( pixelArea.width, 34.f, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.height, 34.f, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliImageAtlasUploadP(void)
+{
+ ToolkitTestApplication application;
+ 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);
+
+ Vector4 textureRect1;
+ atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34) );
+ Vector4 textureRect2;
+ atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50) );
+ Vector4 textureRect3;
+ atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128) );
+
+ eventTrigger->WaitingForTrigger( 3 );// waiting until all three images are loaded
+
+ CallbackBase::Execute( *callback );
+
+ application.SendNotification();
+ application.Render(RENDER_FRAME_INTERVAL);
+
+ callStack.Enable(false);
+
+ Rect<int> pixelArea1 = TextureCoordinateToPixelArea(textureRect1, size);
+ DALI_TEST_EQUALS( pixelArea1.width, 34, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea1.height, 34, TEST_LOCATION );
+ std::stringstream out;
+ out<<pixelArea1.x<<", "<<pixelArea1.y<<", "<<pixelArea1.width<<", "<<pixelArea1.height;
+ DALI_TEST_CHECK( callStack.FindMethodAndParams("TexSubImage2D", out.str()) );
+
+ Rect<int> pixelArea2 = TextureCoordinateToPixelArea(textureRect2, size);
+ DALI_TEST_EQUALS( pixelArea2.width, 50, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea2.height, 50, TEST_LOCATION );
+ out.str("");
+ out<<pixelArea2.x<<", "<<pixelArea2.y<<", "<<pixelArea2.width<<", "<<pixelArea2.height;
+ DALI_TEST_CHECK( callStack.FindMethodAndParams("TexSubImage2D", out.str()) );
+
+ Rect<int> pixelArea3 = TextureCoordinateToPixelArea(textureRect3, size);
+ DALI_TEST_EQUALS( pixelArea3.width, 128, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea3.height, 128, TEST_LOCATION );
+ out.str("");
+ out<<pixelArea3.x<<", "<<pixelArea3.y<<", "<<pixelArea3.width<<", "<<pixelArea3.height;
+ DALI_TEST_CHECK( callStack.FindMethodAndParams("TexSubImage2D", out.str()) );
+
+ DALI_TEST_CHECK( ! IsOverlap(pixelArea1, pixelArea2) );
+ DALI_TEST_CHECK( ! IsOverlap(pixelArea1, pixelArea3) );
+ DALI_TEST_CHECK( ! IsOverlap(pixelArea2, pixelArea3) );
+
+ END_TEST;
+}
+
+int UtcDaliImageAtlasRemove(void)
+{
+ TestApplication application;
+ unsigned int size = 100;
+ ImageAtlas atlas = ImageAtlas::New( size, size );
+ Vector4 textureRect1;
+ atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34) );
+
+ atlas.Remove( textureRect1 );
+
+ Vector4 textureRect2;
+ atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50) );
+
+ // one pixel gap
+ Rect<int> pixelArea = TextureCoordinateToPixelArea(textureRect2, size);
+ DALI_TEST_EQUALS( pixelArea.x, 0.f, TEST_LOCATION );
+ DALI_TEST_EQUALS( pixelArea.y, 0.f, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliImageAtlasImageView(void)
+{
+ ToolkitTestApplication application;
+
+ TraceCallStack& callStack = application.GetGlAbstraction().GetTextureTrace();
+ callStack.Reset();
+ callStack.Enable(true);
+
+ ImageView imageView1 = ImageView::New( gImage_34_RGBA, ImageDimensions(34, 34) );
+ ImageView imageView2 = ImageView::New( gImage_50_RGBA, ImageDimensions(50, 50) );
+ Stage::GetCurrent().Add( imageView1 );
+ Stage::GetCurrent().Add( imageView2 );
+
+ EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+ while( eventTrigger == NULL) // waiting uintil the ImageAtlas is created by ImageAtlasManager
+ {
+ usleep(10);
+ eventTrigger = EventThreadCallback::Get();
+ }
+ CallbackBase* callback = eventTrigger->GetCallback();
+
+ eventTrigger->WaitingForTrigger( 2 );// waiting until both images are loaded
+
+ CallbackBase::Execute( *callback );
+
+ application.SendNotification();
+ application.Render(RENDER_FRAME_INTERVAL);
+
+ callStack.Enable(false);
+
+ DALI_TEST_CHECK( callStack.FindMethodAndParams("TexSubImage2D", "0, 0, 34, 34" ) );
+ DALI_TEST_CHECK( callStack.FindMethodAndParams("TexSubImage2D", "0, 34, 50, 50" ) );
+
+ callStack.Reset();
+ callStack.Enable(true);
+
+ // remove the imageView2 from stage, the second image will also be removed from atlas
+ // then the space on the atlas will be used by the third image added.
+ Stage::GetCurrent().Remove( imageView2 );
+ application.SendNotification();
+ application.Render(RENDER_FRAME_INTERVAL);
+ ImageView imageView3 = ImageView::New( gImage_128_RGB, ImageDimensions(100, 100) );
+ Stage::GetCurrent().Add( imageView3 );
+
+ eventTrigger->WaitingForTrigger( 3 ); // waiting for the third image loaded
+ CallbackBase::Execute( *callback );
+
+ application.SendNotification();
+ application.Render(RENDER_FRAME_INTERVAL);
+
+ callStack.Enable(false);
+ DALI_TEST_CHECK( callStack.FindMethodAndParams("TexSubImage2D", "0, 34, 100, 100" ) );
+
+ END_TEST;
+}
develapishadowviewdir = $(develapicontrolsdir)/shadow-view
develapisuperblurviewdir = $(develapicontrolsdir)/super-blur-view
develapifocusmanagerdir = $(develapidir)/focus-manager
+develapiimageatlasdir = $(develapidir)/image-atlas
develapiscriptingdir = $(develapidir)/scripting
develapishadereffectsdir = $(develapidir)/shader-effects
develapitransitioneffectsdir = $(develapidir)/transition-effects
develapibuilder_HEADERS = $(devel_api_builder_header_files)
develapieffectsview_HEADERS = $(devel_api_effects_view_header_files)
develapifocusmanager_HEADERS = $(devel_api_focus_manager_header_files)
+develapiimageatlas_HEADERS = $(devel_api_image_atlas_header_files)
develapimagnifier_HEADERS = $(devel_api_magnifier_header_files)
develapipopup_HEADERS = $(devel_api_popup_header_files)
develapirendererfactory_HEADERS = $(devel_api_renderer_factory_header_files)
GetImplementation( *this ).ResetRenderer( renderer, actor, image );
}
-ControlRenderer RendererFactory::GetControlRenderer( const std::string& url )
+ControlRenderer RendererFactory::GetControlRenderer( const std::string& url, ImageDimensions size )
{
- return GetImplementation( *this ).GetControlRenderer( url );
+ return GetImplementation( *this ).GetControlRenderer( url, size );
}
-void RendererFactory::ResetRenderer( ControlRenderer& renderer, Actor& actor, const std::string& url )
+void RendererFactory::ResetRenderer( ControlRenderer& renderer, Actor& actor, const std::string& url, ImageDimensions size )
{
- GetImplementation( *this ).ResetRenderer( renderer, actor, url );
+ GetImplementation( *this ).ResetRenderer( renderer, actor, url, size );
}
void RendererFactory::ResetRenderer( ControlRenderer& renderer, Actor& actor, const Property::Map& propertyMap )
// EXTERNAL INCLUDES
#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/images/image-operations.h>
// INTERNAK INCLUDES
#include <dali-toolkit/devel-api/controls/renderer-factory/control-renderer.h>
* @brief Request the control renderer to render the given resource at the url.
*
* @param[in] url The URL to the resource to be rendered.
+ * @param[in] size The width and height to fit the loaded image to.
* @return The pointer pointing to the control renderer
*/
- ControlRenderer GetControlRenderer( const std::string& url );
+ ControlRenderer GetControlRenderer( const std::string& url,
+ ImageDimensions size = ImageDimensions() );
/**
* @brief Request the current control renderer to render the given resource at the url
* @param[in] renderer The ControlRenderer to reset
* @param[in] actor The Actor the renderer is applied to if, empty if the renderer has not been applied to any Actor
* @param[in] url The URL to the resource to be rendered.
+ * @param[in] size The width and height to fit the loaded image to.
*/
- void ResetRenderer( ControlRenderer& renderer, Actor& actor, const std::string& url );
+ void ResetRenderer( ControlRenderer& renderer, Actor& actor, const std::string& url,
+ ImageDimensions size = ImageDimensions() );
/**
$(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)/styling/style-manager.cpp \
$(devel_api_src_dir)/scripting/script.cpp \
$(devel_api_src_dir)/transition-effects/cube-transition-cross-effect.cpp \
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_styling_header_files = \
$(devel_api_src_dir)/styling/style-manager.h
--- /dev/null
+ /*
+ * Copyright (c) 2015 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 "image-atlas.h"
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/image-atlas/image-atlas-impl.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+ImageAtlas::ImageAtlas()
+{
+}
+
+ImageAtlas::~ImageAtlas()
+{
+}
+
+ImageAtlas::ImageAtlas(Internal::ImageAtlas* internal)
+: BaseHandle( internal )
+{
+}
+
+ImageAtlas::ImageAtlas( const ImageAtlas& handle )
+: BaseHandle( handle )
+{
+}
+
+ImageAtlas& ImageAtlas::operator=( const ImageAtlas& handle )
+{
+ BaseHandle::operator=(handle);
+ return *this;
+}
+
+ImageAtlas ImageAtlas::New(SizeType width, SizeType height,
+ Pixel::Format pixelFormat)
+{
+ IntrusivePtr<Internal::ImageAtlas> internal = Internal::ImageAtlas::New( width, height, pixelFormat);
+ return ImageAtlas( internal.Get() );
+}
+
+Image ImageAtlas::GetAtlas()
+{
+ return GetImplementation( *this ).GetAtlas();
+}
+
+void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
+{
+ GetImplementation( *this ).SetBrokenImage( brokenImageUrl );
+}
+
+bool ImageAtlas::Upload( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection )
+{
+ return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection );
+}
+
+void ImageAtlas::Remove(const Vector4& textureRect)
+{
+ GetImplementation(*this).Remove( textureRect );
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_TOOLKIT_IMAGE_ATLAS_H__
+#define __DALI_TOOLKIT_IMAGE_ATLAS_H__
+/*
+ * Copyright (c) 2015 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 <string>
+#include <stdint.h>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/images/image.h>
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/images/pixel.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal DALI_INTERNAL
+{
+class ImageAtlas;
+}
+
+/**
+ * @brief An ImageAtlas is a large image 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.
+ */
+class DALI_IMPORT_API ImageAtlas : public BaseHandle
+{
+public:
+
+ typedef uint32_t SizeType;
+
+public:
+
+ /**
+ * @brief Create a new ImageAtlas.
+ *
+ * @param [in] width The atlas width in pixels.
+ * @param [in] height The atlas height in pixels.
+ * @param [in] pixelFormat The pixel format (rgba 32 bit by default).
+ * @return A handle to a new ImageAtlas.
+ */
+ static ImageAtlas New( SizeType width, SizeType height,
+ Pixel::Format pixelFormat = Pixel::RGBA8888 );
+
+ /**
+ * @brief Create an empty handle.
+ *
+ * Calling member functions of an empty handle is not allowed.
+ */
+ ImageAtlas();
+
+ /**
+ * @brief Destructor.
+ */
+ ~ImageAtlas();
+
+ /**
+ * @brief This copy constructor is required for (smart) pointer semantics.
+ *
+ * @param [in] handle A reference to the copied handle
+ */
+ ImageAtlas( const ImageAtlas& handle );
+
+ /**
+ * @brief This assignment operator is required for (smart) pointer semantics.
+ *
+ * @param [in] rhs A reference to the copied handle
+ * @return A reference to this
+ */
+ ImageAtlas& operator=( const ImageAtlas& handle );
+
+ /**
+ * @brief Get the atlas image.
+ *
+ * This atlas image is still valid after destroying the ImageAtlas object.
+ *
+ * @return the atlas image with type of Dali::Atlas
+ */
+ Image GetAtlas();
+
+ /**
+ * @brief Set the broken image which is used to replace the image if loading fails.
+ *
+ * @param[in] brokenImageUrl The url of the broken image.
+ */
+ void SetBrokenImage( const std::string& brokenImageUrl );
+
+ /**
+ * @brief Upload a resource image to the atlas.
+ *
+ * @note To make the atlasing efficient, an 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.
+ * @return True if there is enough space to fit this image in,false otherwise.
+ */
+ bool Upload( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size = ImageDimensions(),
+ FittingMode::Type fittingMode = FittingMode::DEFAULT,
+ bool orientationCorrection = true );
+
+ /**
+ * @brief Remove the image at the given rectangle.
+ *
+ * The rectangular area is marked unoccupied, so new image can be added to this area.
+ *
+ * @param [in] textureRect The texture area to be removed.
+ */
+ void Remove( const Vector4& textureRect );
+
+public: // Not intended for developer use
+
+ explicit DALI_INTERNAL ImageAtlas( Internal::ImageAtlas* impl );
+};
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_IMAGE_ATLAS_H__ */
mImageSize = ImageDimensions( width, height );
}
-void ImageView::SetImage( const std::string& url )
+void ImageView::SetImage( const std::string& url, ImageDimensions size )
{
if( mUrl != url )
{
mUrl = url;
- Actor self = Self();
- Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, self, mUrl );
+ if( size.GetWidth() == 0u && size.GetHeight() == 0u )
+ {
+ mImageSize = ResourceImage::GetImageSize( mUrl );
+ }
+ else
+ {
+ mImageSize = size;
+ }
- mImageSize = ResourceImage::GetImageSize( mUrl );
+ Actor self = Self();
+ Toolkit::RendererFactory::Get().ResetRenderer( mRenderer, self, mUrl, mImageSize );
}
}
if( value.Get( imageUrl ) )
{
ImageView& impl = GetImpl( imageView );
- impl.SetImage( imageUrl );
+ impl.SetImage( imageUrl, ImageDimensions() );
}
// if its not a string then get a Property::Map from the property if possible.
/**
* @copydoc Dali::Toolkit::SetImage
*/
- void SetImage( const std::string& imageUrl );
+ void SetImage( const std::string& imageUrl, ImageDimensions size );
// Properties
/**
if( GetIsOnStage() )
{
DoSetOffStage( actor );
- actor.RemoveRenderer( mImpl->mRenderer );
- mImpl->mRenderer.Reset();
mImpl->mFlags &= ~Impl::IS_ON_STAGE;
}
void ControlRenderer::DoSetOffStage( Actor& actor )
{
+ actor.RemoveRenderer( mImpl->mRenderer );
+ mImpl->mRenderer.Reset();
}
void ControlRenderer::CreatePropertyMap( Property::Map& map ) const
--- /dev/null
+ /*
+ * Copyright (c) 2015 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 "image-atlas-manager.h"
+
+// EXTERNAL HEADER
+#include <dali/public-api/images/resource-image.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+namespace
+{
+const uint32_t DEFAULT_ATLAS_SIZE( 1024u ); // this size can fit 8 by 8 images of average size 128*128
+const uint32_t MAX_ITEM_SIZE( 512u );
+const uint32_t MAX_ITEM_AREA( MAX_ITEM_SIZE*MAX_ITEM_SIZE );
+}
+
+ImageAtlasManager::ImageAtlasManager( Shader shader, const std::string& textureUniformName )
+: mShader( shader ),
+ mTextureUniformName( textureUniformName ),
+ mBrokenImageUrl( "" )
+{
+}
+
+ImageAtlasManager::~ImageAtlasManager()
+{
+}
+
+Material ImageAtlasManager::Add( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection )
+{
+ ImageDimensions dimensions = size;
+ ImageDimensions zero;
+ if( size == zero )
+ {
+ dimensions = ResourceImage::GetImageSize( url );
+ }
+
+ // big image, atlasing is not applied
+ if( static_cast<uint32_t>(dimensions.GetWidth()) * static_cast<uint32_t>(dimensions.GetHeight()) > MAX_ITEM_AREA
+ || dimensions.GetWidth()>DEFAULT_ATLAS_SIZE
+ || dimensions.GetHeight()>DEFAULT_ATLAS_SIZE)
+ {
+ return Material();
+ }
+
+ unsigned int i = 0;
+ for( AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
+ {
+ if( (*iter).Upload( textureRect, url, size, fittingMode, orientationCorrection ) )
+ {
+ return mMaterialList[i];
+ }
+ i++;
+ }
+
+ Toolkit::ImageAtlas newAtlas = Toolkit::ImageAtlas::New( DEFAULT_ATLAS_SIZE, DEFAULT_ATLAS_SIZE );
+ if( !mBrokenImageUrl.empty() )
+ {
+ newAtlas.SetBrokenImage( mBrokenImageUrl );
+ }
+ mAtlasList.push_back( newAtlas );
+ Material newMaterial = Material::New( mShader );
+ newMaterial.AddTexture( newAtlas.GetAtlas(), mTextureUniformName );
+ mMaterialList.push_back( newMaterial );
+
+ newAtlas.Upload( textureRect, url, size, fittingMode, orientationCorrection );
+
+ return newMaterial;
+}
+
+void ImageAtlasManager::Remove( Material material, const Vector4& textureRect )
+{
+ unsigned int i = 0;
+ for( MaterialContainer::iterator iter = mMaterialList.begin(); iter != mMaterialList.end(); ++iter)
+ {
+ if( (*iter) == material )
+ {
+ mAtlasList[i].Remove(textureRect);
+ return;
+ }
+ i++;
+ }
+}
+
+void ImageAtlasManager::SetBrokenImage( const std::string& brokenImageUrl )
+{
+ if( !brokenImageUrl.empty() )
+ {
+ mBrokenImageUrl = brokenImageUrl;
+ }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_TOOLKIT_IMAGE_ATLAS_MANAGER_H__
+#define __DALI_TOOLKIT_IMAGE_ATLAS_MANAGER_H__
+
+/*
+ * Copyright (c) 2015 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 <string>
+#include <dali/public-api/common/vector-wrapper.h>
+#include <dali/public-api/object/ref-object.h>
+#include <dali/devel-api/rendering/material.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-atlas/image-atlas.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+/**
+ * The manager for automatic image atlasing. Owned by RendererFactory
+ */
+class ImageAtlasManager : public RefObject
+{
+public:
+ typedef std::vector< Toolkit::ImageAtlas > AtlasContainer;
+ typedef std::vector< Material > MaterialContainer;
+
+public:
+
+ /**
+ * Construtor
+ *
+ * @param[in] shader The shader for material.
+ * @param[in] textureUniformName The texture uniform name for the atlas image.
+ */
+ ImageAtlasManager( Shader shader, const std::string& textureUniformName );
+
+ /**
+ * @brief Add an image to the atlas.
+ *
+ * @note To make the atlasing efficient, an valid size should be provided.
+ * If size is not provided, then the image file will be opened to read the actual size for loading.
+ *
+ * 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.
+ * @return True if there is enough space to fit this image in,false otherwise.
+ */
+ Material Add( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size = ImageDimensions(),
+ FittingMode::Type fittingMode = FittingMode::DEFAULT,
+ bool orientationCorrection = true );
+
+ /**
+ * Remove the image at the given rectangle from the material.
+ *
+ * @param [in] material The material containing the atlas image.
+ * @param [in] textureRect The texture area to be removed.
+ */
+ void Remove( Material material, const Vector4& textureRect );
+
+ /**
+ * @brief Set the broken image which is used to replace the image if loading fails.
+ *
+ * @param[in] brokenImageUrl The url of the broken image.
+ */
+ void SetBrokenImage( const std::string& brokenImageUrl );
+
+protected:
+
+ /**
+ * Destructor
+ */
+ virtual ~ImageAtlasManager();
+
+ /**
+ * Undefined copy constructor.
+ */
+ ImageAtlasManager(const ImageAtlasManager&);
+
+ /**
+ * Undefined assignment operator.
+ */
+ ImageAtlasManager& operator=(const ImageAtlasManager& rhs);
+
+
+private:
+
+ AtlasContainer mAtlasList;
+ MaterialContainer mMaterialList;
+ Shader mShader;
+ std::string mTextureUniformName;
+ std::string mBrokenImageUrl;
+
+};
+
+} // name Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // __DALI_TOOLKIT_ATLAS_MANAGER_H__
#include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
#include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
#include <dali-toolkit/internal/controls/renderers/control-renderer-data-impl.h>
+#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
namespace Dali
{
const char * const NO_FILTER("noFilter");
const char * const DONT_CARE("dontCare");
-std::string TEXTURE_UNIFORM_NAME = "sTexture";
+const std::string TEXTURE_UNIFORM_NAME = "sTexture";
+const std::string TEXTURE_RECT_UNIFORM_NAME = "uTextureRect";
+const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
attribute mediump vec2 aPosition;\n
varying mediump vec2 vTexCoord;\n
uniform mediump mat4 uMvpMatrix;\n
uniform mediump vec3 uSize;\n
+ uniform mediump vec4 uTextureRect;\n
\n
void main()\n
{\n
vertexPosition.xyz *= uSize;\n
vertexPosition = uMvpMatrix * vertexPosition;\n
\n
- vTexCoord = aPosition + vec2(0.5);\n
+ vTexCoord = mix( uTextureRect.xy, uTextureRect.zw, aPosition + vec2(0.5));\n
gl_Position = vertexPosition;\n
}\n
);
} //unnamed namespace
-ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache )
+ImageRenderer::ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
: ControlRenderer( factoryCache ),
+ mAtlasManager( atlasManager ),
+ mTextureRect( FULL_TEXTURE_RECT ),
mDesiredSize(),
mFittingMode( FittingMode::DEFAULT ),
mSamplingMode( SamplingMode::DEFAULT )
//clean the cache
if( !oldImageUrl.empty() )
{
- mFactoryCache.CleanRendererCache( oldImageUrl );
+ CleanCache( oldImageUrl );
}
//Initialize the renderer
if( !mImpl->mCustomShader )
{
geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
- shader = mFactoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
- if( !shader )
- {
- shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
- mFactoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
- }
+ shader = GetImageShader(mFactoryCache);
}
else
{
geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
{
- shader = mFactoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
- if( !shader )
- {
- shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
- mFactoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
- }
+ shader = GetImageShader(mFactoryCache);
}
else
{
mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
if( !mImpl->mRenderer )
{
- mImpl->mRenderer = CreateRenderer();
+ Material material = mAtlasManager.Add(mTextureRect, imageUrl, mDesiredSize, mFittingMode, mSamplingMode );
+ if( material )
+ {
+ Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
+ mImpl->mRenderer = Renderer::New( geometry, material );
+ SetTextureRectUniform(mTextureRect);
+ }
+ else // big image, atlasing is not applied
+ {
+ mImpl->mRenderer = CreateRenderer();
- ResourceImage image = Dali::ResourceImage::New( imageUrl );
- image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
- Material material = mImpl->mRenderer.GetMaterial();
- material.AddTexture( image, TEXTURE_UNIFORM_NAME );
+ ResourceImage image = Dali::ResourceImage::New( imageUrl );
+ image.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
+ Material material = mImpl->mRenderer.GetMaterial();
+ material.AddTexture( image, TEXTURE_UNIFORM_NAME );
+ }
- mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
+ mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
+ }
+ else
+ {
+ Property::Value textureRect = mImpl->mRenderer.GetProperty( mImpl->mRenderer.GetPropertyIndex(TEXTURE_RECT_UNIFORM_NAME) );
+ textureRect.Get( mTextureRect );
}
mImpl->mFlags |= Impl::IS_FROM_CACHE;
}
mImpl->mRenderer = CreateRenderer();
ApplyImageToSampler( image );
+ SetTextureRectUniform( FULL_TEXTURE_RECT );
}
ResourceImage resourceImage = Dali::ResourceImage::New( mImageUrl, mDesiredSize, mFittingMode, mSamplingMode );
resourceImage.LoadingFinishedSignal().Connect( this, &ImageRenderer::OnImageLoaded );
image = resourceImage;
+
+ // Set value to the uTextureRect uniform
+ SetTextureRectUniform( FULL_TEXTURE_RECT );
}
ApplyImageToSampler( image );
//If we own the image then make sure we release it when we go off stage
if( !mImageUrl.empty() )
{
- //clean the renderer from the cache since it may no longer be in use
- mFactoryCache.CleanRendererCache( mImageUrl );
+ actor.RemoveRenderer( mImpl->mRenderer );
+ CleanCache(mImageUrl);
mImage.Reset();
}
+ else
+ {
+ actor.RemoveRenderer( mImpl->mRenderer );
+ mImpl->mRenderer.Reset();
+ }
}
void ImageRenderer::DoCreatePropertyMap( Property::Map& map ) const
}
}
-void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl )
+Shader ImageRenderer::GetImageShader( RendererFactoryCache& factoryCache )
{
- SetImage( actor, imageUrl, 0, 0, Dali::FittingMode::DEFAULT, Dali::SamplingMode::DEFAULT );
+ Shader shader = factoryCache.GetShader( RendererFactoryCache::IMAGE_SHADER );
+ if( !shader )
+ {
+ shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
+ factoryCache.SaveShader( RendererFactoryCache::IMAGE_SHADER, shader );
+ }
+ return shader;
}
-void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
+void ImageRenderer::SetImage( Actor& actor, const std::string& imageUrl, ImageDimensions size, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode )
{
+ mDesiredSize = size;
+ mFittingMode = fittingMode;
+ mSamplingMode = samplingMode;
+
if( mImageUrl != imageUrl )
{
if( mImpl->mRenderer )
//clean the cache
if( !mImageUrl.empty() )
{
- mFactoryCache.CleanRendererCache( mImageUrl );
+ CleanCache(mImageUrl);
}
//Initialize the renderer
}
mImageUrl = imageUrl;
- mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
- mFittingMode = fittingMode;
- mSamplingMode = samplingMode;
+
mImage.Reset();
}
}
//clean the cache
if( !mImageUrl.empty() )
{
- mFactoryCache.CleanRendererCache( mImageUrl );
+ CleanCache(mImageUrl);
}
//Initialize the renderer
ApplyImageToSampler( image );
}
}
+ SetTextureRectUniform( FULL_TEXTURE_RECT );
mImage = image;
mImageUrl.clear();
}
}
+void ImageRenderer::SetTextureRectUniform( const Vector4& textureRect )
+{
+ if( mImpl->mRenderer )
+ {
+ Property::Index index = mImpl->mRenderer.GetPropertyIndex( TEXTURE_RECT_UNIFORM_NAME );
+ if( index == Property::INVALID_INDEX )
+ {
+ index = mImpl->mRenderer.RegisterProperty( TEXTURE_RECT_UNIFORM_NAME, textureRect );
+ }
+ else
+ {
+ mImpl->mRenderer.SetProperty( index, textureRect );
+ }
+ }
+}
+
+void ImageRenderer::CleanCache(const std::string& url)
+{
+ Material material = mImpl->mRenderer.GetMaterial();
+ mImpl->mRenderer.Reset();
+ if( mFactoryCache.CleanRendererCache( url ) )
+ {
+ mAtlasManager.Remove( material, mTextureRect );
+ }
+}
+
} // namespace Internal
} // namespace Toolkit
// INTERNAL INCLUDES
#include <dali-toolkit/internal/controls/renderers/control-renderer-impl.h>
+#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
// EXTERNAL INCLUDES
#include <dali/public-api/images/image.h>
/**
* @brief Constructor.
*
- * @param[in] factoryCache A pointer pointing to the RendererFactoryCache object
+ * @param[in] factoryCache The RendererFactoryCache object
+ * @param[in] atlasManager The atlasManager object
*/
- ImageRenderer( RendererFactoryCache& factoryCache );
+ ImageRenderer( RendererFactoryCache& factoryCache, ImageAtlasManager& atlasManager );
/**
* @brief A reference counted object may only be deleted by calling Unreference().
public:
/**
- * @brief Sets the image of this renderer to the resource at imageUrl
- * The renderer will load the Image asynchronously when the associated actor is put on stage, and destroy the image when it is off stage
- *
- * @param[in] actor The Actor the renderer is applied to if, empty if the renderer has not been applied to any Actor
- * @param[in] imageUrl The URL to to image resource to use
+ * Get the standard image rendering shader.
+ * @param[in] factoryCache A pointer pointing to the RendererFactoryCache object
*/
- void SetImage( Actor& actor, const std::string& imageUrl );
+ static Shader GetImageShader( RendererFactoryCache& factoryCache );
/**
* @brief Sets the image of this renderer to the resource at imageUrl
*
* @param[in] actor The Actor the renderer is applied to if, empty if the renderer has not been applied to any Actor
* @param[in] imageUrl The URL to to image resource to use
- * @param[in] desiredWidth The desired width of the resource to load
- * @param[in] desiredHeight The desired height of the resource to load
+ * @param[in] size The width and height to fit the loaded image to.
* @param[in] fittingMode The FittingMode of the resource to load
* @param[in] samplingMode The SamplingMode of the resource to load
*/
- void SetImage( Actor& actor, const std::string& imageUrl, int desiredWidth, int desiredHeight, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode );
+ void SetImage( Actor& actor,
+ const std::string& imageUrl,
+ ImageDimensions size=ImageDimensions(),
+ FittingMode::Type fittingMode = FittingMode::DEFAULT,
+ Dali::SamplingMode::Type samplingMode = SamplingMode::BOX_THEN_LINEAR );
/**
* @brief Sets the image of this renderer to use
*/
void OnImageLoaded( ResourceImage image );
+ /**
+ * Set the value to the uTextureRect uniform
+ * @param[in] textureRect The texture rectangular area.
+ */
+ void SetTextureRectUniform( const Vector4& textureRect );
+
+ /**
+ * Clean the renderer from cache, and remove the image from atlas if it is not used anymore
+ */
+ void CleanCache(const std::string& url);
+
private:
Image mImage;
+ ImageAtlasManager& mAtlasManager;
+ Vector4 mTextureRect;
std::string mImageUrl;
Dali::ImageDimensions mDesiredSize;
void NPatchRenderer::DoSetOffStage( Actor& actor )
{
mCroppedImage.Reset();
+ actor.RemoveRenderer( mImpl->mRenderer );
+ mImpl->mRenderer.Reset();
}
void NPatchRenderer::DoCreatePropertyMap( Property::Map& map ) const
}
}
-void RendererFactoryCache::CleanRendererCache( const std::string& key )
+bool RendererFactoryCache::CleanRendererCache( const std::string& key )
{
int index = FindRenderer( key );
if( index != -1 )
delete cachedRenderer;
cachedRenderer = NULL;
+ return true;
}
}
+ return false;
}
Geometry RendererFactoryCache::CreateQuadGeometry()
* @brief Cleans the renderer cache by removing the renderer from the cache based on the given key if there are no longer any references to it
*
* @param[in] key The key used for caching
+ *
+ * @return True if the renderer is no longer used anywhere, false otherwise
*/
- void CleanRendererCache( const std::string& key );
+ bool CleanRendererCache( const std::string& key );
protected:
#include <dali-toolkit/internal/controls/renderers/npatch/npatch-renderer.h>
#include <dali-toolkit/internal/controls/renderers/image/image-renderer.h>
#include <dali-toolkit/internal/controls/renderers/renderer-factory-cache.h>
+#include <dali-toolkit/internal/controls/renderers/image-atlas-manager.h>
namespace
{
const char * const IMAGE_RENDERER("imageRenderer");
const char * const N_PATCH_RENDERER("nPatchRenderer");
+const std::string TEXTURE_UNIFORM_NAME = "sTexture";
+
const char * const BROKEN_RENDERER_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
}
}
else if( typeValue == IMAGE_RENDERER )
{
- rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ) );
+ CreateAtlasManager();
+ rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
}
else if( typeValue == N_PATCH_RENDERER )
{
}
else
{
- ImageRenderer* rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ) );
+ CreateAtlasManager();
+ ImageRenderer* rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
Actor actor;
rendererPtr->SetImage( actor, image );
}
}
-Toolkit::ControlRenderer RendererFactory::GetControlRenderer( const std::string& url )
+Toolkit::ControlRenderer RendererFactory::GetControlRenderer( const std::string& url, ImageDimensions size )
{
if( !mFactoryCache )
{
}
else
{
- ImageRenderer* rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ) );
+ CreateAtlasManager();
+ ImageRenderer* rendererPtr = new ImageRenderer( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
Actor actor;
- rendererPtr->SetImage( actor, url );
+ rendererPtr->SetImage( actor, url, size );
return Toolkit::ControlRenderer( rendererPtr );
}
}
-void RendererFactory::ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const std::string& url )
+void RendererFactory::ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const std::string& url, ImageDimensions size )
{
if( renderer )
{
ImageRenderer* rendererPtr = dynamic_cast< ImageRenderer* >( &GetImplementation( renderer ) );
if( rendererPtr )
{
- rendererPtr->SetImage( actor, url );
+ rendererPtr->SetImage( actor, url, size );
return;
}
}
renderer.RemoveAndReset( actor );
}
- renderer = GetControlRenderer( url );
+ renderer = GetControlRenderer( url, size );
if( actor && actor.OnStage() )
{
renderer.SetOnStage( actor );
return ResourceImage::New( BROKEN_RENDERER_IMAGE_URL );
}
+void RendererFactory::CreateAtlasManager()
+{
+ if( !mAtlasManager )
+ {
+ Shader shader = ImageRenderer::GetImageShader( *( mFactoryCache.Get() ) );
+ mAtlasManager = new ImageAtlasManager(shader, TEXTURE_UNIFORM_NAME);
+ mAtlasManager->SetBrokenImage( BROKEN_RENDERER_IMAGE_URL );
+ }
+}
+
} // namespace Internal
} // namespace Toolkit
class RendererFactoryCache;
typedef IntrusivePtr<RendererFactoryCache> RendererFactoryCachePtr;
+class ImageAtlasManager;
+typedef IntrusivePtr<ImageAtlasManager> ImageAtlasManagerPtr;
+
/**
* @copydoc Toolkit::RendererFactory
*/
void ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const Image& image );
/**
- * @copydoc Toolkit::RenderFactory::GetControlRenderer( const std::string& )
+ * @copydoc Toolkit::RenderFactory::GetControlRenderer( const std::string&, ImageDimensions )
*/
- Toolkit::ControlRenderer GetControlRenderer( const std::string& image );
+ Toolkit::ControlRenderer GetControlRenderer( const std::string& image, ImageDimensions size );
/**
- * @copydoc Toolkit::RendererFactory::ResetRenderer( Toolkit::ControlRenderer&, Actor& actor, const std::string& )
+ * @copydoc Toolkit::RendererFactory::ResetRenderer( Toolkit::ControlRenderer&, Actor& actor, const std::string&, ImageDimensions )
*/
- void ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const std::string& image );
+ void ResetRenderer( Toolkit::ControlRenderer& renderer, Actor& actor, const std::string& image, ImageDimensions size );
public:
/**
private:
+ /**
+ * Prepare the atlas manager
+ */
+ void CreateAtlasManager();
+
/**
* Undefined copy constructor.
*/
private:
RendererFactoryCachePtr mFactoryCache;
+ ImageAtlasManagerPtr mAtlasManager;
};
} // namespace Internal
$(toolkit_src_dir)/builder/replacement.cpp \
$(toolkit_src_dir)/controls/renderers/control-renderer-impl.cpp \
$(toolkit_src_dir)/controls/renderers/control-renderer-data-impl.cpp \
+ $(toolkit_src_dir)/controls/renderers/image-atlas-manager.cpp \
$(toolkit_src_dir)/controls/renderers/renderer-factory-cache.cpp \
$(toolkit_src_dir)/controls/renderers/renderer-factory-impl.cpp \
$(toolkit_src_dir)/controls/renderers/border/border-renderer.cpp \
$(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)/styling/style-manager-impl.cpp \
$(toolkit_src_dir)/text/bidirectional-support.cpp \
$(toolkit_src_dir)/text/character-set-conversion.cpp \
--- /dev/null
+/*
+ * Copyright (c) 2015 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-packer.h"
+
+// EXTERNAL HEADER
+#include <stdlib.h> // For abs()
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+namespace
+{
+
+bool ApproximatelyEqual( uint32_t a, uint32_t b )
+{
+ return abs( a-b ) <= 1;
+}
+
+}
+
+AtlasPacker::Node::Node( Node* parent, SizeType x, SizeType y, SizeType width, SizeType height )
+: rectArea( x, y, width, height ),
+ parent(parent),
+ occupied( false )
+{
+ child[0] = NULL;
+ child[1] = NULL;
+}
+
+AtlasPacker:: AtlasPacker( SizeType atlasWidth, SizeType atlasHeight )
+: mAvailableArea( atlasWidth * atlasHeight )
+{
+ mRoot = new Node( NULL, 0u, 0u, atlasWidth, atlasHeight );
+}
+
+AtlasPacker::~AtlasPacker()
+{
+ DeleteNode( mRoot );
+}
+
+bool AtlasPacker::Pack( SizeType blockWidth, SizeType blockHeight,
+ SizeType& packPositionX, SizeType& packPositionY)
+{
+ Node* firstFit = InsertNode( mRoot, blockWidth, blockHeight );
+ if( firstFit != NULL )
+ {
+ firstFit->occupied = true;
+ packPositionX = firstFit->rectArea.x;
+ packPositionY = firstFit->rectArea.y;
+ mAvailableArea -= blockWidth*blockHeight;
+ return true;
+ }
+ return false;
+}
+
+void AtlasPacker::DeleteBlock( SizeType packPositionX, SizeType packPositionY, SizeType blockWidth, SizeType blockHeight )
+{
+ Node* node = SearchNode( mRoot, packPositionX, packPositionY, blockWidth, blockHeight );
+ if( node != NULL )
+ {
+ mAvailableArea += blockWidth*blockHeight;
+ MergeToNonOccupied( node );
+ }
+}
+
+unsigned int AtlasPacker::GetAvailableArea() const
+{
+ return mAvailableArea;
+}
+
+AtlasPacker::Node* AtlasPacker::InsertNode( Node* root, SizeType blockWidth, SizeType blockHeight )
+{
+ if( root == NULL )
+ {
+ return NULL;
+ }
+
+ if( root->occupied )
+ {
+ // if not the leaf, then try insert into the first child.
+ Node* newNode = InsertNode(root->child[0], blockWidth, blockHeight);
+ if( newNode == NULL )// no room, try insert into the second child.
+ {
+ newNode = InsertNode(root->child[1], blockWidth, blockHeight);
+ }
+ return newNode;
+ }
+
+ // too small, return
+ if( root->rectArea.width < blockWidth || root->rectArea.height < blockHeight )
+ {
+ return NULL;
+ }
+
+ // right size, accept
+ if( root->rectArea.width == blockWidth && root->rectArea.height == blockHeight )
+ {
+ return root;
+ }
+
+ //too much room, need to split
+ SplitNode( root, blockWidth, blockHeight );
+ // insert into the first child created.
+ return InsertNode( root->child[0], blockWidth, blockHeight);
+}
+
+void AtlasPacker::SplitNode( Node* node, SizeType blockWidth, SizeType blockHeight )
+{
+ node->occupied = true;
+
+ // decide which way to split
+ SizeType remainingWidth = node->rectArea.width - blockWidth;
+ SizeType remainingHeight = node->rectArea.height - blockHeight;
+
+ if( remainingWidth > remainingHeight ) // split vertically
+ {
+ node->child[0] = new Node( node, node->rectArea.x, node->rectArea.y, blockWidth, node->rectArea.height );
+ node->child[1] = new Node( node, node->rectArea.x+blockWidth, node->rectArea.y, node->rectArea.width-blockWidth, node->rectArea.height );
+ }
+ else // split horizontally
+ {
+ node->child[0] = new Node( node, node->rectArea.x, node->rectArea.y, node->rectArea.width, blockHeight );
+ node->child[1] = new Node( node, node->rectArea.x, node->rectArea.y+blockHeight, node->rectArea.width, node->rectArea.height-blockHeight );
+ }
+}
+
+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
+ {
+ 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 NULL;
+}
+
+void AtlasPacker::MergeToNonOccupied( Node* node )
+{
+ node->occupied = false;
+ Node* parent = node->parent;
+ // both child are not occupied, merge the space to parent
+ if( parent !=NULL && parent->child[0]->occupied == false && parent->child[1]->occupied == false)
+ {
+ delete parent->child[0];
+ parent->child[0] = NULL;
+ delete parent->child[1];
+ parent->child[0] = NULL;
+
+ MergeToNonOccupied( parent );
+ }
+}
+
+void AtlasPacker::DeleteNode( Node *node )
+{
+ if( node != NULL )
+ {
+ DeleteNode( node->child[0] );
+ DeleteNode( node->child[1] );
+ delete node;
+ }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_TOOLKIT_ATLAS_PACKER_H__
+#define __DALI_TOOLKIT_ATLAS_PACKER_H__
+
+/*
+ * Copyright (c) 2015 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 <stdint.h>
+#include <dali/public-api/math/rect.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+/**
+ * Binary space tree based bin packing algorithm.
+ * It is initialised with a fixed width and height and will fit each block into the first node where it fits
+ * and then split that node into 2 parts (down and right) to track the remaining empty space.
+ */
+class AtlasPacker
+{
+public:
+
+ /**
+ * rectangular area (x,y,width,height)
+ */
+ typedef uint32_t SizeType;
+ typedef Rect<SizeType> RectArea;
+
+ /**
+ * Tree node.
+ */
+ struct Node
+ {
+ Node( Node* parent, SizeType x, SizeType y, SizeType width, SizeType height );
+
+ RectArea rectArea;
+ Node* parent;
+ Node* child[2];
+ bool occupied;
+ };
+
+ /**
+ * Constructor.
+ *
+ * @param[in] atlasWidth The width of the atlas.
+ * @param[in] atlasHeight The height of the atlas.
+ */
+ AtlasPacker( SizeType atlasWidth, SizeType atlasHeight );
+
+ /**
+ * Destructor
+ */
+ ~AtlasPacker();
+
+ /**
+ * Pack a block into the atlas.
+ *
+ * @param[in] blockWidth The width of the block to pack.
+ * @param[in] blockHeight The height of the block to pack.
+ * @param[out] packPositionX The x coordinate of the position to pack the block.
+ * @param[out] packPositionY The y coordinate of the position to pack the block.
+ * @return True if there are room for this block, false otherwise.
+ */
+ bool Pack( SizeType blockWidth, SizeType blockHeight,
+ SizeType& packPositionX, SizeType& packPositionY);
+
+ /**
+ * Delete the block.
+ *
+ * @param[in] packPositionX The x coordinate of the pack position.
+ * @param[in] packPositionY The y coordinate of the pack position.
+ * @param[in] blockWidth The width of the block to delete.
+ * @param[in] blockHeight The height of the block to delete.
+ */
+ void DeleteBlock( SizeType packPositionX, SizeType packPositionY, SizeType blockWidth, SizeType blockHeight );
+
+ /**
+ * Query how much empty space left.
+ *
+ * @return The area available for packing.
+ */
+ unsigned int GetAvailableArea() const;
+
+private:
+
+ /*
+ * Search the node which can pack the block with given size.
+ *
+ * @param[in] root The root node of the subtree to be searched.
+ * @param[in] blockWidth The width of the block to pack.
+ * @param[in] blockHeight The height of the block to pack.
+ * @return The poniter pointing to node that can pack the block.
+ * If it is NULL, there are no room in the subtree to pack the block.
+ */
+ Node* InsertNode( Node* root, SizeType blockWidth, SizeType blockHeight );
+
+ /**
+ * Split the node into two to fit the block width/size.
+ *
+ * @parm[in] node The node to split.
+ * @param[in] blockWidth The width of the block to pack.
+ * @param[in] blockHeight The height of the block to pack.
+ */
+ void SplitNode( Node* node, SizeType blockWidth, SizeType blockHeight );
+
+ /**
+ * Search the node at the given position and with the given size.
+
+ * @param[in] node The root node of the subtree to be searched.
+ * @param[in] packPositionX The x coordinate of the pack position.
+ * @param[in] packPositionY The y coordinate of the pack position.
+ * @param[in] blockWidth The width of the block.
+ * @param[in] blockHeight The height of the block.
+ */
+ Node* SearchNode( Node* node, SizeType packPositionX, SizeType packPositionY, SizeType blockWidth, SizeType blockHeight );
+
+ /**
+ * Merge the rect of the node to non-occupied area.
+ *
+ * @param[in] node The node to me merged to the non-occupied area
+ */
+ void MergeToNonOccupied( Node* node );
+
+ /**
+ * Delete a node and its subtree.
+ *
+ * @parm[in] node The node to delete.
+ */
+ void DeleteNode( Node* node );
+
+ // Undefined
+ AtlasPacker( const AtlasPacker& imageAtlas);
+
+ // Undefined
+ AtlasPacker& operator=( const AtlasPacker& imageAtlas );
+
+private:
+
+ Node* mRoot; ///< The root of the binary space tree
+ unsigned int mAvailableArea;
+
+};
+
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_ATLAS_PACKER_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2015 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 "image-atlas-impl.h"
+
+// EXTERNAL INCLUDES
+#include <string.h>
+#include <dali/public-api/signals/callback.h>
+#include <dali/public-api/images/resource-image.h>
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+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 ),
+ mBrokenImageUrl(""),
+ mBrokenImageSize(),
+ mPixelFormat( pixelFormat ),
+ mLoadingThreadStarted( false )
+{
+ mAtlas = Atlas::New( width, height, pixelFormat );
+ mWidth = static_cast<float>(width);
+ mHeight = static_cast<float>( height );
+}
+
+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();
+ }
+}
+
+IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
+{
+ IntrusivePtr<ImageAtlas> internal = new ImageAtlas( width, height, pixelFormat );
+ return internal;
+}
+
+Image ImageAtlas::GetAtlas()
+{
+ return mAtlas;
+}
+
+void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
+{
+ mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl );
+ if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0 ) // check the url is valid
+ {
+ mBrokenImageUrl = brokenImageUrl;
+ }
+}
+
+bool ImageAtlas::Upload( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection )
+{
+ ImageDimensions dimensions = size;
+ ImageDimensions zero;
+ if( size == zero ) // image size not provided
+ {
+ dimensions = ResourceImage::GetImageSize( url );
+ if( dimensions == zero ) // Fail to read the image & broken image file exists
+ {
+ if( !mBrokenImageUrl.empty() )
+ {
+ return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true );
+ }
+ else
+ {
+ textureRect = Vector4::ZERO;
+ return true;
+ }
+ }
+ }
+
+ if( static_cast<unsigned int>(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 );
+
+ // 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
+
+ return true;
+ }
+
+ return false;
+}
+
+void ImageAtlas::Remove( const Vector4& textureRect )
+{
+ mPacker.DeleteBlock( static_cast<SizeType>(textureRect.x*mWidth),
+ static_cast<SizeType>(textureRect.y*mHeight),
+ static_cast<SizeType>((textureRect.z-textureRect.x)*mWidth+1.f),
+ static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
+}
+
+void ImageAtlas::UploadToAtlas()
+{
+ while( LoadingTask* next = mCompleteQueue.NextTask() )
+ {
+ if( ! next->loader.IsLoaded() )
+ {
+ if(!mBrokenImageUrl.empty()) // replace with the broken image
+ {
+ UploadBrokenImage( next->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 )
+ {
+ 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 );
+ }
+
+ mAtlas.Upload( next->loader.GetPixelData(), next->packRect.x, next->packRect.y );
+ }
+
+ delete next;
+ }
+}
+
+void ImageAtlas::UploadBrokenImage( const Rect<SizeType>& area )
+{
+ BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
+ loader.Load();
+ SizeType loadedWidth = loader.GetPixelData()->GetWidth();
+ SizeType loadedHeight = loader.GetPixelData()->GetHeight();
+
+ bool needBackgroundClear = false;
+ SizeType packX = area.x;
+ SizeType packY = area.y;
+ // locate the broken image in the middle.
+ if( area.width > loadedWidth)
+ {
+ packX += (area.width - loadedWidth)/2;
+ needBackgroundClear = true;
+ }
+ if( area.height > loadedHeight)
+ {
+ packY += (area.height - loadedHeight)/2;
+ needBackgroundClear = true;
+ }
+
+ if( needBackgroundClear )
+ {
+ SizeType size = area.width * area.height * Pixel::GetBytesPerPixel( mPixelFormat );
+ PixelBuffer* buffer = new PixelBuffer [size];
+ PixelDataPtr background = PixelData::New( buffer, area.width, area.height, mPixelFormat, PixelData::DELETE_ARRAY );
+ for( SizeType idx = 0; idx < size; idx++ )
+ {
+ buffer[idx] = 0x00;
+ }
+ mAtlas.Upload( background, area.x, area.y );
+ }
+
+ mAtlas.Upload( loader.GetPixelData(), packX, packY );
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_TOOLKIT_IMAGE_ATLAS_IMPL_H__
+#define __DALI_TOOLKIT_IMAGE_ATLAS_IMPL_H__
+
+/*
+ * Copyright (c) 2015 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 <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/object/base-object.h>
+#include <dali/devel-api/images/atlas.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-atlas/image-atlas.h>
+#include <dali-toolkit/internal/image-atlas/atlas-packer.h>
+#include <dali-toolkit/internal/image-atlas/image-load-thread.h>
+
+namespace Dali
+{
+class EventThreadCallback;
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+class ImageAtlas : public BaseObject
+{
+public:
+
+ typedef Toolkit::ImageAtlas::SizeType SizeType;
+
+ /**
+ * Constructor
+ * @param [in] width The atlas width in pixels.
+ * @param [in] height The atlas height in pixels.
+ * @param [in] pixelFormat The pixel format.
+ */
+ ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat );
+
+ /**
+ * @copydoc Toolkit::ImageAtlas::New
+ */
+ static IntrusivePtr<ImageAtlas> New( SizeType width, SizeType height, Pixel::Format pixelFormat );
+
+ /**
+ * @copydoc Toolkit::ImageAtlas::GetAtlas
+ */
+ Image GetAtlas();
+
+ /**
+ * @copydoc Toolkit::ImageAtlas::SetBrokenImage
+ */
+ void SetBrokenImage( const std::string& brokenImageUrl );
+
+ /**
+ * @copydoc Toolkit::ImageAtlas::Upload
+ */
+ bool Upload( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection);
+
+ /**
+ * @copydoc Toolkit::ImageAtlas::Remove
+ */
+ void Remove( const Vector4& textureRect );
+
+protected:
+
+ /**
+ * Destructor
+ */
+ ~ImageAtlas();
+
+private:
+
+ /**
+ * Upload the bitmap to atlas when the image is loaded in the worker thread.
+ */
+ void UploadToAtlas();
+
+ /**
+ * Upload broken image
+ *
+ * @param[in] area The pixel area for uploading.
+ */
+ void UploadBrokenImage( const Rect<SizeType>& area );
+
+ // Undefined
+ ImageAtlas( const ImageAtlas& imageAtlas);
+
+ // Undefined
+ ImageAtlas& operator=( const ImageAtlas& imageAtlas );
+
+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;
+
+};
+
+} // namespace Internal
+
+inline const Internal::ImageAtlas& GetImplementation( const Toolkit::ImageAtlas& imageAtlas )
+{
+ DALI_ASSERT_ALWAYS( imageAtlas && "ImageAtlas handle is empty" );
+
+ const BaseObject& handle = imageAtlas.GetBaseObject();
+
+ return static_cast<const Internal::ImageAtlas&>(handle);
+}
+
+inline Internal::ImageAtlas& GetImplementation( Toolkit::ImageAtlas& imageAtlas )
+{
+ DALI_ASSERT_ALWAYS( imageAtlas && "ImageAtlas handle is empty" );
+
+ BaseObject& handle = imageAtlas.GetBaseObject();
+
+ return static_cast<Internal::ImageAtlas&>(handle);
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_IMAGE_ATLAS_IMPL_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2015 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 "image-load-thread.h"
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+LoadingTask::LoadingTask(BitmapLoader loader, uint32_t packPositionX, uint32_t packPositionY, uint32_t width, uint32_t height )
+: loader( loader ),
+ packRect( packPositionX, packPositionY, width, height )
+{
+}
+
+LoadQueue::LoadQueue()
+{
+}
+
+LoadQueue::~LoadQueue()
+{
+}
+
+LoadingTask* LoadQueue::NextTask()
+{
+ // Lock while popping task out from the queue
+ ConditionalWait::ScopedLock lock( mConditionalWait );
+
+ while( mTasks.Empty() )
+ {
+ mConditionalWait.Wait( lock );
+ }
+
+ Vector< LoadingTask* >::Iterator next = mTasks.Begin();
+ LoadingTask* nextTask = *next;
+ mTasks.Erase( next );
+
+ return nextTask;
+}
+
+void LoadQueue::AddTask( LoadingTask* task )
+{
+ bool wasEmpty = false;
+
+ {
+ // Lock while adding task to the queue
+ ConditionalWait::ScopedLock lock( mConditionalWait );
+ wasEmpty = mTasks.Empty();
+ mTasks.PushBack( task );
+ }
+
+ if( wasEmpty)
+ {
+ // wake up the image loading thread
+ mConditionalWait.Notify();
+ }
+}
+
+CompleteQueue::CompleteQueue(EventThreadCallback* trigger)
+: mTrigger( trigger )
+{}
+
+CompleteQueue::~CompleteQueue()
+{
+ delete mTrigger;
+}
+
+LoadingTask* CompleteQueue::NextTask()
+{
+ while( mTasks.Empty() )
+ {
+ return NULL;
+ }
+
+ // Lock while popping task out from the queue
+ Mutex::ScopedLock lock( mMutex );
+
+ Vector< LoadingTask* >::Iterator next = mTasks.Begin();
+ LoadingTask* nextTask = *next;
+ mTasks.Erase( next );
+
+ return nextTask;
+}
+
+void CompleteQueue::AddTask( LoadingTask* task )
+{
+ // Lock while adding task to the queue
+ Mutex::ScopedLock lock( mMutex );
+ mTasks.PushBack( task );
+
+ // wake up the main thread
+ mTrigger->Trigger();
+}
+
+
+ImageLoadThread::ImageLoadThread( LoadQueue& loadQueue, CompleteQueue& completeQueue )
+: mLoadQueue( loadQueue ),
+ mCompleteQueue( completeQueue )
+{
+}
+
+ImageLoadThread::~ImageLoadThread()
+{
+}
+
+void ImageLoadThread::Run()
+{
+ while( LoadingTask* task = mLoadQueue.NextTask() )
+ {
+ task->loader.Load();
+ mCompleteQueue.AddTask( task );
+ }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef __DALI_TOOLKIT_IMAGE_LOAD_THREAD_H__
+#define __DALI_TOOLKIT_IMAGE_LOAD_THREAD_H__
+
+/*
+ * Copyright (c) 2015 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 <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/object/ref-object.h>
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/threading/thread.h>
+#include <dali/devel-api/adaptor-framework/bitmap-loader.h>
+#include <dali/devel-api/adaptor-framework/event-thread-callback.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+/**
+ * The task of loading and packing an image into the atlas.
+ */
+struct LoadingTask
+{
+ /**
+ * Constructor.
+ */
+ LoadingTask( BitmapLoader loader, uint32_t packPositionX, uint32_t packPositionY, uint32_t width, uint32_t height );
+
+private:
+
+ // Undefined
+ LoadingTask( const LoadingTask& queue );
+
+ // Undefined
+ LoadingTask& operator=( const LoadingTask& queue );
+
+public:
+
+ BitmapLoader loader; ///< The loader used to load the bitmap from URL
+ Rect<uint32_t> packRect; ///< The x coordinate of the position to pack the image.
+
+};
+
+/**
+ * The queue of the tasks waiting to load the bitmap from the URL in the worker thread/
+ */
+class LoadQueue //: public TaskQueue
+{
+public:
+
+ /**
+ * Constructor
+ */
+ LoadQueue();
+
+ /**
+ * Destructor.
+ */
+ ~LoadQueue();
+
+ /**
+ * Pop the next task out from the queue.
+ *
+ * @return The next task to be processed.
+ */
+ LoadingTask* NextTask();
+
+ /**
+ * Add a task in to the 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
+ *
+ * @param[in] mTrigger The trigger to wake up the main thread.
+ */
+ CompleteQueue( EventThreadCallback* mTrigger );
+
+ /**
+ * Destructor.
+ */
+ ~CompleteQueue();
+
+ /**
+ * Pop the next task out from the queue.
+ *
+ * @return The next task to be processed.
+ */
+ LoadingTask* NextTask();
+
+ /**
+ * Add a task in to the queue
+ *
+ * @param[in] task The task added to the queue.
+ */
+ void AddTask( LoadingTask* task );
+
+private:
+
+ // Undefined
+ CompleteQueue( const CompleteQueue& queue );
+
+ // Undefined
+ CompleteQueue& operator=( const CompleteQueue& queue );
+
+private:
+
+ Vector< LoadingTask* > mTasks;
+ Dali::Mutex mMutex;
+ EventThreadCallback* mTrigger;
+};
+
+/**
+ * The worker thread for image loading.
+ */
+class ImageLoadThread : public Thread
+{
+public:
+
+ /**
+ * Constructor.
+ *
+ * @param[in] loadQueue The task queue with images for loading.
+ * @param[in] completeQurue The task queue with images loaded.
+ */
+ ImageLoadThread( LoadQueue& loadQueue, CompleteQueue& completeQueue );
+
+ /**
+ * Destructor.
+ */
+ virtual ~ImageLoadThread();
+
+protected:
+
+ /**
+ * The entry function of the worker thread.
+ * It fetches loading task from the loadQueue, loads the image and adds to the completeQueue.
+ */
+ virtual void Run();
+
+private:
+
+ // Undefined
+ ImageLoadThread( const ImageLoadThread& thread );
+
+ // Undefined
+ ImageLoadThread& operator=( const ImageLoadThread& thread );
+
+private:
+
+ LoadQueue& mLoadQueue; ///<The task queue with images for loading.
+ CompleteQueue& mCompleteQueue; ///<The task queue with images loaded.
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* __DALI_TOOLKIT_IMAGE_LOAD_THREAD_H__ */
return ImageView( imageView );
}
-ImageView ImageView::New( const std::string& url )
+ImageView ImageView::New( const std::string& url,
+ ImageDimensions size)
{
ImageView imageView = Internal::ImageView::New();
- imageView.SetImage( url );
+ imageView.SetImage( url, size );
return imageView;
}
Dali::Toolkit::GetImpl( *this ).SetImage( image );
}
-void ImageView::SetImage( const std::string& url )
+void ImageView::SetImage( const std::string& url,
+ ImageDimensions size )
{
- Dali::Toolkit::GetImpl( *this ).SetImage( url );
+ Dali::Toolkit::GetImpl( *this ).SetImage( url, size );
}
Image ImageView::GetImage() const
// INTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/control.h>
+// EXTERNAL INCLUDES
+#include <dali/public-api/images/image-operations.h>
+
namespace Dali
{
/**
* @brief Create an initialized ImageView from an Image resource url
*
+ * @note A valid size is preferable for efficiency.
+ * However, do not set 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.
+ *
* If the string is empty, ImageView will display nothing
* @param[in] url The url of the image resource to display.
+ * @param [in] size The width and height to fit the loaded image to.
* @return A handle to a newly allocated ImageView.
*/
- static ImageView New( const std::string& url );
+ static ImageView New( const std::string& url,
+ ImageDimensions size = ImageDimensions() );
/**
* @brief Destructor
* @since DALi 1.1.4
*
* @param[in] url The Image resource to display.
+ * @param [in] size The width and height to fit the loaded image to.
*/
- void SetImage( const std::string& url );
+ void SetImage( const std::string& url,
+ ImageDimensions size = ImageDimensions() );
/**
* @deprecated Gets the Image