#include <dali/devel-api/scripting/scripting.h>
#include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
#include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/devel-api/image-loader/texture-manager.h>
#include <dali/public-api/rendering/renderer.h>
#include <test-native-image.h>
END_TEST;
}
-
int UtcDaliImageViewSyncLoading02(void)
{
ToolkitTestApplication application;
END_TEST;
}
+int UtcDaliImageViewAddedTexture(void)
+{
+ ToolkitTestApplication application;
+
+ tet_infoline("ImageView Testing image view with texture provided manager url");
+
+ ImageView imageView = ImageView::New();
+
+ // empty texture is ok, though pointless from app point of view
+ TextureSet empty;
+ std::string url = TextureManager::AddTexture(empty);
+ DALI_TEST_CHECK(url.size() > 0u);
+
+ Property::Map propertyMap;
+ propertyMap[ImageVisual::Property::URL] = url;
+ imageView.SetProperty(ImageView::Property::IMAGE, propertyMap);
+
+ Stage::GetCurrent().Add( imageView );
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
int UtcDaliImageViewSizeWithBackground(void)
{
ToolkitTestApplication application;
devel_api_image_loader_header_files = \
$(devel_api_src_dir)/image-loader/async-image-loader-devel.h \
$(devel_api_src_dir)/image-loader/atlas-upload-observer.h \
- $(devel_api_src_dir)/image-loader/image-atlas.h
+ $(devel_api_src_dir)/image-loader/image-atlas.h \
+ $(devel_api_src_dir)/image-loader/texture-manager.h
devel_api_scripting_header_files = \
$(devel_api_src_dir)/scripting/script.h \
mWrapModeU( WrapMode::DEFAULT ),
mWrapModeV( WrapMode::DEFAULT ),
mAttemptAtlasing( false ),
- mTextureLoading( false )
+ mLoadingStatus( false )
{
}
mWrapModeU( WrapMode::DEFAULT ),
mWrapModeV( WrapMode::DEFAULT ),
mAttemptAtlasing( false ),
- mTextureLoading( false )
+ mLoadingStatus( false )
{
}
ImageVisual::~ImageVisual()
{
+ if( mMaskingData && Stage::IsInstalled() )
+ {
+ // TextureManager could have been deleted before the actor that contains this
+ // ImageVisual is destroyed (e.g. due to stage shutdown). Ensure the stage
+ // is still valid before accessing texture manager.
+ if( mMaskingData->mAlphaMaskId != TextureManager::INVALID_TEXTURE_ID )
+ {
+ TextureManager& textureManager = mFactoryCache.GetTextureManager();
+ textureManager.Remove( mMaskingData->mAlphaMaskId );
+ }
+ }
delete mMaskingData;
}
}
}
}
-
}
void ImageVisual::DoSetProperty( Property::Index index, const Property::Value& value )
{
AllocateMaskData();
// Immediately trigger the alpha mask loading (it may just get a cached value)
- mMaskingData->SetImage( alphaUrl );
+ mMaskingData->mAlphaMaskUrl = alphaUrl;
+ TextureManager& textureManager = mFactoryCache.GetTextureManager();
+ mMaskingData->mAlphaMaskId = textureManager.RequestMaskLoad( alphaUrl );
}
break;
}
{
if( mMaskingData == NULL )
{
- TextureManager& textureManager = mFactoryCache.GetTextureManager();
- mMaskingData = new MaskingData(textureManager);
+ mMaskingData = new TextureManager::MaskingData();
}
}
return mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
}
-TextureSet ImageVisual::CreateTextureSet( Vector4& textureRect, bool synchronousLoading, bool attemptAtlasing )
-{
- TextureSet textureSet;
-
- mTextureLoading = false;
- textureRect = FULL_TEXTURE_RECT;
-
- if( synchronousLoading )
- {
- PixelData data;
- if( mImageUrl.IsValid() )
- {
- // if sync loading is required, the loading should immediately when actor is on stage
- Devel::PixelBuffer pixelBuffer = LoadImageFromFile( mImageUrl.GetUrl(), mDesiredSize, mFittingMode, mSamplingMode );
-
- if( pixelBuffer )
- {
- data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
- }
- }
- if( !data )
- {
- // use broken image
- textureSet = TextureSet::New();
- TextureSetImage( textureSet, 0u, VisualFactoryCache::GetBrokenVisualImage() );
- }
- else
- {
- if( attemptAtlasing )
- {
- textureSet = mFactoryCache.GetAtlasManager()->Add( textureRect, data );
- mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
- }
- if( !textureSet ) // big image, no atlasing or atlasing failed
- {
- mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
- Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(),
- data.GetWidth(), data.GetHeight() );
- texture.Upload( data );
- textureSet = TextureSet::New();
- textureSet.SetTexture( 0u, texture );
- }
- }
- }
- else
- {
- mTextureLoading = true;
- if( attemptAtlasing )
- {
- textureSet = mFactoryCache.GetAtlasManager()->Add( textureRect, mImageUrl.GetUrl(), mDesiredSize, mFittingMode, true, this );
- mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
- }
- if( !textureSet ) // big image, no atlasing or atlasing failed
- {
- mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
- TextureManager& textureManager = mFactoryCache.GetTextureManager();
- if( mMaskingData == NULL )
- {
- mTextureId = textureManager.RequestLoad( mImageUrl, mDesiredSize, mFittingMode,
- mSamplingMode, TextureManager::NO_ATLAS, this );
- }
- else
- {
- mTextureId = textureManager.RequestLoad( mImageUrl,
- mMaskingData->mAlphaMaskId,
- mMaskingData->mContentScaleFactor,
- mDesiredSize,
- mFittingMode, mSamplingMode,
- TextureManager::NO_ATLAS,
- mMaskingData->mCropToMask,
- this );
- }
-
- TextureManager::LoadState loadState = textureManager.GetTextureState( mTextureId );
- mTextureLoading = ( loadState == TextureManager::LOADING );
-
- if( loadState == TextureManager::UPLOADED )
- {
- // UploadComplete has already been called - keep the same texture set
- textureSet = textureManager.GetTextureSet(mTextureId);
- }
- }
- }
-
- if( ! (mImpl->mFlags & Impl::IS_ATLASING_APPLIED) && textureSet )
- {
- Sampler sampler = Sampler::New();
- sampler.SetWrapMode( mWrapModeU, mWrapModeV );
- textureSet.SetSampler( 0u, sampler );
- }
-
- return textureSet;
-}
-
+/*
+( VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode,
+ ImageVisual::MaskingData* maskInfo, bool synchronousLoading,
+ TextureManager::TextureId textureId, Vector4& textureRect, bool& atlasingStatus, bool& loadingStatus
+ */
void ImageVisual::InitializeRenderer()
{
mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
+ TextureManager& textureManager = mFactoryCache.GetTextureManager();
+
if( ! mImpl->mCustomShader && mImageUrl.GetProtocolType() == VisualUrl::LOCAL )
{
bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
Vector4 atlasRect;
+
+ auto attemptAtlasing = mAttemptAtlasing;
+
// texture set has to be created first as we need to know if atlasing succeeded or not
// when selecting the shader
- TextureSet textures = CreateTextureSet( atlasRect, IsSynchronousResourceLoading(), mAttemptAtlasing );
+ TextureSet textures =
+ textureManager.LoadTexture(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode,
+ mMaskingData, IsSynchronousResourceLoading(), mTextureId,
+ atlasRect, attemptAtlasing, mLoadingStatus, mWrapModeU,
+ mWrapModeV, this, this, mFactoryCache.GetAtlasManager());
+ if(attemptAtlasing)
+ {
+ mImpl->mFlags |= Impl::IS_ATLASING_APPLIED;
+ }
CreateRenderer( textures );
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.
+ if( !defaultWrapMode ) // custom wrap mode
{
Vector2 wrapMode(mWrapModeU-WrapMode::CLAMP_TO_EDGE, mWrapModeV-WrapMode::CLAMP_TO_EDGE);
wrapMode.Clamp( Vector2::ZERO, Vector2( 2.f, 2.f ) );
}
else
{
+ auto attemptAtlasing = false;
// for custom shader or remote image, atlas is not applied
Vector4 atlasRect; // ignored in this case
- TextureSet textures = CreateTextureSet( atlasRect, IsSynchronousResourceLoading(), false );
+ TextureSet textures =
+ textureManager.LoadTexture(mImageUrl, mDesiredSize, mFittingMode, mSamplingMode,
+ mMaskingData, IsSynchronousResourceLoading(), mTextureId,
+ atlasRect, attemptAtlasing, mLoadingStatus, mWrapModeU, mWrapModeV, this,
+ nullptr, nullptr); // no atlasing
+ DALI_ASSERT_DEBUG(attemptAtlasing == false);
CreateRenderer( textures );
}
}
void ImageVisual::InitializeRenderer( const Image& image )
{
- // don't reuse CreateTextureSet
TextureSet textures = TextureSet::New();
NativeImage nativeImage = NativeImage::DownCast( image );
mImpl->mRenderer.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, mPixelArea );
}
- if( mTextureLoading == false )
+ if( mLoadingStatus == false )
{
actor.AddRenderer( mImpl->mRenderer );
mPlacementActor.Reset();
RemoveTexture( mImageUrl.GetUrl() );
mImage.Reset();
}
- mTextureLoading = false;
+ mLoadingStatus = false;
mImpl->mRenderer.Reset();
mPlacementActor.Reset();
}
// reset the weak handle so that the renderer only get added to actor once
mPlacementActor.Reset();
}
- mTextureLoading = false;
+ mLoadingStatus = false;
}
// From Texture Manager
ResourceReady();
}
}
- mTextureLoading = false;
+ mLoadingStatus = false;
}
void ImageVisual::RemoveTexture(const std::string& url)
}
}
-
-ImageVisual::MaskingData::MaskingData( TextureManager& textureManager )
-: mTextureManager( textureManager ),
- mAlphaMaskUrl(),
- mAlphaMaskId( TextureManager::INVALID_TEXTURE_ID ),
- mContentScaleFactor( 1.0f ),
- mCropToMask( true )
-{
-}
-
-ImageVisual::MaskingData::~MaskingData()
-{
- if( Stage::IsInstalled() )
- {
- // TextureManager could have been deleted before the actor that contains this
- // ImageVisual is destroyed (e.g. due to stage shutdown). Ensure the stage
- // is still valid before accessing texture manager.
- if( mAlphaMaskId != TextureManager::INVALID_TEXTURE_ID )
- {
- mTextureManager.Remove( mAlphaMaskId );
- }
- }
-}
-
-void ImageVisual::MaskingData::SetImage( const std::string& maskUrl )
-{
- mAlphaMaskUrl = maskUrl;
- mAlphaMaskId = mTextureManager.RequestMaskLoad( mAlphaMaskUrl );
-}
-
} // namespace Internal
} // namespace Toolkit
void DoSetProperty( Property::Index index, const Property::Value& value );
private:
- struct MaskingData
- {
- MaskingData( TextureManager& textureManager );
- ~MaskingData();
- void SetImage( const std::string& url );
-
- TextureManager& mTextureManager;
- VisualUrl mAlphaMaskUrl;
- TextureManager::TextureId mAlphaMaskId;
- float mContentScaleFactor;
- bool mCropToMask;
- };
Image mImage;
Vector4 mPixelArea;
WeakHandle<Actor> mPlacementActor;
VisualUrl mImageUrl;
- MaskingData* mMaskingData;
+ TextureManager::MaskingData* mMaskingData;
Dali::ImageDimensions mDesiredSize;
TextureManager::TextureId mTextureId;
Dali::SamplingMode::Type mSamplingMode:4;
Dali::WrapMode::Type mWrapModeU:3;
Dali::WrapMode::Type mWrapModeV:3;
- bool mAttemptAtlasing:1; ///< If true will attempt atlasing, otherwise create unique texture
- bool mTextureLoading:1; ///< True if the texture is being loaded asynchronously, or false when it has loaded.
+ bool mAttemptAtlasing; ///< If true will attempt atlasing, otherwise create unique texture
+ bool mLoadingStatus; ///< True if the texture is being loaded asynchronously, or false when it has loaded.
};
// EXTERNAL HEADERS
#include <cstdlib>
#include <string>
+#include <dali/public-api/math/vector4.h>
#include <dali/devel-api/adaptor-framework/environment-variable.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
#include <dali/devel-api/common/hash.h>
#include <dali/devel-api/images/texture-set-image.h>
#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
// INTERNAL HEADERS
#include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
+#include <dali-toolkit/internal/visuals/image-atlas-manager.h>
namespace
{
} // Anonymous namespace
+TextureManager::MaskingData::MaskingData()
+: mAlphaMaskUrl(),
+ mAlphaMaskId( INVALID_TEXTURE_ID ),
+ mContentScaleFactor( 1.0f ),
+ mCropToMask( true )
+{
+}
TextureManager::TextureManager()
: mAsyncLocalLoaders( GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ),
{
}
+TextureSet TextureManager::LoadTexture(
+ VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode,
+ Dali::SamplingMode::Type samplingMode, const MaskingDataPointer& maskInfo,
+ bool synchronousLoading, TextureManager::TextureId& textureId, Vector4& textureRect,
+ bool& atlasingStatus, bool& loadingStatus, Dali::WrapMode::Type wrapModeU,
+ Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver,
+ AtlasUploadObserver* atlasObserver, ImageAtlasManagerPtr imageAtlasManager)
+{
+ TextureSet textureSet;
+
+ loadingStatus = false;
+ textureRect = FULL_ATLAS_RECT;
+
+ if( VisualUrl::TEXTURE == url.GetProtocolType())
+ {
+ std::string location = url.GetLocation();
+ if( location.size() > 0u )
+ {
+ TextureId id = std::stoi( location );
+ for( auto&& elem : mExternalTextures )
+ {
+ if( elem.textureId == id )
+ {
+ return elem.textureSet;
+ }
+ }
+ }
+ }
+ else if( synchronousLoading )
+ {
+ PixelData data;
+ if( url.IsValid() )
+ {
+ // if sync loading is required, the loading should immediately when actor is on stage
+ Devel::PixelBuffer pixelBuffer = LoadImageFromFile( url.GetUrl(), desiredSize, fittingMode, samplingMode );
+ if( pixelBuffer )
+ {
+ data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+ }
+ }
+ if( !data )
+ {
+ // use broken image
+ textureSet = TextureSet::New();
+ Devel::PixelBuffer pixelBuffer = LoadImageFromFile( BROKEN_IMAGE_URL );
+ if( pixelBuffer )
+ {
+ data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+ }
+ Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(),
+ data.GetWidth(), data.GetHeight() );
+ texture.Upload( data );
+ textureSet = TextureSet::New();
+ textureSet.SetTexture( 0u, texture );
+ }
+ else
+ {
+ if( atlasingStatus ) // attempt atlasing
+ {
+ textureSet = imageAtlasManager->Add( textureRect, data );
+ }
+ if( !textureSet ) // big image, no atlasing or atlasing failed
+ {
+ atlasingStatus = false;
+ Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, data.GetPixelFormat(),
+ data.GetWidth(), data.GetHeight() );
+ texture.Upload( data );
+ textureSet = TextureSet::New();
+ textureSet.SetTexture( 0u, texture );
+ }
+ }
+ }
+ else
+ {
+ loadingStatus = true;
+ if( atlasingStatus )
+ {
+ textureSet = imageAtlasManager->Add( textureRect, url.GetUrl(), desiredSize, fittingMode, true, atlasObserver );
+ }
+ if( !textureSet ) // big image, no atlasing or atlasing failed
+ {
+ atlasingStatus = false;
+ if( !maskInfo )
+ {
+ textureId = RequestLoad( url, desiredSize, fittingMode, samplingMode, TextureManager::NO_ATLAS, textureObserver );
+ }
+ else
+ {
+ textureId = RequestLoad( url,
+ maskInfo->mAlphaMaskId,
+ maskInfo->mContentScaleFactor,
+ desiredSize,
+ fittingMode, samplingMode,
+ TextureManager::NO_ATLAS,
+ maskInfo->mCropToMask,
+ textureObserver );
+ }
+
+ TextureManager::LoadState loadState = GetTextureState( textureId );
+ loadingStatus = ( loadState == TextureManager::LOADING );
+
+ if( loadState == TextureManager::UPLOADED )
+ {
+ // UploadComplete has already been called - keep the same texture set
+ textureSet = GetTextureSet( textureId );
+ }
+ }
+ }
+
+ if( ! atlasingStatus && textureSet )
+ {
+ Sampler sampler = Sampler::New();
+ sampler.SetWrapMode( wrapModeU, wrapModeV );
+ textureSet.SetSampler( 0u, sampler );
+ }
+
+ return textureSet;
+}
+
TextureManager::TextureId TextureManager::RequestLoad(
const VisualUrl& url,
const ImageDimensions desiredSize,
return RequestLoadInternal( maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, false, KEEP_PIXEL_BUFFER, NULL );
}
-
TextureManager::TextureId TextureManager::RequestLoadInternal(
const VisualUrl& url,
TextureId maskTextureId,
}
}
-TextureManager::~TextureManager()
-{
-}
-
TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager)
: AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager,
AsyncLoadingInfoContainerType())
namespace Internal
{
+class ImageAtlasManager;
+typedef IntrusivePtr<ImageAtlasManager> ImageAtlasManagerPtr;
/**
* The TextureManager provides a common Image loading API for Visuals.
public:
+ struct MaskingData
+ {
+ MaskingData();
+ ~MaskingData() = default;
+
+ VisualUrl mAlphaMaskUrl;
+ TextureManager::TextureId mAlphaMaskId;
+ float mContentScaleFactor;
+ bool mCropToMask;
+ };
+
/**
* Constructor.
*/
/**
* Destructor.
*/
- ~TextureManager();
+ ~TextureManager() = default;
// TextureManager Main API:
+ TextureSet LoadTexture(VisualUrl& url, Dali::ImageDimensions desiredSize,
+ Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode,
+ MaskingData* maskInfo, bool synchronousLoading,
+ TextureManager::TextureId& textureId, Vector4& textureRect,
+ bool& atlasingStatus, bool& loadingStatus, Dali::WrapMode::Type wrapModeU,
+ Dali::WrapMode::Type wrapModeV, TextureUploadObserver* textureObserver,
+ AtlasUploadObserver* atlasObserver,
+ ImageAtlasManagerPtr imageAtlasManager);
+
/**
* @brief Requests an image load of the given URL.
*
unsigned short loadId; ///< The load Id used by the async loader to reference this load
};
- /**
- * @brief This struct is used within a container to manage atlas creation and destruction.
- */
- struct AtlasInfo
- {
- AtlasInfo( Toolkit::ImageAtlas atlas, TextureSet textureSet )
- : atlas( atlas ),
- textureSet( textureSet )
- {
- }
-
- Toolkit::ImageAtlas atlas; ///< The ImageAtlas object
- TextureSet textureSet; ///< The TextureSet is kept in the struct to allow fast lookup of TextureSet to Atlas
- };
-
// Private typedefs:
typedef std::deque<AsyncLoadingInfo> AsyncLoadingInfoContainerType; ///< The container type used to manage Asynchronous loads in progress
- typedef std::vector<AtlasInfo> AtlasInfoContainerType; ///< The container type used to manage Atlas creation and destruction
typedef std::vector<TextureInfo> TextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures
/**
private: // Member Variables:
- AtlasInfoContainerType mAtlasContainer; ///< Used to manage Atlas creation and destruction
TextureInfoContainerType mTextureInfoContainer; ///< Used to manage the life-cycle and caching of Textures
RoundRobinContainerView< AsyncLoadingHelper > mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads
RoundRobinContainerView< AsyncLoadingHelper > mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads