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