Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / misc / bitmap_gif.cpp
blob3ca98593cc901b4953bd430e2e9242b9e889a326
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdmisc.h"
18 #include "nel/misc/bitmap.h"
20 #ifdef USE_GIF
21 #include <gif_lib.h>
22 #endif
24 using namespace std;
26 #ifdef DEBUG_NEW
27 #define new DEBUG_NEW
28 #endif
30 namespace NLMISC
33 #ifdef USE_GIF
35 // GIFLIB_MAJOR is defined from version 5
36 #ifndef GIFLIB_MAJOR
37 #define GIFLIB_MAJOR 4
38 #endif
40 static uint8 GIF_TRANSPARENT_MASK = 0x01;
41 static uint8 GIF_DISPOSE_MASK = 0x07;
42 static sint8 GIF_NOT_TRANSPARENT = -1;
44 static uint8 GIF_DISPOSE_NONE = 0;
45 static uint8 GIF_DISPOSE_LEAVE = 1;
46 static uint8 GIF_DISPOSE_BACKGROUND = 2;
47 static uint8 GIF_DISPOSE_RESTORE = 3;
49 static NLMISC::IStream *GIFStream = NULL;
51 #if GIFLIB_MAJOR < 5
52 static uint8 INTERLACED_OFFSET[] = { 0, 4, 2, 1 };
53 static uint8 INTERLACED_JUMP[] = { 8, 8, 4, 2 };
54 #endif
56 static int readGIFData(GifFileType *gif, GifByteType *data, int length)
58 NLMISC::IStream *f = static_cast<NLMISC::IStream *>(gif->UserData);
60 if(!f->isReading()) return 0;
62 try
64 f->serialBuffer((uint8*) data, length);
66 catch(...)
68 nlwarning("error while reading GIF image");
70 return 0;
73 return length;
76 /*-------------------------------------------------------------------*\
77 readGIF
78 \*-------------------------------------------------------------------*/
79 uint8 CBitmap::readGIF( NLMISC::IStream &f )
81 if(!f.isReading()) return false;
84 // check gif canvas dimension
85 uint16 ver;
86 uint16 width;
87 uint16 height;
89 f.serial(ver);
90 f.serial(width);
91 f.serial(height);
93 // limit image size as we are using 32bit pixels
94 // 4000x4000x4 ~ 61MiB
95 if (width*height > 4000*4000)
97 nlwarning("GIF image size is too big (width=%d, height=%d)", width, height);
98 return 0;
101 // rewind for gif decoder
102 f.seek(-10, IStream::current);
105 #if GIFLIB_MAJOR >= 5
106 sint32 errorCode;
107 GifFileType *gif = DGifOpen(&f, readGIFData, &errorCode);
108 if (gif == NULL)
110 nlwarning("failed to open gif, error=%d", errorCode);
111 return 0;
113 #else
114 GifFileType *gif = DGifOpen(&f, readGIFData);
115 if (gif == NULL)
117 nlwarning("failed to open gif, error=%d", GifLastError());
118 return 0;
120 #endif
122 // this will read and decode all frames
123 sint32 ret = DGifSlurp(gif);
124 if (ret != GIF_OK)
126 nlwarning("failed to read gif, error=%d", ret);
127 #if GIFLIB_MAJOR >= 5 && GIFLIB_MINOR >= 1
128 DGifCloseFile(gif, &errorCode);
129 #else
130 DGifCloseFile(gif);
131 #endif
132 return 0;
135 // resize target buffer
136 uint32 dstChannels = 4; // RGBA
137 resize (gif->SWidth, gif->SHeight, RGBA);
139 // make transparent
140 _Data[0].fill(0);
142 // make sure background color index exists in global colormap
143 if (gif->SColorMap && gif->SColorMap->ColorCount < gif->SBackGroundColor)
145 gif->SBackGroundColor = 0;
148 // merge all frames one by one into single target
149 ColorMapObject *ColorMap;
150 sint32 transparency = GIF_NOT_TRANSPARENT;
151 uint8 r, g, b, a;
152 uint32 offset_x, offset_y, width, height;
154 // disable loop as we only interested in first frame
155 // for (uint32 frame = 0; frame < gif->ImageCount; frame++)
157 uint32 frame = 0;
158 SavedImage *curFrame = &gif->SavedImages[frame];
160 if (curFrame->ExtensionBlockCount > 0)
162 for(sint e = 0; e < curFrame->ExtensionBlockCount; e++)
164 ExtensionBlock *ext = &curFrame->ExtensionBlocks[e];
166 if (ext->Function == GRAPHICS_EXT_FUNC_CODE)
168 uint8 flag = ext->Bytes[0];
169 //delay = (ext.Bytes[1] << 8) | ext.Bytes[2];
170 transparency = (flag & GIF_TRANSPARENT_MASK) ? ext->Bytes[3] : GIF_NOT_TRANSPARENT;
171 //dispose = ((flag >> 2) & GIF_DISPOSE_MASK);
176 // select color map for frame
177 if (curFrame->ImageDesc.ColorMap)
179 ColorMap = curFrame->ImageDesc.ColorMap;
181 else
182 if (gif->SColorMap)
184 ColorMap = gif->SColorMap;
186 else
188 nlwarning("GIF has no global or local color map");
189 ColorMap = NULL;
192 // copy frame to canvas
193 offset_x = curFrame->ImageDesc.Left;
194 offset_y = curFrame->ImageDesc.Top;
195 width = curFrame->ImageDesc.Width;
196 height = curFrame->ImageDesc.Height;
198 #if GIFLIB_MAJOR < 5
199 // giflib 4 does not handle interlaced images, so we must do it
200 if (curFrame->ImageDesc.Interlace)
202 uint32 srcOffset = 0;
203 for (uint8 pass = 0; pass < 4; pass++)
205 uint32 nextLine = INTERLACED_OFFSET[pass];
207 // y is destination row
208 for (uint32 y = 0; y < height; y++)
210 if (y != nextLine)
211 continue;
213 uint32 dstOffset = (y + offset_y)*gif->SWidth*dstChannels + offset_x*dstChannels;
214 nextLine += INTERLACED_JUMP[pass];
216 for (uint32 x = 0; x < width; x++)
218 uint32 index = curFrame->RasterBits[srcOffset];
220 if ((sint32)index != transparency)
222 // make sure color index is not outside colormap
223 if (ColorMap)
225 if ((sint)index > ColorMap->ColorCount)
227 index = 0;
229 r = ColorMap->Colors[index].Red;
230 g = ColorMap->Colors[index].Green;
231 b = ColorMap->Colors[index].Blue;
233 else
235 // broken gif, no colormap
236 r = g = b = 0;
238 a = 255;
240 else
242 // transparent
243 r = g = b = a = 0;
246 _Data[0][dstOffset] = r;
247 _Data[0][dstOffset+1] = g;
248 _Data[0][dstOffset+2] = b;
249 _Data[0][dstOffset+3] = a;
251 srcOffset++;
252 dstOffset+= dstChannels;
253 } // x loop
254 } // y loop
255 } // pass loop
257 else
258 #endif
259 for (uint32 y = 0; y < height; y++)
261 uint32 srcOffset = y*width;
262 uint32 dstOffset = (y + offset_y)*gif->SWidth*dstChannels + offset_x*dstChannels;
263 for (uint32 x = 0; x < width; x++)
265 uint32 index = curFrame->RasterBits[srcOffset];
267 if ((sint32)index != transparency)
269 // make sure color index is not outside colormap
270 if (ColorMap)
272 if ((sint)index > ColorMap->ColorCount)
274 index = 0;
276 r = ColorMap->Colors[index].Red;
277 g = ColorMap->Colors[index].Green;
278 b = ColorMap->Colors[index].Blue;
280 else
282 // broken gif, no colormap
283 r = g = b = 0;
285 a = 255;
287 else
289 // transparent
290 r = g = b = a = 0;
293 _Data[0][dstOffset] = r;
294 _Data[0][dstOffset+1] = g;
295 _Data[0][dstOffset+2] = b;
296 _Data[0][dstOffset+3] = a;
298 srcOffset++;
299 dstOffset+= dstChannels;
300 } // x loop
301 } // y loop
304 // clean up after the read, and free any memory allocated
305 #if GIFLIB_MAJOR >= 5 && GIFLIB_MINOR >= 1
306 DGifCloseFile(gif, &errorCode);
307 #else
308 DGifCloseFile(gif);
309 #endif
311 //return the size of a pixel as 32bits
312 return 32;
315 #else
317 uint8 CBitmap::readGIF( NLMISC::IStream &/* f */)
319 nlwarning ("You must compile NLMISC with USE_GIF if you want gif support");
320 return 0;
323 #endif
324 }//namespace