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