1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
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.
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/>.
18 #include "nel/misc/bitmap.h"
35 // GIFLIB_MAJOR is defined from version 5
37 #define GIFLIB_MAJOR 4
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
;
52 static uint8 INTERLACED_OFFSET
[] = { 0, 4, 2, 1 };
53 static uint8 INTERLACED_JUMP
[] = { 8, 8, 4, 2 };
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;
64 f
->serialBuffer((uint8
*) data
, length
);
68 nlwarning("error while reading GIF image");
76 /*-------------------------------------------------------------------*\
78 \*-------------------------------------------------------------------*/
79 uint8
CBitmap::readGIF( NLMISC::IStream
&f
)
81 if(!f
.isReading()) return false;
84 // check gif canvas dimension
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
);
101 // rewind for gif decoder
102 f
.seek(-10, IStream::current
);
105 #if GIFLIB_MAJOR >= 5
107 GifFileType
*gif
= DGifOpen(&f
, readGIFData
, &errorCode
);
110 nlwarning("failed to open gif, error=%d", errorCode
);
114 GifFileType
*gif
= DGifOpen(&f
, readGIFData
);
117 nlwarning("failed to open gif, error=%d", GifLastError());
122 // this will read and decode all frames
123 sint32 ret
= DGifSlurp(gif
);
126 nlwarning("failed to read gif, error=%d", ret
);
127 #if GIFLIB_MAJOR >= 5 && GIFLIB_MINOR >= 1
128 DGifCloseFile(gif
, &errorCode
);
135 // resize target buffer
136 uint32 dstChannels
= 4; // RGBA
137 resize (gif
->SWidth
, gif
->SHeight
, RGBA
);
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
;
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++)
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
;
184 ColorMap
= gif
->SColorMap
;
188 nlwarning("GIF has no global or local color map");
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
;
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
++)
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
225 if ((sint
)index
> ColorMap
->ColorCount
)
229 r
= ColorMap
->Colors
[index
].Red
;
230 g
= ColorMap
->Colors
[index
].Green
;
231 b
= ColorMap
->Colors
[index
].Blue
;
235 // broken gif, no colormap
246 _Data
[0][dstOffset
] = r
;
247 _Data
[0][dstOffset
+1] = g
;
248 _Data
[0][dstOffset
+2] = b
;
249 _Data
[0][dstOffset
+3] = a
;
252 dstOffset
+= dstChannels
;
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
272 if ((sint
)index
> ColorMap
->ColorCount
)
276 r
= ColorMap
->Colors
[index
].Red
;
277 g
= ColorMap
->Colors
[index
].Green
;
278 b
= ColorMap
->Colors
[index
].Blue
;
282 // broken gif, no colormap
293 _Data
[0][dstOffset
] = r
;
294 _Data
[0][dstOffset
+1] = g
;
295 _Data
[0][dstOffset
+2] = b
;
296 _Data
[0][dstOffset
+3] = a
;
299 dstOffset
+= dstChannels
;
304 // clean up after the read, and free any memory allocated
305 #if GIFLIB_MAJOR >= 5 && GIFLIB_MINOR >= 1
306 DGifCloseFile(gif
, &errorCode
);
311 //return the size of a pixel as 32bits
317 uint8
CBitmap::readGIF( NLMISC::IStream
&/* f */)
319 nlwarning ("You must compile NLMISC with USE_GIF if you want gif support");