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