db9a48f55790147ad3a3c24ea00444d4850aeec5
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / webp-loading.cpp
1 /*
2  * Copyright (c) 2020 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 <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <cstring>
39 #include <dali/internal/imaging/common/file-download.h>
40 #include <dali/internal/system/common/file-reader.h>
41
42 typedef unsigned char WebPByteType;
43
44 namespace Dali
45 {
46
47 namespace Internal
48 {
49
50 namespace Adaptor
51 {
52
53 namespace
54 {
55
56 #if defined(DEBUG_ENABLED)
57 Debug::Filter *gWebPLoadingLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_GIF_LOADING" );
58 #endif
59
60 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE  = 50 * 1024 * 1024;
61
62 }
63
64 struct WebPLoading::Impl
65 {
66 public:
67   Impl( const std::string& url, bool isLocalResource )
68   : mUrl( url ),
69     mLoadingFrame( 0 )
70   {
71 #ifdef DALI_WEBP_ENABLED
72     if( ReadWebPInformation( isLocalResource ) )
73     {
74       WebPAnimDecoderOptions webPAnimDecoderOptions;
75       WebPAnimDecoderOptionsInit( &webPAnimDecoderOptions );
76       webPAnimDecoderOptions.color_mode = MODE_RGBA;
77       mWebPAnimDecoder = WebPAnimDecoderNew( &mWebPData, &webPAnimDecoderOptions );
78       WebPAnimDecoderGetInfo( mWebPAnimDecoder, &mWebPAnimInfo );
79       mTimeStamp.assign( mWebPAnimInfo.frame_count, 0 );
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;
184
185 #ifdef DALI_WEBP_ENABLED
186   WebPData mWebPData;
187   WebPAnimDecoder* mWebPAnimDecoder;
188   WebPAnimInfo mWebPAnimInfo;
189 #endif
190 };
191
192 AnimatedImageLoadingPtr WebPLoading::New( const std::string &url, bool isLocalResource )
193 {
194 #ifndef DALI_WEBP_ENABLED
195   DALI_LOG_ERROR( "The system does not support Animated WebP format.\n" );
196 #endif
197   return AnimatedImageLoadingPtr( new WebPLoading( url, isLocalResource ) );
198 }
199
200 WebPLoading::WebPLoading( const std::string &url, bool isLocalResource )
201 : mImpl( new WebPLoading::Impl( url, isLocalResource ) )
202 {
203 }
204
205 WebPLoading::~WebPLoading()
206 {
207   delete mImpl;
208 }
209
210 bool WebPLoading::LoadNextNFrames( uint32_t frameStartIndex, int count, std::vector<Dali::PixelData> &pixelData )
211 {
212 #ifdef DALI_WEBP_ENABLED
213   if( frameStartIndex  >= mImpl->mWebPAnimInfo.frame_count )
214   {
215     return false;
216   }
217
218   DALI_LOG_INFO( gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count );
219
220   if( mImpl->mLoadingFrame > frameStartIndex  )
221   {
222     mImpl->mLoadingFrame = 0;
223     WebPAnimDecoderReset( mImpl->mWebPAnimDecoder );
224   }
225
226   for( ; mImpl->mLoadingFrame < frameStartIndex ; ++mImpl->mLoadingFrame )
227   {
228     uint8_t* frameBuffer;
229     int timestamp;
230     WebPAnimDecoderGetNext( mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp );
231     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
232   }
233
234   for( int i = 0; i < count; ++i )
235   {
236     const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof( uint32_t );
237     uint8_t* frameBuffer;
238     int timestamp;
239     WebPAnimDecoderGetNext( mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp );
240
241     auto pixelBuffer = new uint8_t[ bufferSize ];
242     memcpy( pixelBuffer, frameBuffer, bufferSize );
243     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
244
245     if( pixelBuffer )
246     {
247       pixelData.push_back( Dali::PixelData::New( pixelBuffer, bufferSize,
248                                                  mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height,
249                                                  Dali::Pixel::RGBA8888, Dali::PixelData::DELETE_ARRAY) );
250     }
251
252     mImpl->mLoadingFrame++;
253     if( mImpl->mLoadingFrame >= mImpl->mWebPAnimInfo.frame_count )
254     {
255       mImpl->mLoadingFrame = 0;
256       WebPAnimDecoderReset( mImpl->mWebPAnimDecoder );
257     }
258   }
259
260   return true;
261 #else
262   return false;
263 #endif
264 }
265
266 Dali::Devel::PixelBuffer WebPLoading::LoadFrame( uint32_t frameIndex )
267 {
268   Dali::Devel::PixelBuffer pixelBuffer;
269 #ifdef DALI_WEBP_ENABLED
270   if( frameIndex  >= mImpl->mWebPAnimInfo.frame_count )
271   {
272     return pixelBuffer;
273   }
274
275   DALI_LOG_INFO( gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( 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 >= GetImageCount() )
331   {
332     return 0u;
333   }
334   else
335   {
336     if( frameIndex > 0 )
337     {
338       return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
339     }
340     return mImpl->mTimeStamp[frameIndex];
341   }
342 }
343
344 std::string WebPLoading::GetUrl() const
345 {
346   return mImpl->mUrl;
347 }
348
349 } // namespace Adaptor
350
351 } // namespace Internal
352
353 } // namespace Dali