result = CompareType<Quaternion>(a, b, epsilon);
break;
}
+ case Property::STRING:
+ {
+ std::string a, b;
+ q1.Get(a);
+ q2.Get(b);
+ result = (a.compare(b) == 0);
+ break;
+ }
case Property::MATRIX:
case Property::MATRIX3:
- case Property::STRING:
case Property::ARRAY:
case Property::MAP:
{
#include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
#include <dali-toolkit/devel-api/visual-factory/transition-data.h>
#include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
+#include <dali-toolkit/devel-api/controls/control-devel.h>
#include <dali-toolkit/dali-toolkit.h>
#include "dummy-control.h"
namespace
{
-const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery_small-1.jpg";
+const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
const char* TEST_LARGE_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/tbcol.png";
const char* TEST_SMALL_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/icon-edit.png";
const char* TEST_REMOTE_IMAGE_FILE_NAME = "https://www.tizen.org/sites/all/themes/tizen_theme/logo.png";
const char* TEST_INVALID_FILE_NAME = TEST_RESOURCE_DIR "/invalid.jpg";
const char* TEST_REMOTE_INVALID_FILE_NAME = "https://www.tizen.org/invalid.png";
+const char* TEST_MASK_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/mask.png";
}
END_TEST;
}
+
+int UtcDaliImageVisualAlphaMask(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "Request image visual with a Property::Map containing an Alpha mask" );
+
+ VisualFactory factory = VisualFactory::Get();
+ DALI_TEST_CHECK( factory );
+
+ Property::Map propertyMap;
+ propertyMap.Insert( Visual::Property::TYPE, Visual::IMAGE );
+ propertyMap.Insert( ImageVisual::Property::URL, TEST_LARGE_IMAGE_FILE_NAME );
+ propertyMap.Insert( DevelImageVisual::Property::ALPHA_MASK_URL, TEST_MASK_IMAGE_FILE_NAME );
+
+ Visual::Base visual = factory.CreateVisual( propertyMap );
+ DALI_TEST_CHECK( visual );
+
+ Property::Map testMap;
+ visual.CreatePropertyMap(testMap);
+ DALI_TEST_EQUALS(*testMap.Find(DevelImageVisual::Property::ALPHA_MASK_URL),Property::Value(TEST_MASK_IMAGE_FILE_NAME), TEST_LOCATION );
+
+ // For tesing the LoadResourceFunc is called, a big image size should be set, so the atlasing is not applied.
+ // Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
+
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ TraceCallStack& textureTrace = gl.GetTextureTrace();
+ textureTrace.Enable(true);
+
+ DummyControl actor = DummyControl::New();
+ DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+ dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+
+ actor.SetSize( 200.f, 200.f );
+ DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+ DALI_TEST_EQUALS( DevelControl::IsResourceReady( actor ), false, TEST_LOCATION );
+
+ Stage::GetCurrent().Add( actor );
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION );
+ DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+ DALI_TEST_EQUALS( DevelControl::IsResourceReady( actor ), true, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliImageVisualRemoteAlphaMask(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline( "Request image visual with a Property::Map containing an Alpha mask" );
+
+ VisualFactory factory = VisualFactory::Get();
+ DALI_TEST_CHECK( factory );
+
+ const std::string MASK_IMAGE = TEST_REMOTE_IMAGE_FILE_NAME;
+
+ Property::Map propertyMap;
+ propertyMap.Insert( Visual::Property::TYPE, Visual::IMAGE );
+ propertyMap.Insert( ImageVisual::Property::URL, TEST_IMAGE_FILE_NAME );
+ propertyMap.Insert( "alphaMaskUrl", MASK_IMAGE );
+
+ Visual::Base visual = factory.CreateVisual( propertyMap );
+ DALI_TEST_CHECK( visual );
+
+ Property::Map testMap;
+ visual.CreatePropertyMap(testMap);
+ DALI_TEST_EQUALS(*testMap.Find(DevelImageVisual::Property::ALPHA_MASK_URL),Property::Value(MASK_IMAGE), TEST_LOCATION );
+
+ // For tesing the LoadResourceFunc is called, a big image size should be set, so the atlasing is not applied.
+ // Image with a size smaller than 512*512 will be uploaded as a part of the atlas.
+
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ TraceCallStack& textureTrace = gl.GetTextureTrace();
+ textureTrace.Enable(true);
+
+ DummyControl actor = DummyControl::New();
+ DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+ dummyImpl.RegisterVisual( Control::CONTROL_PROPERTY_END_INDEX + 1, visual );
+ DALI_TEST_EQUALS( DevelControl::IsResourceReady( actor ), false, TEST_LOCATION );
+
+ actor.SetSize( 200.f, 200.f );
+ DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+ Stage::GetCurrent().Add( actor );
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 2 ), true, TEST_LOCATION );
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS( actor.GetRendererCount(), 1u, TEST_LOCATION );
+ DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+ DALI_TEST_EQUALS( DevelControl::IsResourceReady( actor ), true, TEST_LOCATION );
+
+ END_TEST;
+}
*/
ATLASING = WRAP_MODE_V + 2,
+
+ /**
+ * @brief URL of a masking image
+ * @details Name "alphaMaskUrl", type Property::STRING, URL of image to apply as
+ * a mask after image loading. If set after the main URL has finished loading, this
+ * may necessitate a re-load of the main image. The alpha mask image will be scaled
+ * on load to match the size of the main image, then applied to the pixel data
+ * before uploading to GL.
+ * @note Optional.
+ */
+
+ ALPHA_MASK_URL = WRAP_MODE_V + 3,
};
} //namespace Property
const char * const IMAGE_DESIRED_HEIGHT( "desiredHeight" );
const char * const SYNCHRONOUS_LOADING( "synchronousLoading" );
const char * const IMAGE_ATLASING("atlasing");
+const char * const ALPHA_MASK_URL("alphaMaskUrl");
// fitting modes
DALI_ENUM_TO_STRING_TABLE_BEGIN( FITTING_MODE )
mPixelArea( FULL_TEXTURE_RECT ),
mPlacementActor(),
mImageUrl( imageUrl ),
+ mAlphaMaskUrl(),
mDesiredSize( size ),
mTextureId( TextureManager::INVALID_TEXTURE_ID ),
+ mAlphaMaskId( TextureManager::INVALID_TEXTURE_ID ),
mFittingMode( fittingMode ),
mSamplingMode( samplingMode ),
mWrapModeU( WrapMode::DEFAULT ),
mPixelArea( FULL_TEXTURE_RECT ),
mPlacementActor(),
mImageUrl(),
+ mAlphaMaskUrl(),
mDesiredSize(),
mTextureId( TextureManager::INVALID_TEXTURE_ID ),
+ mAlphaMaskId( TextureManager::INVALID_TEXTURE_ID ),
mFittingMode( FittingMode::DEFAULT ),
mSamplingMode( SamplingMode::DEFAULT ),
mWrapModeU( WrapMode::DEFAULT ),
ImageVisual::~ImageVisual()
{
+ if( mAlphaMaskId != TextureManager::INVALID_TEXTURE_ID )
+ {
+ TextureManager& textureManager = mFactoryCache.GetTextureManager();
+ textureManager.Remove( mAlphaMaskId );
+ }
}
void ImageVisual::DoSetProperties( const Property::Map& propertyMap )
{
DoSetProperty( Toolkit::DevelImageVisual::Property::ATLASING, keyValue.second );
}
+ else if ( keyValue.first == ALPHA_MASK_URL )
+ {
+ DoSetProperty( Toolkit::DevelImageVisual::Property::ALPHA_MASK_URL, keyValue.second );
+ }
}
}
+ if( mAlphaMaskUrl.IsValid() )
+ {
+ // Immediately trigger the alpha mask loading (it may just get a cached value)
+ TextureManager& textureManager = mFactoryCache.GetTextureManager();
+ mAlphaMaskId = textureManager.RequestMaskLoad( mAlphaMaskUrl );
+ }
+
if( ( mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING ) && mImageUrl.IsValid() )
{
// if sync loading is required, the loading should start
bool atlasing = false;
mAttemptAtlasing = value.Get( atlasing );
}
+
+ case Toolkit::DevelImageVisual::Property::ALPHA_MASK_URL:
+ {
+ std::string alphaUrl;
+ if( value.Get( alphaUrl ) )
+ {
+ mAlphaMaskUrl = VisualUrl( alphaUrl );
+ }
+ }
}
}
{
mImpl->mFlags &= ~Impl::IS_ATLASING_APPLIED;
TextureManager& textureManager = mFactoryCache.GetTextureManager();
- mTextureId = textureManager.RequestLoad( mImageUrl, mDesiredSize, mFittingMode,
- mSamplingMode, TextureManager::NO_ATLAS, this );
+ if( mAlphaMaskId == TextureManager::INVALID_TEXTURE_ID )
+ {
+ mTextureId = textureManager.RequestLoad( mImageUrl, mDesiredSize, mFittingMode,
+ mSamplingMode, TextureManager::NO_ATLAS, this );
+ }
+ else
+ {
+ mTextureId = textureManager.RequestLoad( mImageUrl, mAlphaMaskId, mDesiredSize,
+ mFittingMode, mSamplingMode,
+ TextureManager::NO_ATLAS, this );
+ }
TextureManager::LoadState loadState = textureManager.GetTextureState( mTextureId );
map.Insert( Toolkit::ImageVisual::Property::WRAP_MODE_V, mWrapModeV );
map.Insert( Toolkit::DevelImageVisual::Property::ATLASING, mAttemptAtlasing );
+ map.Insert( Toolkit::DevelImageVisual::Property::ALPHA_MASK_URL, mAlphaMaskUrl.GetUrl() );
}
void ImageVisual::DoCreateInstancePropertyMap( Property::Map& map ) const
typedef IntrusivePtr< ImageVisual > ImageVisualPtr;
/**
- * The visual which renders an image to the control's quad
+ * The visual which renders an image to a quad geometry
*
* The following properties are optional
*
* | %Property Name | Type |
* |--------------------|-------------------|
* | url | STRING |
+ * | alphaMaskUrl | STRING |
* | fittingMode | INTEGER OR STRING |
* | samplingMode | INTEGER OR STRING |
* | desiredWidth | INTEGER |
Vector4 mPixelArea;
WeakHandle<Actor> mPlacementActor;
VisualUrl mImageUrl;
+ VisualUrl mAlphaMaskUrl;
Dali::ImageDimensions mDesiredSize;
TextureManager::TextureId mTextureId;
+ TextureManager::TextureId mAlphaMaskId;
Dali::FittingMode::Type mFittingMode:3;
Dali::SamplingMode::Type mSamplingMode:4;
// EXTERNAL HEADERS
#include <dali/devel-api/common/hash.h>
+#include <dali/devel-api/images/pixel-data-mask.h>
#include <dali/devel-api/images/texture-set-image.h>
#include <dali/integration-api/debug.h>
#include <dali-toolkit/internal/image-loader/image-atlas-impl.h>
#include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
+
namespace Dali
{
const UseAtlas useAtlas,
TextureUploadObserver* observer )
{
+ return RequestInternalLoad( url, INVALID_TEXTURE_ID, desiredSize, fittingMode, samplingMode, useAtlas, GPU_UPLOAD, observer );
+}
+
+TextureManager::TextureId TextureManager::RequestLoad(
+ const VisualUrl& url,
+ TextureId maskTextureId,
+ const ImageDimensions desiredSize,
+ FittingMode::Type fittingMode,
+ Dali::SamplingMode::Type samplingMode,
+ const UseAtlas useAtlas,
+ TextureUploadObserver* observer )
+{
+ return RequestInternalLoad( url, maskTextureId, desiredSize, fittingMode, samplingMode, useAtlas, GPU_UPLOAD, observer );
+}
+
+TextureManager::TextureId TextureManager::RequestMaskLoad( const VisualUrl& maskUrl )
+{
+ // Use the normal load procedure to get the alpha mask.
+ return RequestInternalLoad( maskUrl, INVALID_TEXTURE_ID, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, NO_ATLAS, CPU, NULL );
+}
+
+
+TextureManager::TextureId TextureManager::RequestInternalLoad(
+ const VisualUrl& url,
+ TextureId maskTextureId,
+ const ImageDimensions desiredSize,
+ FittingMode::Type fittingMode,
+ Dali::SamplingMode::Type samplingMode,
+ UseAtlas useAtlas,
+ StorageType storageType,
+ TextureUploadObserver* observer )
+{
// First check if the requested Texture is cached.
- const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas );
+ const TextureHash textureHash = GenerateHash( url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
- // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
- int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas );
TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
+ // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
+ int cacheIndex = FindCachedTexture( textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId );
+
// Check if the requested Texture exists in the cache.
if( cacheIndex != INVALID_CACHE_INDEX )
{
DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture @%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId );
}
- else
+
+ if( textureId == INVALID_TEXTURE_ID ) // There was no caching, or caching not required
{
// We need a new Texture.
textureId = GenerateUniqueTextureId();
- mTextureInfoContainer.push_back( TextureInfo( textureId, url.GetUrl(), desiredSize, fittingMode, samplingMode, false, useAtlas, textureHash ) );
+ mTextureInfoContainer.push_back( TextureInfo( textureId, maskTextureId, url.GetUrl(),
+ desiredSize, fittingMode, samplingMode,
+ false, useAtlas, textureHash ) );
cacheIndex = mTextureInfoContainer.size() - 1u;
DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId );
}
// The below code path is common whether we are using the cache or not.
- // The textureInfoIndex now refers to either a pre-existing cached TextureInfo, or a new TextureInfo just created.
+ // The textureInfoIndex now refers to either a pre-existing cached TextureInfo,
+ // or a new TextureInfo just created.
TextureInfo& textureInfo( mTextureInfoContainer[ cacheIndex ] );
+ textureInfo.maskTextureId = maskTextureId;
+ textureInfo.storageType = storageType;
DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureInfo loadState:%s\n",
textureInfo.loadState == TextureManager::NOT_STARTED ? "NOT_STARTED" :
{
// The Texture has already loaded. The other observers have already been notified.
// We need to send a "late" loaded notification for this observer.
- observer->UploadComplete( textureInfo.loadingSucceeded,
+ observer->UploadComplete( true,
textureInfo.textureSet, textureInfo.useAtlas,
textureInfo.atlasRect );
}
ObserveTexture( textureInfo, observer );
break;
}
+ case TextureManager::LOAD_FINISHED:
+ case TextureManager::WAITING_FOR_MASK:
+ case TextureManager::LOAD_FAILED:
+ // Loading has already completed. Do nothing.
+ break;
}
// Return the TextureId for which this Texture can now be referenced by externally.
return textureSet;
}
-
-
bool TextureManager::LoadTexture( TextureInfo& textureInfo )
{
bool success = true;
if( textureInfo.url.IsLocal() )
{
mAsyncLocalLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) );
- mAsyncLocalLoadingInfoContainer.back().loadId = GetImplementation(mAsyncLocalLoader).Load(
- textureInfo.url, textureInfo.desiredSize,
- textureInfo.fittingMode, textureInfo.samplingMode, true );
+ mAsyncLocalLoadingInfoContainer.back().loadId =
+ GetImplementation(mAsyncLocalLoader).Load( textureInfo.url, textureInfo.desiredSize,
+ textureInfo.fittingMode,
+ textureInfo.samplingMode, true );
}
else
{
mAsyncRemoteLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) );
- mAsyncRemoteLoadingInfoContainer.back().loadId = GetImplementation(mAsyncRemoteLoader).Load(
- textureInfo.url, textureInfo.desiredSize,
- textureInfo.fittingMode, textureInfo.samplingMode, true );
+ mAsyncRemoteLoadingInfoContainer.back().loadId =
+ GetImplementation(mAsyncRemoteLoader).Load( textureInfo.url, textureInfo.desiredSize,
+ textureInfo.fittingMode,
+ textureInfo.samplingMode, true );
}
}
}
int cacheIndex = GetCacheIndexFromId( loadingInfo.textureId );
if( cacheIndex != INVALID_CACHE_INDEX )
{
- // Once we have found the TextureInfo data, we call a common function used to process loaded data for both sync and async loads.
TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " CacheIndex:%d LoadState: %d\n", cacheIndex, textureInfo.loadState );
- // Only perform atlasing if the load has not been cancelled since the request.
if( textureInfo.loadState != CANCELLED )
{
- // Perform atlasing and finalize the load.
PostLoad( textureInfo, pixelData );
}
else
}
}
-
-bool TextureManager::PostLoad( TextureInfo& textureInfo, PixelData pixelData )
+void TextureManager::PostLoad( TextureInfo& textureInfo, PixelData pixelData )
{
- bool success = false;
-
// Was the load successful?
if( pixelData && ( pixelData.GetWidth() != 0 ) && ( pixelData.GetHeight() != 0 ) )
{
- // Regardless of whether the atlasing succeeds or not, we have a valid image, so we mark it as successful.
- success = true;
-
- bool usingAtlas = false;
-
// No atlas support for now
textureInfo.useAtlas = NO_ATLAS;
- if( ! usingAtlas )
+ if( textureInfo.storageType == GPU_UPLOAD )
+ {
+ // If there is a mask texture ID associated with this texture, then apply the mask
+ // if it's already loaded. If it hasn't, and the mask is still loading,
+ // wait for the mask to finish loading.
+ if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
+ {
+ LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId );
+ if( maskLoadState == LOADING )
+ {
+ textureInfo.pixelData = pixelData; // Store the pixel data temporarily
+ textureInfo.loadState = WAITING_FOR_MASK;
+ }
+ else if( maskLoadState == LOAD_FINISHED )
+ {
+ ApplyMask( pixelData, textureInfo.maskTextureId );
+ UploadTexture( pixelData, textureInfo );
+ NotifyObservers( textureInfo, true );
+ }
+ }
+ else
+ {
+ UploadTexture( pixelData, textureInfo );
+ NotifyObservers( textureInfo, true );
+ }
+ }
+ else // currently, CPU textures are local to texture manager
{
- DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " TextureManager::PostLoad() textureId:%d\n", textureInfo.textureId );
+ textureInfo.pixelData = pixelData; // Store the pixel data
+ textureInfo.loadState = LOAD_FINISHED;
- Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight() );
- texture.Upload( pixelData );
- textureInfo.textureSet = TextureSet::New();
- textureInfo.textureSet.SetTexture( 0u, texture );
+ // Check if there was another texture waiting for this load to complete
+ // (e.g. if this was an image mask, and its load is on a different thread)
+ CheckForWaitingTexture( textureInfo );
}
}
-
- if( ! success )
+ else
{
DALI_LOG_ERROR( "TextureManager::AsyncImageLoad(%s) failed\n", textureInfo.url.GetUrl().c_str() );
// @todo If the load was unsuccessful, upload the broken image.
+ textureInfo.loadState = LOAD_FAILED;
+ CheckForWaitingTexture( textureInfo );
+ NotifyObservers( textureInfo, false );
+ }
+}
+
+void TextureManager::CheckForWaitingTexture( TextureInfo& maskTextureInfo )
+{
+ // Search the cache, checking if any texture has this texture id as a
+ // maskTextureId:
+ const unsigned int size = mTextureInfoContainer.size();
+
+ for( unsigned int cacheIndex = 0; cacheIndex < size; ++cacheIndex )
+ {
+ if( mTextureInfoContainer[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
+ mTextureInfoContainer[cacheIndex].loadState == WAITING_FOR_MASK )
+ {
+ TextureInfo& textureInfo( mTextureInfoContainer[cacheIndex] );
+ PixelData pixelData = textureInfo.pixelData;
+ textureInfo.pixelData.Reset();
+
+ if( maskTextureInfo.loadState == LOAD_FINISHED )
+ {
+ ApplyMask( pixelData, maskTextureInfo.textureId );
+ UploadTexture( pixelData, textureInfo );
+ NotifyObservers( textureInfo, true );
+ }
+ else
+ {
+ DALI_LOG_ERROR( "TextureManager::ApplyMask to %s failed\n", textureInfo.url.GetUrl().c_str() );
+ textureInfo.loadState = LOAD_FAILED;
+ NotifyObservers( textureInfo, false );
+ }
+ }
+ }
+}
+
+void TextureManager::ApplyMask( PixelData pixelData, TextureId maskTextureId )
+{
+ int maskCacheIndex = GetCacheIndexFromId( maskTextureId );
+ PixelData maskPixelData = mTextureInfoContainer[maskCacheIndex].pixelData;
+ Dali::ApplyMask( pixelData, maskPixelData );
+}
+
+void TextureManager::UploadTexture( PixelData pixelData, TextureInfo& textureInfo )
+{
+ if( textureInfo.useAtlas != USE_ATLAS );
+ {
+ DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, " TextureManager::UploadTexture() New Texture for textureId:%d\n", textureInfo.textureId );
+
+ Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight() );
+ texture.Upload( pixelData );
+ textureInfo.textureSet = TextureSet::New();
+ textureInfo.textureSet.SetTexture( 0u, texture );
}
// Update the load state.
// load attempt is in progress or not. If unsuccessful, a broken
// image is still loaded.
textureInfo.loadState = UPLOADED;
+}
- // We need to store the load succeeded state as if a future request to load this texture comes in,
- // we need to re-broadcast the UploadComplete notification to that observer.
- textureInfo.loadingSucceeded = success;
-
- // If there is an observer: Notify the load is complete, whether successful or not:
+void TextureManager::NotifyObservers( TextureInfo& textureInfo, bool success )
+{
+ // If there is an observer: Notify the upload is complete
const unsigned int observerCount = textureInfo.observerList.Count();
for( unsigned int i = 0; i < observerCount; ++i )
{
}
textureInfo.observerList.Clear();
-
- return success;
}
+
TextureManager::TextureId TextureManager::GenerateUniqueTextureId()
{
return mCurrentTextureId++;
const ImageDimensions size,
const FittingMode::Type fittingMode,
const Dali::SamplingMode::Type samplingMode,
- const UseAtlas useAtlas )
+ const UseAtlas useAtlas,
+ TextureId maskTextureId )
{
std::string hashTarget( url );
const size_t urlLength = hashTarget.length();
hashTarget[ urlLength ] = useAtlas;
}
+ if( maskTextureId != INVALID_TEXTURE_ID )
+ {
+ hashTarget.resize( urlLength + sizeof( TextureId ) );
+ TextureId* hashTargetPtr = reinterpret_cast<TextureId*>(&( hashTarget[ urlLength ] ));
+
+ // Append the hash target to the end of the URL byte by byte:
+ // (to avoid SIGBUS / alignment issues)
+ for( size_t byteIter = 0; byteIter < sizeof( TextureId ); ++byteIter )
+ {
+ *hashTargetPtr++ = maskTextureId & 0xff;
+ maskTextureId >>= 8u;
+ }
+ }
+
return Dali::CalculateHash( hashTarget );
}
const ImageDimensions size,
const FittingMode::Type fittingMode,
const Dali::SamplingMode::Type samplingMode,
- const bool useAtlas )
+ const bool useAtlas,
+ TextureId maskTextureId)
{
// Default to an invalid ID, in case we do not find a match.
int cacheIndex = INVALID_CACHE_INDEX;
if( ( url == textureInfo.url.GetUrl() ) &&
( useAtlas == textureInfo.useAtlas ) &&
+ ( maskTextureId == textureInfo.maskTextureId ) &&
( size == textureInfo.desiredSize ) &&
( ( size.GetWidth() == 0 && size.GetHeight() == 0 ) ||
( fittingMode == textureInfo.fittingMode &&
namespace Internal
{
+class MaskTextureObserver;
+
/**
* The TextureManager provides a common Image loading API for Visuals.
*
typedef int32_t TextureId; ///< The TextureId type. This is used as a handle to refer to a particular Texture.
static const int INVALID_TEXTURE_ID = -1; ///< Used to represent a null TextureId or error
+ /**
+ * Whether the texture should be atlased or uploaded into it's own GPU texture
+ */
enum UseAtlas
{
NO_ATLAS,
USE_ATLAS
};
+ /**
+ * Whether the texture should be stored in CPU memory, or uploaded to a GPU texture
+ */
+ enum StorageType
+ {
+ CPU,
+ GPU_UPLOAD
+ };
+
+ /**
+ * Whether the texture should be loaded synchronously or asynchronously.
+ */
enum LoadType
{
LOAD_ASYNCHRONOUSLY,
};
/**
- * @brief The LoadState Enumeration represents the current state of a particular Textures life-cycle.
+ * @brief The LoadState Enumeration represents the current state of a particular Texture's life-cycle.
*/
enum LoadState
{
NOT_STARTED, ///< Default
LOADING, ///< Loading has been started, but not finished.
- UPLOADED, ///< Loaded (and ready).
+ LOAD_FINISHED, ///< Loading has finished. (for CPU storage only)
+ WAITING_FOR_MASK,///< Loading has finished, but waiting for mask image
+ UPLOADED, ///< Uploaded and ready. (For GPU upload only)
CANCELLED, ///< Removed before loading completed
+ LOAD_FAILED ///< Async loading failed, e.g. connection problem
};
public:
~TextureManager();
-// TextureManager Main API:
+ // TextureManager Main API:
/**
* @brief Requests an image load of the given URL.
TextureUploadObserver* observer );
/**
+ * @brief Requests an image load of the given URL, when the texture has
+ * have loaded, it will perform a CPU blend with the image mask, and upload
+ * the blend texture.
+ *
+ * The parameters are used to specify how the image is loaded.
+ * The observer has the UploadComplete method called when the load is ready.
+ *
+ * When the client has finished with the Texture, Remove() should be called.
+ *
+ * @param[in] url The URL of the image to load
+ * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
+ * @param[in] fittingMode The FittingMode to use
+ * @param[in] samplingMode The SamplingMode to use
+ * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
+ * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
+ * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
+ * This is called when an image load completes (or fails).
+ * @return A TextureId to use as a handle to reference this Texture
+ */
+ TextureId RequestLoad( const VisualUrl& url,
+ TextureId maskTextureId,
+ const ImageDimensions desiredSize,
+ FittingMode::Type fittingMode,
+ Dali::SamplingMode::Type samplingMode,
+ const UseAtlas useAtlasing,
+ TextureUploadObserver* observer );
+
+ /**
+ * Requests a masking image to be loaded. This mask is not uploaded to GL,
+ * instead, it is stored in CPU memory, and can be used for CPU blending.
+ */
+ TextureId RequestMaskLoad( const VisualUrl& maskUrl );
+
+ /**
* @brief Remove a Texture from the TextureManager.
*
* Textures are cached and therefore only the removal of the last
private:
+ /**
+ * @brief Requests an image load of the given URL, when the texture has
+ * have loaded, if there is a valid maskTextureId, it will perform a
+ * CPU blend with the mask, and upload the blend texture.
+ *
+ * The parameters are used to specify how the image is loaded.
+ * The observer has the UploadComplete method called when the load is ready.
+ *
+ * When the client has finished with the Texture, Remove() should be called.
+ *
+ * @param[in] url The URL of the image to load
+ * @param[in] maskTextureId The texture id of an image to use as a mask. If no mask is required, then set to INVALID_TEXTURE_ID
+ * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic
+ * @param[in] fittingMode The FittingMode to use
+ * @param[in] samplingMode The SamplingMode to use
+ * @param[in] useAtlasing Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
+ * but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
+ * @param[in] storageType, Whether the pixel data is stored in the cache or uploaded to the GPU
+ * @param[in] observer The client object should inherit from this and provide the "UploadCompleted" virtual.
+ * This is called when an image load completes (or fails).
+ * @return A TextureId to use as a handle to reference this Texture
+ */
+ TextureId RequestInternalLoad(
+ const VisualUrl& url,
+ TextureId maskTextureId,
+ const ImageDimensions desiredSize,
+ FittingMode::Type fittingMode,
+ Dali::SamplingMode::Type samplingMode,
+ UseAtlas useAtlas,
+ StorageType storageType,
+ TextureUploadObserver* observer );
+
typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
/**
* @brief This struct is used to manage the life-cycle of Texture loading and caching.
- * TODO-TX: pimpl this
*/
struct TextureInfo
{
TextureInfo( TextureId textureId,
+ TextureId maskTextureId,
const VisualUrl& url,
ImageDimensions desiredSize,
FittingMode::Type fittingMode,
useSize( desiredSize ),
atlasRect( 0.0f, 0.0f, 1.0f, 1.0f ), // Full atlas rectangle
textureId( textureId ),
+ maskTextureId( maskTextureId ),
hash( hash ),
referenceCount( 1u ),
loadState( NOT_STARTED ),
fittingMode( fittingMode ),
samplingMode( samplingMode ),
+ storageType( GPU_UPLOAD ),
loadSynchronously( loadSynchronously ),
- useAtlas( useAtlas ),
- loadingSucceeded( false )
+ useAtlas( useAtlas )
{
}
ObserverListType observerList; ///< Container used to store all observer clients of this Texture
Toolkit::ImageAtlas atlas; ///< The atlas this Texture lays within (if any)
- PixelData pixelData; ///< The PixelData holding the image data (this is used if atlasing is deferred)
+ PixelData pixelData; ///< The PixelData holding the image data (this is used if atlasing is deferred or CPU storage is required)
TextureSet textureSet; ///< The TextureSet holding the Texture
VisualUrl url; ///< The URL of the image
ImageDimensions desiredSize; ///< The size requested
ImageDimensions useSize; ///< The size used
Vector4 atlasRect; ///< The atlas rect used if atlased
TextureId textureId; ///< The TextureId associated with this Texture
+ TextureId maskTextureId; ///< The mask TextureId to be applied on load
TextureManager::TextureHash hash; ///< The hash used to cache this Texture
int16_t referenceCount; ///< The reference count of clients using this Texture
LoadState loadState:3; ///< The load state showing the load progress of the Texture
FittingMode::Type fittingMode:2; ///< The requested FittingMode
Dali::SamplingMode::Type samplingMode:3; ///< The requested SamplingMode
- bool loadSynchronously; ///< True if synchronous loading was requested
- UseAtlas useAtlas; ///< USE_ATLAS if an atlas was requested. This is updated to false if atlas is not used
- bool loadingSucceeded; ///< True if the image was loaded successfully
+ StorageType storageType:1; ///< CPU storage / GPU upload;
+ bool loadSynchronously:1; ///< True if synchronous loading was requested
+ UseAtlas useAtlas:1; ///< USE_ATLAS if an atlas was requested. This is updated to false if atlas is not used
};
// Structs:
/**
* @brief Performs Post-Load steps including atlasing.
- * @param[in] textureInfo The struct associated with this Texture
- * @param[in] pixelData The image pixelData
- * @return True if successful
+ * @param[in] textureInfo The struct associated with this Texture
+ * @param[in] pixelData The image pixelData
+ * @return True if successful
*/
- bool PostLoad( TextureManager::TextureInfo& textureInfo, PixelData pixelData );
+ void PostLoad( TextureManager::TextureInfo& textureInfo, PixelData pixelData );
+
+ /**
+ * Check if there is a texture waiting to be masked. If there
+ * is then apply this mask and upload it.
+ * @param[in] maskTextureInfo The texture info of the mask that has just loaded.
+ */
+ void CheckForWaitingTexture( TextureInfo& maskTextureInfo );
+
+ /**
+ * Apply the mask texture to the image texture.
+ * @param[in] pixelData The image pixelData to apply the mask to
+ * @param[in] maskTextureId The texture id of the mask.
+ */
+ void ApplyMask( PixelData pixelData, TextureId maskTextureId );
+
+ /**
+ * Upload the texture specified in pixelData to the appropriate location
+ * @param[in] pixelData The image data to upload
+ * @param[in] textureInfo The texture info containing the location to
+ * store the data to.
+ */
+ void UploadTexture( PixelData pixelData, TextureInfo& textureInfo );
+
+ /**
+ * Mark the texture as complete, and inform observers
+ * @param[in] textureInfo The struct associated with this Texture
+ */
+ void UploadComplete( TextureInfo& textureInfo );
+
+ /**
+ * Notify the current observers that the texture upload is complete,
+ * then remove the observers from the list.
+ * @param[in] textureInfo The struct associated with this Texture
+ * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU
+ */
+ void NotifyObservers( TextureInfo& textureInfo, bool success );
/**
* @brief Generates a new, unique TextureId
/**
* @brief Generates a hash for caching based on the input parameters.
+ * Only applies size, fitting mode andsampling mode if the size is specified.
+ * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID
+ * Always applies useAtlas.
* @param[in] url The URL of the image to load
* @param[in] size The image size
* @param[in] fittingMode The FittingMode to use
* @param[in] samplingMode The SamplingMode to use
* @param[in] useAtlas True if atlased
+ * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID)
* @return A hash of the provided data for caching.
*/
TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
const FittingMode::Type fittingMode,
- const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas );
+ const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
+ TextureId maskTextureId );
/**
* @brief Looks up a cached texture by its hash.
* If found, the given parameters are used to check there is no hash-collision.
- * @param[in] hash The hash to look up
- * @param[in] url The URL of the image to load
- * @param[in] size The image size
- * @param[in] fittingMode The FittingMode to use
- * @param[in] samplingMode The SamplingMode to use
- * @param[in] useAtlas True if atlased
- * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
+ * @param[in] hash The hash to look up
+ * @param[in] url The URL of the image to load
+ * @param[in] size The image size
+ * @param[in] fittingMode The FittingMode to use
+ * @param[in] samplingMode The SamplingMode to use
+ * @param[in] useAtlas True if atlased
+ * @param[in] maskTextureId Optional texture ID to use to mask this image
+ * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
*/
- TextureManager::TextureId FindCachedTexture( const TextureManager::TextureHash hash, const std::string& url, const ImageDimensions size,
- const FittingMode::Type fittingMode, const Dali::SamplingMode::Type samplingMode, const bool useAtlas );
+ TextureManager::TextureId FindCachedTexture(
+ const TextureManager::TextureHash hash,
+ const std::string& url,
+ const ImageDimensions size,
+ const FittingMode::Type fittingMode,
+ const Dali::SamplingMode::Type samplingMode,
+ const bool useAtlas,
+ TextureId maskTextureId );
private: