2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "rolling-animated-image-cache.h"
23 #include <dali-toolkit/devel-api/image-loader/texture-manager.h>
24 #include <dali-toolkit/internal/visuals/image-atlas-manager.h> // For ImageAtlasManagerPtr
25 #include <dali/integration-api/debug.h>
29 #if defined(DEBUG_ENABLED)
30 Debug::Filter* gAnimImgLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ANIMATED_IMAGE");
34 std::ostringstream oss; \
35 oss << "Size:" << mQueue.Count() << " [ "; \
36 for(std::size_t _i = 0; _i < mQueue.Count(); ++_i) \
38 oss << _i << "={ frm#: " << mQueue[_i].mFrameNumber << " tex: " << mImageUrls[mQueue[_i].mFrameNumber].mTextureId << "}, "; \
40 oss << " ]" << std::endl; \
41 DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "%s", oss.str().c_str()); \
48 const bool ENABLE_ORIENTATION_CORRECTION(true);
58 RollingAnimatedImageCache::RollingAnimatedImageCache(
59 TextureManager& textureManager, AnimatedImageLoading& animatedImageLoading, uint32_t frameCount, ImageCache::FrameReadyObserver& observer, uint16_t cacheSize, uint16_t batchSize, bool isSynchronousLoading)
60 : ImageCache(textureManager, observer, batchSize),
61 mAnimatedImageLoading(animatedImageLoading),
62 mFrameCount(frameCount),
64 mCacheSize(cacheSize),
66 mIsSynchronousLoading(isSynchronousLoading),
69 mImageUrls.resize(mFrameCount);
70 mIntervals.assign(mFrameCount, 0);
74 RollingAnimatedImageCache::~RollingAnimatedImageCache()
76 if(mTextureManagerAlive)
78 while(!mQueue.IsEmpty())
80 ImageFrame imageFrame = mQueue.PopFront();
81 mTextureManager.Remove(mImageUrls[imageFrame.mFrameNumber].mTextureId, this);
86 TextureSet RollingAnimatedImageCache::Frame(uint32_t frameIndex)
88 bool popExist = false;
89 while(!mQueue.IsEmpty() && mQueue.Front().mFrameNumber != frameIndex)
91 ImageFrame imageFrame = mQueue.PopFront();
92 mTextureManager.Remove(mImageUrls[imageFrame.mFrameNumber].mTextureId, this);
93 mImageUrls[imageFrame.mFrameNumber].mTextureId = TextureManager::INVALID_TEXTURE_ID;
97 TextureSet textureSet;
98 // If we need to load new frame that are not stored in queue.
99 // Load the frame synchronously.
100 if(mIsSynchronousLoading && mQueue.IsEmpty())
102 bool synchronousLoading = true;
103 textureSet = mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, mImageUrls[frameIndex].mTextureId, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this);
104 mFrameIndex = (frameIndex + 1) % mFrameCount;
107 if(popExist || mQueue.IsEmpty())
109 // If the frame of frameIndex was already loaded, load batch from the last frame of queue
110 if(!mQueue.IsEmpty())
112 if(!mLoadWaitingQueue.empty())
114 mFrameIndex = (mLoadWaitingQueue.back() + 1) % mFrameCount;
118 mFrameIndex = (mQueue.Back().mFrameNumber + 1) % mFrameCount;
124 // If the request is for the first frame or a jumped frame(JUMP_TO) remove current waiting queue.
125 mLoadWaitingQueue.clear();
126 // If the queue is empty, and the frame of frameIndex is not loaded synchronously. load batch from the frame of frameIndex
129 mFrameIndex = frameIndex;
137 if(IsFrontReady() == true)
139 textureSet = GetFrontTextureSet();
143 mWaitingForReadyFrame = true;
150 TextureSet RollingAnimatedImageCache::FirstFrame()
155 TextureSet RollingAnimatedImageCache::NextFrame()
157 TextureSet textureSet;
158 if(!mQueue.IsEmpty())
160 uint32_t frameIndex = mQueue.Front().mFrameNumber;
163 frameIndex = (frameIndex + 1) % mFrameCount;
165 textureSet = Frame(frameIndex);
169 DALI_LOG_ERROR("Cache is empty.");
175 uint32_t RollingAnimatedImageCache::GetFrameInterval(uint32_t frameIndex) const
177 return mAnimatedImageLoading.GetFrameInterval(frameIndex);
180 int32_t RollingAnimatedImageCache::GetCurrentFrameIndex() const
186 return mQueue.Front().mFrameNumber;
189 int32_t RollingAnimatedImageCache::GetTotalFrameCount() const
194 bool RollingAnimatedImageCache::IsFrontReady() const
196 return (!mQueue.IsEmpty() && mQueue.Front().mReady);
199 void RollingAnimatedImageCache::RequestFrameLoading(uint32_t frameIndex)
201 ImageFrame imageFrame;
202 imageFrame.mFrameNumber = frameIndex;
203 imageFrame.mReady = false;
205 mQueue.PushBack(imageFrame);
207 mRequestingLoad = true;
209 bool synchronousLoading = false;
210 mTextureManager.LoadAnimatedImageTexture(mAnimatedImageLoading, frameIndex, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, mImageUrls[frameIndex].mTextureId, Dali::WrapMode::Type::DEFAULT, Dali::WrapMode::Type::DEFAULT, this);
212 mRequestingLoad = false;
215 void RollingAnimatedImageCache::LoadBatch()
217 // Try and load up to mBatchSize images, until the cache is filled.
218 // Once the cache is filled, as frames progress, the old frame is
219 // removed, and another frame is loaded
221 bool frontFrameReady = IsFrontReady();
222 for(unsigned int i = 0; i < mBatchSize && mQueue.Count() + mLoadWaitingQueue.size() < static_cast<uint32_t>(mCacheSize) && !mQueue.IsFull(); ++i)
227 RequestFrameLoading(mFrameIndex);
231 mLoadWaitingQueue.push_back(mFrameIndex);
235 mFrameIndex %= mFrameCount;
238 CheckFrontFrame(frontFrameReady);
243 void RollingAnimatedImageCache::SetImageFrameReady(TextureManager::TextureId textureId)
245 for(std::size_t i = 0; i < mQueue.Count(); ++i)
247 if(GetCachedTextureId(i) == textureId)
249 mQueue[i].mReady = true;
255 TextureSet RollingAnimatedImageCache::GetFrontTextureSet() const
257 DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "RollingAnimatedImageCache::GetFrontTextureSet() FrameNumber:%d\n", mQueue[0].mFrameNumber);
259 TextureManager::TextureId textureId = GetCachedTextureId(0);
260 return mTextureManager.GetTextureSet(textureId);
263 TextureManager::TextureId RollingAnimatedImageCache::GetCachedTextureId(int index) const
265 return mImageUrls[mQueue[index].mFrameNumber].mTextureId;
268 void RollingAnimatedImageCache::CheckFrontFrame(bool wasReady)
270 if(mWaitingForReadyFrame && wasReady == false && IsFrontReady())
272 mWaitingForReadyFrame = false;
273 mObserver.FrameReady(GetFrontTextureSet());
277 void RollingAnimatedImageCache::LoadComplete(bool loadSuccess, TextureInformation textureInformation)
279 DALI_LOG_INFO(gAnimImgLogFilter, Debug::Concise, "AnimatedImageVisual::LoadComplete(textureId:%d) start\n", textureInformation.textureId);
282 bool frontFrameReady = IsFrontReady();
286 SetImageFrameReady(textureInformation.textureId);
288 CheckFrontFrame(frontFrameReady);
292 // LoadComplete has been called from within RequestLoad. TextureManager must
293 // therefore already have the texture cached, so make the texture ready.
294 // (Use the last texture, as the texture id hasn't been assigned yet)
295 mQueue.Back().mReady = true;
299 // The frames of a single animated image can not be loaded parallelly.
300 // Therefore, a frame is now loading, other orders are waiting.
301 // And, after the frame is loaded, requests load of next order.
302 if(!mLoadWaitingQueue.empty())
304 uint32_t loadingIndex = mLoadWaitingQueue.front();
305 mLoadWaitingQueue.erase(mLoadWaitingQueue.begin());
307 RequestFrameLoading(loadingIndex);
313 } //namespace Internal
314 } //namespace Toolkit