Use broken image when animated image loading is failed.
[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     mLoadSucceeded(true)
66   {
67 #ifdef DALI_WEBP_ENABLED
68     if(ReadWebPInformation(isLocalResource))
69     {
70       WebPAnimDecoderOptions webPAnimDecoderOptions;
71       WebPAnimDecoderOptionsInit(&webPAnimDecoderOptions);
72       webPAnimDecoderOptions.color_mode = MODE_RGBA;
73       mWebPAnimDecoder                  = WebPAnimDecoderNew(&mWebPData, &webPAnimDecoderOptions);
74       WebPAnimDecoderGetInfo(mWebPAnimDecoder, &mWebPAnimInfo);
75       mTimeStamp.assign(mWebPAnimInfo.frame_count, 0);
76     }
77     else
78     {
79       mLoadSucceeded = false;
80     }
81 #endif
82   }
83
84   bool ReadWebPInformation(bool isLocalResource)
85   {
86 #ifdef DALI_WEBP_ENABLED
87     WebPDataInit(&mWebPData);
88     if(isLocalResource)
89     {
90       Internal::Platform::FileReader fileReader(mUrl);
91       FILE*                          fp = fileReader.GetFile();
92       if(fp == NULL)
93       {
94         return false;
95       }
96
97       if(fseek(fp, 0, SEEK_END) <= -1)
98       {
99         return false;
100       }
101
102       mWebPData.size = ftell(fp);
103       if((!fseek(fp, 0, SEEK_SET)))
104       {
105         unsigned char* WebPDataBuffer;
106         WebPDataBuffer  = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * mWebPData.size));
107         mWebPData.size  = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp);
108         mWebPData.bytes = WebPDataBuffer;
109       }
110       else
111       {
112         return false;
113       }
114     }
115     else
116     {
117       // remote file
118       bool                  succeeded;
119       Dali::Vector<uint8_t> dataBuffer;
120       size_t                dataSize;
121
122       succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory(mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE);
123       if(succeeded)
124       {
125         size_t blobSize = dataBuffer.Size();
126         if(blobSize > 0U)
127         {
128           // Open a file handle on the memory buffer:
129           Dali::Internal::Platform::FileReader fileReader(dataBuffer, blobSize);
130           FILE* const                          fp = fileReader.GetFile();
131           if(NULL != fp)
132           {
133             if((!fseek(fp, 0, SEEK_SET)))
134             {
135               unsigned char* WebPDataBuffer;
136               WebPDataBuffer  = reinterpret_cast<WebPByteType*>(malloc(sizeof(WebPByteType) * blobSize));
137               mWebPData.size  = fread(WebPDataBuffer, sizeof(WebPByteType), mWebPData.size, fp);
138               mWebPData.bytes = WebPDataBuffer;
139             }
140             else
141             {
142               DALI_LOG_ERROR("Error seeking within file\n");
143             }
144           }
145           else
146           {
147             DALI_LOG_ERROR("Error reading file\n");
148           }
149         }
150       }
151     }
152     return true;
153 #else
154     return false;
155 #endif
156   }
157
158   // Moveable but not copyable
159
160   Impl(const Impl&) = delete;
161   Impl& operator=(const Impl&) = delete;
162   Impl(Impl&&)                 = default;
163   Impl& operator=(Impl&&) = default;
164
165   ~Impl()
166   {
167 #ifdef DALI_WEBP_ENABLED
168     if(&mWebPData != NULL)
169     {
170       free((void*)mWebPData.bytes);
171       mWebPData.bytes = nullptr;
172       WebPDataInit(&mWebPData);
173     }
174     if(mWebPAnimDecoder)
175     {
176       WebPAnimDecoderDelete(mWebPAnimDecoder);
177     }
178 #endif
179   }
180
181   std::string           mUrl;
182   std::vector<uint32_t> mTimeStamp;
183   uint32_t              mLoadingFrame{0};
184   bool                  mLoadSucceeded;
185
186 #ifdef DALI_WEBP_ENABLED
187   WebPData         mWebPData{0};
188   WebPAnimDecoder* mWebPAnimDecoder{nullptr};
189   WebPAnimInfo     mWebPAnimInfo{0};
190 #endif
191 };
192
193 AnimatedImageLoadingPtr WebPLoading::New(const std::string& url, bool isLocalResource)
194 {
195 #ifndef DALI_WEBP_ENABLED
196   DALI_LOG_ERROR("The system does not support Animated WebP format.\n");
197 #endif
198   return AnimatedImageLoadingPtr(new WebPLoading(url, isLocalResource));
199 }
200
201 WebPLoading::WebPLoading(const std::string& url, bool isLocalResource)
202 : mImpl(new WebPLoading::Impl(url, isLocalResource))
203 {
204 }
205
206 WebPLoading::~WebPLoading()
207 {
208   delete mImpl;
209 }
210
211 bool WebPLoading::LoadNextNFrames(uint32_t frameStartIndex, int count, std::vector<Dali::PixelData>& pixelData)
212 {
213 #ifdef DALI_WEBP_ENABLED
214   if(frameStartIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded)
215   {
216     return false;
217   }
218
219   DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count);
220
221   if(mImpl->mLoadingFrame > frameStartIndex)
222   {
223     mImpl->mLoadingFrame = 0;
224     WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
225   }
226
227   for(; mImpl->mLoadingFrame < frameStartIndex; ++mImpl->mLoadingFrame)
228   {
229     uint8_t* frameBuffer;
230     int      timestamp;
231     WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
232     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
233   }
234
235   for(int i = 0; i < count; ++i)
236   {
237     const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
238     uint8_t*  frameBuffer;
239     int       timestamp;
240     WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
241
242     auto pixelBuffer = new uint8_t[bufferSize];
243     memcpy(pixelBuffer, frameBuffer, bufferSize);
244     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
245
246     if(pixelBuffer)
247     {
248       pixelData.push_back(Dali::PixelData::New(pixelBuffer, bufferSize, mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY));
249     }
250
251     mImpl->mLoadingFrame++;
252     if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
253     {
254       mImpl->mLoadingFrame = 0;
255       WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
256     }
257   }
258
259   return true;
260 #else
261   return false;
262 #endif
263 }
264
265 Dali::Devel::PixelBuffer WebPLoading::LoadFrame(uint32_t frameIndex)
266 {
267   Dali::Devel::PixelBuffer pixelBuffer;
268
269 #ifdef DALI_WEBP_ENABLED
270   if(frameIndex >= mImpl->mWebPAnimInfo.frame_count || !mImpl->mLoadSucceeded)
271   {
272     return pixelBuffer;
273   }
274
275   DALI_LOG_INFO(gWebPLoadingLogFilter, Debug::Concise, "LoadFrame( frameIndex:%d )\n", frameIndex);
276
277   if(mImpl->mLoadingFrame > frameIndex)
278   {
279     mImpl->mLoadingFrame = 0;
280     WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
281   }
282
283   for(; mImpl->mLoadingFrame < frameIndex; ++mImpl->mLoadingFrame)
284   {
285     uint8_t* frameBuffer;
286     int      timestamp;
287     WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
288     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
289   }
290
291   const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof(uint32_t);
292   uint8_t*  frameBuffer;
293   int       timestamp;
294   WebPAnimDecoderGetNext(mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp);
295
296   pixelBuffer = Dali::Devel::PixelBuffer::New(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height, Dali::Pixel::RGBA8888);
297   memcpy(pixelBuffer.GetBuffer(), frameBuffer, bufferSize);
298   mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
299
300   mImpl->mLoadingFrame++;
301   if(mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count)
302   {
303     mImpl->mLoadingFrame = 0;
304     WebPAnimDecoderReset(mImpl->mWebPAnimDecoder);
305   }
306 #endif
307   return pixelBuffer;
308 }
309
310 ImageDimensions WebPLoading::GetImageSize() const
311 {
312 #ifdef DALI_WEBP_ENABLED
313   return ImageDimensions(mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height);
314 #else
315   return ImageDimensions();
316 #endif
317 }
318
319 uint32_t WebPLoading::GetImageCount() const
320 {
321 #ifdef DALI_WEBP_ENABLED
322   return mImpl->mWebPAnimInfo.frame_count;
323 #else
324   return 0u;
325 #endif
326 }
327
328 uint32_t WebPLoading::GetFrameInterval(uint32_t frameIndex) const
329 {
330   // If frameIndex is above the value of ImageCount or current frame is not loading yet, return 0u.
331   if(frameIndex >= GetImageCount() || (frameIndex > 0 && mImpl->mTimeStamp[frameIndex - 1] > mImpl->mTimeStamp[frameIndex]))
332   {
333     return 0u;
334   }
335   else
336   {
337     if(frameIndex > 0)
338     {
339       return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
340     }
341     return mImpl->mTimeStamp[frameIndex];
342   }
343 }
344
345 std::string WebPLoading::GetUrl() const
346 {
347   return mImpl->mUrl;
348 }
349
350 bool WebPLoading::HasLoadingSucceeded() const
351 {
352   return mImpl->mLoadSucceeded;
353 }
354
355 } // namespace Adaptor
356
357 } // namespace Internal
358
359 } // namespace Dali