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