Support WebP format
[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 #include <webp/decode.h>
23 #include <webp/demux.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/images/pixel-data.h>
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <cstring>
32 #include <dali/internal/imaging/common/file-download.h>
33 #include <dali/internal/system/common/file-reader.h>
34
35 typedef unsigned char WebPByteType;
36
37 namespace Dali
38 {
39
40 namespace Internal
41 {
42
43 namespace Adaptor
44 {
45
46 namespace
47 {
48
49 #if defined(DEBUG_ENABLED)
50 Debug::Filter *gWebPLoadingLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_GIF_LOADING" );
51 #endif
52
53 constexpr size_t MAXIMUM_DOWNLOAD_IMAGE_SIZE  = 50 * 1024 * 1024;
54
55 }
56
57 struct WebPLoading::Impl
58 {
59 public:
60   Impl( const std::string& url, bool isLocalResource )
61   : mUrl( url ),
62     mLoadingFrame( 0 )
63   {
64 #if WEBP_DEMUX_ABI_VERSION > 0x0101
65     if( ReadWebPInformation( isLocalResource ) )
66     {
67       WebPAnimDecoderOptions webPAnimDecoderOptions;
68       WebPAnimDecoderOptionsInit( &webPAnimDecoderOptions );
69       webPAnimDecoderOptions.color_mode = MODE_RGBA;
70       mWebPAnimDecoder = WebPAnimDecoderNew( &mWebPData, &webPAnimDecoderOptions );
71       WebPAnimDecoderGetInfo( mWebPAnimDecoder, &mWebPAnimInfo );
72       mTimeStamp.assign( mWebPAnimInfo.frame_count, 0 );
73     }
74 #endif
75   }
76
77   bool ReadWebPInformation( bool isLocalResource )
78   {
79 #if WEBP_DEMUX_ABI_VERSION > 0x0101
80     WebPDataInit( &mWebPData );
81     if( isLocalResource )
82     {
83       Internal::Platform::FileReader fileReader( mUrl );
84       FILE *fp = fileReader.GetFile();
85       if( fp == NULL )
86       {
87         return false;
88       }
89
90       if( fseek( fp, 0, SEEK_END ) <= -1 )
91       {
92         return false;
93       }
94
95       mWebPData.size = ftell( fp );
96       if( ( ! fseek( fp, 0, SEEK_SET ) ) )
97       {
98         unsigned char *WebPDataBuffer;
99         WebPDataBuffer = reinterpret_cast<WebPByteType*>( malloc(sizeof( WebPByteType ) * mWebPData.size ) );
100         mWebPData.size = fread( WebPDataBuffer, sizeof( WebPByteType ), mWebPData.size, fp );
101         mWebPData.bytes = WebPDataBuffer;
102       }
103       else
104       {
105         return false;
106       }
107     }
108     else
109     {
110       // remote file
111       bool succeeded;
112       Dali::Vector<uint8_t> dataBuffer;
113       size_t dataSize;
114
115       succeeded = TizenPlatform::Network::DownloadRemoteFileIntoMemory( mUrl, dataBuffer, dataSize, MAXIMUM_DOWNLOAD_IMAGE_SIZE );
116       if( succeeded )
117       {
118         size_t blobSize = dataBuffer.Size();
119         if( blobSize > 0U )
120         {
121           // Open a file handle on the memory buffer:
122           Dali::Internal::Platform::FileReader fileReader( dataBuffer, blobSize );
123           FILE * const fp = fileReader.GetFile();
124           if ( NULL != fp )
125           {
126             if( ( ! fseek( fp, 0, SEEK_SET ) ) )
127             {
128               unsigned char *WebPDataBuffer;
129               WebPDataBuffer = reinterpret_cast<WebPByteType*>( malloc(sizeof( WebPByteType ) * blobSize ) );
130               mWebPData.size = fread( WebPDataBuffer, sizeof( WebPByteType ), mWebPData.size, fp );
131               mWebPData.bytes = WebPDataBuffer;
132             }
133             else
134             {
135               DALI_LOG_ERROR( "Error seeking within file\n" );
136             }
137           }
138           else
139           {
140             DALI_LOG_ERROR( "Error reading file\n" );
141           }
142         }
143       }
144     }
145     return true;
146 #else
147     return false;
148 #endif
149   }
150
151   // Moveable but not copyable
152
153   Impl( const Impl& ) = delete;
154   Impl& operator=( const Impl& ) = delete;
155   Impl( Impl&& ) = default;
156   Impl& operator=( Impl&& ) = default;
157
158   ~Impl()
159   {
160 #if WEBP_DEMUX_ABI_VERSION > 0x0101
161     if( &mWebPData != NULL )
162     {
163       free( (void*)mWebPData.bytes );
164       mWebPData.bytes = nullptr;
165       WebPDataInit( &mWebPData );
166     }
167     if( mWebPAnimDecoder )
168     {
169       WebPAnimDecoderDelete(mWebPAnimDecoder);
170     }
171 #endif
172   }
173
174   std::string mUrl;
175   std::vector<uint32_t> mTimeStamp;
176   uint32_t mLoadingFrame;
177
178 #if WEBP_DEMUX_ABI_VERSION > 0x0101
179   WebPData mWebPData;
180   WebPAnimDecoder* mWebPAnimDecoder;
181   WebPAnimInfo mWebPAnimInfo;
182 #endif
183 };
184
185 AnimatedImageLoadingPtr WebPLoading::New( const std::string &url, bool isLocalResource )
186 {
187 #if WEBP_DEMUX_ABI_VERSION <= 0x0101
188   DALI_LOG_ERROR( "The system do not support Animated WebP format.\n" );
189 #endif
190   return AnimatedImageLoadingPtr( new WebPLoading( url, isLocalResource ) );
191 }
192
193 WebPLoading::WebPLoading( const std::string &url, bool isLocalResource )
194 : mImpl( new WebPLoading::Impl( url, isLocalResource ) )
195 {
196 }
197
198 WebPLoading::~WebPLoading()
199 {
200   delete mImpl;
201 }
202
203 bool WebPLoading::LoadNextNFrames( uint32_t frameStartIndex, int count, std::vector<Dali::PixelData> &pixelData )
204 {
205 #if WEBP_DEMUX_ABI_VERSION > 0x0101
206   if( frameStartIndex  >= mImpl->mWebPAnimInfo.frame_count )
207   {
208     return false;
209   }
210
211   DALI_LOG_INFO( gWebPLoadingLogFilter, Debug::Concise, "LoadNextNFrames( frameStartIndex:%d, count:%d )\n", frameStartIndex, count );
212
213   if( mImpl->mLoadingFrame > frameStartIndex  )
214   {
215     mImpl->mLoadingFrame = 0;
216     WebPAnimDecoderReset( mImpl->mWebPAnimDecoder );
217   }
218
219   for( ; mImpl->mLoadingFrame < frameStartIndex ; ++mImpl->mLoadingFrame )
220   {
221     uint8_t* frameBuffer;
222     int timestamp;
223     WebPAnimDecoderGetNext( mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp );
224     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
225   }
226
227   for( int i = 0; i < count; ++i )
228   {
229     const int bufferSize = mImpl->mWebPAnimInfo.canvas_width * mImpl->mWebPAnimInfo.canvas_height * sizeof( uint32_t );
230     uint8_t* frameBuffer;
231     int timestamp;
232     WebPAnimDecoderGetNext( mImpl->mWebPAnimDecoder, &frameBuffer, &timestamp );
233
234     auto pixelBuffer = new uint8_t[ bufferSize ];
235     memcpy( pixelBuffer, frameBuffer, bufferSize );
236     mImpl->mTimeStamp[mImpl->mLoadingFrame] = timestamp;
237
238     if( pixelBuffer )
239     {
240       pixelData.push_back( Dali::PixelData::New( pixelBuffer, bufferSize,
241                                                  mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height,
242                                                  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 ImageDimensions WebPLoading::GetImageSize() const
260 {
261 #if WEBP_DEMUX_ABI_VERSION > 0x0101
262   return ImageDimensions( mImpl->mWebPAnimInfo.canvas_width, mImpl->mWebPAnimInfo.canvas_height );
263 #else
264   return ImageDimensions();
265 #endif
266 }
267
268 uint32_t WebPLoading::GetImageCount() const
269 {
270 #if WEBP_DEMUX_ABI_VERSION > 0x0101
271   return mImpl->mWebPAnimInfo.frame_count;
272 #else
273   return 0u;
274 #endif
275 }
276
277 uint32_t WebPLoading::GetFrameInterval( uint32_t frameIndex ) const
278 {
279   if( frameIndex >= GetImageCount() )
280   {
281     return 0u;
282   }
283   else
284   {
285     if( frameIndex > 0 )
286     {
287       return mImpl->mTimeStamp[frameIndex] - mImpl->mTimeStamp[frameIndex - 1];
288     }
289     return mImpl->mTimeStamp[frameIndex];
290   }
291 }
292
293 } // namespace Adaptor
294
295 } // namespace Internal
296
297 } // namespace Dali