*
* @param[in] loaderInfo A LoaderInfo structure containing file descriptor and other data about GIF.
* @param[out] prop A ImageProperties structure for storing information about GIF data.
- * @param[out] error Error code
* @return The true or false whether reading was successful or not.
*/
bool ReadHeader(LoaderInfo& loaderInfo,
- ImageProperties& prop, //output struct
- int* error)
+ ImageProperties& prop)
{
GifAnimationData& animated = loaderInfo.animated;
GifCachedColorData& cachedColor = loaderInfo.cachedColor;
cachedColor.globalCachedColor[i] = PixelLookup(colorMap, i);
}
}
-
- // no errors in header scan etc. so set err and return value
- *error = 0;
}
}
}
public:
Impl(const std::string& url, bool isLocalResource)
: mUrl(url),
- mLoadSucceeded(true),
+ mLoadSucceeded(false),
mMutex()
{
loaderInfo.gifAccessor = nullptr;
- int error;
loaderInfo.fileData.fileName = mUrl.c_str();
loaderInfo.fileData.isLocalResource = isLocalResource;
+ }
+
+ bool LoadGifInformation()
+ {
+ // Block to do not load this file again.
+ Mutex::ScopedLock lock(mMutex);
+ if(DALI_LIKELY(mLoadSucceeded))
+ {
+ return mLoadSucceeded;
+ }
- mLoadSucceeded = ReadHeader(loaderInfo, imageProperties, &error);
+ mLoadSucceeded = ReadHeader(loaderInfo, imageProperties);
+ return mLoadSucceeded;
}
// Moveable but not copyable
int error;
bool ret = false;
- Mutex::ScopedLock lock(mImpl->mMutex);
- if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ if(DALI_UNLIKELY(!mImpl->LoadGifInformation()))
{
return false;
}
+ Mutex::ScopedLock lock(mImpl->mMutex);
const int bufferSize = mImpl->imageProperties.w * mImpl->imageProperties.h * sizeof(uint32_t);
DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
{
int error;
Dali::Devel::PixelBuffer pixelBuffer;
- if(!mImpl->mLoadSucceeded)
+
+ DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
+
+ // If Gif file is still not loaded, Load the information.
+ if(DALI_UNLIKELY(!mImpl->LoadGifInformation()))
{
return pixelBuffer;
}
Mutex::ScopedLock lock(mImpl->mMutex);
- DALI_LOG_INFO(gGifLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
-
pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->imageProperties.w, mImpl->imageProperties.h, Dali::Pixel::RGBA8888);
mImpl->loaderInfo.animated.currentFrame = 1 + (frameIndex % mImpl->loaderInfo.animated.frameCount);
ImageDimensions GifLoading::GetImageSize() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
return ImageDimensions(mImpl->imageProperties.w, mImpl->imageProperties.h);
}
uint32_t GifLoading::GetImageCount() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
return mImpl->loaderInfo.animated.frameCount;
}
uint32_t GifLoading::GetFrameInterval(uint32_t frameIndex) const
{
- return mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadGifInformation();
+ }
+
+ uint32_t interval = 0u;
+ if(DALI_LIKELY(mImpl->mLoadSucceeded))
+ {
+ interval = mImpl->loaderInfo.animated.frames[frameIndex].info.delay * 10;
+ }
+ return interval;
}
std::string GifLoading::GetUrl() const
Debug::Filter* gWebPLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GIF_LOADING");
#endif
-constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
+static constexpr int32_t INITIAL_INDEX = -1;
+static constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
} // namespace
mBuffer(nullptr),
mBufferSize(0u),
mImageSize(),
- mLoadSucceeded(true)
+ mLoadSucceeded(false),
+ mIsLocalResource(isLocalResource)
{
+ }
+
+ bool LoadWebPInformation()
+ {
+ // Block to do not load this file again.
+ Mutex::ScopedLock lock(mMutex);
+ if(DALI_UNLIKELY(mLoadSucceeded))
+ {
+ return mLoadSucceeded;
+ }
+
+#ifndef DALI_WEBP_AVAILABLE
+ // If the system doesn't support webp, loading will be failed.
+ mFrameCount = 0u;
+ mLoadSucceeded = false;
+ return mLoadSucceeded;
+#endif
+
// mFrameCount will be 1 if the input image is non-animated image or animated image with single frame.
- if(DALI_LIKELY(ReadWebPInformation(isLocalResource)))
+ if(DALI_LIKELY(ReadWebPInformation()))
{
#ifdef DALI_ANIMATED_WEBP_ENABLED
WebPDataInit(&mWebPData);
mImageSize = ImageDimensions(imageWidth, imageHeight);
}
#endif
-#ifndef DALI_WEBP_AVAILABLE
- // If the system doesn't support webp, loading will be failed.
- mFrameCount = 0u;
- mLoadSucceeded = false;
-#endif
+ mLoadSucceeded = true;
}
else
{
mLoadSucceeded = false;
DALI_LOG_ERROR("Image loading failed for: \"%s\".\n", mUrl.c_str());
}
+
+ return mLoadSucceeded;
}
- bool ReadWebPInformation(bool isLocalResource)
+ bool ReadWebPInformation()
{
FILE* fp = nullptr;
- if(isLocalResource)
+ if(mIsLocalResource)
{
Internal::Platform::FileReader fileReader(mUrl);
fp = fileReader.GetFile();
std::string mUrl;
std::vector<uint32_t> mTimeStamp;
- uint32_t mLoadingFrame{0};
+ int32_t mLatestLoadedFrame{INITIAL_INDEX};
uint32_t mFrameCount;
Mutex mMutex;
// For the case the system doesn't support DALI_ANIMATED_WEBP_ENABLED
uint32_t mBufferSize;
ImageDimensions mImageSize;
bool mLoadSucceeded;
+ bool mIsLocalResource;
#ifdef DALI_ANIMATED_WEBP_ENABLED
- WebPData mWebPData{0};
- WebPAnimDecoder* mWebPAnimDecoder{nullptr};
- WebPAnimInfo mWebPAnimInfo{0};
+ WebPData mWebPData{0};
+ WebPAnimDecoder* mWebPAnimDecoder{nullptr};
+ WebPAnimInfo mWebPAnimInfo{0};
+ Dali::Devel::PixelBuffer mPreLoadedFrame{};
#endif
};
for(int i = 0; i < count; ++i)
{
Dali::Devel::PixelBuffer pixelBuffer = LoadFrame((frameStartIndex + i) % mImpl->mFrameCount);
- Dali::PixelData imageData = Devel::PixelBuffer::Convert(pixelBuffer);
+ if(DALI_UNLIKELY(!pixelBuffer))
+ {
+ return false;
+ }
+
+ Dali::PixelData imageData = Devel::PixelBuffer::Convert(pixelBuffer);
pixelData.push_back(imageData);
}
if(DALI_UNLIKELY(pixelData.size() != static_cast<uint32_t>(count)))
{
Dali::Devel::PixelBuffer pixelBuffer;
+ // If WebP file is still not loaded, Load the information.
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ if(DALI_UNLIKELY(!mImpl->LoadWebPInformation()))
+ {
+ return pixelBuffer;
+ }
+ }
+
// WebPDecodeRGBA is faster than to use demux API for loading non-animated image.
// If frame count is 1, use WebPDecodeRGBA api.
#ifdef DALI_WEBP_AVAILABLE
DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
- if(mImpl->mLoadingFrame > frameIndex)
+ if(mImpl->mPreLoadedFrame && mImpl->mLatestLoadedFrame == static_cast<int32_t>(frameIndex))
+ {
+ pixelBuffer = mImpl->mPreLoadedFrame;
+ }
+ else
+ {
+ pixelBuffer = DecodeFrame(frameIndex);
+ }
+ mImpl->mPreLoadedFrame.Reset();
+
+ // If time stamp of next frame is unknown, load a frame more to know it.
+ if(frameIndex + 1 < mImpl->mWebPAnimInfo.frame_count && mImpl->mTimeStamp[frameIndex + 1] == 0u)
{
- mImpl->mLoadingFrame = 0;
+ mImpl->mPreLoadedFrame = DecodeFrame(frameIndex + 1);
+ }
+
+#endif
+ return pixelBuffer;
+}
+
+Dali::Devel::PixelBuffer WebPLoading::DecodeFrame(uint32_t frameIndex)
+{
+ Dali::Devel::PixelBuffer pixelBuffer;
+#ifdef DALI_ANIMATED_WEBP_ENABLED
+ if(mImpl->mLatestLoadedFrame > static_cast<int32_t>(frameIndex))
+ {
+ mImpl->mLatestLoadedFrame = INITIAL_INDEX;
WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
}
- for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame)
+ uint8_t* frameBuffer;
+ int32_t timestamp;
+ for(; mImpl->mLatestLoadedFrame < static_cast<int32_t>(frameIndex);)
{
- uint8_t* frameBuffer;
- int timestamp;
WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
- mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
+ mImpl->mTimeStamp[++mImpl->mLatestLoadedFrame] = timestamp;
}
-
const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
- uint8_t* frameBuffer;
- int timestamp;
- WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
-
pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize);
- mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
- mImpl->mLoadingFrame++;
- if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
- {
- mImpl->mLoadingFrame = 0;
- WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
- }
#endif
return pixelBuffer;
}
ImageDimensions WebPLoading::GetImageSize() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadWebPInformation();
+ }
return mImpl->mImageSize;
}
uint32_t WebPLoading::GetImageCount() const
{
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
+ {
+ mImpl->LoadWebPInformation();
+ }
return mImpl->mFrameCount;
}
uint32_t WebPLoading::GetFrameInterval(uint32_t frameIndex) const
{
- // If frameIndex is above the value of ImageCount or current frame is not loading yet, return 0u.
- if(frameIndex >= GetImageCount() || (frameIndex > 0 && mImpl->mTimeStamp[frameIndex - 1] > mImpl->mTimeStamp[frameIndex]))
+ if(DALI_UNLIKELY(!mImpl->mLoadSucceeded))
{
+ DALI_LOG_ERROR("WebP file is still not loaded, this frame interval could not be correct value.\n");
+ }
+ if(frameIndex >= GetImageCount())
+ {
+ DALI_LOG_ERROR("Input frameIndex exceeded frame count of the WebP.");
return 0u;
}
else
{
- if(frameIndex > 0)
+ int32_t interval = 0u;
+ if(GetImageCount() == 1u)
+ {
+ return 0u;
+ }
+ else if(frameIndex + 1 == GetImageCount())
+ {
+ // For the interval between last frame and first frame, use last interval again.
+ interval = mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
+ }
+ else
+ {
+ interval = mImpl->mTimeStamp[frameIndex + 1] - mImpl->mTimeStamp[frameIndex];
+ }
+
+ if(DALI_UNLIKELY(interval < 0))
{
- return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
+ DALI_LOG_ERROR("This interval value is not correct, because the frame still hasn't ever been decoded.");
+ return 0u;
}
- return mImpl->mTimeStamp[frameIndex];
+ return static_cast<uint32_t>(interval);
}
}