2 * Copyright (c) 2014 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 "loader-gif.h"
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/bitmap.h>
25 #include <dali/public-api/images/image-attributes.h>
29 using Integration::Bitmap;
30 using Dali::Integration::PixelBuffer;
32 namespace TizenPlatform
38 // simple class to enforce clean-up of GIF structures
41 AutoCleanupGif(GifFileType*& _gifInfo)
50 // clean up GIF resources
51 DGifCloseFile(gifInfo);
55 GifFileType*& gifInfo;
58 // Simple class to enforce clean-up of PixelBuffer
59 struct AutoDeleteBuffer
61 AutoDeleteBuffer( PixelBuffer* _buffer )
74 // Used in the GIF interlace algorithm to determine the starting byte and the increment required
78 unsigned int startingByte;
79 unsigned int incrementalByte;
82 // Used in the GIF interlace algorithm to determine the order and which location to read data from
84 const InterlacePair INTERLACE_PAIR_TABLE [] = {
85 { 0, 8 }, // Starting at 0, read every 8 bytes.
86 { 4, 8 }, // Starting at 4, read every 8 bytes.
87 { 2, 4 }, // Starting at 2, read every 4 bytes.
88 { 1, 2 }, // Starting at 1, read every 2 bytes.
90 const unsigned int INTERLACE_PAIR_TABLE_SIZE( sizeof( INTERLACE_PAIR_TABLE ) / sizeof( InterlacePair ) );
92 /// Function used by Gif_Lib to read from the image file.
93 int ReadDataFromGif(GifFileType *gifInfo, GifByteType *data, int length)
95 FILE *fp = reinterpret_cast<FILE*>(gifInfo->UserData);
96 return fread( data, sizeof( GifByteType ), length, fp);
99 /// Loads the GIF Header.
100 bool LoadGifHeader(FILE *fp, unsigned int &width, unsigned int &height, GifFileType** gifInfo)
102 *gifInfo = DGifOpen(reinterpret_cast<void*>(fp), ReadDataFromGif);
109 width = (*gifInfo)->SWidth;
110 height = (*gifInfo)->SHeight;
112 // No proper size in GIF.
113 if ( width <= 0 || height <= 0 )
121 /// Decode the GIF image.
122 bool DecodeImage( GifFileType* gifInfo, PixelBuffer* decodedData, const unsigned int width, const unsigned int height, const unsigned int bytesPerRow )
124 if ( gifInfo->Image.Interlace )
126 // If the image is interlaced, then use the GIF interlace algorithm to read the file appropriately.
128 const InterlacePair* interlacePairPtr( INTERLACE_PAIR_TABLE );
129 for ( unsigned int interlacePair = 0; interlacePair < INTERLACE_PAIR_TABLE_SIZE; ++interlacePair, ++interlacePairPtr )
131 for( unsigned int currentByte = interlacePairPtr->startingByte; currentByte < height; currentByte += interlacePairPtr->incrementalByte )
133 PixelBuffer* row = decodedData + currentByte * bytesPerRow;
134 if ( DGifGetLine( gifInfo, row, width ) == GIF_ERROR )
136 DALI_LOG_ERROR( "GIF Loader: Error reading Interlaced GIF\n" );
144 // Non-interlace does not require any erratic reading / jumping.
145 PixelBuffer* decodedDataPtr( decodedData );
147 for ( unsigned int row = 0; row < height; ++row )
149 if ( DGifGetLine( gifInfo, decodedDataPtr, width ) == GIF_ERROR)
151 DALI_LOG_ERROR( "GIF Loader: Error reading non-interlaced GIF\n" );
154 decodedDataPtr += bytesPerRow;
160 // Retrieves the colors used in the GIF image.
161 GifColorType* GetImageColors( SavedImage* image, GifFileType* gifInfo )
163 GifColorType* color( NULL );
164 if ( image->ImageDesc.ColorMap )
166 color = image->ImageDesc.ColorMap->Colors;
170 // if there is no color map for this image use the default one
171 color = gifInfo->SColorMap->Colors;
176 /// Called when we want to handle IMAGE_DESC_RECORD_TYPE
177 bool HandleImageDescriptionRecordType( Bitmap& bitmap, ImageAttributes& attributes, GifFileType* gifInfo, unsigned int width, unsigned int height, bool& finished )
179 if ( DGifGetImageDesc( gifInfo ) == GIF_ERROR )
181 DALI_LOG_ERROR( "GIF Loader: Error getting Image Description\n" );
185 // Ensure there is at least 1 image in the GIF.
186 if ( gifInfo->ImageCount < 1 )
188 DALI_LOG_ERROR( "GIF Loader: No Images\n" );
192 SavedImage* image( &gifInfo->SavedImages[ gifInfo->ImageCount - 1 ] );
193 const GifImageDesc& desc( image->ImageDesc );
195 // Create a buffer to store the decoded data.
196 PixelBuffer* decodedData( new PixelBuffer[ width * height * sizeof( GifPixelType ) ] );
197 AutoDeleteBuffer autoDeleteBuffer( decodedData );
199 const unsigned int bytesPerRow( width * sizeof( GifPixelType ) );
200 const unsigned int actualWidth( desc.Width );
201 const unsigned int actualHeight( desc.Height );
203 // Decode the GIF Image
204 if ( !DecodeImage( gifInfo, decodedData, actualWidth, actualHeight, bytesPerRow ) )
209 // TODO: Add scaling support
211 // Get the colormap for the GIF
212 GifColorType* color( GetImageColors( image, gifInfo ) );
214 // If it's an animated GIF, we still only read the first image
216 // Create and populate pixel buffer.
218 Pixel::Format pixelFormat( Pixel::RGB888 );
219 PixelBuffer *pixels = bitmap.GetPackedPixelsProfile()->ReserveBuffer( pixelFormat, actualWidth, actualHeight );
221 for (unsigned int row = 0; row < actualHeight; ++row)
223 for (unsigned int column = 0; column < actualWidth; ++column)
225 unsigned char index = decodedData[row * width + column];
227 pixels[0] = color[index].Red;
228 pixels[1] = color[index].Green;
229 pixels[2] = color[index].Blue;
234 attributes.SetSize( actualWidth, actualHeight );
241 /// Called when we want to handle EXTENSION_RECORD_TYPE
242 bool HandleExtensionRecordType( GifFileType* gifInfo )
245 image.ExtensionBlocks = NULL;
246 image.ExtensionBlockCount = 0;
247 GifByteType *extensionByte( NULL );
249 // Not really interested in the extensions so just skip them unless there is an error.
250 for ( int extRetCode = DGifGetExtension( gifInfo, &image.Function, &extensionByte );
251 extensionByte != NULL;
252 extRetCode = DGifGetExtensionNext( gifInfo, &extensionByte ) )
254 if ( extRetCode == GIF_ERROR )
256 DALI_LOG_ERROR( "GIF Loader: Error reading GIF Extension record.\n" );
264 } // unnamed namespace
266 bool LoadGifHeader(FILE *fp, const ImageAttributes& attributes, unsigned int &width, unsigned int &height )
268 GifFileType* gifInfo = NULL;
269 AutoCleanupGif autoCleanupGif(gifInfo);
271 return LoadGifHeader(fp, width, height, &gifInfo);
274 bool LoadBitmapFromGif(FILE *fp, Bitmap& bitmap, ImageAttributes& attributes, const ResourceLoadingClient& client )
276 // Load the GIF Header file.
278 GifFileType* gifInfo( NULL );
279 unsigned int width( 0 );
280 unsigned int height( 0 );
281 if ( !LoadGifHeader( fp, width, height, &gifInfo ) )
285 AutoCleanupGif autoGif( gifInfo );
287 // Check each record in the GIF file.
289 bool finished( false );
290 GifRecordType recordType( UNDEFINED_RECORD_TYPE );
291 for ( int returnCode = DGifGetRecordType( gifInfo, &recordType );
292 !finished && recordType != TERMINATE_RECORD_TYPE;
293 returnCode = DGifGetRecordType( gifInfo, &recordType ) )
295 if ( returnCode == GIF_ERROR )
297 DALI_LOG_ERROR( "GIF Loader: Error getting Record Type\n" );
301 if( IMAGE_DESC_RECORD_TYPE == recordType )
303 if ( !HandleImageDescriptionRecordType( bitmap, attributes, gifInfo, width, height, finished ) )
308 else if ( EXTENSION_RECORD_TYPE == recordType )
310 if ( !HandleExtensionRecordType( gifInfo ))
320 } // namespace TizenPlatform