Revert "[Tizen] Add codes for Dali Windows Backend"
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-gif.cpp
1 /*
2  * Copyright (c) 2017 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 #include <dali/internal/imaging/common/loader-gif.h>
19
20 #include <gif_lib.h>
21
22 #include <dali/integration-api/debug.h>
23 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
24 #include <memory>
25
26 // We need to check if giflib has the new open and close API (including error parameter).
27 #ifdef GIFLIB_MAJOR
28 #define LIBGIF_VERSION_5_1_OR_ABOVE
29 #endif
30
31 namespace Dali
32 {
33
34 namespace TizenPlatform
35 {
36
37 namespace
38 {
39
40 // simple class to enforce clean-up of GIF structures
41 struct AutoCleanupGif
42 {
43   AutoCleanupGif(GifFileType*& _gifInfo)
44   : gifInfo(_gifInfo)
45   {
46   }
47
48   ~AutoCleanupGif()
49   {
50     if(NULL != gifInfo)
51     {
52       // clean up GIF resources
53 #ifdef LIBGIF_VERSION_5_1_OR_ABOVE
54       int errorCode = 0; //D_GIF_SUCCEEDED is 0
55       DGifCloseFile( gifInfo, &errorCode );
56
57       if( errorCode )
58       {
59         DALI_LOG_ERROR( "GIF Loader: DGifCloseFile Error. Code: %d\n", errorCode );
60       }
61 #else
62       DGifCloseFile( gifInfo );
63 #endif
64     }
65   }
66
67   GifFileType*& gifInfo;
68 };
69
70 // Used in the GIF interlace algorithm to determine the starting byte and the increment required
71 // for each pass.
72 struct InterlacePair
73 {
74   unsigned int startingByte;
75   unsigned int incrementalByte;
76 };
77
78 // Used in the GIF interlace algorithm to determine the order and which location to read data from
79 // the file.
80 const InterlacePair INTERLACE_PAIR_TABLE [] = {
81   { 0, 8 }, // Starting at 0, read every 8 bytes.
82   { 4, 8 }, // Starting at 4, read every 8 bytes.
83   { 2, 4 }, // Starting at 2, read every 4 bytes.
84   { 1, 2 }, // Starting at 1, read every 2 bytes.
85 };
86 const unsigned int INTERLACE_PAIR_TABLE_SIZE( sizeof( INTERLACE_PAIR_TABLE ) / sizeof( InterlacePair ) );
87
88 /// Function used by Gif_Lib to read from the image file.
89 int ReadDataFromGif(GifFileType *gifInfo, GifByteType *data, int length)
90 {
91   FILE *fp = reinterpret_cast<FILE*>(gifInfo->UserData);
92   return fread( data, sizeof( GifByteType ), length, fp);
93 }
94
95 /// Loads the GIF Header.
96 bool LoadGifHeader(FILE *fp, unsigned int &width, unsigned int &height, GifFileType** gifInfo)
97 {
98   int errorCode = 0; //D_GIF_SUCCEEDED is 0
99
100 #ifdef LIBGIF_VERSION_5_1_OR_ABOVE
101   *gifInfo = DGifOpen( reinterpret_cast<void*>(fp), ReadDataFromGif, &errorCode );
102 #else
103   *gifInfo = DGifOpen( reinterpret_cast<void*>(fp), ReadDataFromGif );
104 #endif
105
106   if ( !(*gifInfo) || errorCode )
107   {
108     DALI_LOG_ERROR( "GIF Loader: DGifOpen Error. Code: %d\n", errorCode );
109     return false;
110   }
111
112   width  = (*gifInfo)->SWidth;
113   height = (*gifInfo)->SHeight;
114
115   // No proper size in GIF.
116   if ( width <= 0 || height <= 0 )
117   {
118     return false;
119   }
120
121   return true;
122 }
123
124 /// Decode the GIF image.
125 bool DecodeImage( GifFileType* gifInfo, unsigned char* decodedData, const unsigned int width, const unsigned int height, const unsigned int bytesPerRow )
126 {
127   if ( gifInfo->Image.Interlace )
128   {
129     // If the image is interlaced, then use the GIF interlace algorithm to read the file appropriately.
130
131     const InterlacePair* interlacePairPtr( INTERLACE_PAIR_TABLE );
132     for ( unsigned int interlacePair = 0; interlacePair < INTERLACE_PAIR_TABLE_SIZE; ++interlacePair, ++interlacePairPtr )
133     {
134       for( unsigned int currentByte = interlacePairPtr->startingByte; currentByte < height; currentByte += interlacePairPtr->incrementalByte )
135       {
136         unsigned char* row = decodedData + currentByte * bytesPerRow;
137         if ( DGifGetLine( gifInfo, row, width ) == GIF_ERROR )
138         {
139           DALI_LOG_ERROR( "GIF Loader: Error reading Interlaced GIF\n" );
140           return false;
141         }
142       }
143     }
144   }
145   else
146   {
147     // Non-interlace does not require any erratic reading / jumping.
148     unsigned char* decodedDataPtr( decodedData );
149
150     for ( unsigned int row = 0; row < height; ++row )
151     {
152       if ( DGifGetLine( gifInfo, decodedDataPtr, width ) == GIF_ERROR)
153       {
154         DALI_LOG_ERROR( "GIF Loader: Error reading non-interlaced GIF\n" );
155         return false;
156       }
157       decodedDataPtr += bytesPerRow;
158     }
159   }
160   return true;
161 }
162
163 // Retrieves the colors used in the GIF image.
164 GifColorType* GetImageColors( SavedImage* image, GifFileType* gifInfo )
165 {
166   GifColorType* color( NULL );
167   if ( image->ImageDesc.ColorMap )
168   {
169     color = image->ImageDesc.ColorMap->Colors;
170   }
171   else
172   {
173     // if there is no color map for this image use the default one
174     color = gifInfo->SColorMap->Colors;
175   }
176   return color;
177 }
178
179 /// Called when we want to handle IMAGE_DESC_RECORD_TYPE
180 bool HandleImageDescriptionRecordType( Dali::Devel::PixelBuffer& bitmap, GifFileType* gifInfo, unsigned int width, unsigned int height, bool& finished )
181 {
182   if ( DGifGetImageDesc( gifInfo ) == GIF_ERROR )
183   {
184     DALI_LOG_ERROR( "GIF Loader: Error getting Image Description\n" );
185     return false;
186   }
187
188   // Ensure there is at least 1 image in the GIF.
189   if ( gifInfo->ImageCount < 1 )
190   {
191     DALI_LOG_ERROR( "GIF Loader: No Images\n" );
192     return false;
193   }
194
195   Pixel::Format pixelFormat( Pixel::RGB888 );
196
197   SavedImage* image( &gifInfo->SavedImages[ gifInfo->ImageCount - 1 ] );
198   const GifImageDesc& desc( image->ImageDesc );
199
200   auto decodedData = new unsigned char[ width * height * sizeof( GifPixelType ) ];
201
202   std::unique_ptr<unsigned char[]> ptr{ decodedData };
203
204   const unsigned int bytesPerRow( width * sizeof( GifPixelType ) );
205   const unsigned int actualWidth( desc.Width );
206   const unsigned int actualHeight( desc.Height );
207
208   // Create a buffer to store the decoded data.
209   bitmap = Dali::Devel::PixelBuffer::New( actualWidth, actualHeight, pixelFormat );
210
211   // Decode the GIF Image
212   if ( !DecodeImage( gifInfo, decodedData, actualWidth, actualHeight, bytesPerRow ) )
213   {
214     return false;
215   }
216
217   // Get the colormap for the GIF
218   GifColorType* color( GetImageColors( image, gifInfo ) );
219
220   // If it's an animated GIF, we still only read the first image
221
222   // Create and populate pixel buffer.
223   auto pixels = bitmap.GetBuffer();
224   for (unsigned int row = 0; row < actualHeight; ++row)
225   {
226     for (unsigned int column = 0; column < actualWidth; ++column)
227     {
228       unsigned char index = decodedData[row * width + column];
229
230       pixels[0] = color[index].Red;
231       pixels[1] = color[index].Green;
232       pixels[2] = color[index].Blue;
233       pixels += 3;
234     }
235   }
236   finished = true;
237   return true;
238 }
239
240 /// Called when we want to handle EXTENSION_RECORD_TYPE
241 bool HandleExtensionRecordType( GifFileType* gifInfo )
242 {
243   SavedImage image;
244   GifByteType *extensionByte( NULL );
245
246 #ifdef LIBGIF_VERSION_5_1_OR_ABOVE
247   ExtensionBlock extensionBlocks;
248   image.ExtensionBlocks          = &extensionBlocks;
249   image.ExtensionBlockCount      = 1;
250   int *extensionBlockTypePointer = &image.ExtensionBlocks->Function;
251 #else
252   image.ExtensionBlocks     = NULL;
253   image.ExtensionBlockCount = 0;
254   int *extensionBlockTypePointer = &image.Function;
255 #endif
256
257   // Not really interested in the extensions so just skip them unless there is an error.
258   for ( int extRetCode = DGifGetExtension( gifInfo, extensionBlockTypePointer, &extensionByte );
259         extensionByte != NULL;
260         extRetCode = DGifGetExtensionNext( gifInfo, &extensionByte ) )
261   {
262     if ( extRetCode == GIF_ERROR )
263     {
264       DALI_LOG_ERROR( "GIF Loader: Error reading GIF Extension record.\n" );
265       return false;
266     }
267   }
268
269   return true;
270 }
271
272 } // unnamed namespace
273
274 bool LoadGifHeader( const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height )
275 {
276   GifFileType* gifInfo = NULL;
277   AutoCleanupGif autoCleanupGif(gifInfo);
278   FILE* const fp = input.file;
279
280   return LoadGifHeader(fp, width, height, &gifInfo);
281 }
282
283 bool LoadBitmapFromGif( const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap )
284 {
285   FILE* const fp = input.file;
286   // Load the GIF Header file.
287
288   GifFileType* gifInfo( NULL );
289   unsigned int width( 0 );
290   unsigned int height( 0 );
291   if ( !LoadGifHeader( fp, width, height, &gifInfo ) )
292   {
293     return false;
294   }
295   AutoCleanupGif autoGif( gifInfo );
296
297   // Check each record in the GIF file.
298
299   bool finished( false );
300   GifRecordType recordType( UNDEFINED_RECORD_TYPE );
301   for ( int returnCode = DGifGetRecordType( gifInfo, &recordType );
302         !finished && recordType != TERMINATE_RECORD_TYPE;
303         returnCode = DGifGetRecordType( gifInfo, &recordType ) )
304   {
305     if ( returnCode == GIF_ERROR )
306     {
307       DALI_LOG_ERROR( "GIF Loader: Error getting Record Type\n" );
308       return false;
309     }
310
311     if( IMAGE_DESC_RECORD_TYPE == recordType )
312     {
313       if ( !HandleImageDescriptionRecordType( bitmap, gifInfo, width, height, finished ) )
314       {
315         return false;
316       }
317     }
318     else if ( EXTENSION_RECORD_TYPE == recordType )
319     {
320       if ( !HandleExtensionRecordType( gifInfo ))
321       {
322         return false;
323       }
324     }
325   }
326
327   return true;
328 }
329
330 } // namespace TizenPlatform
331
332 } // namespace Dali