X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Fimaging%2Fcommon%2Fwebp-loading.cpp;h=8efdc5ba272df0e15c8a64e8d97a8466ed8442a6;hb=3a81895fada7100ba8f69f934b383b12147e9ad4;hp=bd1014b022ae88aa89d2687a00e9279f0370a045;hpb=0a830ab89adfb35efb0eccddf526d3ece54e166e;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 bd1014b..8efdc5b 100644 --- a/dali/internal/imaging/common/webp-loading.cpp +++ b/dali/internal/imaging/common/webp-loading.cpp @@ -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,6 +40,9 @@ #include #include +// INTERNAL INCLUDES +#include + typedef unsigned char WebPByteType; namespace Dali @@ -53,7 +57,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,30 +66,66 @@ struct WebPLoading::Impl { public: Impl(const std::string& url, bool isLocalResource) - : mUrl(url) + : mUrl(url), + mFrameCount(1u), + mMutex(), + mBuffer(nullptr), + mBufferSize(0u), + mImageSize(), + mLoadSucceeded(false), + mIsLocalResource(isLocalResource) + { + } + + bool LoadWebPInformation() { -#ifdef DALI_WEBP_ENABLED - if(ReadWebPInformation(isLocalResource)) + // mFrameCount will be 1 if the input image is non-animated image or animated image with single frame. + if(ReadWebPInformation()) { + mLoadSucceeded = true; +#ifdef DALI_ANIMATED_WEBP_ENABLED + WebPDataInit(&mWebPData); + mWebPData.size = mBufferSize; + mWebPData.bytes = mBuffer; 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); +#elif DALI_WEBP_AVAILABLE + int32_t imageWidth, imageHeight; + if(WebPGetInfo(mBuffer, mBufferSize, &imageWidth, &imageHeight)) + { + 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 + } + else + { + mFrameCount = 0u; + mLoadSucceeded = false; + DALI_LOG_ERROR("Image loading failed for: \"%s\".\n", mUrl.c_str()); + } + + return mLoadSucceeded; } - bool ReadWebPInformation(bool isLocalResource) + bool ReadWebPInformation() { -#ifdef DALI_WEBP_ENABLED - WebPDataInit(&mWebPData); - if(isLocalResource) + FILE* fp = nullptr; + if(mIsLocalResource) { Internal::Platform::FileReader fileReader(mUrl); - FILE* fp = fileReader.GetFile(); - if(fp == NULL) + fp = fileReader.GetFile(); + if(fp == nullptr) { return false; } @@ -94,17 +135,12 @@ public: return false; } - mWebPData.size = ftell(fp); - if((!fseek(fp, 0, SEEK_SET))) - { - unsigned char* WebPDataBuffer; - WebPDataBuffer = reinterpret_cast(malloc(sizeof(WebPByteType) * mWebPData.size)); - mWebPData.size = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp); - mWebPData.bytes = WebPDataBuffer; - } - else + mBufferSize = ftell(fp); + if(!fseek(fp, 0, SEEK_SET)) { - return false; + mBuffer = reinterpret_cast(malloc(sizeof(WebPByteType) * mBufferSize)); + mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp); + return true; } } else @@ -117,37 +153,49 @@ public: succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE); if(succeeded) { - size_t blobSize = dataBuffer.Size(); - if(blobSize > 0U) + mBufferSize = dataBuffer.Size(); + if(mBufferSize > 0U) { // Open a file handle on the memory buffer: - Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize); - FILE* const fp = fileReader.GetFile(); - if(NULL != fp) + Internal::Platform::FileReader fileReader(dataBuffer, mBufferSize); + fp = fileReader.GetFile(); + if(fp != nullptr) { - 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 + if(!fseek(fp, 0, SEEK_SET)) { - DALI_LOG_ERROR("Error seeking within file\n"); + mBuffer = reinterpret_cast(malloc(sizeof(WebPByteType) * mBufferSize)); + mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp); + return true; } } - else - { - DALI_LOG_ERROR("Error reading file\n"); - } } } } - return true; -#else return false; + } + + void ReleaseResource() + { +#ifdef DALI_ANIMATED_WEBP_ENABLED + if(&mWebPData != nullptr) + { + mWebPData.bytes = nullptr; + WebPDataInit(&mWebPData); + } + if(mWebPAnimDecoder != nullptr) + { + WebPAnimDecoderDelete(mWebPAnimDecoder); + mWebPAnimDecoder = nullptr; + } #endif + if(mBuffer != nullptr) + { + free((void*)mBuffer); + mBuffer = nullptr; + } + + // Set the flag so that webp information can be reloaded when visual is re-attached to scene. + mLoadSucceeded = false; } // Moveable but not copyable @@ -159,34 +207,32 @@ public: ~Impl() { -#ifdef DALI_WEBP_ENABLED - if(&mWebPData != NULL) - { - free((void*)mWebPData.bytes); - mWebPData.bytes = nullptr; - WebPDataInit(&mWebPData); - } - if(mWebPAnimDecoder) - { - WebPAnimDecoderDelete(mWebPAnimDecoder); - } -#endif + ReleaseResource(); } std::string mUrl; std::vector mTimeStamp; - uint32_t mLoadingFrame{0}; - -#ifdef DALI_WEBP_ENABLED - WebPData mWebPData{0}; - WebPAnimDecoder* mWebPAnimDecoder{nullptr}; - WebPAnimInfo mWebPAnimInfo{0}; + 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 mIsLocalResource; + +#ifdef DALI_ANIMATED_WEBP_ENABLED + WebPData mWebPData{0}; + 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)); @@ -204,134 +250,188 @@ WebPLoading::~WebPLoading() bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector& pixelData) { -#ifdef DALI_WEBP_ENABLED - if(frameStartIndex >= mImpl->mWebPAnimInfo.frame_count) + for(int i = 0; i < count; ++i) { - return false; - } - - DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count); + Dali::Devel::PixelBuffer pixelBuffer = LoadFrame((frameStartIndex + i) % mImpl->mFrameCount); + if(!pixelBuffer) + { + return false; + } - if(mImpl->mLoadingFrame > frameStartIndex) + Dali::PixelData imageData = Devel::PixelBuffer::Convert(pixelBuffer); + pixelData.push_back(imageData); + } + if(pixelData.size() != static_cast(count)) { - mImpl->mLoadingFrame = 0; - WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); + return false; } + return true; +} - for(; mImpl->mLoadingFrame < frameStartIndex; ++mImpl->mLoadingFrame) +Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex) +{ + Dali::Devel::PixelBuffer pixelBuffer; + + // If WebP file is still not loaded, Load the information. + if(!mImpl->mLoadSucceeded) { - uint8_t* frameBuffer; - int timestamp; - WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); - mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp; + if(!mImpl->LoadWebPInformation()) + { + return pixelBuffer; + } } - for(int i = 0; i < count; ++i) + // 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->mFrameCount == 1) { - const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t); - uint8_t* frameBuffer; - int timestamp; - WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); + int32_t width, height; + if(!WebPGetInfo(mImpl->mBuffer, mImpl->mBufferSize, &width, &height)) + { + return pixelBuffer; + } - auto pixelBuffer = new uint8_t[bufferSize]; - memcpy(pixelBuffer, frameBuffer, bufferSize); - mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp; + WebPBitstreamFeatures features; + if(VP8_STATUS_NOT_ENOUGH_DATA == WebPGetFeatures(mImpl->mBuffer, mImpl->mBufferSize, &features)) + { + return pixelBuffer; + } - if(pixelBuffer) + uint32_t channelNumber = (features.has_alpha) ? 4 : 3; + Pixel::Format pixelFormat = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888; + pixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat); + uint8_t* frameBuffer = nullptr; + if(channelNumber == 4) + { + frameBuffer = WebPDecodeRGBA(mImpl->mBuffer, mImpl->mBufferSize, &width, &height); + } + else { - pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY)); + frameBuffer = WebPDecodeRGB(mImpl->mBuffer, mImpl->mBufferSize, &width, &height); } - mImpl->mLoadingFrame++; - if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count) + if(frameBuffer != nullptr) { - mImpl->mLoadingFrame = 0; - WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); + const int32_t imageBufferSize = width * height * sizeof(uint8_t) * channelNumber; + memcpy(pixelBuffer.GetBuffer(), frameBuffer, imageBufferSize); + free((void*)frameBuffer); } - } + mImpl->ReleaseResource(); - return true; -#else - return false; + return pixelBuffer; + } #endif -} -Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex) -{ - Dali::Devel::PixelBuffer pixelBuffer; -#ifdef DALI_WEBP_ENABLED - if(frameIndex >= mImpl->mWebPAnimInfo.frame_count) +#ifdef DALI_ANIMATED_WEBP_ENABLED + Mutex::ScopedLock lock(mImpl->mMutex); + if(frameIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded) { return pixelBuffer; } - DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameIndex:%d )\n", frameIndex); + DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex); - if(mImpl->mLoadingFrame > frameIndex) + if(mImpl->mPreLoadedFrame && mImpl->mLatestLoadedFrame == static_cast(frameIndex)) { - mImpl->mLoadingFrame = 0; - WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); + pixelBuffer = mImpl->mPreLoadedFrame; } - - for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame) + else { - uint8_t* frameBuffer; - int timestamp; - WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); - mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp; + pixelBuffer = DecodeFrame(frameIndex); } + mImpl->mPreLoadedFrame.Reset(); - const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t); - uint8_t* frameBuffer; - int timestamp; - WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); + // 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); + } - 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; +#endif + return pixelBuffer; +} - mImpl->mLoadingFrame++; - if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count) +Dali::Devel::PixelBuffer WebPLoading::DecodeFrame(uint32_t frameIndex) +{ + Dali::Devel::PixelBuffer pixelBuffer; +#ifdef DALI_ANIMATED_WEBP_ENABLED + if(mImpl->mLatestLoadedFrame >= static_cast(frameIndex)) { - mImpl->mLoadingFrame = 0; + mImpl->mLatestLoadedFrame = INITIAL_INDEX; WebPAnimDecoderReset(mImpl->mWebPAnimDecoder); } + + int32_t timestamp; + uint8_t* frameBuffer = nullptr; + for(; mImpl->mLatestLoadedFrame < static_cast(frameIndex);) + { + WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp); + mImpl->mTimeStamp[++mImpl->mLatestLoadedFrame] = timestamp; + } + + if(frameBuffer != nullptr) + { + 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(!mImpl->mLoadSucceeded) + { + DALI_LOG_ERROR("WebP file is still not loaded, this image size could not be correct value.\n"); + } + return mImpl->mImageSize; } uint32_t WebPLoading::GetImageCount() const { -#ifdef DALI_WEBP_ENABLED - return mImpl->mWebPAnimInfo.frame_count; -#else - return 0u; -#endif + if(!mImpl->mLoadSucceeded) + { + DALI_LOG_ERROR("WebP file is still not loaded, this image count could not be correct value.\n"); + } + 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(!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(interval < 0) { - return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1]; + return 0u; } - return mImpl->mTimeStamp[frameIndex]; + return static_cast(interval); } } @@ -340,6 +440,11 @@ std::string WebPLoading::GetUrl() const return mImpl->mUrl; } +bool WebPLoading::HasLoadingSucceeded() const +{ + return mImpl->mLoadSucceeded; +} + } // namespace Adaptor } // namespace Internal