2 * Copyright (c) 2021 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/devel-api/adaptor-framework/pixel-buffer.h>
23 #include <dali/integration-api/debug.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
33 namespace TizenPlatform
37 // simple class to enforce clean-up of GIF structures
40 AutoCleanupGif(GifFileType*& _gifInfo)
49 // clean up GIF resources
50 #ifdef LIBGIF_VERSION_5_1_OR_ABOVE
51 int errorCode = 0; //D_GIF_SUCCEEDED is 0
52 DGifCloseFile(gifInfo, &errorCode);
56 DALI_LOG_ERROR("GIF Loader: DGifCloseFile Error. Code: %d\n", errorCode);
59 DGifCloseFile(gifInfo);
64 GifFileType*& gifInfo;
67 // Used in the GIF interlace algorithm to determine the starting byte and the increment required
71 unsigned int startingByte;
72 unsigned int incrementalByte;
75 // Used in the GIF interlace algorithm to determine the order and which location to read data from
77 const InterlacePair INTERLACE_PAIR_TABLE[] = {
78 {0, 8}, // Starting at 0, read every 8 bytes.
79 {4, 8}, // Starting at 4, read every 8 bytes.
80 {2, 4}, // Starting at 2, read every 4 bytes.
81 {1, 2}, // Starting at 1, read every 2 bytes.
83 const unsigned int INTERLACE_PAIR_TABLE_SIZE(sizeof(INTERLACE_PAIR_TABLE) / sizeof(InterlacePair));
85 /// Function used by Gif_Lib to read from the image file.
86 int ReadDataFromGif(GifFileType* gifInfo, GifByteType* data, int length)
88 FILE* fp = reinterpret_cast<FILE*>(gifInfo->UserData);
89 return fread(data, sizeof(GifByteType), length, fp);
92 /// Loads the GIF Header.
93 bool LoadGifHeader(FILE* fp, unsigned int& width, unsigned int& height, GifFileType** gifInfo)
95 int errorCode = 0; //D_GIF_SUCCEEDED is 0
97 #ifdef LIBGIF_VERSION_5_1_OR_ABOVE
98 *gifInfo = DGifOpen(reinterpret_cast<void*>(fp), ReadDataFromGif, &errorCode);
100 *gifInfo = DGifOpen(reinterpret_cast<void*>(fp), ReadDataFromGif);
103 if(!(*gifInfo) || errorCode)
105 DALI_LOG_ERROR("GIF Loader: DGifOpen Error. Code: %d\n", errorCode);
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, unsigned char* 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 unsigned char* 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 unsigned char* 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(Dali::Devel::PixelBuffer& bitmap, 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 Pixel::Format pixelFormat(Pixel::RGB888);
194 SavedImage* image(&gifInfo->SavedImages[gifInfo->ImageCount - 1]);
195 const GifImageDesc& desc(image->ImageDesc);
197 auto decodedData = new unsigned char[width * height * sizeof(GifPixelType)];
199 std::unique_ptr<unsigned char[]> ptr{decodedData};
201 const unsigned int bytesPerRow(width * sizeof(GifPixelType));
202 const unsigned int actualWidth(desc.Width);
203 const unsigned int actualHeight(desc.Height);
205 // Create a buffer to store the decoded data.
206 bitmap = Dali::Devel::PixelBuffer::New(actualWidth, actualHeight, pixelFormat);
208 // Decode the GIF Image
209 if(!DecodeImage(gifInfo, decodedData, actualWidth, actualHeight, bytesPerRow))
214 // Get the colormap for the GIF
215 GifColorType* color(GetImageColors(image, gifInfo));
217 // If it's an animated GIF, we still only read the first image
219 // Create and populate pixel buffer.
220 auto pixels = bitmap.GetBuffer();
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;
237 /// Called when we want to handle EXTENSION_RECORD_TYPE
238 bool HandleExtensionRecordType(GifFileType* gifInfo)
241 GifByteType* extensionByte(NULL);
243 #ifdef LIBGIF_VERSION_5_1_OR_ABOVE
244 ExtensionBlock extensionBlocks;
245 image.ExtensionBlocks = &extensionBlocks;
246 image.ExtensionBlockCount = 1;
247 int* extensionBlockTypePointer = &image.ExtensionBlocks->Function;
249 image.ExtensionBlocks = NULL;
250 image.ExtensionBlockCount = 0;
251 int* extensionBlockTypePointer = &image.Function;
254 // Not really interested in the extensions so just skip them unless there is an error.
255 for(int extRetCode = DGifGetExtension(gifInfo, extensionBlockTypePointer, &extensionByte);
256 extensionByte != NULL;
257 extRetCode = DGifGetExtensionNext(gifInfo, &extensionByte))
259 if(extRetCode == GIF_ERROR)
261 DALI_LOG_ERROR("GIF Loader: Error reading GIF Extension record.\n");
269 } // unnamed namespace
271 bool LoadGifHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
273 GifFileType* gifInfo = NULL;
274 AutoCleanupGif autoCleanupGif(gifInfo);
275 FILE* const fp = input.file;
277 return LoadGifHeader(fp, width, height, &gifInfo);
280 bool LoadBitmapFromGif(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
282 FILE* const fp = input.file;
283 // Load the GIF Header file.
285 GifFileType* gifInfo(NULL);
286 unsigned int width(0);
287 unsigned int height(0);
288 if(!LoadGifHeader(fp, width, height, &gifInfo))
292 AutoCleanupGif autoGif(gifInfo);
294 // Check each record in the GIF file.
296 bool finished(false);
297 GifRecordType recordType(UNDEFINED_RECORD_TYPE);
298 for(int returnCode = DGifGetRecordType(gifInfo, &recordType);
299 !finished && recordType != TERMINATE_RECORD_TYPE;
300 returnCode = DGifGetRecordType(gifInfo, &recordType))
302 if(returnCode == GIF_ERROR)
304 DALI_LOG_ERROR("GIF Loader: Error getting Record Type\n");
308 if(IMAGE_DESC_RECORD_TYPE == recordType)
310 if(!HandleImageDescriptionRecordType(bitmap, gifInfo, width, height, finished))
315 else if(EXTENSION_RECORD_TYPE == recordType)
317 if(!HandleExtensionRecordType(gifInfo))
327 } // namespace TizenPlatform