Dali 3D User Interface Engine
loader-gif.cpp
Go to the documentation of this file.
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 
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;
35 
36 namespace TizenPlatform
37 {
38 
39 namespace
40 {
41 
42 // simple class to enforce clean-up of GIF structures
44 {
45  AutoCleanupGif(GifFileType*& _gifInfo)
46  : gifInfo(_gifInfo)
47  {
48  }
49 
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
74 {
76  : buffer( _buffer )
77  {
78  }
79 
81  {
82  delete []buffer;
83  }
84 
86 };
87 
88 // Used in the GIF interlace algorithm to determine the starting byte and the increment required
89 // for each pass.
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.
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 
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 
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 
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 
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 
259 bool HandleExtensionRecordType( GifFileType* gifInfo )
260 {
261  SavedImage image;
262  image.ExtensionBlocks = NULL;
263  image.ExtensionBlockCount = 0;
264  GifByteType *extensionByte( NULL );
265 
266 #ifdef LIBGIF_VERSION_5_1_OR_ABOVE
267  int *extensionBlockTypePointer = &image.ExtensionBlocks->Function;
268 #else
269  int *extensionBlockTypePointer = &image.Function;
270 #endif
271 
272  // Not really interested in the extensions so just skip them unless there is an error.
273  for ( int extRetCode = DGifGetExtension( gifInfo, extensionBlockTypePointer, &extensionByte );
274  extensionByte != NULL;
275  extRetCode = DGifGetExtensionNext( gifInfo, &extensionByte ) )
276  {
277  if ( extRetCode == GIF_ERROR )
278  {
279  DALI_LOG_ERROR( "GIF Loader: Error reading GIF Extension record.\n" );
280  return false;
281  }
282  }
283 
284  return true;
285 }
286 
287 } // unnamed namespace
288 
289 bool LoadGifHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
290 {
291  GifFileType* gifInfo = NULL;
292  AutoCleanupGif autoCleanupGif(gifInfo);
293  FILE* const fp = input.file;
294 
295  return LoadGifHeader(fp, width, height, &gifInfo);
296 }
297 
299 {
300  FILE* const fp = input.file;
301  // Load the GIF Header file.
302 
303  GifFileType* gifInfo( NULL );
304  unsigned int width( 0 );
305  unsigned int height( 0 );
306  if ( !LoadGifHeader( fp, width, height, &gifInfo ) )
307  {
308  return false;
309  }
310  AutoCleanupGif autoGif( gifInfo );
311 
312  // Check each record in the GIF file.
313 
314  bool finished( false );
315  GifRecordType recordType( UNDEFINED_RECORD_TYPE );
316  for ( int returnCode = DGifGetRecordType( gifInfo, &recordType );
317  !finished && recordType != TERMINATE_RECORD_TYPE;
318  returnCode = DGifGetRecordType( gifInfo, &recordType ) )
319  {
320  if ( returnCode == GIF_ERROR )
321  {
322  DALI_LOG_ERROR( "GIF Loader: Error getting Record Type\n" );
323  return false;
324  }
325 
326  if( IMAGE_DESC_RECORD_TYPE == recordType )
327  {
328  if ( !HandleImageDescriptionRecordType( bitmap, gifInfo, width, height, finished ) )
329  {
330  return false;
331  }
332  }
333  else if ( EXTENSION_RECORD_TYPE == recordType )
334  {
335  if ( !HandleExtensionRecordType( gifInfo ))
336  {
337  return false;
338  }
339  }
340  }
341 
342  return true;
343 }
344 
345 } // namespace TizenPlatform
346 
347 } // namespace Dali
Dali Docs Home
Read more about Dali