1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imaggif.cpp
3 // Purpose: wxGIFHandler
4 // Author: Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K
6 // Copyright: (c) 1999-2011 Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
17 #if wxUSE_IMAGE && wxUSE_GIF
22 #include "wx/palette.h"
26 #include "wx/imaggif.h"
27 #include "wx/gifdecod.h"
28 #include "wx/stream.h"
29 #include "wx/anidecod.h" // wxImageArray
31 #define GIF89_HDR "GIF89a"
32 #define NETSCAPE_LOOP "NETSCAPE2.0"
34 // see members.aol.com/royalef/gifabout.htm
35 // members.aol.com/royalef/gif89a.txt
39 GIF_MARKER_EXT
= '!', // 0x21
40 GIF_MARKER_SEP
= ',', // 0x2C
41 GIF_MARKER_ENDOFDATA
= ';', // 0x3B
43 GIF_MARKER_EXT_GRAPHICS_CONTROL
= 0xF9,
44 GIF_MARKER_EXT_COMMENT
= 0xFE,
45 GIF_MARKER_EXT_APP
= 0xFF
48 #define LZ_MAX_CODE 4095 // Biggest code possible in 12 bits.
49 #define FLUSH_OUTPUT 4096 // Impossible code, to signal flush.
50 #define FIRST_CODE 4097 // Impossible code, to signal first.
52 #define HT_SIZE 8192 // 12bits = 4096 or twice as big!
53 #define HT_KEY_MASK 0x1FFF // 13bits keys
55 #define HT_GET_KEY(l) (l >> 12)
56 #define HT_GET_CODE(l) (l & 0x0FFF)
57 #define HT_PUT_KEY(l) (l << 12)
58 #define HT_PUT_CODE(l) (l & 0x0FFF)
67 struct GifHashTableType
69 wxUint32 HTable
[HT_SIZE
];
72 /*static*/ wxString
wxGIFHandler::ms_comment
;
74 IMPLEMENT_DYNAMIC_CLASS(wxGIFHandler
,wxImageHandler
)
76 //----------------------------------------------------------------------------
77 // Forward declarations
78 //----------------------------------------------------------------------------
80 static int wxGIFHandler_KeyItem(unsigned long item
);
84 static int wxGIFHandler_BitSize(int n
);
87 static bool wxGIFHandler_GetPalette(const wxImage
& image
,
88 wxRGB
*pal
, int *palCount
, int *mask_index
);
91 int wxGIFHandler_PaletteFind(const wxRGB
& clr
, const wxRGB
*array
, int count
);
93 static bool wxGIFHandler_Write(wxOutputStream
*, const void *buf
, size_t len
);
94 static bool wxGIFHandler_WriteByte(wxOutputStream
*, wxUint8
);
95 static bool wxGIFHandler_WriteWord(wxOutputStream
*, wxUint16
);
96 static bool wxGIFHandler_WriteHeader(wxOutputStream
*, int width
, int height
,
97 bool loop
, const wxRGB
*pal
, int palCount
,
98 const wxString
& comment
= wxEmptyString
);
99 static bool wxGIFHandler_WriteRect(wxOutputStream
*, int width
, int height
);
101 static bool wxGIFHandler_WriteTerm(wxOutputStream
*);
103 static bool wxGIFHandler_WriteZero(wxOutputStream
*);
104 static bool wxGIFHandler_WritePalette(wxOutputStream
*,
105 const wxRGB
*pal
, size_t palCount
, int bpp
);
106 static bool wxGIFHandler_WriteControl(wxOutputStream
*,
107 int maskIndex
, int delayMilliSecs
);
108 static bool wxGIFHandler_WriteComment(wxOutputStream
*, const wxString
&);
109 static bool wxGIFHandler_WriteLoop(wxOutputStream
*);
111 static bool wxGIFHandler_BufferedOutput(wxOutputStream
*, wxUint8
*buf
, int c
);
112 #endif // wxUSE_STREAMS
114 //-----------------------------------------------------------------------------
116 //-----------------------------------------------------------------------------
120 bool wxGIFHandler::LoadFile(wxImage
*image
, wxInputStream
& stream
,
121 bool verbose
, int index
)
124 wxGIFErrorCode error
;
128 decod
= new wxGIFDecoder();
129 error
= decod
->LoadGIF(stream
);
131 if ((error
!= wxGIF_OK
) && (error
!= wxGIF_TRUNCATED
))
137 case wxGIF_INVFORMAT
:
138 wxLogError(_("GIF: error in GIF image format."));
141 wxLogError(_("GIF: not enough memory."));
144 wxLogError(_("GIF: unknown error!!!"));
152 if ((error
== wxGIF_TRUNCATED
) && verbose
)
154 wxLogError(_("GIF: data stream seems to be truncated."));
155 // go on; image data is OK
160 ok
= decod
->ConvertToImage(index
!= -1 ? (size_t)index
: 0, image
);
164 wxLogError(_("GIF: Invalid gif index."));
172 bool wxGIFHandler::SaveFile(wxImage
*image
,
173 wxOutputStream
& stream
, bool verbose
)
180 return wxGIFHandler_GetPalette(*image
, pal
, &palCount
, &maskIndex
)
181 && DoSaveFile(*image
, &stream
, verbose
, true /*first?*/, 0,
182 false /*loop?*/, pal
, palCount
, maskIndex
, ms_comment
)
183 && wxGIFHandler_WriteTerm(&stream
);
187 wxUnusedVar(verbose
);
192 bool wxGIFHandler::DoCanRead( wxInputStream
& stream
)
195 return decod
.CanRead(stream
);
196 // it's ok to modify the stream position here
199 int wxGIFHandler::DoGetImageCount( wxInputStream
& stream
)
202 wxGIFErrorCode error
= decod
.LoadGIF(stream
);
203 if ( (error
!= wxGIF_OK
) && (error
!= wxGIF_TRUNCATED
) )
206 // NOTE: this function modifies the current stream position but it's ok
207 // (see wxImageHandler::GetImageCount)
209 return decod
.GetFrameCount();
212 bool wxGIFHandler::DoSaveFile(const wxImage
& image
, wxOutputStream
*stream
,
213 bool WXUNUSED(verbose
), bool first
, int delayMilliSecs
, bool loop
,
214 const wxRGB
*pal
, int palCount
, int maskIndex
, const wxString
& comment
)
216 const unsigned long colorcount
= image
.CountColours(256+1);
217 bool ok
= colorcount
&& (colorcount
<= 256);
223 int width
= image
.GetWidth();
224 int height
= image
.GetHeight();
225 int width_even
= width
+ ((width
% 2) ? 1 : 0);
229 ok
= wxGIFHandler_WriteHeader(stream
, width
, height
, loop
,
230 pal
, palCount
, comment
);
233 ok
= ok
&& wxGIFHandler_WriteControl(stream
, maskIndex
, delayMilliSecs
)
234 && wxGIFHandler_WriteByte(stream
, GIF_MARKER_SEP
)
235 && wxGIFHandler_WriteRect(stream
, width
, height
);
240 // we already saved the (global) palette
241 ok
= ok
&& wxGIFHandler_WriteZero(stream
);
245 const int bpp
= wxGIFHandler_BitSize(palCount
);
251 b
&=~0x40; // clear interlaced
253 ok
= ok
&& wxGIFHandler_WriteByte(stream
, b
)
254 && wxGIFHandler_WritePalette(stream
, pal
, palCount
, bpp
);
262 if (!InitHashTable())
264 wxLogError(_("Couldn't initialize GIF hash table."));
268 const wxUint8
*src
= image
.GetData();
269 wxUint8
*eightBitData
= new wxUint8
[width
];
271 SetupCompress(stream
, 8);
273 m_pixelCount
= height
* width_even
;
274 for (int y
= 0; y
< height
; y
++)
276 m_pixelCount
-= width_even
;
277 for (int x
= 0; x
< width
; x
++)
283 int index
= wxGIFHandler_PaletteFind(rgb
, pal
, palCount
);
284 wxASSERT(index
!= wxNOT_FOUND
);
285 eightBitData
[x
] = (wxUint8
)index
;
289 ok
= CompressLine(stream
, eightBitData
, width
);
296 delete [] eightBitData
;
298 wxDELETE(m_hashTable
);
303 bool wxGIFHandler::SaveAnimation(const wxImageArray
& images
,
304 wxOutputStream
*stream
, bool verbose
, int delayMilliSecs
,
305 const wxString
& comment
)
312 for (i
= 0; (i
< images
.GetCount()) && ok
; i
++)
314 const wxImage
& image
= images
.Item(i
);
315 wxSize
temp(image
.GetWidth(), image
.GetHeight());
316 ok
= ok
&& image
.HasPalette();
319 ok
= ok
&& (size
== temp
);
327 for (i
= 0; (i
< images
.GetCount()) && ok
; i
++)
329 const wxImage
& image
= images
.Item(i
);
335 ok
= wxGIFHandler_GetPalette(image
, pal
, &palCount
, &maskIndex
)
336 && DoSaveFile(image
, stream
, verbose
, i
== 0 /*first?*/, delayMilliSecs
,
337 true /*loop?*/, pal
, palCount
, maskIndex
,
338 comment
.length() ? comment
: ms_comment
);
341 return ok
&& wxGIFHandler_WriteTerm(stream
);
345 wxUnusedVar(verbose
);
346 wxUnusedVar(delayMilliSecs
);
347 wxUnusedVar(comment
);
353 bool wxGIFHandler::CompressOutput(wxOutputStream
*stream
, int code
)
355 if (code
== FLUSH_OUTPUT
)
357 while (m_crntShiftState
> 0)
359 // Get rid of what is left in DWord, and flush it.
360 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
,
361 m_crntShiftDWord
& 0xff))
365 m_crntShiftDWord
>>= 8;
366 m_crntShiftState
-= 8;
368 m_crntShiftState
= 0; // For next time.
369 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
, FLUSH_OUTPUT
))
376 m_crntShiftDWord
|= ((long) code
) << m_crntShiftState
;
377 m_crntShiftState
+= m_runningBits
;
378 while (m_crntShiftState
>= 8)
380 // Dump out full bytes:
381 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
,
382 m_crntShiftDWord
& 0xff))
386 m_crntShiftDWord
>>= 8;
387 m_crntShiftState
-= 8;
391 // If code can't fit into RunningBits bits, must raise its size. Note
392 // however that codes above LZ_MAX_CODE are used for special signaling.
393 if ( (m_runningCode
>= m_maxCode1
) && (code
<= LZ_MAX_CODE
))
395 m_maxCode1
= 1 << ++m_runningBits
;
400 bool wxGIFHandler::SetupCompress(wxOutputStream
*stream
, int bpp
)
402 m_LZBuf
[0] = 0; // Nothing was output yet.
403 m_clearCode
= (1 << bpp
);
404 m_EOFCode
= m_clearCode
+ 1;
405 m_runningCode
= m_EOFCode
+ 1;
406 m_runningBits
= bpp
+ 1; // Number of bits per code.
407 m_maxCode1
= 1 << m_runningBits
; // Max. code + 1.
408 m_crntCode
= FIRST_CODE
; // Signal that this is first one!
409 m_crntShiftState
= 0; // No information in CrntShiftDWord.
410 m_crntShiftDWord
= 0;
412 // Clear hash table and send Clear to make sure the decoder does the same.
415 return wxGIFHandler_WriteByte(stream
, (wxUint8
)bpp
)
416 && CompressOutput(stream
, m_clearCode
);
419 bool wxGIFHandler::CompressLine(wxOutputStream
*stream
,
420 const wxUint8
*line
, int lineLen
)
422 int i
= 0, crntCode
, newCode
;
423 unsigned long newKey
;
425 if (m_crntCode
== FIRST_CODE
) // It's first time!
426 crntCode
= line
[i
++];
428 crntCode
= m_crntCode
; // Get last code in compression.
432 // Decode lineLen items.
433 pixel
= line
[i
++]; // Get next pixel from stream.
434 // Form a new unique key to search hash table for the code combines
435 // crntCode as Prefix string with Pixel as postfix char.
436 newKey
= (((unsigned long) crntCode
) << 8) + pixel
;
437 if ((newCode
= ExistsHashTable(newKey
)) >= 0)
439 // This Key is already there, or the string is old one, so
440 // simply take new code as our crntCode:
445 // Put it in hash table, output the prefix code, and make our
446 // crntCode equal to Pixel.
447 if (!CompressOutput(stream
, crntCode
))
454 // If however the HashTable is full, we send a clear first and
455 // Clear the hash table.
456 if (m_runningCode
>= LZ_MAX_CODE
)
458 // Time to do some clearance:
459 if (!CompressOutput(stream
, m_clearCode
))
464 m_runningCode
= m_EOFCode
+ 1;
465 m_runningBits
= 8 + 1;
466 m_maxCode1
= 1 << m_runningBits
;
471 // Put this unique key with its relative Code in hash table:
472 InsertHashTable(newKey
, m_runningCode
++);
476 // Preserve the current state of the compression algorithm:
477 m_crntCode
= crntCode
;
478 if (m_pixelCount
== 0)
480 // We are done - output last Code and flush output buffers:
481 if (!CompressOutput(stream
, crntCode
)
482 || !CompressOutput(stream
, m_EOFCode
)
483 || !CompressOutput(stream
, FLUSH_OUTPUT
))
492 #endif // wxUSE_STREAMS
494 bool wxGIFHandler::InitHashTable()
498 m_hashTable
= new GifHashTableType();
511 void wxGIFHandler::ClearHashTable()
514 wxUint32
*HTable
= m_hashTable
->HTable
;
518 HTable
[index
] = 0xfffffffful
;
522 void wxGIFHandler::InsertHashTable(unsigned long key
, int code
)
524 int hKey
= wxGIFHandler_KeyItem(key
);
525 wxUint32
*HTable
= m_hashTable
->HTable
;
527 while (HT_GET_KEY(HTable
[hKey
]) != 0xFFFFFL
)
529 hKey
= (hKey
+ 1) & HT_KEY_MASK
;
531 HTable
[hKey
] = HT_PUT_KEY(key
) | HT_PUT_CODE(code
);
535 int wxGIFHandler::ExistsHashTable(unsigned long key
)
537 int hKey
= wxGIFHandler_KeyItem(key
);
538 wxUint32
*HTable
= m_hashTable
->HTable
, HTKey
;
540 while ((HTKey
= HT_GET_KEY(HTable
[hKey
])) != 0xFFFFFL
)
544 return HT_GET_CODE(HTable
[hKey
]);
546 hKey
= (hKey
+ 1) & HT_KEY_MASK
;
551 // ---------------------------------------------------------------------------
552 // implementation of global private functions
553 // ---------------------------------------------------------------------------
555 int wxGIFHandler_KeyItem(unsigned long item
)
557 return ((item
>> 12) ^ item
) & HT_KEY_MASK
;
562 int wxGIFHandler_BitSize(int n
)
565 for (i
= 1; i
<= 8; i
++)
576 bool wxGIFHandler_GetPalette(const wxImage
& image
,
577 wxRGB
*pal
, int *pPalCount
, int *pMaskIndex
)
579 if (!image
.HasPalette())
584 const wxPalette
& palette
= image
.GetPalette();
585 int palCount
= palette
.GetColoursCount();
587 for (int i
= 0; i
< palCount
; ++i
)
589 if (!palette
.GetRGB(i
, &pal
[i
].red
, &pal
[i
].green
, &pal
[i
].blue
))
598 mask
.red
= image
.GetMaskRed();
599 mask
.green
= image
.GetMaskGreen();
600 mask
.blue
= image
.GetMaskBlue();
601 *pMaskIndex
= wxGIFHandler_PaletteFind(mask
, pal
, palCount
);
602 if ( (*pMaskIndex
== wxNOT_FOUND
) && (palCount
< 256))
604 *pMaskIndex
= palCount
;
605 pal
[palCount
++] = mask
;
610 *pMaskIndex
= wxNOT_FOUND
;
612 *pPalCount
= palCount
;
616 #endif // wxUSE_PALETTE
618 int wxGIFHandler_PaletteFind(const wxRGB
& clr
, const wxRGB
*array
, int count
)
620 for (int i
= 0; i
< count
; i
++)
622 if ( (clr
.red
== array
[i
].red
)
623 && (clr
.green
== array
[i
].green
)
624 && (clr
.blue
== array
[i
].blue
))
633 bool wxGIFHandler_Write(wxOutputStream
*stream
, const void *buf
, size_t len
)
635 return (len
== stream
->Write(buf
, len
).LastWrite());
638 bool wxGIFHandler_WriteByte(wxOutputStream
*stream
, wxUint8 byte
)
640 return wxGIFHandler_Write(stream
, &byte
, sizeof(byte
));
643 bool wxGIFHandler_WriteWord(wxOutputStream
*stream
, wxUint16 word
)
647 buf
[0] = word
& 0xff;
648 buf
[1] = (word
>> 8) & 0xff;
649 return wxGIFHandler_Write(stream
, &word
, sizeof(word
));
652 bool wxGIFHandler_WriteHeader(wxOutputStream
*stream
, int width
, int height
,
653 bool loop
, const wxRGB
*pal
, int palCount
, const wxString
& comment
)
655 const int bpp
= wxGIFHandler_BitSize(palCount
);
658 bool ok
= wxGIFHandler_Write(stream
, GIF89_HDR
, sizeof(GIF89_HDR
)-1)
659 && wxGIFHandler_WriteWord(stream
, (wxUint16
) width
)
660 && wxGIFHandler_WriteWord(stream
, (wxUint16
) height
);
663 buf
[0] |=(bpp
- 1) << 5;
665 buf
[1] = 0; // background color == entry 0
666 buf
[2] = 0; // aspect ratio 1:1
667 ok
= ok
&& wxGIFHandler_Write(stream
, buf
, sizeof(buf
))
668 && wxGIFHandler_WritePalette(stream
, pal
, palCount
, bpp
);
672 ok
= ok
&& wxGIFHandler_WriteLoop(stream
);
675 if (comment
.length())
677 ok
= ok
&& wxGIFHandler_WriteComment(stream
, comment
);
683 bool wxGIFHandler_WriteRect(wxOutputStream
*stream
, int width
, int height
)
685 return wxGIFHandler_WriteWord(stream
, 0) // left
686 && wxGIFHandler_WriteWord(stream
, 0) // top
687 && wxGIFHandler_WriteWord(stream
, (wxUint16
) width
)
688 && wxGIFHandler_WriteWord(stream
, (wxUint16
) height
);
692 bool wxGIFHandler_WriteTerm(wxOutputStream
*stream
)
694 return wxGIFHandler_WriteByte(stream
, GIF_MARKER_ENDOFDATA
);
698 bool wxGIFHandler_WriteZero(wxOutputStream
*stream
)
700 return wxGIFHandler_WriteByte(stream
, 0);
703 bool wxGIFHandler_WritePalette(wxOutputStream
*stream
,
704 const wxRGB
*array
, size_t count
, int bpp
)
707 for (int i
= 0; (i
< (1 << bpp
)); i
++)
711 buf
[0] = array
[i
].red
;
712 buf
[1] = array
[i
].green
;
713 buf
[2] = array
[i
].blue
;
717 buf
[0] = buf
[1] = buf
[2] = 0;
720 if ( !wxGIFHandler_Write(stream
, buf
, sizeof(buf
)) )
729 bool wxGIFHandler_WriteControl(wxOutputStream
*stream
,
730 int maskIndex
, int delayMilliSecs
)
734 buf
[0] = GIF_MARKER_EXT
; // extension marker
735 buf
[1] = GIF_MARKER_EXT_GRAPHICS_CONTROL
;
736 buf
[2] = 4; // length of block
737 buf
[3] = (maskIndex
!= wxNOT_FOUND
) ? 1 : 0; // has transparency
738 buf
[4] = delayMilliSecs
/ 10; // delay time
740 buf
[6] = (maskIndex
!= wxNOT_FOUND
) ? (wxUint8
) maskIndex
: 0;
742 return wxGIFHandler_Write(stream
, buf
, sizeof(buf
));
745 bool wxGIFHandler_WriteComment(wxOutputStream
*stream
, const wxString
& comment
)
748 wxCharBuffer
text(comment
.mb_str());
749 size_t len
= strlen(text
.data());
750 len
= wxMin(len
, 255);
752 buf
[0] = GIF_MARKER_EXT
;
753 buf
[1] = GIF_MARKER_EXT_COMMENT
;
754 buf
[2] = (wxUint8
)len
;
756 return wxGIFHandler_Write(stream
, buf
, sizeof(buf
))
757 && wxGIFHandler_Write(stream
, text
.data(), len
)
758 && wxGIFHandler_WriteZero(stream
);
761 bool wxGIFHandler_WriteLoop(wxOutputStream
*stream
)
764 const int loopcount
= 0; // infinite
766 buf
[0] = GIF_MARKER_EXT
;
767 buf
[1] = GIF_MARKER_EXT_APP
;
769 bool ok
= wxGIFHandler_Write(stream
, buf
, 3)
770 && wxGIFHandler_Write(stream
, NETSCAPE_LOOP
, sizeof(NETSCAPE_LOOP
)-1);
774 buf
[2] = loopcount
& 0xFF;
775 buf
[3] = loopcount
>> 8;
777 return ok
&& wxGIFHandler_Write(stream
, buf
, 4)
778 && wxGIFHandler_WriteZero(stream
);
781 bool wxGIFHandler_BufferedOutput(wxOutputStream
*stream
, wxUint8
*buf
, int c
)
785 if (c
== FLUSH_OUTPUT
)
787 // Flush everything out.
790 ok
= wxGIFHandler_Write(stream
, buf
, buf
[0]+1);
792 // Mark end of compressed data, by an empty block (see GIF doc):
793 wxGIFHandler_WriteZero(stream
);
799 // Dump out this buffer - it is full:
800 ok
= wxGIFHandler_Write(stream
, buf
, buf
[0] + 1);
809 #endif // wxUSE_STREAMS
811 #endif // wxUSE_IMAGE && wxUSE_GIF