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_ANIMATED_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>
44 #include <dali/devel-api/adaptor-framework/image-loading.h>
46 typedef unsigned char WebPByteType;
56 #if defined(DEBUG_ENABLED)
57 Debug::Filter* gWebPLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GIF_LOADING");
60 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
64 struct WebPLoading::Impl
67 Impl(const std::string& url, bool isLocalResource)
76 // mFrameCount will be 1 if the input image is non-animated image or animated image with single frame.
77 if(ReadWebPInformation(isLocalResource))
79 #ifdef DALI_ANIMATED_WEBP_ENABLED
80 WebPDataInit(&mWebPData);
81 mWebPData.size = mBufferSize;
82 mWebPData.bytes = mBuffer;
83 WebPAnimDecoderOptions webPAnimDecoderOptions;
84 WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
85 webPAnimDecoderOptions.color_mode = MODE_RGBA;
86 mWebPAnimDecoder = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions);
87 WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo);
88 mTimeStamp.assign(mWebPAnimInfo.frame_count, 0);
89 mFrameCount = mWebPAnimInfo.frame_count;
90 mImageSize = ImageDimensions(mWebPAnimInfo.canvas_width, mWebPAnimInfo.canvas_height);
91 #elif DALI_WEBP_AVAILABLE
92 int32_t imageWidth, imageHeight;
93 if(WebPGetInfo(mBuffer, mBufferSize, &imageWidth, &imageHeight))
95 mImageSize = ImageDimensions(imageWidth, imageHeight);
98 #ifndef DALI_WEBP_AVAILABLE
99 // If the system doesn't support webp, loading will be failed.
101 mLoadSucceeded = false;
107 mLoadSucceeded = false;
108 DALI_LOG_ERROR("Image loading failed for: \"%s\".\n", mUrl.c_str());
112 bool ReadWebPInformation(bool isLocalResource)
117 Internal::Platform::FileReader fileReader(mUrl);
118 fp = fileReader.GetFile();
124 if(fseek(fp, 0, SEEK_END) <= -1)
129 mBufferSize = ftell(fp);
130 if(!fseek(fp, 0, SEEK_SET))
132 mBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mBufferSize));
133 mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp);
141 Dali::Vector<uint8_t> dataBuffer;
144 succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
147 mBufferSize = dataBuffer.Size();
150 // Open a file handle on the memory buffer:
151 Internal::Platform::FileReader fileReader(dataBuffer, mBufferSize);
152 fp = fileReader.GetFile();
155 if(!fseek(fp, 0, SEEK_SET))
157 mBuffer = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mBufferSize));
158 mBufferSize = fread(mBuffer, sizeof(WebPByteType), mBufferSize, fp);
168 void ReleaseResource()
170 #ifdef DALI_ANIMATED_WEBP_ENABLED
171 if(&mWebPData != nullptr)
173 mWebPData.bytes = nullptr;
174 WebPDataInit(&mWebPData);
176 if(mWebPAnimDecoder != nullptr)
178 WebPAnimDecoderDelete(mWebPAnimDecoder);
179 mWebPAnimDecoder = nullptr;
182 if(mBuffer != nullptr)
184 free((void*)mBuffer);
189 // Moveable but not copyable
191 Impl(const Impl&) = delete;
192 Impl& operator=(const Impl&) = delete;
193 Impl(Impl&&) = default;
194 Impl& operator=(Impl&&) = default;
202 std::vector<uint32_t> mTimeStamp;
203 uint32_t mLoadingFrame{0};
204 uint32_t mFrameCount;
206 // For the case the system doesn't support DALI_ANIMATED_WEBP_ENABLED
207 unsigned char* mBuffer;
208 uint32_t mBufferSize;
209 ImageDimensions mImageSize;
212 #ifdef DALI_ANIMATED_WEBP_ENABLED
213 WebPData mWebPData{0};
214 WebPAnimDecoder* mWebPAnimDecoder{nullptr};
215 WebPAnimInfo mWebPAnimInfo{0};
219 AnimatedImageLoadingPtr WebPLoading::New(const std::string& url, bool isLocalResource)
221 #ifndef DALI_ANIMATED_WEBP_ENABLED
222 DALI_LOG_ERROR("The system does not support Animated WebP format.\n");
224 return AnimatedImageLoadingPtr(new WebPLoading(url, isLocalResource));
227 WebPLoading::WebPLoading(const std::string& url, bool isLocalResource)
228 : mImpl(new WebPLoading::Impl(url, isLocalResource))
232 WebPLoading::~WebPLoading()
237 bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
239 for(int i = 0; i < count; ++i)
241 Dali::Devel::PixelBuffer pixelBuffer = LoadFrame((frameStartIndex + i) % mImpl->mFrameCount);
242 Dali::PixelData imageData = Devel::PixelBuffer::Convert(pixelBuffer);
243 pixelData.push_back(imageData);
245 if(pixelData.size() != static_cast<uint32_t>(count))
252 Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex)
254 Dali::Devel::PixelBuffer pixelBuffer;
256 // WebPDecodeRGBA is faster than to use demux API for loading non-animated image.
257 // If frame count is 1, use WebPDecodeRGBA api.
258 #ifdef DALI_WEBP_AVAILABLE
259 if(mImpl->mFrameCount == 1)
261 int32_t width, height;
262 if(!WebPGetInfo(mImpl->mBuffer, mImpl->mBufferSize, &width, &height))
267 WebPBitstreamFeatures features;
268 if(VP8_STATUS_NOT_ENOUGH_DATA == WebPGetFeatures(mImpl->mBuffer, mImpl->mBufferSize, &features))
273 uint32_t channelNumber = (features.has_alpha) ? 4 : 3;
274 Pixel::Format pixelFormat = (channelNumber == 4) ? Pixel::RGBA8888 : Pixel::RGB888;
275 pixelBuffer = Dali::Devel::PixelBuffer::New(width, height, pixelFormat);
276 uint8_t* frameBuffer = nullptr;
277 if(channelNumber == 4)
279 frameBuffer = WebPDecodeRGBA(mImpl->mBuffer, mImpl->mBufferSize, &width, &height);
283 frameBuffer = WebPDecodeRGB(mImpl->mBuffer, mImpl->mBufferSize, &width, &height);
286 if(frameBuffer != nullptr)
288 const int32_t imageBufferSize = width * height * sizeof(uint8_t) * channelNumber;
289 memcpy(pixelBuffer.GetBuffer(), frameBuffer, imageBufferSize);
290 free((void*)frameBuffer);
292 mImpl->ReleaseResource();
297 #ifdef DALI_ANIMATED_WEBP_ENABLED
298 Mutex::ScopedLock lock(mImpl->mMutex);
299 if(frameIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded)
304 DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
306 if(mImpl->mLoadingFrame > frameIndex)
308 mImpl->mLoadingFrame = 0;
309 WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
312 for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame)
314 uint8_t* frameBuffer;
316 WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
317 mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
320 const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
321 uint8_t* frameBuffer;
323 WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, ×tamp);
325 pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
326 memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize);
327 mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
329 mImpl->mLoadingFrame++;
330 if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
332 mImpl->mLoadingFrame = 0;
333 WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
339 ImageDimensions WebPLoading::GetImageSize() const
341 return mImpl->mImageSize;
344 uint32_t WebPLoading::GetImageCount() const
346 return mImpl->mFrameCount;
349 uint32_t WebPLoading::GetFrameInterval(uint32_t frameIndex) const
351 // If frameIndex is above the value of ImageCount or current frame is not loading yet, return 0u.
352 if(frameIndex >= GetImageCount() || (frameIndex > 0 && mImpl->mTimeStamp[frameIndex - 1] > mImpl->mTimeStamp[frameIndex]))
360 return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
362 return mImpl->mTimeStamp[frameIndex];
366 std::string WebPLoading::GetUrl() const
371 bool WebPLoading::HasLoadingSucceeded() const
373 return mImpl->mLoadSucceeded;
376 } // namespace Adaptor
378 } // namespace Internal