Merge branch 'devel/master' into tizen
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / webp-loading.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/imaging/common/webp-loading.h>
20
21 // EXTERNAL INCLUDES
22 #ifdef DALI_WEBP_AVAILABLE
23 #include <webp/decode.h>
24 #include <webp/demux.h>
25
26 #if WEBP_DEMUX_ABI_VERSION > 0x0101
27 #define DALI_WEBP_ENABLED 1
28 #endif
29
30 #endif
31 #include <dali/integration-api/debug.h>
32 #include <dali/public-api/images/pixel-data.h>
33
34 #include <dali/internal/imaging/common/file-download.h>
35 #include <dali/internal/system/common/file-reader.h>
36 #include <fcntl.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <cstring>
41
42 typedef unsigned char WebPByteType;
43
44 namespace Dali
45 {
46 namespace Internal
47 {
48 namespace Adaptor
49 {
50 namespace
51 {
52 #if defined(DEBUG_ENABLED)
53 Debug::Filter* gWebPLoadingLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_GIF_LOADING");
54 #endif
55
56 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE = 50 * 1024 * 1024;
57
58 } // namespace
59
60 struct WebPLoading::Impl
61 {
62 public:
63   Impl(const std::string& url, bool isLocalResource)
64   : mUrl(url)
65   {
66 #ifdef DALI_WEBP_ENABLED
67     if(ReadWebPInformation(isLocalResource))
68     {
69       WebPAnimDecoderOptions webPAnimDecoderOptions;
70       WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
71       webPAnimDecoderOptions.color_mode = MODE_RGBA;
72       mWebPAnimDecoder                  = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions);
73       WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo);
74       mTimeStamp.assign(mWebPAnimInfo.frame_count, 0);
75     }
76 #endif
77   }
78
79   bool ReadWebPInformation(bool isLocalResource)
80   {
81 #ifdef DALI_WEBP_ENABLED
82     WebPDataInit(&mWebPData);
83     if(isLocalResource)
84     {
85       Internal::Platform::FileReader fileReader(mUrl);
86       FILE*                          fp = fileReader.GetFile();
87       if(fp == NULL)
88       {
89         return false;
90       }
91
92       if(fseek(fp, 0, SEEK_END) <= -1)
93       {
94         return false;
95       }
96
97       mWebPData.size = ftell(fp);
98       if((!fseek(fp, 0, SEEK_SET)))
99       {
100         unsigned char* WebPDataBuffer;
101         WebPDataBuffer  = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mWebPData.size));
102         mWebPData.size  = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp);
103         mWebPData.bytes = WebPDataBuffer;
104       }
105       else
106       {
107         return false;
108       }
109     }
110     else
111     {
112       // remote file
113       bool                  succeeded;
114       Dali::Vector<uint8_t> dataBuffer;
115       size_t                dataSize;
116
117       succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
118       if(succeeded)
119       {
120         size_t blobSize = dataBuffer.Size();
121         if(blobSize > 0U)
122         {
123           // Open a file handle on the memory buffer:
124           Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize);
125           FILE* const                          fp = fileReader.GetFile();
126           if(NULL != fp)
127           {
128             if((!fseek(fp, 0, SEEK_SET)))
129             {
130               unsigned char* WebPDataBuffer;
131               WebPDataBuffer  = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * blobSize));
132               mWebPData.size  = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp);
133               mWebPData.bytes = WebPDataBuffer;
134             }
135             else
136             {
137               DALI_LOG_ERROR("Error seeking within file\n");
138             }
139           }
140           else
141           {
142             DALI_LOG_ERROR("Error reading file\n");
143           }
144         }
145       }
146     }
147     return true;
148 #else
149     return false;
150 #endif
151   }
152
153   // Moveable but not copyable
154
155   Impl(const Impl&) = delete;
156   Impl& operator=(const Impl&) = delete;
157   Impl(Impl&&)                 = default;
158   Impl& operator=(Impl&&) = default;
159
160   ~Impl()
161   {
162 #ifdef DALI_WEBP_ENABLED
163     if(&mWebPData != NULL)
164     {
165       free((void*)mWebPData.bytes);
166       mWebPData.bytes = nullptr;
167       WebPDataInit(&mWebPData);
168     }
169     if(mWebPAnimDecoder)
170     {
171       WebPAnimDecoderDelete(mWebPAnimDecoder);
172     }
173 #endif
174   }
175
176   std::string           mUrl;
177   std::vector<uint32_t> mTimeStamp;
178   uint32_t              mLoadingFrame{0};
179
180 #ifdef DALI_WEBP_ENABLED
181   WebPData         mWebPData{0};
182   WebPAnimDecoder* mWebPAnimDecoder{nullptr};
183   WebPAnimInfo     mWebPAnimInfo{0};
184 #endif
185 };
186
187 AnimatedImageLoadingPtr WebPLoading::New(const std::string& url, bool isLocalResource)
188 {
189 #ifndef DALI_WEBP_ENABLED
190   DALI_LOG_ERROR("The system does not support Animated WebP format.\n");
191 #endif
192   return AnimatedImageLoadingPtr(new WebPLoading(url, isLocalResource));
193 }
194
195 WebPLoading::WebPLoading(const std::string& url, bool isLocalResource)
196 : mImpl(new WebPLoading::Impl(url, isLocalResource))
197 {
198 }
199
200 WebPLoading::~WebPLoading()
201 {
202   delete mImpl;
203 }
204
205 bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
206 {
207 #ifdef DALI_WEBP_ENABLED
208   if(frameStartIndex >= mImpl->mWebPAnimInfo.frame_count)
209   {
210     return false;
211   }
212
213   DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
214
215   if(mImpl->mLoadingFrame > frameStartIndex)
216   {
217     mImpl->mLoadingFrame = 0;
218     WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
219   }
220
221   for(; mImpl->mLoadingFrame < frameStartIndex; ++mImpl->mLoadingFrame)
222   {
223     uint8_t* frameBuffer;
224     int      timestamp;
225     WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
226     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
227   }
228
229   for(int i = 0; i < count; ++i)
230   {
231     const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
232     uint8_t*  frameBuffer;
233     int       timestamp;
234     WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
235
236     auto pixelBuffer = new uint8_t[bufferSize];
237     memcpy(pixelBuffer, frameBuffer, bufferSize);
238     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
239
240     if(pixelBuffer)
241     {
242       pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY));
243     }
244
245     mImpl->mLoadingFrame++;
246     if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
247     {
248       mImpl->mLoadingFrame = 0;
249       WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
250     }
251   }
252
253   return true;
254 #else
255   return false;
256 #endif
257 }
258
259 Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex)
260 {
261   Dali::Devel::PixelBuffer pixelBuffer;
262 #ifdef DALI_WEBP_ENABLED
263   if(frameIndex >= mImpl->mWebPAnimInfo.frame_count)
264   {
265     return pixelBuffer;
266   }
267
268   DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameIndex:%d )\n", frameIndex);
269
270   if(mImpl->mLoadingFrame > frameIndex)
271   {
272     mImpl->mLoadingFrame = 0;
273     WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
274   }
275
276   for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame)
277   {
278     uint8_t* frameBuffer;
279     int      timestamp;
280     WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
281     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
282   }
283
284   const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
285   uint8_t*  frameBuffer;
286   int       timestamp;
287   WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
288
289   pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
290   memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize);
291   mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
292
293   mImpl->mLoadingFrame++;
294   if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
295   {
296     mImpl->mLoadingFrame = 0;
297     WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
298   }
299 #endif
300   return pixelBuffer;
301 }
302
303 ImageDimensions WebPLoading::GetImageSize() const
304 {
305 #ifdef DALI_WEBP_ENABLED
306   return ImageDimensions(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height);
307 #else
308   return ImageDimensions();
309 #endif
310 }
311
312 uint32_t WebPLoading::GetImageCount() const
313 {
314 #ifdef DALI_WEBP_ENABLED
315   return mImpl->mWebPAnimInfo.frame_count;
316 #else
317   return 0u;
318 #endif
319 }
320
321 uint32_t WebPLoading::GetFrameInterval(uint32_t frameIndex) const
322 {
323   // If frameIndex is above the value of ImageCount or current frame is not loading yet, return 0u.
324   if(frameIndex >= GetImageCount() || (frameIndex > 0 && mImpl->mTimeStamp[frameIndex - 1] > mImpl->mTimeStamp[frameIndex]))
325   {
326     return 0u;
327   }
328   else
329   {
330     if(frameIndex > 0)
331     {
332       return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
333     }
334     return mImpl->mTimeStamp[frameIndex];
335   }
336 }
337
338 std::string WebPLoading::GetUrl() const
339 {
340   return mImpl->mUrl;
341 }
342
343 } // namespace Adaptor
344
345 } // namespace Internal
346
347 } // namespace Dali