X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Fimaging%2Fcommon%2Fwebp-loading.cpp;h=61baf2af78967a30b9d8963c409dead5d534d50d;hb=08e5bf588392bd72b6f18fa1008190cf8ceedc16;hp=2068240844231f21fa815146077f8898df8f9ae8;hpb=a789ecd1b77400851e12bd8a68fc90670b15fc78;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git diff --git a/dali/internal/imaging/common/webp-loading.cpp b/dali/internal/imaging/common/webp-loading.cpp index 2068240..61baf2a 100644 --- a/dali/internal/imaging/common/webp-loading.cpp +++ b/dali/internal/imaging/common/webp-loading.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -24,13 +24,14 @@ #include #if WEBP_DEMUX_ABI_VERSION > 0x0101 -#define DALI_WEBP_ENABLED 1 +#define DALI_ANIMATED_WEBP_ENABLED 1 #endif #endif #include #include +#include #include #include #include @@ -39,7 +40,11 @@ #include #include -typedef unsigned char WebPByteType; +// INTERNAL INCLUDES +#include +#include + +typedef uint8_t WebPByteType; namespace Dali { @@ -53,7 +58,8 @@ namespace 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 @@ -61,98 +67,178 @@ struct WebPLoading::Impl { public: Impl(const std::string& url, bool isLocalResource) - : mUrl(url), - mLoadSucceeded(true) + : mFile(nullptr), + mUrl(url), + mFrameCount(1u), + mMutex(), + mBuffer(nullptr), + mBufferSize(0u), + mImageSize(), + mLoadSucceeded(false), + mIsAnimatedImage(false), + mIsLocalResource(isLocalResource) + { + } + + Impl(FILE* const fp) + : mFile(fp), + mUrl(), + mFrameCount(1u), + mMutex(), + mBuffer(nullptr), + mBufferSize(0u), + mImageSize(), + mLoadSucceeded(false), + mIsAnimatedImage(false), + mIsLocalResource(true) + { + } + + bool LoadWebPInformation() { -#ifdef DALI_WEBP_ENABLED - if(ReadWebPInformation(isLocalResource)) + // Block to do not load this file again. + Mutex::ScopedLock lock(mMutex); + if(DALI_UNLIKELY(mLoadSucceeded)) { - WebPAnimDecoderOptions webPAnimDecoderOptions; - WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions); - webPAnimDecoderOptions.color_mode = MODE_RGBA; - mWebPAnimDecoder = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions); - WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo); - mTimeStamp.assign(mWebPAnimInfo.frame_count, 0); - } - else - { - mLoadSucceeded = false; + return mLoadSucceeded; } + +#ifndef DALI_WEBP_AVAILABLE + // If the system doesn't support webp, loading will be failed. + mFrameCount = 0u; + mLoadSucceeded = false; + return mLoadSucceeded; #endif - } - bool ReadWebPInformation(bool isLocalResource) - { -#ifdef DALI_WEBP_ENABLED - WebPDataInit(&mWebPData); - if(isLocalResource) + // mFrameCount will be 1 if the input image is non-animated image or animated image with single frame. + if(DALI_LIKELY(ReadWebPInformation())) { - Internal::Platform::FileReader fileReader(mUrl); - FILE* fp = fileReader.GetFile(); - if(fp == NULL) - { - return false; - } +#ifdef DALI_WEBP_AVAILABLE + WebPDataInit(&mWebPData); + mWebPData.size = mBufferSize; + mWebPData.bytes = mBuffer; - if(fseek(fp, 0, SEEK_END) <= -1) + WebPDemuxer* demuxer = WebPDemux(&mWebPData); + uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS); + if(flags & ANIMATION_FLAG) { - return false; + mIsAnimatedImage = true; } - mWebPData.size = ftell(fp); - if((!fseek(fp, 0, SEEK_SET))) + if(!mIsAnimatedImage) { - unsigned char* WebPDataBuffer; - WebPDataBuffer = reinterpret_cast(malloc(sizeof(WebPByteType) * mWebPData.size)); - mWebPData.size = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp); - mWebPData.bytes = WebPDataBuffer; + int32_t imageWidth, imageHeight; + if(WebPGetInfo(mBuffer, mBufferSize, &imageWidth, &imageHeight)) + { + mImageSize = ImageDimensions(imageWidth, imageHeight); + } } - else +#endif +#ifdef DALI_ANIMATED_WEBP_ENABLED + if(mIsAnimatedImage) { - return false; + WebPAnimDecoderOptions webPAnimDecoderOptions; + WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions); + webPAnimDecoderOptions.color_mode = MODE_RGBA; + mWebPAnimDecoder = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions); + WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo); + mTimeStamp.assign(mWebPAnimInfo.frame_count, 0); + mFrameCount = mWebPAnimInfo.frame_count; + mImageSize = ImageDimensions(mWebPAnimInfo.canvas_width, mWebPAnimInfo.canvas_height); } +#endif + mLoadSucceeded = true; } else { - // remote file - bool succeeded; - Dali::Vector dataBuffer; - size_t dataSize; + mFrameCount = 0u; + mLoadSucceeded = false; + DALI_LOG_ERROR("Image loading failed for: \"%s\".\n", mUrl.c_str()); + } + + return mLoadSucceeded; + } + + bool ReadWebPInformation() + { + mBufferSize = 0; - succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE); - if(succeeded) + FILE* fp = mFile; + std::unique_ptr fileReader; + Dali::Vector dataBuffer; + if(fp == nullptr) + { + if(mIsLocalResource) + { + fileReader = std::make_unique(mUrl); + } + else { - size_t blobSize = dataBuffer.Size(); - if(blobSize > 0U) + size_t dataSize; + if(DALI_LIKELY(TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE))) { - // Open a file handle on the memory buffer: - Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize); - FILE* const fp = fileReader.GetFile(); - if(NULL != fp) + mBufferSize = dataBuffer.Size(); + if(DALI_LIKELY(mBufferSize > 0U)) { - if((!fseek(fp, 0, SEEK_SET))) - { - unsigned char* WebPDataBuffer; - WebPDataBuffer = reinterpret_cast(malloc(sizeof(WebPByteType) * blobSize)); - mWebPData.size = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp); - mWebPData.bytes = WebPDataBuffer; - } - else - { - DALI_LOG_ERROR("Error seeking within file\n"); - } - } - else - { - DALI_LOG_ERROR("Error reading file\n"); + // Open a file handle on the memory buffer: + fileReader = std::make_unique(dataBuffer, mBufferSize); } } } + + if(fileReader) + { + fp = fileReader->GetFile(); + } + } + + if(DALI_LIKELY(fp != nullptr)) + { + if(mBufferSize == 0) + { + if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END) <= -1)) + { + return false; + } + mBufferSize = ftell(fp); + } + + if(DALI_LIKELY(!fseek(fp, 0, SEEK_SET))) + { + mBuffer = reinterpret_cast(malloc(sizeof(WebPByteType) * mBufferSize)); + mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp); + return true; + } } - return true; -#else return false; + } + + void ReleaseResource() + { +#ifdef DALI_WEBP_AVAILABLE + if(&mWebPData != nullptr) + { + mWebPData.bytes = nullptr; + WebPDataInit(&mWebPData); + } #endif +#ifdef DALI_ANIMATED_WEBP_ENABLED + if(mIsAnimatedImage) + { + if(mWebPAnimDecoder != nullptr) + { + WebPAnimDecoderDelete(mWebPAnimDecoder); + mWebPAnimDecoder = nullptr; + } + } +#endif + if(mBuffer != nullptr) + { + free((void*)mBuffer); + mBuffer = nullptr; + } + + mLoadSucceeded = false; } // Moveable but not copyable @@ -164,181 +250,230 @@ public: ~Impl() { -#ifdef DALI_WEBP_ENABLED - if(&mWebPData != NULL) - { - free((void*)mWebPData.bytes); - mWebPData.bytes = nullptr; - WebPDataInit(&mWebPData); - } - if(mWebPAnimDecoder) - { - WebPAnimDecoderDelete(mWebPAnimDecoder); - } -#endif + ReleaseResource(); } + FILE* mFile; std::string mUrl; std::vector mTimeStamp; - uint32_t mLoadingFrame{0}; - bool mLoadSucceeded; + int32_t mLatestLoadedFrame{INITIAL_INDEX}; + uint32_t mFrameCount; + Mutex mMutex; + // For the case the system doesn't support DALI_ANIMATED_WEBP_ENABLED + unsigned char* mBuffer; + uint32_t mBufferSize; + ImageDimensions mImageSize; + bool mLoadSucceeded; + bool mIsAnimatedImage; + bool mIsLocalResource; + +#ifdef DALI_WEBP_AVAILABLE + WebPData mWebPData{0}; +#endif -#ifdef DALI_WEBP_ENABLED - WebPData mWebPData{0}; - WebPAnimDecoder* mWebPAnimDecoder{nullptr}; - WebPAnimInfo mWebPAnimInfo{0}; +#ifdef DALI_ANIMATED_WEBP_ENABLED + WebPAnimDecoder* mWebPAnimDecoder{nullptr}; + WebPAnimInfo mWebPAnimInfo{0}; + Dali::Devel::PixelBuffer mPreLoadedFrame{}; #endif }; AnimatedImageLoadingPtr WebPLoading::New(const std::string& url, bool isLocalResource) { -#ifndef DALI_WEBP_ENABLED +#ifndef DALI_ANIMATED_WEBP_ENABLED DALI_LOG_ERROR("The system does not support Animated WebP format.\n"); #endif return AnimatedImageLoadingPtr(new WebPLoading(url, isLocalResource)); } +AnimatedImageLoadingPtr WebPLoading::New(FILE* const fp) +{ +#ifndef DALI_ANIMATED_WEBP_ENABLED + DALI_LOG_ERROR("The system does not support Animated WebP format.\n"); +#endif + return AnimatedImageLoadingPtr(new WebPLoading(fp)); +} + WebPLoading::WebPLoading(const std::string& url, bool isLocalResource) : mImpl(new WebPLoading::Impl(url, isLocalResource)) { } +WebPLoading::WebPLoading(FILE* const fp) +: mImpl(new WebPLoading::Impl(fp)) +{ +} + WebPLoading::~WebPLoading() { delete mImpl; } -bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector& pixelData) +Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex) { -#ifdef DALI_WEBP_ENABLED - if(frameStartIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded) - { - return false; - } - - DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count); + Dali::Devel::PixelBuffer pixelBuffer; - if(mImpl->mLoadingFrame > frameStartIndex) + // If WebP file is still not loaded, Load the information. + if(DALI_UNLIKELY(!mImpl->mLoadSucceeded)) { - mImpl->mLoadingFrame = 0; - WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); + if(DALI_UNLIKELY(!mImpl->LoadWebPInformation())) + { + mImpl->ReleaseResource(); + return pixelBuffer; + } } - for(; mImpl->mLoadingFrame < frameStartIndex; ++mImpl->mLoadingFrame) + // 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 + if(!mImpl->mIsAnimatedImage) { - uint8_t* frameBuffer; - int timestamp; - WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); - mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp; + int32_t width, height; + if(DALI_LIKELY(WebPGetInfo(mImpl->mBuffer, mImpl->mBufferSize, &width, &height))) + { + WebPBitstreamFeatures features; + if(DALI_LIKELY(VP8_STATUS_NOT_ENOUGH_DATA != WebPGetFeatures(mImpl->mBuffer, mImpl->mBufferSize, &features))) + { + uint32_t channelNumber = (features.has_alpha) ? 4 : 3; + uint8_t* frameBuffer = nullptr; + if(channelNumber == 4) + { + frameBuffer = WebPDecodeRGBA(mImpl->mBuffer, mImpl->mBufferSize, &width, &height); + } + else + { + frameBuffer = WebPDecodeRGB(mImpl->mBuffer, mImpl->mBufferSize, &width, &height); + } + + if(frameBuffer != nullptr) + { + Pixel::Format pixelFormat = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888; + int32_t bufferSize = width * height * Dali::Pixel::GetBytesPerPixel(pixelFormat); + Internal::Adaptor::PixelBufferPtr internal = + Internal::Adaptor::PixelBuffer::New(frameBuffer, bufferSize, width, height, width, pixelFormat); + pixelBuffer = Devel::PixelBuffer(internal.Get()); + } + } + } + // The single frame resource should be released after loading. + mImpl->ReleaseResource(); } +#endif - for(int i = 0; i < count; ++i) +#ifdef DALI_ANIMATED_WEBP_ENABLED + if(mImpl->mIsAnimatedImage && mImpl->mBuffer != nullptr) { - const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t); - uint8_t* frameBuffer; - int timestamp; - WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); + Mutex::ScopedLock lock(mImpl->mMutex); + if(DALI_LIKELY(frameIndex < mImpl->mWebPAnimInfo.frame_count && mImpl->mLoadSucceeded)) + { + DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex); - auto pixelBuffer = new uint8_t[bufferSize]; - memcpy(pixelBuffer, frameBuffer, bufferSize); - mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp; + if(mImpl->mPreLoadedFrame && mImpl->mLatestLoadedFrame == static_cast(frameIndex)) + { + pixelBuffer = mImpl->mPreLoadedFrame; + } + else + { + pixelBuffer = DecodeFrame(frameIndex); + } + mImpl->mPreLoadedFrame.Reset(); - if(pixelBuffer) - { - pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY)); + // 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->mPreLoadedFrame = DecodeFrame(frameIndex + 1); + } } - - mImpl->mLoadingFrame++; - if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count) + else { - mImpl->mLoadingFrame = 0; - WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); + mImpl->ReleaseResource(); } } - - return true; -#else - return false; #endif + return pixelBuffer; } -Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex) +Dali::Devel::PixelBuffer WebPLoading::DecodeFrame(uint32_t frameIndex) { Dali::Devel::PixelBuffer pixelBuffer; - -#ifdef DALI_WEBP_ENABLED - if(frameIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded) +#ifdef DALI_ANIMATED_WEBP_ENABLED + if(mImpl->mLatestLoadedFrame > static_cast(frameIndex)) { - return pixelBuffer; - } - - DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex); - - if(mImpl->mLoadingFrame > frameIndex) - { - mImpl->mLoadingFrame = 0; + mImpl->mLatestLoadedFrame = INITIAL_INDEX; WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); } - for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame) + uint8_t* frameBuffer = nullptr; + int32_t timestamp = 0u; + for(; mImpl->mLatestLoadedFrame < static_cast(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) + if(frameBuffer != nullptr) { - mImpl->mLoadingFrame = 0; - WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); + const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t); + pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888); + memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize); } + #endif return pixelBuffer; } ImageDimensions WebPLoading::GetImageSize() const { -#ifdef DALI_WEBP_ENABLED - return ImageDimensions(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height); -#else - return ImageDimensions(); -#endif + if(DALI_UNLIKELY(!mImpl->mLoadSucceeded)) + { + mImpl->LoadWebPInformation(); + } + return mImpl->mImageSize; } uint32_t WebPLoading::GetImageCount() const { -#ifdef DALI_WEBP_ENABLED - return mImpl->mWebPAnimInfo.frame_count; -#else - return 0u; -#endif + 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(interval); } }