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.
19 #include <dali/internal/imaging/common/webp-loading.h>
22 #ifdef DALI_WEBP_AVAILABLE
23 #include <webp/decode.h>
24 #include <webp/demux.h>
26 #if WEBP_DEMUX_ABI_VERSION > 0x0101
27 #define DALI_WEBP_ENABLED 1
31 #include <dali/integration-api/debug.h>
32 #include <dali/public-api/images/pixel-data.h>
34 #include <dali/devel-api/threading/mutex.h>
35 #include <dali/internal/imaging/common/file-download.h>
36 #include <dali/internal/system/common/file-reader.h>
39 #include <sys/types.h>
43 typedef unsigned char WebPByteType;
53 #if defined(DEBUG_ENABLED)
54 Debug::Filter* gWebPLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GIF_LOADING");
57 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
61 struct WebPLoading::Impl
64 Impl(const std::string& url, bool isLocalResource)
69 #ifdef DALI_WEBP_ENABLED
70 if(ReadWebPInformation(isLocalResource))
72 WebPAnimDecoderOptions webPAnimDecoderOptions;
73 WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
74 webPAnimDecoderOptions.color_mode = MODE_RGBA;
75 mWebPAnimDecoder = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions);
76 WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo);
77 mTimeStamp.assign(mWebPAnimInfo.frame_count, 0);
81 mLoadSucceeded = false;
86 bool ReadWebPInformation(bool isLocalResource)
88 #ifdef DALI_WEBP_ENABLED
89 WebPDataInit(&mWebPData);
92 Internal::Platform::FileReader fileReader(mUrl);
93 FILE* fp = fileReader.GetFile();
99 if(fseek(fp, 0, SEEK_END) <= -1)
104 mWebPData.size = ftell(fp);
105 if((!fseek(fp, 0, SEEK_SET)))
107 unsigned char* WebPDataBuffer;
108 WebPDataBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mWebPData.size));
109 mWebPData.size = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp);
110 mWebPData.bytes = WebPDataBuffer;
121 Dali::Vector<uint8_t> dataBuffer;
124 succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
127 size_t blobSize = dataBuffer.Size();
130 // Open a file handle on the memory buffer:
131 Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize);
132 FILE* const fp = fileReader.GetFile();
135 if((!fseek(fp, 0, SEEK_SET)))
137 unsigned char* WebPDataBuffer;
138 WebPDataBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * blobSize));
139 mWebPData.size = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp);
140 mWebPData.bytes = WebPDataBuffer;
144 DALI_LOG_ERROR("Error seeking within file\n");
149 DALI_LOG_ERROR("Error reading file\n");
160 // Moveable but not copyable
162 Impl(const Impl&) = delete;
163 Impl& operator=(const Impl&) = delete;
164 Impl(Impl&&) = default;
165 Impl& operator=(Impl&&) = default;
169 #ifdef DALI_WEBP_ENABLED
170 if(&mWebPData != NULL)
172 free((void*)mWebPData.bytes);
173 mWebPData.bytes = nullptr;
174 WebPDataInit(&mWebPData);
178 WebPAnimDecoderDelete(mWebPAnimDecoder);
184 std::vector<uint32_t> mTimeStamp;
185 uint32_t mLoadingFrame{0};
189 #ifdef DALI_WEBP_ENABLED
190 WebPData mWebPData{0};
191 WebPAnimDecoder* mWebPAnimDecoder{nullptr};
192 WebPAnimInfo mWebPAnimInfo{0};
196 AnimatedImageLoadingPtr WebPLoading::New(const std::string& url, bool isLocalResource)
198 #ifndef DALI_WEBP_ENABLED
199 DALI_LOG_ERROR("The system does not support Animated WebP format.\n");
201 return AnimatedImageLoadingPtr(new WebPLoading(url, isLocalResource));
204 WebPLoading::WebPLoading(const std::string& url, bool isLocalResource)
205 : mImpl(new WebPLoading::Impl(url, isLocalResource))
209 WebPLoading::~WebPLoading()
214 bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
216 #ifdef DALI_WEBP_ENABLED
217 Mutex::ScopedLock lock(mImpl->mMutex);
218 if(frameStartIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded)
223 DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
225 if(mImpl->mLoadingFrame > frameStartIndex)
227 mImpl->mLoadingFrame = 0;
228 WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
231 for(; mImpl->mLoadingFrame < frameStartIndex; ++mImpl->mLoadingFrame)
233 uint8_t* frameBuffer;
235 WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
236 mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
239 for(int i = 0; i < count; ++i)
241 const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
242 uint8_t* frameBuffer;
244 WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
246 auto pixelBuffer = new uint8_t[bufferSize];
247 memcpy(pixelBuffer, frameBuffer, bufferSize);
248 mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
252 pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY));
255 mImpl->mLoadingFrame++;
256 if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
258 mImpl->mLoadingFrame = 0;
259 WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
269 Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex)
271 Dali::Devel::PixelBuffer pixelBuffer;
273 #ifdef DALI_WEBP_ENABLED
274 Mutex::ScopedLock lock(mImpl->mMutex);
275 if(frameIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded)
280 DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
282 if(mImpl->mLoadingFrame > frameIndex)
284 mImpl->mLoadingFrame = 0;
285 WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
288 for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame)
290 uint8_t* frameBuffer;
292 WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
293 mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
296 const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
297 uint8_t* frameBuffer;
299 WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
301 pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
302 memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize);
303 mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
305 mImpl->mLoadingFrame++;
306 if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
308 mImpl->mLoadingFrame = 0;
309 WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
315 ImageDimensions WebPLoading::GetImageSize() const
317 #ifdef DALI_WEBP_ENABLED
318 return ImageDimensions(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height);
320 return ImageDimensions();
324 uint32_t WebPLoading::GetImageCount() const
326 #ifdef DALI_WEBP_ENABLED
327 return mImpl->mWebPAnimInfo.frame_count;
333 uint32_t WebPLoading::GetFrameInterval(uint32_t frameIndex) const
335 // If frameIndex is above the value of ImageCount or current frame is not loading yet, return 0u.
336 if(frameIndex >= GetImageCount() || (frameIndex > 0 && mImpl->mTimeStamp[frameIndex - 1] > mImpl->mTimeStamp[frameIndex]))
344 return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
346 return mImpl->mTimeStamp[frameIndex];
350 std::string WebPLoading::GetUrl() const
355 bool WebPLoading::HasLoadingSucceeded() const
357 return mImpl->mLoadSucceeded;
360 } // namespace Adaptor
362 } // namespace Internal