utc-Dali-DebugRendering.cpp
utc-Dali-ImageAtlas.cpp
utc-Dali-VideoView.cpp
+ utc-Dali-AsyncImageLoader.cpp
+ utc-Dali-SyncImageLoader.cpp
)
# Append list of test harness files (Won't get parsed for test cases)
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#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-toolkit/dali-toolkit.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";
+
+// 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<size; i++ )
+ {
+ if( mIDs[i] == id )
+ {
+ return mPixelDataList[i].GetWidth() == width
+ && mPixelDataList[i].GetHeight() == height;
+ }
+ }
+
+ return false;
+ }
+
+private:
+
+ int mCount;
+
+ std::vector<uint32_t> mIDs;
+ std::vector<PixelData> 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 UtcDaliAsyncImageLoaderDownCastP(void)
+{
+ AsyncImageLoader asyncImageLoader = AsyncImageLoader::New();
+ BaseHandle object(asyncImageLoader);
+
+ AsyncImageLoader asyncImageLoader2 = AsyncImageLoader::DownCast( object );
+
+ DALI_TEST_CHECK( asyncImageLoader2 );
+
+ END_TEST;
+}
+
+int UtcDaliAsyncImageLoaderDownCastN(void)
+{
+ BaseHandle unInitializedObject;
+ AsyncImageLoader asyncImageLoader = AsyncImageLoader::DownCast( unInitializedObject );
+
+ DALI_TEST_CHECK( !asyncImageLoader );
+
+ 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;
+}
+
+// Note: This is not an ideal test, but we cannot guarantee we can call Cancel() before the image has finished loading.
+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 );
+
+ EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+ CallbackBase* callback = eventTrigger->GetCallback();
+
+ eventTrigger->WaitingForTrigger( 3 ); // waiting until images are loaded
+
+ CallbackBase::Execute( *callback );
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK( loadedSignalVerifier.LoadedImageCount() == 3 );
+
+ DALI_TEST_CHECK( !loader.Cancel( id03 ) ); // Cannot cancel a task that is already implemeted
+
+ DALI_TEST_CHECK( loadedSignalVerifier.Verify( id01, 34, 34 ) ); // first image is loaded
+ DALI_TEST_CHECK( loadedSignalVerifier.Verify( id02, 25, 25 ) ); // second image is loaded
+ DALI_TEST_CHECK( loadedSignalVerifier.Verify( id03, 100, 100 ) ); // third image is loaded
+
+ END_TEST;
+}
+
+int UtcDaliAsyncImageLoaderCancelAll(void)
+{
+ ToolkitTestApplication application;
+
+ AsyncImageLoader loader = AsyncImageLoader::New();
+
+ // Test that it is safe to call CancelAll even there is no loading task requested.
+ try
+ {
+ loader.CancelAll();
+ }
+ catch(Dali::DaliException& e)
+ {
+ DALI_TEST_ASSERT(e, "AsyncImageLoader::LoadAll", TEST_LOCATION);
+ }
+
+ // Test that cancelling a non-existing loading task will return false
+ uint32_t id = 1;
+ DALI_TEST_CHECK( !(loader.Cancel( id )) );
+
+ END_TEST;
+}
+
#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/devel-api/image-loader/image-atlas.h>
#include <dali-toolkit/public-api/controls/image-view/image-view.h>
using namespace Dali;
&& rect2.y < rect1.y+rect1.height;
}
+static unsigned int gCountOfTestFuncCall;
+class TestUploadObserver : public AtlasUploadObserver
+{
+public:
+ TestUploadObserver()
+ {}
+
+ virtual ~TestUploadObserver()
+ {}
+
+ void UploadCompleted()
+ {
+ gCountOfTestFuncCall++;
+ }
+};
+
} // anonymous namespace
void dali_image_atlas_startup(void)
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;
}
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 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 );
END_TEST;
}
+int UtcDaliImageAtlasUploadWithObserver01(void)
+{
+ TestApplication application;
+ ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+ EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+ CallbackBase* callback = eventTrigger->GetCallback();
+
+ gCountOfTestFuncCall = 0;
+ TestUploadObserver uploadObserver;
+
+ Vector4 textureRect1;
+ atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, &uploadObserver );
+ Vector4 textureRect2;
+ atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, NULL );
+ Vector4 textureRect3;
+ atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, &uploadObserver );
+
+ // waiting until all three images are loaded and uploaded to atlas
+ eventTrigger->WaitingForTrigger( 3 );
+ CallbackBase::Execute( *callback );
+ application.SendNotification();
+ application.Render(RENDER_FRAME_INTERVAL);
+
+ // Check that TestFunc is called twice
+ DALI_TEST_EQUALS( gCountOfTestFuncCall, 2, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliImageAtlasUploadWithObserver02(void)
+{
+ TestApplication application;
+ ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+ EventThreadCallback* eventTrigger = EventThreadCallback::Get();
+ CallbackBase* callback = eventTrigger->GetCallback();
+
+ gCountOfTestFuncCall = 0;
+ TestUploadObserver* uploadObserver = new TestUploadObserver;
+
+ Vector4 textureRect1;
+ atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, uploadObserver );
+ Vector4 textureRect2;
+ atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, uploadObserver );
+ Vector4 textureRect3;
+ atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, uploadObserver );
+
+ // destroy the object.
+ delete uploadObserver;
+
+ // waiting until all three images are loaded and uploaded to atlas
+ eventTrigger->WaitingForTrigger( 3 );
+ CallbackBase::Execute( *callback );
+ application.Render(RENDER_FRAME_INTERVAL);
+ application.SendNotification();
+
+ // Check that TestFunc is called twice
+ DALI_TEST_EQUALS( gCountOfTestFuncCall, 0, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliImageAtlasUploadWithObserver03(void)
+{
+ TestApplication application;
+
+ gCountOfTestFuncCall = 0;
+ TestUploadObserver* uploadObserver = new TestUploadObserver;
+
+ {
+ ImageAtlas atlas = ImageAtlas::New( 200, 200 );
+
+ Vector4 textureRect1;
+ atlas.Upload( textureRect1, gImage_34_RGBA, ImageDimensions(34, 34), FittingMode::DEFAULT, true, uploadObserver );
+ Vector4 textureRect2;
+ atlas.Upload( textureRect2, gImage_50_RGBA, ImageDimensions(50, 50), FittingMode::DEFAULT, true, uploadObserver );
+ Vector4 textureRect3;
+ atlas.Upload( textureRect3, gImage_128_RGB, ImageDimensions(128, 128), FittingMode::DEFAULT, true, uploadObserver );
+ }
+
+ //ImageAtlas is out of scope, so it will get destroyed
+
+ application.Render(RENDER_FRAME_INTERVAL);
+ application.SendNotification();
+ application.SendNotification();
+ application.Render(RENDER_FRAME_INTERVAL);
+
+ // Check that TestFunc is called twice
+ DALI_TEST_EQUALS( gCountOfTestFuncCall, 0, TEST_LOCATION );
+
+ END_TEST;
+}
+
int UtcDaliImageAtlasRemove(void)
{
TestApplication application;
callStack.Reset();
callStack.Enable(true);
+ BitmapLoader::ResetLatestCreated();
ImageView imageView = ImageView::New( gImage_34_RGBA, ImageDimensions( 34, 34 ) );
// By default, Aysnc loading is used
loader.WaitForLoading();// waiting until the image to be loaded
DALI_TEST_CHECK( loader.IsLoaded() );
+ eventTrigger->WaitingForTrigger( 1 );
CallbackBase* callback = eventTrigger->GetCallback();
CallbackBase::Execute( *callback );
#include <sstream>
#include <dali-toolkit-test-suite-utils.h>
#include <dali/integration-api/events/pan-gesture-event.h>
-#include <dali/public-api/images/buffer-image.h>
#include <dali-toolkit/devel-api/controls/page-turn-view/page-factory.h>
#include <dali-toolkit/devel-api/controls/page-turn-view/page-turn-landscape-view.h>
#include <dali-toolkit/devel-api/controls/page-turn-view/page-turn-portrait-view.h>
const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS)
const unsigned int TOTAL_PAGE_NUMBER = 20;
const Vector2 PAGE_SIZE( 300.f,400.f );
-const unsigned int IMAGE_WIDTH = 30;
-const unsigned int IMAGE_HEIGHT = 30;
-const Vector2 IMAGE_SIZE( static_cast<float>( IMAGE_WIDTH ), static_cast<float>(IMAGE_HEIGHT) );
const Vector2 SPINE_SHADOW_PARAMETER( 60.0f, 30.0f );
static bool gObjectCreatedCallBackCalled;
{
public:
- TestPageFactory(ToolkitTestApplication& application)
- : mApplication( application )
+ TestPageFactory(ToolkitTestApplication& application, bool returnValidTexture = true )
+ : mApplication( application ),
+ mValidTexture( returnValidTexture )
{
mTotalPageNumber = TOTAL_PAGE_NUMBER;
}
}
/**
- * Create an image to represent a page content.
+ * Create an texture to represent a page content.
* @param[in] pageId The ID of the page to create.
* @return An image, or an empty handle if the ID is out of range.
*/
- virtual Image NewPage( unsigned int pageId )
+ virtual Texture NewPage( unsigned int pageId )
{
- return BufferImage::WHITE();
+ if( mValidTexture )
+ {
+ return Texture::New( Dali::TextureType::TEXTURE_2D, Pixel::RGB888, 100, 100 );
+ }
+ return Texture(); // empty handle
}
private:
ToolkitTestApplication& mApplication;
unsigned int mTotalPageNumber;
+ bool mValidTexture;
};
}// namespace
DALI_TEST_CHECK( factory.GetExtension() == NULL );
END_TEST;
}
+
+int UtcDaliPageTurnEmptyTextureHandle(void)
+{
+ ToolkitTestApplication application;
+
+ tet_infoline(" UtcDaliPageTurnEmptyTextureHandle ");
+
+ application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE );
+
+ TestPageFactory factory(application, false); // returns empty handles
+ Vector2 size = Stage::GetCurrent().GetSize();
+ try
+ {
+ PageTurnView portraitView = PageTurnPortraitView::New( factory, size );
+ portraitView.SetParentOrigin( ParentOrigin::CENTER );
+ Stage::GetCurrent().Add( portraitView );
+
+ tet_result(TET_FAIL);
+ }
+ catch (DaliException& e)
+ {
+ DALI_TEST_ASSERT(e, "must pass in valid texture", TEST_LOCATION );
+ }
+ catch (...)
+ {
+ tet_result(TET_FAIL);
+ }
+
+ END_TEST;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <dali/dali.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+
+namespace
+{
+
+// 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";
+
+
+void VerifyLoad( PixelData pixelData, uint32_t width, uint32_t height )
+{
+ DALI_TEST_CHECK( pixelData );
+ DALI_TEST_EQUALS<unsigned int>( pixelData.GetWidth(), width, TEST_LOCATION );
+ DALI_TEST_EQUALS<unsigned int>( pixelData.GetHeight(), height, TEST_LOCATION );
+}
+
+} // anonymous namespace
+
+
+int UtcDaliSyncImageLoaderLoad(void)
+{
+ PixelData pixelData = Toolkit::SyncImageLoader::Load( gImage_50_RGBA );
+
+ DALI_TEST_EQUALS<bool>( pixelData, true, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliSyncImageLoaderLoadWithDimensions(void)
+{
+ PixelData pixelData = Toolkit::SyncImageLoader::Load( gImage_50_RGBA, ImageDimensions( 25, 25 ) );
+
+ VerifyLoad( pixelData, 25u, 25u );
+
+ END_TEST;
+}
+
+int UtcDaliSyncImageLoaderLoadWithAllOptions(void)
+{
+ PixelData pixelData = Toolkit::SyncImageLoader::Load( gImage_128_RGB, ImageDimensions( 100, 100 ), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, true );
+
+ VerifyLoad( pixelData, 100u, 100u );
+
+ END_TEST;
+}
+
+
propertyMap.Insert( ImageVisual::Property::DESIRED_HEIGHT, 30 );
propertyMap.Insert( ImageVisual::Property::FITTING_MODE, FittingMode::FIT_HEIGHT );
propertyMap.Insert( ImageVisual::Property::SAMPLING_MODE, SamplingMode::BOX_THEN_NEAREST );
+ propertyMap.Insert( ImageVisual::Property::PIXEL_AREA, Vector4( 0.25f, 0.25f, 0.5f, 0.5f ) );
+ propertyMap.Insert( ImageVisual::Property::WRAP_MODE_U, WrapMode::REPEAT );
+ propertyMap.Insert( ImageVisual::Property::WRAP_MODE_V, WrapMode::MIRRORED_REPEAT );
propertyMap.Insert( "synchronousLoading", true );
Visual::Base imageVisual = factory.CreateVisual(propertyMap);
DALI_TEST_CHECK( value );
DALI_TEST_CHECK( value->Get<int>() == 30 );
+ value = resultMap.Find( ImageVisual::Property::PIXEL_AREA, Property::VECTOR4 );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_EQUALS( value->Get<Vector4>(), Vector4( 0.25f, 0.25f, 0.5f, 0.5f ), Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+ value = resultMap.Find( ImageVisual::Property::WRAP_MODE_U, Property::INTEGER );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_CHECK( value->Get<int>() == WrapMode::REPEAT);
+
+ value = resultMap.Find( ImageVisual::Property::WRAP_MODE_V, Property::INTEGER );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_CHECK( value->Get<int>() == WrapMode::MIRRORED_REPEAT);
+
value = resultMap.Find( "synchronousLoading", Property::BOOLEAN );
DALI_TEST_CHECK( value );
DALI_TEST_CHECK( value->Get<bool>() == true );
DALI_TEST_CHECK( value );
DALI_TEST_CHECK( value->Get<int>() == 200 );
+ value = resultMap.Find( ImageVisual::Property::PIXEL_AREA, Property::VECTOR4 );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_EQUALS( value->Get<Vector4>(), Vector4( 0.f, 0.f, 1.f, 1.f ), Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+ value = resultMap.Find( ImageVisual::Property::WRAP_MODE_U, Property::INTEGER );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_CHECK( value->Get<int>() == WrapMode::DEFAULT);
+
+ value = resultMap.Find( ImageVisual::Property::WRAP_MODE_V, Property::INTEGER );
+ DALI_TEST_CHECK( value );
+ DALI_TEST_CHECK( value->Get<int>() == WrapMode::DEFAULT);
+
value = resultMap.Find( "synchronousLoading", Property::BOOLEAN );
DALI_TEST_CHECK( value );
DALI_TEST_CHECK( value->Get<bool>() == false );
#include <iostream>
#include <stdlib.h>
#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-bitmap-loader.h>
#include <toolkit-event-thread-callback.h>
#include <dali/public-api/rendering/renderer.h>
#include <dali/public-api/rendering/texture-set.h>
const char* TEST_IMAGE_FILE_NAME = "gallery_image_01.jpg";
const char* TEST_NPATCH_FILE_NAME = "gallery_image_01.9.png";
-
const char* TEST_SVG_FILE_NAME = TEST_RESOURCE_DIR "/svg1.svg";
const char* TEST_OBJ_FILE_NAME = TEST_RESOURCE_DIR "/Cube.obj";
const char* TEST_MTL_FILE_NAME = TEST_RESOURCE_DIR "/ToyRobot-Metal.mtl";
const char* TEST_SIMPLE_OBJ_FILE_NAME = TEST_RESOURCE_DIR "/Cube-Points-Only.obj";
const char* TEST_SIMPLE_MTL_FILE_NAME = TEST_RESOURCE_DIR "/ToyRobot-Metal-Simple.mtl";
+// resolution: 34*34, pixel format: RGBA8888
+static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png";
+// resolution: 600*600, pixel format: RGB888
+static const char* gImage_600_RGB = TEST_RESOURCE_DIR "/test-image-600.jpg";
+
Integration::Bitmap* CreateBitmap( unsigned int imageWidth, unsigned int imageHeight, unsigned int initialColor, Pixel::Format pixelFormat )
{
Integration::Bitmap* bitmap = Integration::Bitmap::New( Integration::Bitmap::BITMAP_2D_PACKED_PIXELS, ResourcePolicy::OWNED_RETAIN );
VisualFactory newFactory = VisualFactory::Get();
DALI_TEST_CHECK( newFactory );
- // Check that renderer factory is a singleton
+ // Check that visual factory is a singleton
DALI_TEST_CHECK(factory == newFactory);
END_TEST;
int UtcDaliVisualFactoryGetImageVisual1(void)
{
ToolkitTestApplication application;
- tet_infoline( "UtcDaliVisualFactoryGetImageVisual1: Request image renderer with a Property::Map" );
+ tet_infoline( "UtcDaliVisualFactoryGetImageVisual1: Request image visual with a Property::Map" );
VisualFactory factory = VisualFactory::Get();
DALI_TEST_CHECK( factory );
int UtcDaliVisualFactoryGetImageVisual2(void)
{
ToolkitTestApplication application;
- tet_infoline( "UtcDaliVisualFactoryGetImageVisual2: Request image renderer with an image handle" );
+ tet_infoline( "UtcDaliVisualFactoryGetImageVisual2: Request image visual with an image handle" );
VisualFactory factory = VisualFactory::Get();
DALI_TEST_CHECK( factory );
END_TEST;
}
+int UtcDaliVisualFactoryGetImageVisual3(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "UtcDaliVisualFactoryGetImageVisual3: Request image visual with a Property::Map, test custom wrap mode and pixel area with atlasing" );
+
+ VisualFactory factory = VisualFactory::Get();
+ DALI_TEST_CHECK( factory );
+
+ // Test wrap mode with atlasing. Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
+ const int width=34;
+ const int height=34;
+ const Vector4 pixelArea(-0.5f, -0.5f, 2.f, 2.f);
+
+ Property::Map propertyMap;
+ propertyMap.Insert( Visual::Property::TYPE, Visual::IMAGE );
+ propertyMap.Insert( ImageVisual::Property::URL, gImage_34_RGBA );
+ propertyMap.Insert( ImageVisual::Property::DESIRED_WIDTH, width );
+ propertyMap.Insert( ImageVisual::Property::DESIRED_HEIGHT, height );
+ propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, true );
+ propertyMap.Insert( ImageVisual::Property::PIXEL_AREA, pixelArea );
+ propertyMap.Insert( ImageVisual::Property::WRAP_MODE_U, WrapMode::MIRRORED_REPEAT );
+ propertyMap.Insert( ImageVisual::Property::WRAP_MODE_V, WrapMode::REPEAT );
+
+ Visual::Base visual = factory.CreateVisual( propertyMap );
+ DALI_TEST_CHECK( visual );
+
+ Actor actor = Actor::New();
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ TraceCallStack& textureTrace = gl.GetTextureTrace();
+ textureTrace.Enable(true);
+ TraceCallStack& texParameterTrace = gl.GetTexParameterTrace();
+ texParameterTrace.Enable( true );
+
+ actor.SetSize( 200.f, 200.f );
+ Stage::GetCurrent().Add( actor );
+ visual.SetOnStage( actor );
+
+ // loading started
+ application.SendNotification();
+ application.Render();
+ application.Render();
+ application.SendNotification();
+ BitmapLoader loader = BitmapLoader::GetLatestCreated();
+ DALI_TEST_CHECK( loader );
+ loader.WaitForLoading();// waiting until the image to be loaded
+ DALI_TEST_CHECK( loader.IsLoaded() );
+
+ DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+
+ DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+
+ // WITH atlasing, the wrapping is handled manually in shader, so the following gl function should not be called
+ std::stringstream out;
+ out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_S << ", " << GL_MIRRORED_REPEAT;
+ DALI_TEST_CHECK( !texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
+ out.str("");
+ out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_T << ", " << GL_REPEAT;
+ DALI_TEST_CHECK( !texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
+
+ // test the uniforms which used to handle the wrap mode
+ Renderer renderer = actor.GetRendererAt( 0u );
+ DALI_TEST_CHECK( renderer );
+
+ Property::Value pixelAreaValue = renderer.GetProperty( renderer.GetPropertyIndex( "pixelArea" ) );
+ DALI_TEST_EQUALS( pixelAreaValue.Get<Vector4>(), pixelArea, TEST_LOCATION );
+ Vector4 pixelAreaUniform;
+ DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "pixelArea", pixelAreaUniform ) );
+ DALI_TEST_EQUALS( pixelArea, pixelAreaUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+ Property::Value wrapModeValue = renderer.GetProperty( renderer.GetPropertyIndex( "wrapMode" ) );
+ Vector2 wrapMode( WrapMode::MIRRORED_REPEAT-1, WrapMode::REPEAT-1 );
+ DALI_TEST_EQUALS( wrapModeValue.Get<Vector2>(), wrapMode, TEST_LOCATION );
+ Vector2 wrapModeUniform;
+ DALI_TEST_CHECK( gl.GetUniformValue<Vector2>( "wrapMode", wrapModeUniform ) );
+ DALI_TEST_EQUALS( wrapMode, wrapModeUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+ visual.SetOffStage( actor );
+ DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+
+ END_TEST;
+}
+
+int UtcDaliVisualFactoryGetImageVisual4(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "UtcDaliVisualFactoryGetImageVisual4: Request image visual with a Property::Map, test custom wrap mode and pixel area without atlasing" );
+
+ VisualFactory factory = VisualFactory::Get();
+ DALI_TEST_CHECK( factory );
+
+ // Test wrap mode without atlasing. Image with a size bigger than 512*512 will NOT be uploaded as a part of the atlas.
+ const int width=600;
+ const int height=600;
+ const Vector4 pixelArea(-0.5f, -0.5f, 2.f, 2.f);
+
+ Property::Map propertyMap;
+ propertyMap.Insert( Visual::Property::TYPE, Visual::IMAGE );
+ propertyMap.Insert( ImageVisual::Property::URL, gImage_600_RGB );
+ propertyMap.Insert( ImageVisual::Property::DESIRED_WIDTH, width );
+ propertyMap.Insert( ImageVisual::Property::DESIRED_HEIGHT, height );
+ propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, true );
+ propertyMap.Insert( ImageVisual::Property::PIXEL_AREA, pixelArea );
+ propertyMap.Insert( ImageVisual::Property::WRAP_MODE_U, WrapMode::MIRRORED_REPEAT );
+ propertyMap.Insert( ImageVisual::Property::WRAP_MODE_V, WrapMode::REPEAT );
+
+ Visual::Base visual = factory.CreateVisual( propertyMap );
+ DALI_TEST_CHECK( visual );
+
+ Actor actor = Actor::New();
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ TraceCallStack& textureTrace = gl.GetTextureTrace();
+ textureTrace.Enable(true);
+ TraceCallStack& texParameterTrace = gl.GetTexParameterTrace();
+ texParameterTrace.Enable( true );
+
+ actor.SetSize( 200.f, 200.f );
+ Stage::GetCurrent().Add( actor );
+ visual.SetOnStage( actor );
+
+ // loading started
+ application.SendNotification();
+ application.Render();
+ application.Render();
+ application.SendNotification();
+ BitmapLoader loader = BitmapLoader::GetLatestCreated();
+ DALI_TEST_CHECK( loader );
+ loader.WaitForLoading();// waiting until the image to be loaded
+ DALI_TEST_CHECK( loader.IsLoaded() );
+
+ DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
+
+ DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+
+ // WITHOUT atlasing, the wrapping is handled by setting gl texture parameters
+ std::stringstream out;
+ out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_S << ", " << GL_MIRRORED_REPEAT;
+ DALI_TEST_CHECK( texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
+ out.str("");
+ out << GL_TEXTURE_2D << ", " << GL_TEXTURE_WRAP_T << ", " << GL_REPEAT;
+ DALI_TEST_CHECK( texParameterTrace.FindMethodAndParams("TexParameteri", out.str()) );
+
+ // test the uniforms which used to handle the wrap mode
+ Renderer renderer = actor.GetRendererAt( 0u );
+ DALI_TEST_CHECK( renderer );
+
+ Property::Value pixelAreaValue = renderer.GetProperty( renderer.GetPropertyIndex( "pixelArea" ) );
+ DALI_TEST_EQUALS( pixelAreaValue.Get<Vector4>(), pixelArea, TEST_LOCATION );
+ Vector4 pixelAreaUniform;
+ DALI_TEST_CHECK( gl.GetUniformValue<Vector4>( "pixelArea", pixelAreaUniform ) );
+ DALI_TEST_EQUALS( pixelArea, pixelAreaUniform, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+
+ Property::Index wrapModeIndex = renderer.GetPropertyIndex( "wrapMode" );
+ DALI_TEST_CHECK(wrapModeIndex == Property::INVALID_INDEX);
+
+ visual.SetOffStage( actor );
+ DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
+
+ END_TEST;
+}
+
int UtcDaliVisualFactoryGetNPatchVisual1(void)
{
ToolkitTestApplication application;
- tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual1: Request 9-patch renderer with a Property::Map" );
+ tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual1: Request 9-patch visual with a Property::Map" );
VisualFactory factory = VisualFactory::Get();
DALI_TEST_CHECK( factory );
int UtcDaliVisualFactoryGetNPatchVisual2(void)
{
ToolkitTestApplication application;
- tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual2: Request n-patch renderer with a Property::Map" );
+ tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual2: Request n-patch visual with a Property::Map" );
VisualFactory factory = VisualFactory::Get();
DALI_TEST_CHECK( factory );
int UtcDaliVisualFactoryGetNPatchVisual3(void)
{
ToolkitTestApplication application;
- tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual3: Request 9-patch renderer with an image url" );
+ tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual3: Request 9-patch visual with an image url" );
VisualFactory factory = VisualFactory::Get();
DALI_TEST_CHECK( factory );
END_TEST;
}
-//Creates a mesh renderer from the given propertyMap and tries to load it on stage in the given application.
+//Creates a mesh visual from the given propertyMap and tries to load it on stage in the given application.
//This is expected to succeed, which will then pass the test.
void MeshVisualLoadsCorrectlyTest( Property::Map& propertyMap, ToolkitTestApplication& application )
{
END_TEST;
}
-//Test if primitive shape renderer handles the case of not being passed a specific shape to use.
+//Test if primitive shape visual handles the case of not being passed a specific shape to use.
int UtcDaliVisualFactoryGetPrimitiveVisualN1(void)
{
//Set up test application first, so everything else can be handled.
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
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)
# public api source
publicapidir = $(topleveldir)/public-api
publicapicontrolsdir = $(publicapidir)/controls
+publicapiimageloaderdir = $(publicapidir)/image-loader
publicapiaccessibilitymanagerdir = $(publicapidir)/accessibility-manager
publicapialignmentdir = $(publicapicontrolsdir)/alignment
publicapibuttonsdir = $(publicapicontrolsdir)/buttons
# public api headers
publicapi_HEADERS = $(public_api_header_files)
publicapicontrols_HEADERS = $(public_api_controls_header_files)
+publicapiimageloader_HEADERS = $(public_api_image_loader_header_files)
publicapiaccessibilitymanager_HEADERS = $(public_api_accessibility_manager_header_files)
publicapialignment_HEADERS = $(public_api_alignment_header_files)
publicapibuttons_HEADERS = $(public_api_buttons_header_files)
ALIASES += SINCE_1_2_2="@since 1.2.2"
ALIASES += SINCE_1_2_4="@since 1.2.4"
ALIASES += SINCE_1_2_5="@since 1.2.5"
+ALIASES += SINCE_1_2_10="@since 1.2.10"
+ALIASES += SINCE_1_2_14="@since 1.2.14"
ALIASES += DEPRECATED_1_0="@deprecated Deprecated since 1.0"
ALIASES += DEPRECATED_1_1="@deprecated Deprecated since 1.1"
#ALIASES += SINCE_1_2_2="\par Since:\n 3.0, DALi version 1.2.2"
#ALIASES += SINCE_1_2_4="\par Since:\n 3.0, DALi version 1.2.4"
#ALIASES += SINCE_1_2_5="\par Since:\n 3.0, DALi version 1.2.5"
+#ALIASES += SINCE_1_2_10="\par Since:\n 3.0, DALi version 1.2.10"
+#ALIASES += SINCE_1_2_14="\par Since:\n 3.0, DALi version 1.2.14"
## DALi has no deprecated API in Tizen 2.4 because it's DALi's first release.
## Thus deprecated APIs in DALi 1.0.xx will be deprecated in Tizen 3.0.
#include <dali-toolkit/public-api/controls/text-controls/text-label.h>
#include <dali-toolkit/public-api/controls/video-view/video-view.h>
+#include <dali-toolkit/public-api/image-loader/async-image-loader.h>
+#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
+
#include <dali-toolkit/public-api/accessibility-manager/accessibility-manager.h>
#include <dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h>
*/
// EXTERNAL INCLUDES
-#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/rendering/texture.h>
namespace Dali
{
{
/**
- * @brief PageFactory is an abstract interface for providing images to PageTurnView
+ * @brief PageFactory is an abstract interface for providing textures to PageTurnView
* Each page is identified by a unique ID, and has a linear order from 0 to GetNumberOfPages()-1
*
* @SINCE_1_1.4
/**
* @brief Virtual destructor
- * @SINCE_1_1.4
*/
virtual ~PageFactory(){};
* @brief Query the number of pages available from the factory.
*
* The maximum available page has an ID of GetNumberOfPages()-1.
- * @SINCE_1_1.4
* @return The page count.
*/
virtual unsigned int GetNumberOfPages() = 0;
/**
- * @brief Create an actor to represent the page content.
- * @SINCE_1_1.30
+ * @brief Return the texture for the page
*
- * If no valid image provided, a broken image is displayed.
- * For double-sided page( PageTurnLandscapeView ), the left half of image is used as page front side, and the right half as page back side.
+ * For double-sided page( PageTurnLandscapeView ), the left half of texture is used as page front side, and the right half as page back side.
+ *
+ * @note Must return a valid texture handle!
*
* @param[in] pageId The ID of the page to create.
* @return An actor, or an uninitialized pointer if the ID is out of range.
*/
- virtual Image NewPage( unsigned int pageId ) = 0;
+ virtual Texture NewPage( unsigned int pageId ) = 0;
/**
* @brief Retrieve the extension for this factory
- * @SINCE_1_1.30
*
* @return The extension if available, NULL otherwise.
*/
$(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/atlas-upload-observer.cpp \
+ $(devel_api_src_dir)/image-loader/image-atlas.cpp \
$(devel_api_src_dir)/scripting/script.cpp \
$(devel_api_src_dir)/transition-effects/cube-transition-cross-effect.cpp \
$(devel_api_src_dir)/transition-effects/cube-transition-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_image_loader_header_files = \
+ $(devel_api_src_dir)/image-loader/atlas-upload-observer.h \
+ $(devel_api_src_dir)/image-loader/image-atlas.h
devel_api_scripting_header_files = \
$(devel_api_src_dir)/scripting/script.h \
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "atlas-upload-observer.h"
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+AtlasUploadObserver::AtlasUploadObserver()
+{}
+
+AtlasUploadObserver::~AtlasUploadObserver()
+{
+ // Notify the registerd ImageAtlas object about the destruction of observer.
+ const std::size_t size( mAtlasList.Count() );
+ for( std::size_t i = 0; i < size; ++i )
+ {
+ if( mAtlasList[i] )
+ {
+ mAtlasList[i]->ObserverDestroyed( this );
+ }
+ }
+ mAtlasList.Clear();
+}
+
+void AtlasUploadObserver::Register( Internal::ImageAtlas& imageAtlas )
+{
+ // Add to the list so that the ImageAtlas could get notified in the destructor.
+ // If the same atlas is exist in the list already, we would still save the duplicated copy.
+ mAtlasList.PushBack( &imageAtlas );
+}
+
+void AtlasUploadObserver::Unregister( Internal::ImageAtlas& imageAtlas )
+{
+ const std::size_t size( mAtlasList.Count() );
+ for( std::size_t i = 0; i < size; i++ )
+ {
+ if( mAtlasList[i] == &imageAtlas )
+ {
+ // Remove from list
+ mAtlasList.Erase( mAtlasList.Begin() + i );
+ // If there are duplicated copies of same pointer, only the first one is removed
+ return;
+ }
+ }
+}
+
+}
+
+}
--- /dev/null
+#ifndef DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H
+#define DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H
+
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/signals/callback.h>
+
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+class ImageAtlas;
+}
+
+/**
+ * @brief Base class used to observe the upload status of the ImageAtlas when requesting an image atlasing.
+ *
+ * Derived class should implement the UploadCompleted method which would get executed once the texture is ready.
+ */
+class DALI_IMPORT_API AtlasUploadObserver
+{
+public:
+
+ /**
+ * @brief Constructor.
+ */
+ AtlasUploadObserver();
+
+ /**
+ * @brief Virtual destructor.
+ */
+ virtual ~AtlasUploadObserver();
+
+ /**
+ * The action to be taken once the upload is completed.
+ */
+ virtual void UploadCompleted() = 0;
+
+public: // not intended for developer, called by ImageAtlas internally to get notified when this observer dies
+
+ /**
+ * @brief Register an ImageAtlas which be notified when the observer is destructing.
+ * @param[in] imageAtlas The ImageAtlas object to get notification about the destruction of the observer.
+ */
+ void Register( Internal::ImageAtlas& imageAtlas );
+
+ /**
+ * @brief Unregister an ImageAtlas which be notified when the observer is destructing.
+ * @param[in] imageAtlas The ImageAtlas object to get notification about the destruction of the observer.
+ */
+ void Unregister( Internal::ImageAtlas& imageAtlas );
+
+private:
+
+ Vector<Internal::ImageAtlas*> mAtlasList; ///< The list of the registered ImageAtlas object
+
+};
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif /* DALI_TOOLKIT_ATLAS_UPLOAD_OBSERVER_H */
#include "image-atlas.h"
// INTERNAL INCLUDES
-#include <dali-toolkit/internal/image-atlas/image-atlas-impl.h>
+#include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
namespace Dali
{
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 );
FittingMode::Type fittingMode,
bool orientationCorrection )
{
- return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection );
+ return Upload( textureRect, url, size, fittingMode, orientationCorrection, NULL );
+}
+
+bool ImageAtlas::Upload( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver )
+{
+ return GetImplementation(*this).Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver );
}
bool ImageAtlas::Upload( Vector4& textureRect, PixelData pixelData )
#include <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>
#include <dali/public-api/images/pixel-data.h>
+#include <dali/public-api/rendering/texture.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
namespace Dali
{
}
/**
- * @brief 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
{
/**
* @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 texture
+ */
+ Texture GetAtlas();
+
+ /*
+ * @brief Query what percentage of space is been occupied in the atlas.
*
- * @return the atlas image with type of Dali::Atlas
+ * @return The occupancy rate of the atlas.
*/
- Image GetAtlas();
+ float GetOccupancyRate() const;
/**
* @brief Set the broken image which is used to replace the image if loading fails.
/**
* @brief Upload a resource image to the atlas.
*
- * @note To make the atlasing efficient, an valid size should be provided.
+ * @note To make the atlasing efficient, a valid size should be provided.
* If size is not provided, then the image file will be opened to read the actual size for loading.
* Do not set a size that is bigger than the actual image size, as the up-scaling is not available,
* the content of the area not covered by actual image is undefined, it will not be cleared.
bool orientationCorrection = true );
/**
+ * @brief Upload a resource image to the atlas.
+ *
+ * @note To make the atlasing efficient, a valid size should be provided.
+ * If size is not provided, then the image file will be opened to read the actual size for loading.
+ * Do not set a size that is bigger than the actual image size, as the up-scaling is not available,
+ * the content of the area not covered by actual image is undefined, it will not be cleared.
+ *
+ * SamplingMode::BOX_THEN_LINEAR is used to sampling pixels from the input image while fitting it to desired size.
+ *
+ * @param [out] textureRect The texture area of the resource image in the atlas.
+ * @param [in] url The URL of the resource image file to use.
+ * @param [in] size The width and height to fit the loaded image to.
+ * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
+ * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ * @param[in] atlasUploadObserver The observer to observe the upload state inside the ImageAtlas.
+ * @return True if there is enough space to fit this image in,false otherwise.
+ * @note The valid callback function here is required to have the signature of void( void ).
+ */
+ bool Upload( Vector4& textureRect,
+ const std::string& url,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver );
+
+ /**
* @brief Upload a pixel buffer to atlas
*
* @param [out] textureRect The texture area of the resource image in the atlas.
#include <cstring> // for strcmp
#include <dali/public-api/animation/animation.h>
#include <dali/public-api/animation/constraint.h>
-#include <dali/public-api/images/resource-image.h>
#include <dali/public-api/object/type-registry.h>
#include <dali/public-api/object/type-registry-helper.h>
-#include <dali/devel-api/images/texture-set-image.h>
#include <dali/integration-api/debug.h>
// INTERNAL INCLUDES
namespace //Unnamed namespace
{
-// broken image is loaded if there is no valid image provided for the page
-const char * const BROKEN_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
-
// properties set on shader, these properties have the constant value in regardless of the page status
const char * const PROPERTY_SPINE_SHADOW ( "uSpineShadowParameter" ); // uniform for both spine and turn effect
propertyTurnDirection = actor.RegisterProperty(PROPERTY_TURN_DIRECTION, -1.f);
}
-void PageTurnView::Page::SetImage( Image image )
+void PageTurnView::Page::SetTexture( Texture texture )
{
if( !textureSet )
{
textureSet = TextureSet::New();
}
-
- TextureSetImage( textureSet, 0u, image );
+ textureSet.SetTexture( 0u, texture );
}
void PageTurnView::Page::UseEffect(Shader newShader)
Self().Add( mPages[i].actor );
}
- // create the layer for turning images
+ // create the layer for turning pages
mTurningPageLayer = Layer::New();
mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
mTurningPageLayer.SetBehavior(Layer::LAYER_3D);
{
int index = pageIndex % NUMBER_OF_CACHED_PAGES;
- Image newPageImage;
- newPageImage = mPageFactory->NewPage( pageIndex );
-
- if( !newPageImage ) // load the broken image
- {
- newPageImage = ResourceImage::New( BROKEN_IMAGE_URL );
- }
+ Texture newPage;
+ newPage = mPageFactory->NewPage( pageIndex );
+ DALI_ASSERT_ALWAYS( newPage && "must pass in valid texture" );
bool isLeftSide = ( pageIndex < mCurrentPageIndex );
if( mPages[index].isTurnBack != isLeftSide )
mPages[index].actor.SetOrientation( Degree( degree ), Vector3::YAXIS );
mPages[index].actor.SetVisible( false );
mPages[index].UseEffect( mSpineEffectShader, mGeometry );
- mPages[index].SetImage( newPageImage );
+ mPages[index].SetTexture( newPage );
// For Portrait, nothing to do
// For Landscape, set the parent origin to CENTER
- OnAddPage( mPages[index].actor, isLeftSide );
+ OnAddPage( mPages[index].actor, isLeftSide );
}
}
~Page(){};
/**
- * Set the page image content
- * @param[in] image The content of the page.
+ * Set the page texture content
+ * @param[in] texture The content of the page.
*/
- void SetImage( Image image );
+ void SetTexture( Texture texture );
/**
* Apply an effect onto the page actor.
$(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 \
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "async-image-loader-impl.h"
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/bitmap-loader.h>
+#include <dali/integration-api/adaptors/adaptor.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+AsyncImageLoader::AsyncImageLoader()
+: mLoadedSignal(),
+ mLoadThread( new EventThreadCallback( MakeCallback( this, &AsyncImageLoader::ProcessLoadedImage ) ) ),
+ mLoadTaskId( 0u ),
+ mIsLoadThreadStarted( false )
+{
+}
+
+AsyncImageLoader::~AsyncImageLoader()
+{
+ mLoadThread.CancelAll();
+}
+
+IntrusivePtr<AsyncImageLoader> AsyncImageLoader::New()
+{
+ IntrusivePtr<AsyncImageLoader> internal = new AsyncImageLoader();
+ return internal;
+}
+
+uint32_t AsyncImageLoader::Load( const std::string& url,
+ ImageDimensions dimensions,
+ FittingMode::Type fittingMode,
+ SamplingMode::Type samplingMode,
+ bool orientationCorrection )
+{
+ if( !mIsLoadThreadStarted )
+ {
+ mLoadThread.Start();
+ mIsLoadThreadStarted = true;
+ }
+
+ BitmapLoader loader = BitmapLoader::New( url, dimensions, 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
--- /dev/null
+#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 <dali/public-api/images/pixel-data.h>
+#include <dali/public-api/object/base-object.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/image-loader/async-image-loader.h>
+#include <dali-toolkit/internal/image-loader/image-load-thread.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+class AsyncImageLoader : public BaseObject
+{
+public:
+
+ /**
+ * Constructor
+ */
+ AsyncImageLoader();
+
+ /**
+ * @copydoc Toolkit::AsyncImageLoader::New()
+ */
+ static IntrusivePtr<AsyncImageLoader> New();
+
+ /**
+ * @copydoc Toolkit::AsyncImageLoader::Load( const std::string&, ImageDimensions, FittingMode::Type, SamplingMode::Type, bool )
+ */
+ uint32_t Load( const std::string& url,
+ ImageDimensions dimensions,
+ 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& handle )
+{
+ DALI_ASSERT_ALWAYS( handle && "AsyncImageLoader handle is empty" );
+
+ const BaseObject& object = handle.GetBaseObject();
+
+ return static_cast<const Internal::AsyncImageLoader&>( object );
+}
+
+inline Internal::AsyncImageLoader& GetImplementation( Toolkit::AsyncImageLoader& handle )
+{
+ DALI_ASSERT_ALWAYS( handle && "AsyncImageLoader handle is empty" );
+
+ BaseObject& object = handle.GetBaseObject();
+
+ return static_cast<Internal::AsyncImageLoader&>( object );
+}
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_ASYNC_IMAGE_LOADER_IMPL_H
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;
/*
- * 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.
#include <string.h>
#include <dali/public-api/signals/callback.h>
#include <dali/public-api/images/resource-image.h>
+#include <dali/devel-api/adaptor-framework/bitmap-loader.h>
#include <dali/integration-api/debug.h>
namespace Dali
{
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<float>(width) ),
+ mHeight( static_cast<float>( height ) ),
+ mPixelFormat( pixelFormat )
{
- mAtlas = Atlas::New( width, height, pixelFormat );
- mWidth = static_cast<float>(width);
- mHeight = static_cast<float>( height );
+ mAsyncLoader.ImageLoadedSignal().Connect( this, &ImageAtlas::UploadToAtlas );
}
ImageAtlas::~ImageAtlas()
{
- if( mLoadingThreadStarted )
+ const std::size_t count = mLoadingTaskInfoContainer.Count();
+ for( std::size_t i=0; i < count; ++i )
{
- // 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();
+ // Call unregister to every observer in the list.
+ // Note that, the Atlas can be registered to same observer multiple times, and the Unregister method only remove one item each time.
+ // In this way, the atlas is actually detached from a observer either every upload call invoked by this observer is completed or atlas is destroyed.
+ if( mLoadingTaskInfoContainer[i]->observer )
+ {
+ mLoadingTaskInfoContainer[i]->observer->Unregister( *this );
+ }
}
+
+ mLoadingTaskInfoContainer.Clear();
}
IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
{
IntrusivePtr<ImageAtlas> 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<float>( mPacker.GetAvailableArea() ) / ( mWidth*mHeight );
+}
+
void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
{
mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl );
const std::string& url,
ImageDimensions size,
FittingMode::Type fittingMode,
- bool orientationCorrection )
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver )
{
ImageDimensions dimensions = size;
ImageDimensions zero;
{
if( !mBrokenImageUrl.empty() )
{
- return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true );
+ return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver );
}
else
{
}
}
- if( 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 );
-
+ unsigned short loadId = mAsyncLoader.Load( url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection );
+ mLoadingTaskInfoContainer.PushBack( new LoadingTaskInfo( loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight(), atlasUploadObserver ) );
// apply the half pixel correction
textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
textureRect.z = ( static_cast<float>( packPositionX + dimensions.GetX() )-0.5f ) / mWidth; // right
textureRect.w = ( static_cast<float>( packPositionY + dimensions.GetY() )-0.5f ) / mHeight;// bottom
+ if( atlasUploadObserver )
+ {
+ // register to the observer,
+ // Not that a matching unregister call should be invoked in UploadToAtlas if the observer is still alive by then.
+ atlasUploadObserver->Register( *this );
+ }
+
return true;
}
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<float>( packPositionX ) +0.5f ) / mWidth; // left
static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
}
-void ImageAtlas::UploadToAtlas()
+void ImageAtlas::ObserverDestroyed( AtlasUploadObserver* observer )
+{
+ const std::size_t count = mLoadingTaskInfoContainer.Count();
+ for( std::size_t i=0; i < count; ++i )
+ {
+ if( mLoadingTaskInfoContainer[i]->observer == observer )
+ {
+ // the observer is destructing, so its member function should not be called anymore
+ mLoadingTaskInfoContainer[i]->observer = NULL;
+ }
+ }
+}
+
+void ImageAtlas::UploadToAtlas( uint32_t id, PixelData pixelData )
{
- while( LoadingTask* next = mCompleteQueue.NextTask() )
+ if( mLoadingTaskInfoContainer[0]->loadTaskId == id)
{
- if( ! next->loader.IsLoaded() )
+ Rect<unsigned int> packRect( mLoadingTaskInfoContainer[0]->packRect );
+ if( !pixelData || ( pixelData.GetWidth() ==0 && pixelData.GetHeight() == 0 ))
{
if(!mBrokenImageUrl.empty()) // replace with the broken image
{
- UploadBrokenImage( next->packRect );
+ UploadBrokenImage( 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() < packRect.width || pixelData.GetHeight() < packRect.height )
{
DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
- next->loader.GetPixelData().GetWidth(),
- next->loader.GetPixelData().GetHeight(),
- next->packRect.width,
- next->packRect.height );
+ pixelData.GetWidth(), pixelData.GetHeight(),
+ packRect.width, packRect.height );
}
- mAtlas.Upload( next->loader.GetPixelData(), next->packRect.x, next->packRect.y );
+ mAtlas.Upload( pixelData, 0u, 0u, packRect.x, packRect.y, packRect.width, packRect.height );
+ }
+
+ if( mLoadingTaskInfoContainer[0]->observer )
+ {
+ mLoadingTaskInfoContainer[0]->observer->UploadCompleted();
+ mLoadingTaskInfoContainer[0]->observer->Unregister( *this );
}
- delete next;
+ mLoadingTaskInfoContainer.Erase( mLoadingTaskInfoContainer.Begin() );
}
}
-void ImageAtlas::UploadBrokenImage( const Rect<SizeType>& area )
+void ImageAtlas::UploadBrokenImage( const Rect<unsigned int>& area )
{
BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
loader.Load();
{
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
// EXTERNAL INCLUDES
#include <dali/public-api/common/intrusive-ptr.h>
#include <dali/public-api/object/base-object.h>
+#include <dali/public-api/signals/connection-tracker.h>
+#include <dali/devel-api/common/owner-container.h>
#include <dali/devel-api/images/atlas.h>
+#include <dali/devel-api/common/owner-container.h>
// INTERNAL INCLUDES
-#include <dali-toolkit/devel-api/image-atlas/image-atlas.h>
-#include <dali-toolkit/internal/image-atlas/atlas-packer.h>
-#include <dali-toolkit/internal/image-atlas/image-load-thread.h>
+#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
+#include <dali-toolkit/internal/image-loader/atlas-packer.h>
+#include <dali-toolkit/public-api/image-loader/async-image-loader.h>
namespace Dali
{
namespace Internal
{
-class ImageAtlas : public BaseObject
+class ImageAtlas : public BaseObject, public ConnectionTracker
{
public:
/**
* @copydoc Toolkit::ImageAtlas::GetAtlas
*/
- Image GetAtlas();
+ Texture GetAtlas();
+
+ /**
+ * @copydoc Toolkit::ImageAtlas::GetOccupancyRate
+ */
+ float GetOccupancyRate() const;
/**
* @copydoc Toolkit::ImageAtlas::SetBrokenImage
const std::string& url,
ImageDimensions size,
FittingMode::Type fittingMode,
- bool orientationCorrection);
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver );
/**
* @copydoc Toolkit::ImageAtlas::Upload( Vector4&, PixelData )
*/
void Remove( const Vector4& textureRect );
+ /**
+ * Resets the destroying observer pointer so that we know not to call methods of this object any more.
+ */
+ void ObserverDestroyed( AtlasUploadObserver* observer );
+
protected:
/**
private:
/**
- * Upload the bitmap to atlas when the image is loaded in the worker thread.
+ * @copydoc PixelDataRequester::ProcessPixels
*/
- void UploadToAtlas();
+ void UploadToAtlas( uint32_t id, PixelData pixelData );
/**
* Upload broken image
*
* @param[in] area The pixel area for uploading.
*/
- void UploadBrokenImage( const Rect<SizeType>& area );
+ void UploadBrokenImage( const Rect<unsigned int>& area );
// Undefined
ImageAtlas( const ImageAtlas& imageAtlas);
private:
- Atlas mAtlas;
- AtlasPacker mPacker;
-
- LoadQueue mLoadQueue;
- CompleteQueue mCompleteQueue;
- ImageLoadThread mLoadingThread;
+ /**
+ * Each loading task( identified with an ID ) is associated with a rect region for packing the loaded pixel data into the atlas,
+ * and an AtlasUploadObserver whose UploadCompleted method should get executed once the sub texture is ready.
+ */
+ struct LoadingTaskInfo
+ {
+ LoadingTaskInfo( unsigned short loadTaskId,
+ unsigned int packPositionX,
+ unsigned int packPositionY,
+ unsigned int width,
+ unsigned int height,
+ AtlasUploadObserver* observer )
+ : loadTaskId( loadTaskId ),
+ packRect( packPositionX, packPositionY, width, height ),
+ observer( observer )
+ {}
+
+ unsigned short loadTaskId;
+ Rect<unsigned int> packRect;
+ AtlasUploadObserver* observer;
+ };
+
+ OwnerContainer<LoadingTaskInfo*> mLoadingTaskInfoContainer;
+
+ Texture mAtlas;
+ AtlasPacker mPacker;
+ Toolkit::AsyncImageLoader mAsyncLoader;
+ std::string mBrokenImageUrl;
+ ImageDimensions mBrokenImageSize;
+ float mWidth;
+ float mHeight;
+ Pixel::Format mPixelFormat;
- std::string mBrokenImageUrl;
- ImageDimensions mBrokenImageSize;
- float mWidth;
- float mHeight;
- Pixel::Format mPixelFormat;
- bool mLoadingThreadStarted;
};
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)
+ if( wasEmpty )
{
// wake up the image loading thread
mConditionalWait.Notify();
}
}
-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
/**
* Constructor.
*/
- LoadingTask( BitmapLoader loader, uint32_t packPositionX, uint32_t packPositionY, uint32_t width, uint32_t height );
+ LoadingTask( uint32_t id, BitmapLoader loader );
private:
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.
-
+ 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:
private:
- LoadQueue& mLoadQueue; ///<The task queue with images for loading.
- CompleteQueue& mCompleteQueue; ///<The task queue with images loaded.
+ Vector< LoadingTask* > mLoadQueue; ///<The task queue with images for loading.
+ Vector< LoadingTask* > mCompleteQueue; ///<The task queue with images loaded.
+
+ ConditionalWait mConditionalWait;
+ Dali::Mutex mMutex;
+ EventThreadCallback* mTrigger;
};
} // namespace Internal
const std::string& url,
ImageDimensions size,
FittingMode::Type fittingMode,
- bool orientationCorrection )
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver )
{
ImageDimensions dimensions = size;
ImageDimensions zero;
unsigned int i = 0;
for( AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
{
- if( (*iter).Upload( textureRect, url, size, fittingMode, orientationCorrection ) )
+ if( (*iter).Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver ) )
{
return mTextureSetList[i];
}
}
CreateNewAtlas();
- mAtlasList.back().Upload( textureRect, url, size, fittingMode, orientationCorrection );
+ mAtlasList.back().Upload( textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver );
return mTextureSetList.back();
}
TextureSet ImageAtlasManager::Add( Vector4& textureRect,
- PixelData pixelData )
+ PixelData pixelData )
{
// big buffer, atlasing is not applied
}
mAtlasList.push_back( newAtlas );
TextureSet textureSet = TextureSet::New();
- TextureSetImage( textureSet, 0u, newAtlas.GetAtlas() );
+ textureSet.SetTexture( 0u, newAtlas.GetAtlas() );
mTextureSetList.push_back( textureSet );
}
#include <dali/public-api/rendering/texture-set.h>
// INTERNAL INCLUDES
-#include <dali-toolkit/devel-api/image-atlas/image-atlas.h>
+#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
namespace Dali
{
namespace Toolkit
{
+class AtlasUploadObserver;
+
namespace Internal
{
* @param [in] size The width and height to fit the loaded image to.
* @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
* @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ * @param [in] atlasUploadObserver The object to observe the uploading state inside ImageAtlas.
* @return The texture set containing the image.
*/
TextureSet Add( Vector4& textureRect,
- const std::string& url,
- ImageDimensions size = ImageDimensions(),
- FittingMode::Type fittingMode = FittingMode::DEFAULT,
- bool orientationCorrection = true );
-
+ const std::string& url,
+ ImageDimensions size = ImageDimensions(),
+ FittingMode::Type fittingMode = FittingMode::DEFAULT,
+ bool orientationCorrection = true,
+ AtlasUploadObserver* atlasUploadObserver = NULL );
/**
* @brief Add a pixel buffer to the atlas
*
const char * const IMAGE_SAMPLING_MODE( "samplingMode" );
const char * const IMAGE_DESIRED_WIDTH( "desiredWidth" );
const char * const IMAGE_DESIRED_HEIGHT( "desiredHeight" );
+const char * const IMAGE_WRAP_MODE_U("wrapModeU");
+const char * const IMAGE_WRAP_MODE_V("wrapModeV");
const char * const SYNCHRONOUS_LOADING( "synchronousLoading" );
// fitting modes
DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::SamplingMode, DONT_CARE )
DALI_ENUM_TO_STRING_TABLE_END( SAMPLING_MODE )
+// wrap modes
+DALI_ENUM_TO_STRING_TABLE_BEGIN( WRAP_MODE )
+DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, DEFAULT )
+DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, CLAMP_TO_EDGE )
+DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, REPEAT )
+DALI_ENUM_TO_STRING_WITH_SCOPE( Dali::WrapMode, MIRRORED_REPEAT )
+DALI_ENUM_TO_STRING_TABLE_END( WRAP_MODE )
+
const std::string PIXEL_AREA_UNIFORM_NAME = "pixelArea";
+const std::string WRAP_MODE_UNIFORM_NAME = "wrapMode";
const Vector4 FULL_TEXTURE_RECT(0.f, 0.f, 1.f, 1.f);
attribute mediump vec2 aPosition;\n
uniform mediump mat4 uMvpMatrix;\n
uniform mediump vec3 uSize;\n
- uniform mediump vec4 uAtlasRect;\n
uniform mediump vec4 pixelArea;
varying mediump vec2 vTexCoord;\n
\n
vertexPosition.xyz *= uSize;\n
vertexPosition = uMvpMatrix * vertexPosition;\n
\n
- vTexCoord = mix( uAtlasRect.xy, uAtlasRect.zw, pixelArea.xy+pixelArea.zw*(aPosition + vec2(0.5) ) );\n
+ vTexCoord = pixelArea.xy+pixelArea.zw*(aPosition + vec2(0.5) );\n
gl_Position = vertexPosition;\n
}\n
);
-const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
+const char* FRAGMENT_SHADER_NO_ATLAS = DALI_COMPOSE_SHADER(
varying mediump vec2 vTexCoord;\n
uniform sampler2D sTexture;\n
uniform lowp vec4 uColor;\n
}\n
);
+const char* FRAGMENT_SHADER_ATLAS_CLAMP = DALI_COMPOSE_SHADER(
+ varying mediump vec2 vTexCoord;\n
+ uniform sampler2D sTexture;\n
+ uniform mediump vec4 uAtlasRect;\n
+ uniform lowp vec4 uColor;\n
+ \n
+ void main()\n
+ {\n
+ mediump vec2 texCoord = mix( uAtlasRect.xy, uAtlasRect.zw, clamp( vTexCoord, 0.0, 1.0 ) );\n
+ gl_FragColor = texture2D( sTexture, texCoord ) * uColor;\n
+ }\n
+);
+
+const char* FRAGMENT_SHADER_ATLAS_VARIOUS_WRAP = DALI_COMPOSE_SHADER(
+ varying mediump vec2 vTexCoord;\n
+ uniform sampler2D sTexture;\n
+ uniform mediump vec4 uAtlasRect;\n
+ // WrapMode -- 0: CLAMP; 1: REPEAT; 2: REFLECT;
+ uniform lowp vec2 wrapMode;\n
+ uniform lowp vec4 uColor;\n
+ \n
+ mediump float wrapCoordinate( mediump float coordinate, lowp float wrap )\n
+ {\n
+ if( abs(wrap-2.0) < 0.5 )\n // REFLECT
+ return 1.0-abs(fract(coordinate*0.5)*2.0 - 1.0);\n
+ else \n// warp == 0 or 1
+ return mix( clamp( coordinate, 0.0, 1.0 ), fract( coordinate ), wrap);\n
+ }\n
+ void main()\n
+ {\n
+ mediump vec2 texCoord = mix( uAtlasRect.xy, uAtlasRect.zw,
+ vec2( wrapCoordinate( vTexCoord.x, wrapMode.x ), wrapCoordinate( vTexCoord.y, wrapMode.y ) ) );\n
+ gl_FragColor = texture2D( sTexture, texCoord ) * uColor;\n
+ }\n
+);
+
Geometry CreateGeometry( VisualFactoryCache& factoryCache, ImageDimensions gridSize )
{
Geometry geometry;
} //unnamed namespace
-ImageVisual::ImageVisual( VisualFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
+ImageVisual::ImageVisual( VisualFactoryCache& factoryCache )
: Visual::Base( factoryCache ),
- mAtlasManager( atlasManager ),
+ mPixelArea( FULL_TEXTURE_RECT ),
mDesiredSize(),
mFittingMode( FittingMode::DEFAULT ),
mSamplingMode( SamplingMode::DEFAULT ),
+ mWrapModeU( WrapMode::DEFAULT ),
+ mWrapModeV( WrapMode::DEFAULT ),
mNativeFragmentShaderCode( ),
mNativeImageFlag( false )
{
desiredHeightValue->Get( desiredHeight );
}
+ Property::Value* pixelAreaValue = propertyMap.Find( Toolkit::ImageVisual::Property::PIXEL_AREA, PIXEL_AREA_UNIFORM_NAME );
+ if( pixelAreaValue )
+ {
+ pixelAreaValue->Get( mPixelArea );
+ }
+
+ Property::Value* wrapModeValueU = propertyMap.Find( Toolkit::ImageVisual::Property::WRAP_MODE_U, IMAGE_WRAP_MODE_U );
+ if( wrapModeValueU )
+ {
+ Scripting::GetEnumerationProperty( *wrapModeValueU, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, mWrapModeU );
+ }
+
+ Property::Value* wrapModeValueV = propertyMap.Find( Toolkit::ImageVisual::Property::WRAP_MODE_V, IMAGE_WRAP_MODE_V );
+ if( wrapModeValueV )
+ {
+ Scripting::GetEnumerationProperty( *wrapModeValueV, WRAP_MODE_TABLE, WRAP_MODE_TABLE_COUNT, mWrapModeV );
+ }
+
mDesiredSize = ImageDimensions( desiredWidth, desiredHeight );
}
}
}
-void ImageVisual::SetSize( const Vector2& size )
-{
- Visual::Base::SetSize( size );
-}
-
void ImageVisual::GetNaturalSize( Vector2& naturalSize ) const
{
if(mImage)
naturalSize = Vector2::ZERO;
}
-void ImageVisual::SetClipRect( const Rect<int>& clipRect )
-{
- Visual::Base::SetClipRect( clipRect );
-}
-
-void ImageVisual::SetOffset( const Vector2& offset )
-{
-}
-
Renderer ImageVisual::CreateRenderer() const
{
Geometry geometry;
{
geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
- shader = GetImageShader(mFactoryCache);
+ shader = GetImageShader( mFactoryCache,
+ mImpl->mFlags & Impl::IS_ATLASING_APPLIED,
+ mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE );
}
else
{
geometry = CreateGeometry( mFactoryCache, mImpl->mCustomShader->mGridSize );
if( mImpl->mCustomShader->mVertexShader.empty() && mImpl->mCustomShader->mFragmentShader.empty() )
{
- shader = GetImageShader(mFactoryCache);
+ shader = GetImageShader(mFactoryCache, false, true);
}
else
{
shader = Shader::New( mImpl->mCustomShader->mVertexShader.empty() ? VERTEX_SHADER : mImpl->mCustomShader->mVertexShader,
- mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER : mImpl->mCustomShader->mFragmentShader,
+ mImpl->mCustomShader->mFragmentShader.empty() ? FRAGMENT_SHADER_NO_ATLAS : mImpl->mCustomShader->mFragmentShader,
mImpl->mCustomShader->mHints );
if( mImpl->mCustomShader->mVertexShader.empty() )
{
- shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
}
}
geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
shader = Shader::New( VERTEX_SHADER, mNativeFragmentShaderCode );
- shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
}
else
mImpl->mCustomShader->mHints );
if( mImpl->mCustomShader->mVertexShader.empty() )
{
- shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
}
}
TextureSet textureSet = TextureSet::New();
Renderer renderer = Renderer::New( geometry, shader );
renderer.SetTextures( textureSet );
-
return renderer;
}
if( !mPixels )
{
// use broken image
- return VisualFactory::GetBrokenVisualImage();
+ return VisualFactoryCache::GetBrokenVisualImage();
}
Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() );
image.Upload( mPixels, 0, 0 );
{
// use broken image
textureSet = TextureSet::New();
- TextureSetImage( textureSet, 0u, VisualFactory::GetBrokenVisualImage() );
+ TextureSetImage( textureSet, 0u, VisualFactoryCache::GetBrokenVisualImage() );
}
else
{
- textureSet = mAtlasManager.Add(textureRect, mPixels );
+ textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, mPixels );
+ mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
if( !textureSet ) // big image, no atlasing
{
+ mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
Atlas image = Atlas::New( mPixels.GetWidth(), mPixels.GetHeight(), mPixels.GetPixelFormat() );
image.Upload( mPixels, 0, 0 );
textureSet = TextureSet::New();
}
else
{
- textureSet = mAtlasManager.Add(textureRect, url, mDesiredSize, mFittingMode, mSamplingMode );
+ textureSet = mFactoryCache.GetAtlasManager()->Add(textureRect, url, mDesiredSize, mFittingMode, true, this );
+ mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
if( !textureSet ) // big image, no atlasing
{
+ mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
ResourceImage resourceImage = Dali::ResourceImage::New( url, mDesiredSize, mFittingMode, mSamplingMode );
resourceImage.LoadingFinishedSignal().Connect( this, &ImageVisual::OnImageLoaded );
textureSet = TextureSet::New();
}
}
+ if( !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
+ {
+ Sampler sampler = Sampler::New();
+ sampler.SetWrapMode( mWrapModeU, mWrapModeV );
+ textureSet.SetSampler( 0u, sampler );
+ }
+
return textureSet;
}
mImageUrl = imageUrl;
mImpl->mRenderer.Reset();
+ mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
if( !mImpl->mCustomShader &&
( strncasecmp( imageUrl.c_str(), HTTP_URL, sizeof(HTTP_URL) -1 ) != 0 ) && // ignore remote images
( strncasecmp( imageUrl.c_str(), HTTPS_URL, sizeof(HTTPS_URL) -1 ) != 0 ) )
{
- mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
- if( !mImpl->mRenderer )
+ bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
+ bool cacheable = defaultWrapMode && mPixelArea == FULL_TEXTURE_RECT;
+
+ mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
+ if( cacheable ) // fetch the renderer from cache if exist
+ {
+ mImpl->mRenderer = mFactoryCache.GetRenderer( imageUrl );
+ mImpl->mFlags |= Impl::IS_FROM_CACHE;
+ }
+
+ if( !mImpl->mRenderer ) // new renderer is needed
{
Vector4 atlasRect;
TextureSet textureSet = CreateTextureSet(atlasRect, imageUrl, IsSynchronousResourceLoading() );
Geometry geometry = CreateGeometry( mFactoryCache, ImageDimensions( 1, 1 ) );
- Shader shader( GetImageShader(mFactoryCache) );
+ Shader shader( GetImageShader(mFactoryCache, mImpl->mFlags & Impl::IS_ATLASING_APPLIED, defaultWrapMode) );
mImpl->mRenderer = Renderer::New( geometry, shader );
mImpl->mRenderer.SetTextures( textureSet );
- if( atlasRect != FULL_TEXTURE_RECT )
+
+ if( mImpl->mFlags & Impl::IS_ATLASING_APPLIED ) // the texture is packed inside atlas
{
mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
+ if( !defaultWrapMode ) // custom wrap mode, renderer is not cached.
+ {
+ Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
+ wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
+ mImpl->mRenderer.RegisterProperty( WRAP_MODE_UNIFORM_NAME, wrapMode );
+ }
}
- mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
- }
- mImpl->mFlags |= Impl::IS_FROM_CACHE;
+ // save the renderer to cache only when default wrap mode and default pixel area is used
+ if( cacheable )
+ {
+ mFactoryCache.SaveRenderer( imageUrl, mImpl->mRenderer );
+ }
+ }
}
else
{
// for custom shader or remote image, renderer is not cached and atlas is not applied
-
mImpl->mFlags &= ~Impl::IS_FROM_CACHE;
mImpl->mRenderer = CreateRenderer();
Image image = LoadImage( imageUrl, IsSynchronousResourceLoading() );
{
ApplyImageToSampler( image );
}
+
+ if( mPixelArea != FULL_TEXTURE_RECT )
+ {
+ mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
+ }
}
+void ImageVisual::UploadCompleted()
+{
+ // Resource image is loaded. If weak handle is holding a placement actor, it is the time to add the renderer to actor.
+ Actor actor = mPlacementActor.GetHandle();
+ if( actor )
+ {
+ actor.AddRenderer( mImpl->mRenderer );
+ // reset the weak handle so that the renderer only get added to actor once
+ mPlacementActor.Reset();
+ }
+}
+
void ImageVisual::DoSetOnStage( Actor& actor )
{
+ mPlacementActor = actor;
+
if( !mImageUrl.empty() )
{
InitializeRenderer( mImageUrl );
InitializeRenderer( mImage );
}
- actor.AddRenderer( mImpl->mRenderer );
+ if( IsSynchronousResourceLoading() || !(mImpl->mFlags & Impl::IS_ATLASING_APPLIED) )
+ {
+ actor.AddRenderer( mImpl->mRenderer );
+ mPlacementActor.Reset();
+ }
}
void ImageVisual::DoSetOffStage( Actor& actor )
actor.RemoveRenderer( mImpl->mRenderer );
mImpl->mRenderer.Reset();
}
+ mPlacementActor.Reset();
}
void ImageVisual::DoCreatePropertyMap( Property::Map& map ) const
map.Insert( Toolkit::ImageVisual::Property::FITTING_MODE, mFittingMode );
map.Insert( Toolkit::ImageVisual::Property::SAMPLING_MODE, mSamplingMode );
+
+ map.Insert( Toolkit::ImageVisual::Property::PIXEL_AREA, mPixelArea );
+ map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_U, mWrapModeU );
+ map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
}
-Shader ImageVisual::GetImageShader( VisualFactoryCache& factoryCache )
+Shader ImageVisual::GetImageShader( VisualFactoryCache& factoryCache, bool atlasing, bool defaultTextureWrapping )
{
- Shader shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER );
- if( !shader )
+ Shader shader;
+ if( atlasing )
{
- shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
- factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER, shader );
- shader.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, FULL_TEXTURE_RECT );
- shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
+ if( defaultTextureWrapping )
+ {
+ shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP );
+ if( !shader )
+ {
+ shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_CLAMP );
+ factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_DEFAULT_WRAP, shader );
+ }
+ }
+ else
+ {
+ shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP );
+ if( !shader )
+ {
+ shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_ATLAS_VARIOUS_WRAP );
+ factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER_ATLAS_CUSTOM_WRAP, shader );
+ }
+ }
+ }
+ else
+ {
+ shader = factoryCache.GetShader( VisualFactoryCache::IMAGE_SHADER );
+ if( !shader )
+ {
+ shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER_NO_ATLAS );
+ factoryCache.SaveShader( VisualFactoryCache::IMAGE_SHADER, shader );
+ }
}
+ shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
return shader;
}
mImpl->mRenderer.SetTextures( textureSet );
}
TextureSetImage( textureSet, 0u, image );
+ Sampler sampler = Sampler::New();
+ sampler.SetWrapMode( mWrapModeU, mWrapModeV );
+ textureSet.SetSampler( 0u, sampler );
}
}
{
if( image.GetLoadingState() == Dali::ResourceLoadingFailed )
{
- Image brokenImage = VisualFactory::GetBrokenVisualImage();
+ Image brokenImage = VisualFactoryCache::GetBrokenVisualImage();
if( mImpl->mRenderer )
{
ApplyImageToSampler( brokenImage );
mImpl->mRenderer.Reset();
if( mFactoryCache.CleanRendererCache( url ) && index != Property::INVALID_INDEX )
{
- mAtlasManager.Remove( textureSet, atlasRect );
+ mFactoryCache.GetAtlasManager()->Remove( textureSet, atlasRect );
}
}
}
else
{
- mNativeFragmentShaderCode += FRAGMENT_SHADER;
+ mNativeFragmentShaderCode += FRAGMENT_SHADER_NO_ATLAS;
}
if( customSamplerTypename )
// INTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/visual-base-impl.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+#include <dali-toolkit/devel-api/image-loader/atlas-upload-observer.h>
// EXTERNAL INCLUDES
#include <dali/public-api/images/image.h>
#include <dali/public-api/images/image-operations.h>
#include <dali/public-api/images/resource-image.h>
+#include <dali/devel-api/object/weak-handle.h>
namespace Dali
{
* "DEFAULT"
*
*/
-class ImageVisual: public Visual::Base, public ConnectionTracker
+class ImageVisual: public Visual::Base, public ConnectionTracker, public AtlasUploadObserver
{
public:
* @brief Constructor.
*
* @param[in] factoryCache The VisualFactoryCache object
- * @param[in] atlasManager The atlasManager object
*/
- ImageVisual( VisualFactoryCache& factoryCache, ImageAtlasManager& atlasManager );
+ ImageVisual( VisualFactoryCache& factoryCache );
/**
* @brief A reference counted object may only be deleted by calling Unreference().
public: // from Visual
/**
- * @copydoc Visual::Base::SetSize
- */
- virtual void SetSize( const Vector2& size );
-
- /**
- * @copydoc Visual::Base::GetNaturalSize
+ * @copydoc Visual::GetNaturalSize
*/
virtual void GetNaturalSize( Vector2& naturalSize ) const;
/**
- * @copydoc Visual::Base::SetClipRect
- */
- virtual void SetClipRect( const Rect<int>& clipRect );
-
- /**
- * @copydoc Visual::Base::SetOffset
- */
- virtual void SetOffset( const Vector2& offset );
-
- /**
- * @copydoc Visual::Base::CreatePropertyMap
+ * @copydoc Visual::CreatePropertyMap
*/
virtual void DoCreatePropertyMap( Property::Map& map ) const;
/**
* Get the standard image rendering shader.
* @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
+ * @param[in] atlasing Whether texture atlasing is applied.
+ * @param[in] defaultTextureWrapping Whether the default texture wrap mode is applied.
*/
- static Shader GetImageShader( VisualFactoryCache& factoryCache );
+ static Shader GetImageShader( VisualFactoryCache& factoryCache, bool atlasing, bool defaultTextureWrapping );
/**
* @brief Sets the image of this visual to the resource at imageUrl
*/
void SetImage( Actor& actor, const Image& image );
+ /**
+ * @copydoc AtlasUploadObserver::UploadCompleted
+ *
+ * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+ * This callback is the place to add the renderer as it would be called once the loading is finished.
+ */
+ virtual void UploadCompleted();
+
private:
/**
private:
Image mImage;
- ImageAtlasManager& mAtlasManager;
PixelData mPixels;
+ Vector4 mPixelArea;
+ WeakHandle<Actor> mPlacementActor;
std::string mImageUrl;
Dali::ImageDimensions mDesiredSize;
Dali::FittingMode::Type mFittingMode;
Dali::SamplingMode::Type mSamplingMode;
+ Dali::WrapMode::Type mWrapModeU;
+ Dali::WrapMode::Type mWrapModeV;
std::string mNativeFragmentShaderCode;
bool mNativeImageFlag;
void NPatchVisual::InitializeFromBrokenImage()
{
- mCroppedImage = VisualFactory::GetBrokenVisualImage();
+ mCroppedImage = VisualFactoryCache::GetBrokenVisualImage();
mImageSize = ImageDimensions( mCroppedImage.GetWidth(), mCroppedImage.GetHeight() );
mStretchPixelsX.Clear();
#include <dali-toolkit/third-party/nanosvg/nanosvg.h>
#include <dali-toolkit/internal/visuals/svg/svg-rasterize-thread.h>
#include <dali-toolkit/internal/visuals/image/image-visual.h>
+#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
#include <dali-toolkit/internal/visuals/visual-string-constants.h>
#include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
namespace Internal
{
-SvgVisual::SvgVisual( VisualFactoryCache& factoryCache, ImageAtlasManager& atlasManager )
+SvgVisual::SvgVisual( VisualFactoryCache& factoryCache )
: Visual::Base( factoryCache ),
mAtlasRect( FULL_TEXTURE_RECT ),
- mAtlasManager( atlasManager ),
mParsedImage( NULL )
{
// the rasterized image is with pre-multiplied alpha format
void SvgVisual::DoSetOnStage( Actor& actor )
{
- Shader shader = ImageVisual::GetImageShader( mFactoryCache );
+ Shader shader = ImageVisual::GetImageShader( mFactoryCache, true, true );
Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
if( !geometry )
{
TextureSet currentTextureSet = mImpl->mRenderer.GetTextures();
if( mAtlasRect != FULL_TEXTURE_RECT )
{
- mAtlasManager.Remove( currentTextureSet, mAtlasRect );
+ mFactoryCache.GetAtlasManager()->Remove( currentTextureSet, mAtlasRect );
}
Vector4 atlasRect;
- TextureSet textureSet = mAtlasManager.Add(atlasRect, rasterizedPixelData );
+ TextureSet textureSet = mFactoryCache.GetAtlasManager()->Add(atlasRect, rasterizedPixelData );
if( textureSet ) // atlasing
{
if( textureSet != currentTextureSet )
}
mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, atlasRect );
mAtlasRect = atlasRect;
+ mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
}
else // no atlasing
{
Atlas texture = Atlas::New( rasterizedPixelData.GetWidth(), rasterizedPixelData.GetHeight() );
texture.Upload( rasterizedPixelData, 0, 0 );
+ mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
if( mAtlasRect == FULL_TEXTURE_RECT )
{
// INTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/visual-base-impl.h>
-#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
struct NSVGimage;
*
* @param[in] factoryCache A pointer pointing to the VisualFactoryCache object
*/
- SvgVisual( VisualFactoryCache& factoryCache, ImageAtlasManager& atlasManager );
+ SvgVisual( VisualFactoryCache& factoryCache );
/**
* @brief A reference counted object may only be deleted by calling Unreference().
private:
Vector4 mAtlasRect;
- ImageAtlasManager& mAtlasManager;
std::string mImageUrl;
NSVGimage* mParsedImage;
WeakHandle<Actor> mPlacementActor;
{
IS_ON_STAGE = 1,
IS_FROM_CACHE = 1 << 1,
- IS_PREMULTIPLIED_ALPHA = 1 << 2,
- IS_SYNCHRONOUS_RESOURCE_LOADING = 1 << 3
+ IS_ATLASING_APPLIED = 1<<2,
+ IS_PREMULTIPLIED_ALPHA = 1 << 3,
+ IS_SYNCHRONOUS_RESOURCE_LOADING = 1 << 4
};
struct CustomShader
// EXTERNAL HEADER
#include <dali/devel-api/common/hash.h>
+#include <dali/public-api/images/resource-image.h>
// INTERNAL HEADER
#include <dali-toolkit/internal/visuals/color/color-visual.h>
#include <dali-toolkit/internal/visuals/svg/svg-visual.h>
+#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
+
+namespace
+{
+const char * const BROKEN_VISUAL_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
+}
namespace Dali
{
return geometry;
}
+ImageAtlasManagerPtr VisualFactoryCache::GetAtlasManager()
+{
+ if( !mAtlasManager )
+ {
+ mAtlasManager = new ImageAtlasManager();
+ mAtlasManager->SetBrokenImage( BROKEN_VISUAL_IMAGE_URL );
+ }
+
+ return mAtlasManager;
+}
+
SvgRasterizeThread* VisualFactoryCache::GetSVGRasterizationThread()
{
if( !mSvgRasterizeThread )
return geometry;
}
+Image VisualFactoryCache::GetBrokenVisualImage()
+{
+ return ResourceImage::New( BROKEN_VISUAL_IMAGE_URL );
+}
+
} // namespace Internal
} // namespace Toolkit
namespace Internal
{
+class ImageAtlasManager;
+typedef IntrusivePtr<ImageAtlasManager> ImageAtlasManagerPtr;
+
/**
* Caches shaders and geometries. Owned by VisualFactory.
*/
GRADIENT_SHADER_RADIAL_USER_SPACE,
GRADIENT_SHADER_RADIAL_BOUNDING_BOX,
IMAGE_SHADER,
+ IMAGE_SHADER_ATLAS_DEFAULT_WRAP,
+ IMAGE_SHADER_ATLAS_CUSTOM_WRAP,
NINE_PATCH_SHADER,
SVG_SHADER,
SHADER_TYPE_MAX = SVG_SHADER
*/
static Geometry CreateGridGeometry( Uint16Pair gridSize );
+ /**
+ * @brief Returns an image to be used when a visual has failed to correctly render
+ * @return The broken image handle.
+ */
+ static Image GetBrokenVisualImage();
+
public:
/**
Renderer GetWireframeRenderer();
/**
+ * Get the image atlas manager.
+ * @return A pointer pointing to the atlas manager
+ */
+ ImageAtlasManagerPtr GetAtlasManager();
+
+ /**
* Get the SVG rasterization thread.
* @return A pointer pointing to the SVG rasterization thread.
*/
Renderer mWireframeRenderer;
+ ImageAtlasManagerPtr mAtlasManager;
SvgRasterizeThread* mSvgRasterizeThread;
};
#include <dali-toolkit/internal/visuals/wireframe/wireframe-visual.h>
#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
#include <dali-toolkit/internal/visuals/visual-string-constants.h>
+#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
-namespace
-{
-const char * const BROKEN_VISUAL_IMAGE_URL( DALI_IMAGE_DIR "broken.png");
-}
-
namespace Dali
{
{
visualPtr = new NPatchVisual( *( mFactoryCache.Get() ) );
}
+ else if( SvgVisual::IsSvgUrl( imageUrl ) )
+ {
+ visualPtr = new SvgVisual( *( mFactoryCache.Get() ) );
+ }
else
{
- CreateAtlasManager();
-
- if( SvgVisual::IsSvgUrl( imageUrl ) )
- {
- visualPtr = new SvgVisual( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
- }
- else
- {
- visualPtr = new ImageVisual( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
- }
+ visualPtr = new ImageVisual( *( mFactoryCache.Get() ) );
}
}
+
break;
}
}
else
{
- CreateAtlasManager();
- ImageVisual* visualPtr = new ImageVisual( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
+ ImageVisual* visualPtr = new ImageVisual( *( mFactoryCache.Get() ) );
Actor actor;
visualPtr->SetImage( actor, image );
}
else if( SvgVisual::IsSvgUrl( url ) )
{
- CreateAtlasManager();
- SvgVisual* visualPtr = new SvgVisual( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
+ SvgVisual* visualPtr = new SvgVisual( *( mFactoryCache.Get() ) );
visualPtr->SetImage( url, size );
return Toolkit::Visual::Base( visualPtr );
}
else
{
- CreateAtlasManager();
- ImageVisual* visualPtr = new ImageVisual( *( mFactoryCache.Get() ), *( mAtlasManager.Get() ) );
+ ImageVisual* visualPtr = new ImageVisual( *( mFactoryCache.Get() ));
Actor actor;
visualPtr->SetImage( actor, url, size );
}
}
-Image VisualFactory::GetBrokenVisualImage()
-{
- return ResourceImage::New( BROKEN_VISUAL_IMAGE_URL );
-}
-
-void VisualFactory::CreateAtlasManager()
-{
- if( !mAtlasManager )
- {
- Shader shader = ImageVisual::GetImageShader( *( mFactoryCache.Get() ) );
- mAtlasManager = new ImageAtlasManager();
- mAtlasManager->SetBrokenImage( BROKEN_VISUAL_IMAGE_URL );
- }
-}
-
} // namespace Internal
} // namespace Toolkit
class VisualFactoryCache;
typedef IntrusivePtr<VisualFactoryCache> VisualFactoryCachePtr;
-class ImageAtlasManager;
-typedef IntrusivePtr<ImageAtlasManager> ImageAtlasManagerPtr;
-
/**
* @copydoc Toolkit::VisualFactory
*/
*/
Toolkit::Visual::Base CreateVisual( const std::string& image, ImageDimensions size );
-public:
- /**
- * @brief Returns an image to be used when a visual has failed to correctly render
- */
- static Image GetBrokenVisualImage();
protected:
private:
/**
- * Prepare the atlas manager
- */
- void CreateAtlasManager();
-
- /**
* Undefined copy constructor.
*/
VisualFactory(const VisualFactory&);
private:
VisualFactoryCachePtr mFactoryCache;
- ImageAtlasManagerPtr mAtlasManager;
bool mDebugEnabled;
};
$(public_api_src_dir)/controls/text-controls/text-editor.cpp \
$(public_api_src_dir)/controls/text-controls/text-label.cpp \
$(public_api_src_dir)/controls/text-controls/text-field.cpp \
+ $(public_api_src_dir)/image-loader/async-image-loader.cpp \
+ $(public_api_src_dir)/image-loader/sync-image-loader.cpp \
$(public_api_src_dir)/styling/style-manager.cpp \
$(public_api_src_dir)/accessibility-manager/accessibility-manager.cpp \
$(public_api_src_dir)/focus-manager/keyboard-focus-manager.cpp \
$(public_api_src_dir)/controls/scrollable/item-view/item-view-declarations.h \
$(public_api_src_dir)/controls/scrollable/item-view/item-view.h
+public_api_image_loader_header_files = \
+ $(public_api_src_dir)/image-loader/async-image-loader.h \
+ $(public_api_src_dir)/image-loader/sync-image-loader.h
+
public_api_scrollable_header_files = \
$(public_api_src_dir)/controls/scrollable/scrollable.h
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include "async-image-loader.h"
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
+
+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::DownCast( BaseHandle handle )
+{
+ return AsyncImageLoader( dynamic_cast<Dali::Toolkit::Internal::AsyncImageLoader*>( handle.GetObjectPtr() ) );
+}
+
+AsyncImageLoader AsyncImageLoader::New()
+{
+ IntrusivePtr<Internal::AsyncImageLoader> 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 dimensions )
+{
+ return GetImplementation( *this ).Load( url, dimensions, FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
+}
+
+uint32_t AsyncImageLoader::Load( const std::string& url,
+ ImageDimensions dimensions,
+ FittingMode::Type fittingMode,
+ SamplingMode::Type samplingMode,
+ bool orientationCorrection )
+{
+ return GetImplementation(*this).Load( url, dimensions, 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
--- /dev/null
+#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 <string>
+#include <dali/public-api/object/base-handle.h>
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/signals/dali-signal.h>
+
+namespace Dali
+{
+class PixelData;
+
+namespace Toolkit
+{
+
+namespace Internal DALI_INTERNAL
+{
+class AsyncImageLoader;
+}
+
+/**
+ * @brief The AsyncImageLoader is used to load pixel data from a URL asynchronously.
+ *
+ * The images are loaded in a worker thread to avoid blocking the main event thread.
+ *
+ * To keep track of the loading images, each load call is assigned an ID (which is returned by the Load() call).
+ * To know when the Load has completed, connect to the ImageLoadedSignal.
+ * This signal should be connected before Load is called (in case the signal is emitted immediately).
+ *
+ * Load errors can be detected by checking the PixelData object is valid from within the signal handler.
+
+ * Note: The PixelData object will automatically be destroyed when it leaves its scope.
+ *
+ * Example:
+ *
+ * @code
+ * class MyClass : public ConnectionTracker
+ * {
+ * public:
+ *
+ * MyCallback( uint32_t loadedTaskId, PixelData pixelData )
+ * {
+ * // First check if the image loaded correctly.
+ * if( pixelData )
+ * {
+ * if( loadedTaskId == mId1 )
+ * {
+ * // use the loaded pixel data from the first image
+ * }
+ * else if( loadedTaskId == 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 );
+ *
+ * // Invoke the load calls (must do this after connecting the signal to guarantee callbacks occur).
+ * myObject.mId1 = imageLoader.Load( "first_image_url.jpg" );
+ * myObject.mId2 = imageLoader.Load( "second_image_url.jpg" );
+ *
+ * @endcode
+ */
+class DALI_IMPORT_API AsyncImageLoader : public BaseHandle
+{
+public:
+
+ typedef Signal< void( uint32_t, PixelData ) > ImageLoadedSignalType; ///< Image loaded signal type @SINCE_1_2_14
+
+public:
+
+ /**
+ * @brief Constructor which creates an empty AsyncImageLoader handle.
+ * @SINCE_1_2_14
+ *
+ * Use AsyncImageLoader::New() to create an initialised object.
+ */
+ AsyncImageLoader();
+
+ /**
+ * @brief Destructor
+ * @SINCE_1_2_14
+ *
+ * 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.
+ * @SINCE_1_2_14
+ *
+ * @param[in] handle A reference to the copied handle
+ */
+ AsyncImageLoader( const AsyncImageLoader& handle );
+
+ /**
+ * @brief This assignment operator is required for (smart) pointer semantics.
+ * @SINCE_1_2_14
+ *
+ * @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.
+ * @SINCE_1_2_14
+ *
+ * @return The image loader.
+ */
+ static AsyncImageLoader New();
+
+ /**
+ * @brief Downcast a handle to AsyncImageLoader handle.
+ *
+ * If the handle points to an AsyncImageLoader object the downcast produces
+ * a valid handle. If not, the returned handle is left uninitialized.
+ *
+ * @SINCE_1_2_14
+ * @param[in] handle A handle to an object
+ * @return A handle to a AsyncImageLoader object or an uninitialized handle
+ */
+ static AsyncImageLoader DownCast( BaseHandle handle );
+
+ /**
+ * @brief Start an image loading task.
+ * Note: When using this method, the following defaults will be used:
+ * fittingMode = FittingMode::DEFAULT
+ * samplingMode = SamplingMode::BOX_THEN_LINEAR
+ * orientationCorrection = true
+ *
+ * @SINCE_1_2_14
+ *
+ * @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 an image loading task.
+ * Note: When using this method, the following defaults will be used:
+ * fittingMode = FittingMode::DEFAULT
+ * samplingMode = SamplingMode::BOX_THEN_LINEAR
+ * orientationCorrection = true
+ *
+ * @SINCE_1_2_14
+ *
+ * @param[in] url The URL of the image file to load.
+ * @param[in] dimensions The width and height to fit the loaded image to.
+ * @return The loading task id.
+ */
+ uint32_t Load( const std::string& url, ImageDimensions dimensions );
+
+ /**
+ * @brief Start an image loading task.
+ * @SINCE_1_2_14
+ *
+ * @param[in] url The URL of the image file to load.
+ * @param[in] dimensions 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 dimensions,
+ FittingMode::Type fittingMode,
+ SamplingMode::Type samplingMode,
+ bool orientationCorrection );
+
+ /**
+ * @brief Cancel a image loading task if it is still queueing in the work thread.
+ * @SINCE_1_2_14
+ *
+ * @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
+ * @SINCE_1_2_14
+ */
+ 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
+ * @SINCE_1_2_14
+ *
+ * @return A reference to a signal object to Connect() with.
+ */
+ ImageLoadedSignalType& ImageLoadedSignal();
+
+public: // Not intended for developer use
+
+ /// @cond internal
+ /**
+ * @brief Allows the creation of a AsyncImageLoader handle from an internal pointer.
+ *
+ * @note Not intended for application developers
+ * @SINCE_1_2_14
+ *
+ * @param[in] impl A pointer to the object.
+ */
+ explicit DALI_INTERNAL AsyncImageLoader( Internal::AsyncImageLoader* impl );
+ /// @endcond
+
+};
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_ASYNC_IMAGE_LOADER_H
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// CLASS HEADER
+#include "sync-image-loader.h"
+#include <dali/devel-api/adaptor-framework/bitmap-loader.h>
+
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace SyncImageLoader
+{
+
+
+PixelData Load( const std::string& url )
+{
+ return Load( url, ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
+}
+
+PixelData Load( const std::string& url, ImageDimensions dimensions )
+{
+ return Load( url, dimensions, FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, true );
+}
+
+PixelData Load( const std::string& url,
+ ImageDimensions dimensions,
+ FittingMode::Type fittingMode,
+ SamplingMode::Type samplingMode,
+ bool orientationCorrection )
+{
+ BitmapLoader loader = BitmapLoader::New( url, dimensions, fittingMode, samplingMode, orientationCorrection );
+
+ // Load the image synchronously (block the thread here).
+ loader.Load();
+
+ return loader.GetPixelData();
+}
+
+
+} // namespace SyncImageLoader
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_SYNC_IMAGE_LOADER_H
+#define DALI_TOOLKIT_SYNC_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 <string>
+#include <dali/public-api/images/image-operations.h>
+#include <dali/public-api/images/pixel-data.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace SyncImageLoader
+{
+
+/**
+ * @brief The methods in the SyncImageLoader namespace are used to load pixel data from a URL synchronously.
+ *
+ * Example:
+ *
+ * @code
+ * PixelData pixelData = Toolkit::SyncImageLoader::Load( "image_url.jpg" );
+ *
+ * // Check the image was loaded without error.
+ * if( pixelData )
+ * {
+ * // Do work...
+ * }
+ *
+ * @endcode
+ */
+
+/**
+ * @brief Load an image synchronously.
+ * Note: When using this method, the following defaults will be used:
+ * fittingMode = FittingMode::DEFAULT
+ * samplingMode = SamplingMode::BOX_THEN_LINEAR
+ * orientationCorrection = true
+ *
+ * @SINCE_1_2_14
+ *
+ * @param[in] url The URL of the image file to load.
+ * @return A PixelData object containing the image, or an invalid object on failure.
+ */
+DALI_IMPORT_API PixelData Load( const std::string& url );
+
+/**
+ * @brief Load an image synchronously by specifying the target dimensions.
+ * Note: When using this method, the following defaults will be used:
+ * fittingMode = FittingMode::DEFAULT
+ * samplingMode = SamplingMode::BOX_THEN_LINEAR
+ * orientationCorrection = true
+ *
+ * @SINCE_1_2_14
+ *
+ * @param[in] url The URL of the image file to load.
+ * @param[in] dimensions The width and height to fit the loaded image to.
+ * @return A PixelData object containing the image, or an invalid object on failure.
+ */
+DALI_IMPORT_API PixelData Load( const std::string& url, ImageDimensions dimensions );
+
+/**
+ * @brief Load an image synchronously by specifying the target dimensions and options.
+ * @SINCE_1_2_14
+ *
+ * @param[in] url The URL of the image file to load.
+ * @param[in] dimensions 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 A PixelData object containing the image, or an invalid object on failure.
+ */
+DALI_IMPORT_API PixelData Load( const std::string& url,
+ ImageDimensions dimensions,
+ FittingMode::Type fittingMode,
+ SamplingMode::Type samplingMode,
+ bool orientationCorrection );
+
+} // namespace SyncImageLoader
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_SYNC_IMAGE_LOADER_H
* @note For N-Patch images only.
*/
BORDER_ONLY,
+
+ /**
+ * @brief The image area to be displayed.
+ * @details Name "pixelArea", type Property::VECTOR4.
+ * It is a rectangular area.
+ * The first two elements indicate the top-left position of the area, and the last two elements are the area width and height respectively.
+ * @SINCE_1_2.1
+ * @note Optional. If not specified, the default value is [0.0, 0.0, 1.0, 1.0], i.e. the entire area of the image.
+ * @note For Normal Quad images only.
+ */
+ PIXEL_AREA,
+
+ /**
+ * @brief The wrap mode for u coordinate.
+ * @details Name "wrapModeU", type Dali::WrapMode::Type (Property::INTEGER) or Property::STRING
+ * It decides how the texture should be sampled when the u coordinate exceeds the range of 0.0 to 1.0.
+ * @SINCE_1_2.1
+ * @note Optional. If not specified, the default is CLAMP.
+ * @note For Normal QUAD image only.
+ */
+ WRAP_MODE_U,
+
+ /**
+ * @brief The wrap mode for v coordinate.
+ * @details Name "wrapModeV", type Dali::WrapMode::Type (Property::INTEGER) or Property::STRING
+ * it decides how the texture should be sampled when the v coordinate exceeds the range of 0.0 to 1.0.
+ * @SINCE_1_2.1
+ * @note Optional. If not specified, the default is CLAMP.
+ * @note For Normal QUAD image only.
+ */
+ WRAP_MODE_V,
};
} // namespace Property