2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <dali/internal/imaging/common/loader-gif.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
26 // We need to check if giflib has the new open and close API (including error parameter).
28 #define LIBGIF_VERSION_5_1_OR_ABOVE
34 namespace TizenPlatform
40 // simple class to enforce clean-up of GIF structures
43 AutoCleanupGif(GifFileType*& _gifInfo)
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 );
59 DALI_LOG_ERROR( "GIF Loader: DGifCloseFile Error. Code: %d\n", errorCode );
62 DGifCloseFile( gifInfo );
67 GifFileType*& gifInfo;
70 // Used in the GIF interlace algorithm to determine the starting byte and the increment required
74 unsigned int startingByte;
75 unsigned int incrementalByte;
78 // Used in the GIF interlace algorithm to determine the order and which location to read data from
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.
86 const unsigned int INTERLACE_PAIR_TABLE_SIZE( sizeof( INTERLACE_PAIR_TABLE ) / sizeof( InterlacePair ) );
88 /// Function used by Gif_Lib to read from the image file.
89 int ReadDataFromGif(GifFileType *gifInfo, GifByteType *data, int length)
91 FILE *fp = reinterpret_cast<FILE*>(gifInfo->UserData);
92 return fread( data, sizeof( GifByteType ), length, fp);
95 /// Loads the GIF Header.
96 bool LoadGifHeader(FILE *fp, unsigned int &width, unsigned int &height, GifFileType** gifInfo)
98 int errorCode = 0; //D_GIF_SUCCEEDED is 0
100 #ifdef LIBGIF_VERSION_5_1_OR_ABOVE
101 *gifInfo = DGifOpen( reinterpret_cast<void*>(fp), ReadDataFromGif, &errorCode );
103 *gifInfo = DGifOpen( reinterpret_cast<void*>(fp), ReadDataFromGif );
106 if ( !(*gifInfo) || errorCode )
108 DALI_LOG_ERROR( "GIF Loader: DGifOpen Error. Code: %d\n", errorCode );
112 width = (*gifInfo)->SWidth;
113 height = (*gifInfo)->SHeight;
115 // No proper size in GIF.
116 if ( width <= 0 || height <= 0 )
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 )
127 if ( gifInfo->Image.Interlace )
129 // If the image is interlaced, then use the GIF interlace algorithm to read the file appropriately.
131 const InterlacePair* interlacePairPtr( INTERLACE_PAIR_TABLE );
132 for ( unsigned int interlacePair = 0; interlacePair < INTERLACE_PAIR_TABLE_SIZE; ++interlacePair, ++interlacePairPtr )
134 for( unsigned int currentByte = interlacePairPtr->startingByte; currentByte < height; currentByte += interlacePairPtr->incrementalByte )
136 unsigned char* row = decodedData + currentByte * bytesPerRow;
137 if ( DGifGetLine( gifInfo, row, width ) == GIF_ERROR )
139 DALI_LOG_ERROR( "GIF Loader: Error reading Interlaced GIF\n" );
147 // Non-interlace does not require any erratic reading / jumping.
148 unsigned char* decodedDataPtr( decodedData );
150 for ( unsigned int row = 0; row < height; ++row )
152 if ( DGifGetLine( gifInfo, decodedDataPtr, width ) == GIF_ERROR)
154 DALI_LOG_ERROR( "GIF Loader: Error reading non-interlaced GIF\n" );
157 decodedDataPtr += bytesPerRow;
163 // Retrieves the colors used in the GIF image.
164 GifColorType* GetImageColors( SavedImage* image, GifFileType* gifInfo )
166 GifColorType* color( NULL );
167 if ( image->ImageDesc.ColorMap )
169 color = image->ImageDesc.ColorMap->Colors;
173 // if there is no color map for this image use the default one
174 color = gifInfo->SColorMap->Colors;
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 )
182 if ( DGifGetImageDesc( gifInfo ) == GIF_ERROR )
184 DALI_LOG_ERROR( "GIF Loader: Error getting Image Description\n" );
188 // Ensure there is at least 1 image in the GIF.
189 if ( gifInfo->ImageCount < 1 )
191 DALI_LOG_ERROR( "GIF Loader: No Images\n" );
195 Pixel::Format pixelFormat( Pixel::RGB888 );
197 SavedImage* image( &gifInfo->SavedImages[ gifInfo->ImageCount - 1 ] );
198 const GifImageDesc& desc( image->ImageDesc );
200 auto decodedData = new unsigned char[ width * height * sizeof( GifPixelType ) ];
202 std::unique_ptr<unsigned char[]> ptr{ decodedData };
204 const unsigned int bytesPerRow( width * sizeof( GifPixelType ) );
205 const unsigned int actualWidth( desc.Width );
206 const unsigned int actualHeight( desc.Height );
208 // Create a buffer to store the decoded data.
209 bitmap = Dali::Devel::PixelBuffer::New( actualWidth, actualHeight, pixelFormat );
211 // Decode the GIF Image
212 if ( !DecodeImage( gifInfo, decodedData, actualWidth, actualHeight, bytesPerRow ) )
217 // Get the colormap for the GIF
218 GifColorType* color( GetImageColors( image, gifInfo ) );
220 // If it's an animated GIF, we still only read the first image
222 // Create and populate pixel buffer.
223 auto pixels = bitmap.GetBuffer();
224 for (unsigned int row = 0; row < actualHeight; ++row)
226 for (unsigned int column = 0; column < actualWidth; ++column)
228 unsigned char index = decodedData[row * width + column];
230 pixels[0] = color[index].Red;
231 pixels[1] = color[index].Green;
232 pixels[2] = color[index].Blue;
240 /// Called when we want to handle EXTENSION_RECORD_TYPE
241 bool HandleExtensionRecordType( GifFileType* gifInfo )
244 GifByteType *extensionByte( NULL );
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;
252 image.ExtensionBlocks = NULL;
253 image.ExtensionBlockCount = 0;
254 int *extensionBlockTypePointer = &image.Function;
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 ) )
262 if ( extRetCode == GIF_ERROR )
264 DALI_LOG_ERROR( "GIF Loader: Error reading GIF Extension record.\n" );
272 } // unnamed namespace
274 bool LoadGifHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
276 GifFileType* gifInfo = NULL;
277 AutoCleanupGif autoCleanupGif(gifInfo);
278 FILE* const fp = input.file;
280 return LoadGifHeader(fp, width, height, &gifInfo);
283 bool LoadBitmapFromGif( const ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap )
285 FILE* const fp = input.file;
286 // Load the GIF Header file.
288 GifFileType* gifInfo( NULL );
289 unsigned int width( 0 );
290 unsigned int height( 0 );
291 if ( !LoadGifHeader( fp, width, height, &gifInfo ) )
295 AutoCleanupGif autoGif( gifInfo );
297 // Check each record in the GIF file.
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 ) )
305 if ( returnCode == GIF_ERROR )
307 DALI_LOG_ERROR( "GIF Loader: Error getting Record Type\n" );
311 if( IMAGE_DESC_RECORD_TYPE == recordType )
313 if ( !HandleImageDescriptionRecordType( bitmap, gifInfo, width, height, finished ) )
318 else if ( EXTENSION_RECORD_TYPE == recordType )
320 if ( !HandleExtensionRecordType( gifInfo ))
330 } // namespace TizenPlatform