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