Added new RollingGifImageCache for animated images.
Change-Id: Idda4fecb7f4c5303f459a9e6b18222fdb3c83a41
ToolkitTestApplication application;
tet_infoline( "UtcDaliVisualFactoryGetAnimatedImageVisual1: Request animated image visual with a gif url" );
- VisualFactory factory = VisualFactory::Get();
- Visual::Base visual = factory.CreateVisual( TEST_GIF_FILE_NAME, ImageDimensions() );
- DALI_TEST_CHECK( visual );
-
TestGlAbstraction& gl = application.GetGlAbstraction();
TraceCallStack& textureTrace = gl.GetTextureTrace();
textureTrace.Enable(true);
+ VisualFactory factory = VisualFactory::Get();
+ Visual::Base visual = factory.CreateVisual( TEST_GIF_FILE_NAME, ImageDimensions() );
+ DALI_TEST_CHECK( visual );
+
DummyControl actor = DummyControl::New(true);
DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
// renderer is added to actor
DALI_TEST_CHECK( actor.GetRendererCount() == 1u );
-
- // test the uniforms which used to handle the atlas rect
- // the four frames should be located inside atlas as follows: atlas size 100*100
- // -------------
- // | | |
- // | 0 | 1 |
- // -------------
- // | | |
- // | 2 | 3 |
- // -------------
-
Renderer renderer = actor.GetRendererAt( 0u );
DALI_TEST_CHECK( renderer );
- Property::Value atlasRectValue = renderer.GetProperty( renderer.GetPropertyIndex( "uAtlasRect" ) );
- // take into consideration the half pixel correction
- DALI_TEST_EQUALS( atlasRectValue.Get<Vector4>(), Vector4(0.5f, 0.5f, 49.5f, 49.5f)/100.f, Math::MACHINE_EPSILON_100, TEST_LOCATION );
-
- // waiting for the resource uploading
- application.SendNotification();
- application.Render();
-
- DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
+ DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
+ textureTrace.Reset();
// Force the timer used by the animatedImageVisual to tick,
Dali::Timer timer = Timer::New( 0 );
timer.MockEmitSignal();
application.SendNotification();
application.Render();
- atlasRectValue = renderer.GetProperty( renderer.GetPropertyIndex( "uAtlasRect" ) );
- // take into consideration the half pixel correction
- DALI_TEST_EQUALS( atlasRectValue.Get<Vector4>(), Vector4(50.5f, 0.5f, 99.5f, 49.5f)/100.f, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+ DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
+ textureTrace.Reset();
+
// Force the timer used by the animatedImageVisual to tick,
timer.MockEmitSignal();
application.SendNotification();
application.Render();
- atlasRectValue = renderer.GetProperty( renderer.GetPropertyIndex( "uAtlasRect" ) );
- // take into consideration the half pixel correction
- DALI_TEST_EQUALS( atlasRectValue.Get<Vector4>(), Vector4(0.5f, 50.5f, 49.5f, 99.5f)/100.f, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+ DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
+ textureTrace.Reset();
// Force the timer used by the animatedImageVisual to tick,
timer.MockEmitSignal();
application.SendNotification();
application.Render();
- atlasRectValue = renderer.GetProperty( renderer.GetPropertyIndex( "uAtlasRect" ) );
- // take into consideration the half pixel correction
- DALI_TEST_EQUALS( atlasRectValue.Get<Vector4>(), Vector4(50.5f, 50.5f, 99.5f, 99.5f)/100.f, Math::MACHINE_EPSILON_100, TEST_LOCATION );
+ DALI_TEST_EQUALS( textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION );
+ textureTrace.Reset();
// Test SetOffStage().
actor.Unparent();
$(toolkit_src_dir)/visuals/animated-image/image-cache.cpp \
$(toolkit_src_dir)/visuals/animated-image/fixed-image-cache.cpp \
$(toolkit_src_dir)/visuals/animated-image/rolling-image-cache.cpp \
+ $(toolkit_src_dir)/visuals/animated-image/rolling-gif-image-cache.cpp \
$(toolkit_src_dir)/visuals/border/border-visual.cpp \
$(toolkit_src_dir)/visuals/color/color-visual.cpp \
$(toolkit_src_dir)/visuals/gradient/gradient-visual.cpp \
#include "animated-image-visual.h"
// EXTERNAL INCLUDES
-#include <dali/devel-api/adaptor-framework/gif-loading.h>
#include <dali/devel-api/adaptor-framework/image-loading.h>
#include <dali/integration-api/debug.h>
+#include <memory>
// INTERNAL INCLUDES
#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
#include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
#include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
#include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
+#include <dali-toolkit/internal/visuals/animated-image/rolling-gif-image-cache.h>
#include <dali-toolkit/internal/visuals/image/image-visual.h>
#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
+#include <dali-toolkit/devel-api/image-loader/texture-manager.h>
namespace Dali
{
/**
* Multi-image Flow of execution
*
- * | DoSetProperties()
+ * | New
+ * | DoSetProperties()
* | LoadFirstBatch()
- * | cache->LoadBatch()
+ * | new cache
+ * | cache->LoadBatch()
* |
* | DoSetOnStage()
+ * | PrepareTextureSet()
+ * | cache->FirstFrame()
* | CreateRenderer() (Doesn't become ready until first frame loads)
+ * | StartFirstFrame()
* |
* | FrameReady(textureSet)
* | start first frame:
AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, const VisualUrl& imageUrl, const Property::Map& properties )
{
- AnimatedImageVisual* visual = new AnimatedImageVisual( factoryCache );
- visual->mImageUrl = imageUrl;
+ AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache ) );
+ visual->InitializeGif( imageUrl );
visual->SetProperties( properties );
+ if( visual->mFrameCount > 0 )
+ {
+ visual->LoadFirstBatch();
+ }
+
return visual;
}
AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, const Property::Array& imageUrls, const Property::Map& properties )
{
- AnimatedImageVisual* visual = new AnimatedImageVisual( factoryCache );
+ AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache ) );
visual->mImageUrls = new ImageCache::UrlList();
visual->mImageUrls->reserve( imageUrls.Count() );
urlStore.mUrl = imageUrls[i].Get<std::string>();
visual->mImageUrls->push_back( urlStore );
}
-
+ visual->mFrameCount = imageUrls.Count();
visual->SetProperties( properties );
- // starts loading immediately
+
+ if( visual->mFrameCount > 0 )
+ {
+ visual->LoadFirstBatch();
+ }
+
return visual;
}
AnimatedImageVisualPtr AnimatedImageVisual::New( VisualFactoryCache& factoryCache, const VisualUrl& imageUrl )
{
- AnimatedImageVisual* visual = new AnimatedImageVisual( factoryCache );
- visual->mImageUrl = imageUrl;
+ AnimatedImageVisualPtr visual( new AnimatedImageVisual( factoryCache ) );
+ visual->InitializeGif( imageUrl );
+
+ if( visual->mFrameCount > 0 )
+ {
+ visual->LoadFirstBatch();
+ }
return visual;
}
+void AnimatedImageVisual::InitializeGif( const VisualUrl& imageUrl )
+{
+ mImageUrl = imageUrl;
+ mGifLoading = GifLoading::New( imageUrl.GetUrl() );
+ mFrameCount = mGifLoading->GetImageCount();
+ mGifLoading->LoadFrameDelays( mFrameDelayContainer );
+}
+
AnimatedImageVisual::AnimatedImageVisual( VisualFactoryCache& factoryCache )
: Visual::Base( factoryCache ),
mFrameDelayTimer(),
mPlacementActor(),
mPixelArea( FULL_TEXTURE_RECT ),
mImageUrl(),
+ mGifLoading( nullptr ),
mCurrentFrameIndex( 0 ),
mImageUrls( NULL ),
mImageCache( NULL ),
mBatchSize( 1 ),
mFrameDelay( 100 ),
mUrlIndex( 0 ),
+ mFrameCount( 0 ),
mImageSize(),
mWrapModeU( WrapMode::DEFAULT ),
mWrapModeV( WrapMode::DEFAULT ),
{
if( mImageUrl.IsValid() )
{
- mImageSize = Dali::GetGifImageSize( mImageUrl.GetUrl() );
+ mImageSize = mGifLoading->GetImageSize();
}
else if( mImageUrls && mImageUrls->size() > 0 )
{
}
}
}
-
- if( mImageUrls && mImageUrls->size() > 0 )
- {
- LoadFirstBatch();
- }
}
void AnimatedImageVisual::DoSetProperty( Property::Index index,
mFrameDelayTimer.Reset();
}
- mTextureRectContainer.Clear();
- mFrameDelayContainer.Clear();
-
actor.RemoveRenderer( mImpl->mRenderer );
mImpl->mRenderer.Reset();
mPlacementActor.Reset();
void AnimatedImageVisual::CreateRenderer()
{
bool defaultWrapMode = mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE;
- bool atlasing = (mTextureRectContainer.Count() > 0) ;
+ bool atlasing = false;
Shader shader = ImageVisual::GetImageShader( mFactoryCache, atlasing, defaultWrapMode );
Geometry geometry = mFactoryCache.GetGeometry( VisualFactoryCache::QUAD_GEOMETRY );
}
mCurrentFrameIndex = 0;
-
- if( mTextureRectContainer.Count() > 0 )
- {
- mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mTextureRectContainer[mCurrentFrameIndex] );
- }
}
void AnimatedImageVisual::LoadFirstBatch()
{
- DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::LoadFirstBatch()\n");
-
// Ensure the batch size and cache size are no bigger than the number of URLs,
// and that the cache is at least as big as the batch size.
- uint16_t numUrls = mImageUrls->size();
- uint16_t batchSize = std::min( mBatchSize, numUrls );
- uint16_t cacheSize = std::min( std::max( batchSize, mCacheSize ), numUrls );
+ uint16_t numUrls = 0;
+ uint16_t batchSize = 1;
+ uint16_t cacheSize = 1;
+
+ if( mImageUrls )
+ {
+ numUrls = mImageUrls->size();
+ }
+ else
+ {
+ numUrls = mFrameCount;
+ }
+
+ batchSize = std::min( mBatchSize, numUrls );
+ cacheSize = std::min( std::max( batchSize, mCacheSize ), numUrls );
+
+ DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::LoadFirstBatch() batchSize:%d cacheSize:%d\n", batchSize, cacheSize);
mUrlIndex = 0;
TextureManager& textureManager = mFactoryCache.GetTextureManager();
- if( batchSize > 0 && cacheSize > 0 )
+ if( mGifLoading != nullptr )
+ {
+ mImageCache = new RollingGifImageCache( textureManager, *mGifLoading, mFrameCount, *this, cacheSize, batchSize );
+ }
+ else if( batchSize > 0 && cacheSize > 0 )
{
if( cacheSize < numUrls )
{
{
mImageCache = new RollingImageCache( textureManager, *mImageUrls, *this, 1, 1 );
}
+ if (!mImageCache)
+ {
+ DALI_LOG_ERROR("mImageCache is null");
+ }
}
void AnimatedImageVisual::StartFirstFrame( TextureSet& textureSet )
mPlacementActor.Reset();
}
- int frameDelay = mFrameDelay;
- if( mFrameDelayContainer.Count() > 1 )
+ mCurrentFrameIndex = 0;
+
+ if( mFrameCount > 1 )
{
- frameDelay = mFrameDelayContainer[0];
- }
- mFrameDelayTimer = Timer::New( frameDelay );
- mFrameDelayTimer.TickSignal().Connect( this, &AnimatedImageVisual::DisplayNextFrame );
- mFrameDelayTimer.Start();
+ int frameDelay = mFrameDelay; // from URL array
+ if( mFrameDelayContainer.Count() > 0 ) // from GIF
+ {
+ frameDelay = mFrameDelayContainer[0];
+ }
- DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"ResourceReady()\n");
+ mFrameDelayTimer = Timer::New( frameDelay );
+ mFrameDelayTimer.TickSignal().Connect( this, &AnimatedImageVisual::DisplayNextFrame );
+ mFrameDelayTimer.Start();
+ }
+ DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"ResourceReady(ResourceStatus::READY)\n");
ResourceReady( Toolkit::Visual::ResourceStatus::READY );
}
TextureSet AnimatedImageVisual::PrepareTextureSet()
{
TextureSet textureSet;
- if( mImageUrl.IsValid() )
+ if (mImageCache)
+ textureSet = mImageCache->FirstFrame();
+ if( textureSet )
{
- textureSet = PrepareAnimatedGifImage();
+ SetImageSize( textureSet );
}
else
{
- textureSet = mImageCache->FirstFrame();
- if( textureSet )
- {
- SetImageSize( textureSet );
- }
- }
- return textureSet;
-}
-
-TextureSet AnimatedImageVisual::PrepareAnimatedGifImage()
-{
- TextureSet textureSet;
-
- // load from image file
- std::vector<Dali::PixelData> pixelDataList;
-
- if( mImageUrl.IsLocalResource() )
- {
- if( Dali::LoadAnimatedGifFromFile( mImageUrl.GetUrl().c_str() , pixelDataList, mFrameDelayContainer ) )
- {
- mImageSize.SetWidth( pixelDataList[0].GetWidth() );
- mImageSize.SetHeight( pixelDataList[0].GetHeight() );
-
- Texture texture = Toolkit::ImageAtlas::PackToAtlas( pixelDataList, mTextureRectContainer );
- textureSet = TextureSet::New();
- textureSet.SetTexture( 0, texture );
- }
+ DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"ResourceReady(ResourceStatus::FAILED)\n");
+ ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
}
return textureSet;
bool AnimatedImageVisual::DisplayNextFrame()
{
- DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame() start\n");
-
- if( mFrameDelayContainer.Count() > 0 )
+ if( mFrameCount > 1 )
{
// Wrap the frame index
++mCurrentFrameIndex;
- mCurrentFrameIndex %= mFrameDelayContainer.Count();
+ mCurrentFrameIndex %= mFrameCount;
+ }
+ DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) FrameCount:%d\n", this, mCurrentFrameIndex);
- mImpl->mRenderer.RegisterProperty( ATLAS_RECT_UNIFORM_NAME, mTextureRectContainer[mCurrentFrameIndex] );
+ if( mFrameDelayContainer.Count() > 0 )
+ {
+ unsigned int delay = mFrameDelayContainer[mCurrentFrameIndex];
- if( mFrameDelayTimer.GetInterval() != mFrameDelayContainer[mCurrentFrameIndex] )
+ if( mFrameDelayTimer.GetInterval() != delay )
{
- mFrameDelayTimer.SetInterval( mFrameDelayContainer[mCurrentFrameIndex] );
+ mFrameDelayTimer.SetInterval( delay );
}
}
- else if( mImageCache )
+
+ TextureSet textureSet;
+ if (mImageCache)
+ textureSet = mImageCache->NextFrame();
+ if( textureSet )
{
- TextureSet textureSet = mImageCache->NextFrame();
- if( textureSet )
- {
- SetImageSize( textureSet );
- mImpl->mRenderer.SetTextures( textureSet );
- }
+ SetImageSize( textureSet );
+ mImpl->mRenderer.SetTextures( textureSet );
}
// Keep timer ticking
#include <dali/public-api/math/vector4.h>
#include <dali/public-api/object/weak-handle.h>
#include <dali/public-api/adaptor-framework/timer.h>
+#include <dali/devel-api/adaptor-framework/gif-loading.h>
// INTERNAL INCLUDES
#include <dali-toolkit/internal/visuals/visual-base-impl.h>
/**
* Adds the texture set to the renderer, and the renderer to the
* placement actor, and starts the frame timer
+ * @param[in] textureSet The texture set to apply
*/
void StartFirstFrame( TextureSet& textureSet );
TextureSet PrepareTextureSet();
/**
- * Load the gif image and pack the frames into atlas.
- * @return The atlas texture.
- */
- TextureSet PrepareAnimatedGifImage();
-
- /**
* Set the image size from the texture set
+ * @param[in] textureSet The texture set to get the size from
*/
void SetImageSize( TextureSet& textureSet );
/**
* Called when the next frame is ready.
+ * @param[in] textureSet the texture set to apply
*/
void FrameReady( TextureSet textureSet );
*/
bool DisplayNextFrame();
+ /**
+ * Initialize the gif variables.
+ * @param[in] imageUrl The url of the animated gif
+ */
+ void InitializeGif( const VisualUrl& imageUrl );
// Undefined
AnimatedImageVisual( const AnimatedImageVisual& animatedImageVisual );
WeakHandle<Actor> mPlacementActor;
// Variables for GIF player
- Dali::Vector<Vector4> mTextureRectContainer;
Dali::Vector<uint32_t> mFrameDelayContainer;
Vector4 mPixelArea;
VisualUrl mImageUrl;
+ std::unique_ptr<Dali::GifLoading> mGifLoading; // Only needed for animated gifs
uint32_t mCurrentFrameIndex; // Frame index into textureRects
// Variables for Multi-Image player
uint16_t mUrlIndex;
// Shared variables
+ uint32_t mFrameCount; // Number of frames
ImageDimensions mImageSize;
Dali::WrapMode::Type mWrapModeU:3;
// CLASS HEADER
#include <dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h>
+// INTERNAL HEADERS
+#include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
+
namespace Dali
{
namespace Toolkit
FixedImageCache::FixedImageCache(
TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer,
unsigned int batchSize )
-: ImageCache( textureManager, urlList, observer, batchSize ),
+: ImageCache( textureManager, observer, batchSize ),
+ mImageUrls( urlList ),
mFront(0u)
{
mReadyFlags.reserve( mImageUrls.size() );
// need to account for this inside the UploadComplete method using mRequestingLoad.
mRequestingLoad = true;
- mImageUrls[ mUrlIndex ].mTextureId =
- mTextureManager.RequestLoad( url, ImageDimensions(), FittingMode::SCALE_TO_FILL,
- SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS,
- this, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED );
+ bool synchronousLoading = false;
+ bool atlasingStatus = false;
+ bool loadingStatus = false;
+ TextureManager::MaskingDataPointer maskInfo = nullptr;
+ AtlasUploadObserver* atlasObserver = nullptr;
+ ImageAtlasManagerPtr imageAtlasManager = nullptr;
+ Vector4 textureRect;
+
+ mTextureManager.LoadTexture(
+ url, ImageDimensions(), FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR, maskInfo,
+ synchronousLoading, mImageUrls[ mUrlIndex ].mTextureId, textureRect,
+ atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT,
+ Dali::WrapMode::Type::DEFAULT, this,
+ atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED );
+
+ if( loadingStatus == false ) // not loading, means it's already ready.
+ {
+ SetImageFrameReady( mImageUrls[ mUrlIndex ].mTextureId );
+ }
mRequestingLoad = false;
++mUrlIndex;
}
namespace Internal
{
-class FixedImageCache : public ImageCache
+class FixedImageCache : public ImageCache, public TextureUploadObserver
{
public:
/**
bool useAtlasing,
const Vector4& atlasRect );
-protected:
+private:
+ std::vector<UrlStore>& mImageUrls;
std::vector<bool> mReadyFlags;
unsigned int mFront;
};
{
ImageCache::ImageCache( TextureManager& textureManager,
- UrlList& urlList,
ImageCache::FrameReadyObserver& observer,
unsigned int batchSize )
: mTextureManager( textureManager ),
mObserver( observer ),
- mImageUrls( urlList ),
mBatchSize( batchSize ),
mUrlIndex(0u),
mWaitingForReadyFrame(false),
namespace Internal
{
-class ImageCache : public TextureUploadObserver
+class ImageCache
{
public:
/**
* batch and cache sizes. The cache is as large as the number of urls.
*/
ImageCache( TextureManager& textureManager,
- UrlList& urlList,
ImageCache::FrameReadyObserver& observer,
unsigned int batchSize );
protected:
TextureManager& mTextureManager;
FrameReadyObserver& mObserver;
- std::vector<UrlStore>& mImageUrls;
unsigned int mBatchSize;
unsigned int mUrlIndex;
bool mWaitingForReadyFrame:1;
--- /dev/null
+/*
+ * Copyright (c) 2017 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 "rolling-gif-image-cache.h"
+
+// EXTERNAL HEADERS
+
+// INTERNAL HEADERS
+#include <dali-toolkit/devel-api/image-loader/texture-manager.h>
+#include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
+#include <dali/integration-api/debug.h>
+
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
+
+#define LOG_CACHE \
+ { \
+ std::ostringstream oss; \
+ oss<<"Size:"<<mQueue.Count()<<" [ "; \
+ for(std::size_t _i=0; _i<mQueue.Count(); ++_i) \
+ { \
+ oss<<_i<< \
+ "={ frm#: " << mQueue[_i].mFrameNumber << \
+ " tex: " << mImageUrls[mQueue[_i].mFrameNumber].mTextureId<<"}, "; \
+ } \
+ oss<<" ]"<<std::endl; \
+ DALI_LOG_INFO(gAnimImgLogFilter,Debug::Concise,"%s",oss.str().c_str()); \
+ }
+
+#else
+ #define LOG_CACHE
+#endif
+
+const bool ENABLE_ORIENTATION_CORRECTION( true );
+
+}
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+RollingGifImageCache::RollingGifImageCache(
+ TextureManager& textureManager, GifLoading& gifLoading, uint32_t frameCount, ImageCache::FrameReadyObserver& observer,
+ uint16_t cacheSize, uint16_t batchSize )
+: ImageCache( textureManager, observer, batchSize ),
+ mGifLoading( gifLoading ),
+ mFrameCount( frameCount ),
+ mFrameIndex( 0 ),
+ mCacheSize( cacheSize ),
+ mQueue( cacheSize )
+{
+ mImageUrls.resize( mFrameCount );
+ LoadBatch();
+}
+
+RollingGifImageCache::~RollingGifImageCache()
+{
+ while( !mQueue.IsEmpty() )
+ {
+ ImageFrame imageFrame = mQueue.PopFront();
+ Dali::Toolkit::TextureManager::RemoveTexture( mImageUrls[ imageFrame.mFrameNumber ].mUrl );
+ }
+}
+
+TextureSet RollingGifImageCache::FirstFrame()
+{
+ return GetFrontTextureSet();
+}
+
+TextureSet RollingGifImageCache::NextFrame()
+{
+ TextureSet textureSet;
+
+ ImageFrame imageFrame = mQueue.PopFront();
+ Dali::Toolkit::TextureManager::RemoveTexture( mImageUrls[ imageFrame.mFrameNumber ].mUrl );
+ mImageUrls[ imageFrame.mFrameNumber ].mTextureId = TextureManager::INVALID_TEXTURE_ID;
+
+ LoadBatch();
+
+ return GetFrontTextureSet();
+}
+
+bool RollingGifImageCache::IsFrontReady() const
+{
+ return ( !mQueue.IsEmpty() );
+}
+
+void RollingGifImageCache::LoadBatch()
+{
+ // Try and load up to mBatchSize images, until the cache is filled.
+ // Once the cache is filled, as frames progress, the old frame is
+ // removed, and another frame is loaded
+
+ std::vector<Dali::PixelData> pixelDataList;
+
+ // Get the smallest number of frames we need to load
+ int batchSize = std::min( std::size_t(mBatchSize), mCacheSize - mQueue.Count() );
+ DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "RollingGifImageCache::LoadBatch() mFrameIndex:%d batchSize:%d\n", mFrameIndex, batchSize );
+
+ if( mGifLoading.LoadNextNFrames( mFrameIndex, batchSize, pixelDataList) )
+ {
+ unsigned int pixelDataListCount = pixelDataList.size();
+
+ for( unsigned int i = 0; i < pixelDataListCount && !mQueue.IsFull(); ++i )
+ {
+ ImageFrame imageFrame;
+
+ // create the texture for uploading the pixel data
+ Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
+ pixelDataList[i].GetPixelFormat(),
+ pixelDataList[i].GetWidth(),
+ pixelDataList[i].GetHeight() );
+
+ texture.Upload( pixelDataList[i] );
+
+ mImageUrls[ mUrlIndex ].mUrl = Dali::Toolkit::TextureManager::AddTexture(texture);
+ imageFrame.mFrameNumber = mUrlIndex;
+
+ ++mUrlIndex;
+ mUrlIndex %= mImageUrls.size();
+
+ mQueue.PushBack( imageFrame );
+
+ bool synchronousLoading = false;
+ bool atlasingStatus = false;
+ bool loadingStatus = false;
+ TextureManager::MaskingDataPointer maskInfo = nullptr;
+ AtlasUploadObserver* atlasObserver = nullptr;
+ ImageAtlasManagerPtr imageAtlasManager = nullptr;
+ Vector4 textureRect;
+
+ mTextureManager.LoadTexture(
+ mImageUrls[ imageFrame.mFrameNumber ].mUrl, ImageDimensions(), FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR, maskInfo,
+ synchronousLoading, mImageUrls[ imageFrame.mFrameNumber ].mTextureId, textureRect,
+ atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT,
+ Dali::WrapMode::Type::DEFAULT, NULL,
+ atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED );
+ }
+
+ mFrameIndex += batchSize;
+ mFrameIndex %= mFrameCount;
+ }
+
+ LOG_CACHE;
+}
+
+TextureSet RollingGifImageCache::GetFrontTextureSet() const
+{
+ DALI_LOG_INFO( gAnimImgLogFilter, Debug::Concise, "RollingGifImageCache::GetFrontTextureSet() FrameNumber:%d\n", mQueue[ 0 ].mFrameNumber );
+
+ TextureManager::TextureId textureId = GetCachedTextureId( 0 );
+ return mTextureManager.GetTextureSet( textureId );
+}
+
+TextureManager::TextureId RollingGifImageCache::GetCachedTextureId( int index ) const
+{
+ return mImageUrls[ mQueue[ index ].mFrameNumber ].mTextureId;
+}
+
+} //namespace Internal
+} //namespace Toolkit
+} //namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_INTERNAL_ROLLING_GIF_IMAGE_CACHE_H
+#define DALI_TOOLKIT_INTERNAL_ROLLING_GIF_IMAGE_CACHE_H
+
+/*
+ * Copyright (c) 2017 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/devel-api/adaptor-framework/gif-loading.h>
+#include <dali/devel-api/common/circular-queue.h>
+#include <dali-toolkit/internal/visuals/animated-image/image-cache.h>
+#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+
+/**
+ * Class to manage a rolling cache of GIF images, where the cache size
+ * is smaller than the total number of images.
+ *
+ * Frames are always ready, so the observer.FrameReady callback is never triggered;
+ * the FirstFrame and NextFrame APIs will always return a texture.
+ */
+class RollingGifImageCache : public ImageCache
+{
+public:
+ /**
+ * Constructor.
+ * @param[in] textureManager The texture manager
+ * @param[in] gifLoader The loaded gif image
+ * @param[in] frameCount The number of frames in the gif
+ * @param[in] observer FrameReady observer
+ * @param[in] cacheSize The size of the cache
+ * @param[in] batchSize The size of a batch to load
+ *
+ * This will start loading textures immediately, according to the
+ * batch and cache sizes.
+ */
+ RollingGifImageCache( TextureManager& textureManager,
+ GifLoading& gifLoader,
+ uint32_t frameCount,
+ ImageCache::FrameReadyObserver& observer,
+ uint16_t cacheSize,
+ uint16_t batchSize );
+
+ /**
+ * Destructor
+ */
+ virtual ~RollingGifImageCache();
+
+ /**
+ * Get the first frame. If it's not ready, this will trigger the
+ * sending of FrameReady() when the image becomes ready.
+ */
+ virtual TextureSet FirstFrame();
+
+ /**
+ * Get the next frame. If it's not ready, this will trigger the
+ * sending of FrameReady() when the image becomes ready.
+ * This will trigger the loading of the next batch.
+ */
+ virtual TextureSet NextFrame();
+
+private:
+ /**
+ * @return true if the front frame is ready
+ */
+ bool IsFrontReady() const;
+
+ /**
+ * Load the next batch of images
+ */
+ void LoadBatch();
+
+ /**
+ * Get the texture set of the front frame.
+ * @return the texture set
+ */
+ TextureSet GetFrontTextureSet() const;
+
+ /**
+ * Get the texture id of the given index
+ */
+ TextureManager::TextureId GetCachedTextureId( int index ) const;
+
+private:
+ /**
+ * Secondary class to hold readiness and index into url
+ */
+ struct ImageFrame
+ {
+ unsigned int mFrameNumber;
+ };
+
+ GifLoading& mGifLoading;
+ uint32_t mFrameCount;
+ int mFrameIndex;
+ std::vector<UrlStore> mImageUrls;
+ uint16_t mCacheSize;
+ CircularQueue<ImageFrame> mQueue;
+};
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
+
+#endif
#include <dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h>
// INTERNAL HEADERS
+#include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
#include <dali/integration-api/debug.h>
// EXTERNAL HEADERS
RollingImageCache::RollingImageCache(
TextureManager& textureManager, UrlList& urlList, ImageCache::FrameReadyObserver& observer,
uint16_t cacheSize, uint16_t batchSize )
-: ImageCache( textureManager, urlList, observer, batchSize ),
+: ImageCache( textureManager, observer, batchSize ),
+ mImageUrls( urlList ),
mQueue( cacheSize )
{
LoadBatch();
// need to account for this inside the UploadComplete method using mRequestingLoad.
mRequestingLoad = true;
- mImageUrls[ imageFrame.mUrlIndex ].mTextureId =
- mTextureManager.RequestLoad( url, ImageDimensions(), FittingMode::SCALE_TO_FILL,
- SamplingMode::BOX_THEN_LINEAR, TextureManager::NO_ATLAS,
- this, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED );
+ bool synchronousLoading = false;
+ bool atlasingStatus = false;
+ bool loadingStatus = false;
+ TextureManager::MaskingDataPointer maskInfo = nullptr;
+ AtlasUploadObserver* atlasObserver = nullptr;
+ ImageAtlasManagerPtr imageAtlasManager = nullptr;
+ Vector4 textureRect;
+
+ mTextureManager.LoadTexture(
+ url, ImageDimensions(), FittingMode::SCALE_TO_FILL,
+ SamplingMode::BOX_THEN_LINEAR, maskInfo,
+ synchronousLoading, mImageUrls[ imageFrame.mUrlIndex ].mTextureId, textureRect,
+ atlasingStatus, loadingStatus, Dali::WrapMode::Type::DEFAULT,
+ Dali::WrapMode::Type::DEFAULT, this,
+ atlasObserver, imageAtlasManager, ENABLE_ORIENTATION_CORRECTION, TextureManager::ReloadPolicy::CACHED );
+
mRequestingLoad = false;
}
* Class to manage a rolling cache of images, where the cache size
* is smaller than the total number of images.
*/
-class RollingImageCache : public ImageCache
+class RollingImageCache : public ImageCache, public TextureUploadObserver
{
public:
/**
bool mReady;
};
+ std::vector<UrlStore>& mImageUrls;
CircularQueue<ImageFrame> mQueue;
};
}
TextureSet TextureManager::LoadTexture(
- VisualUrl& url, Dali::ImageDimensions desiredSize, Dali::FittingMode::Type fittingMode,
+ const 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,
{
if( elem.textureId == id )
{
+ textureId = elem.textureId;
return elem.textureSet;
}
}
reloadPolicy );
}
- TextureManager::LoadState loadState = GetTextureState( textureId );
+ TextureManager::LoadState loadState = GetTextureStateInternal( textureId );
loadingStatus = ( loadState == TextureManager::LOADING );
if( loadState == TextureManager::UPLOADED )
TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
loadState = cachedTextureInfo.loadState;
}
+ else
+ {
+ for( auto&& elem : mExternalTextures )
+ {
+ if( elem.textureId == textureId )
+ {
+ loadState = LoadState::UPLOADED;
+ break;
+ }
+ }
+ }
+ return loadState;
+}
+
+TextureManager::LoadState TextureManager::GetTextureStateInternal( TextureId textureId )
+{
+ LoadState loadState = TextureManager::NOT_STARTED;
+
+ int cacheIndex = GetCacheIndexFromId( textureId );
+ if( cacheIndex != INVALID_CACHE_INDEX )
+ {
+ TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
+ loadState = cachedTextureInfo.loadState;
+ }
+
return loadState;
}
TextureInfo& cachedTextureInfo( mTextureInfoContainer[ cacheIndex ] );
textureSet = cachedTextureInfo.textureSet;
}
+ else
+ {
+ for( auto&& elem : mExternalTextures )
+ {
+ if( elem.textureId == textureId )
+ {
+ textureSet = elem.textureSet;
+ break;
+ }
+ }
+ }
return textureSet;
}
// wait for the mask to finish loading.
if( textureInfo.maskTextureId != INVALID_TEXTURE_ID )
{
- LoadState maskLoadState = GetTextureState( textureInfo.maskTextureId );
+ LoadState maskLoadState = GetTextureStateInternal( textureInfo.maskTextureId );
if( maskLoadState == LOADING )
{
textureInfo.pixelBuffer = pixelBuffer; // Store the pixel buffer temporarily
}
}
- DALI_LOG_WARNING( "Cannot locate TextureId: %d\n", textureId );
return INVALID_CACHE_INDEX;
}
// TextureManager Main API:
- TextureSet LoadTexture(VisualUrl& url, Dali::ImageDimensions desiredSize,
+ TextureSet LoadTexture(const VisualUrl& url, Dali::ImageDimensions desiredSize,
Dali::FittingMode::Type fittingMode, Dali::SamplingMode::Type samplingMode,
const MaskingDataPointer& maskInfo, bool synchronousLoading,
TextureManager::TextureId& textureId, Vector4& textureRect,
bool orientationCorrection,
TextureManager::ReloadPolicy reloadPolicy );
+ /**
+ * @brief Get the current state of a texture
+ * @param[in] textureId The texture id to query
+ * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
+ * is not valid.
+ */
+ LoadState GetTextureStateInternal( TextureId textureId );
typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
return mLocation == VisualUrl::LOCAL;
}
-std::string VisualUrl::GetLocation()
+std::string VisualUrl::GetLocation() const
{
const auto location = mUrl.find( "://" );
if( std::string::npos != location )
/**
* @return the location part of the url
*/
- std::string GetLocation();
+ std::string GetLocation() const;
/**
* Helper to create a URL of type TEXTURE