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 IMPLEMENT_DYNAMIC_CLASS(wxGIFHandler
,wxImageHandler
)
74 //----------------------------------------------------------------------------
75 // Forward declarations
76 //----------------------------------------------------------------------------
78 static int wxGIFHandler_KeyItem(unsigned long item
);
82 static int wxGIFHandler_BitSize(int n
);
85 static bool wxGIFHandler_GetPalette(const wxImage
& image
,
86 wxRGB
*pal
, int *palCount
, int *mask_index
);
89 int wxGIFHandler_PaletteFind(const wxRGB
& clr
, const wxRGB
*array
, int count
);
91 static bool wxGIFHandler_Write(wxOutputStream
*, const void *buf
, size_t len
);
92 static bool wxGIFHandler_WriteByte(wxOutputStream
*, wxUint8
);
93 static bool wxGIFHandler_WriteWord(wxOutputStream
*, wxUint16
);
94 static bool wxGIFHandler_WriteHeader(wxOutputStream
*, int width
, int height
,
95 bool loop
, const wxRGB
*pal
, int palCount
,
96 const wxString
& comment
= wxEmptyString
);
97 static bool wxGIFHandler_WriteRect(wxOutputStream
*, int width
, int height
);
99 static bool wxGIFHandler_WriteTerm(wxOutputStream
*);
101 static bool wxGIFHandler_WriteZero(wxOutputStream
*);
102 static bool wxGIFHandler_WritePalette(wxOutputStream
*,
103 const wxRGB
*pal
, size_t palCount
, int bpp
);
104 static bool wxGIFHandler_WriteControl(wxOutputStream
*,
105 int maskIndex
, int delayMilliSecs
);
106 static bool wxGIFHandler_WriteComment(wxOutputStream
*, const wxString
&);
107 static bool wxGIFHandler_WriteLoop(wxOutputStream
*);
109 static bool wxGIFHandler_BufferedOutput(wxOutputStream
*, wxUint8
*buf
, int c
);
110 #endif // wxUSE_STREAMS
112 //-----------------------------------------------------------------------------
114 //-----------------------------------------------------------------------------
118 bool wxGIFHandler::LoadFile(wxImage
*image
, wxInputStream
& stream
,
119 bool verbose
, int index
)
122 wxGIFErrorCode error
;
126 decod
= new wxGIFDecoder();
127 error
= decod
->LoadGIF(stream
);
129 if ((error
!= wxGIF_OK
) && (error
!= wxGIF_TRUNCATED
))
135 case wxGIF_INVFORMAT
:
136 wxLogError(_("GIF: error in GIF image format."));
139 wxLogError(_("GIF: not enough memory."));
142 wxLogError(_("GIF: unknown error!!!"));
150 if ((error
== wxGIF_TRUNCATED
) && verbose
)
152 wxLogError(_("GIF: data stream seems to be truncated."));
153 // go on; image data is OK
158 ok
= decod
->ConvertToImage(index
!= -1 ? (size_t)index
: 0, image
);
162 wxLogError(_("GIF: Invalid gif index."));
170 bool wxGIFHandler::SaveFile(wxImage
*image
,
171 wxOutputStream
& stream
, bool verbose
)
178 return wxGIFHandler_GetPalette(*image
, pal
, &palCount
, &maskIndex
)
179 && DoSaveFile(*image
, &stream
, verbose
, true /*first?*/, 0,
180 false /*loop?*/, pal
, palCount
, maskIndex
)
181 && wxGIFHandler_WriteTerm(&stream
);
185 wxUnusedVar(verbose
);
190 bool wxGIFHandler::DoCanRead( wxInputStream
& stream
)
193 return decod
.CanRead(stream
);
194 // it's ok to modify the stream position here
197 int wxGIFHandler::DoGetImageCount( wxInputStream
& stream
)
200 wxGIFErrorCode error
= decod
.LoadGIF(stream
);
201 if ( (error
!= wxGIF_OK
) && (error
!= wxGIF_TRUNCATED
) )
204 // NOTE: this function modifies the current stream position but it's ok
205 // (see wxImageHandler::GetImageCount)
207 return decod
.GetFrameCount();
210 bool wxGIFHandler::DoSaveFile(const wxImage
& image
, wxOutputStream
*stream
,
211 bool WXUNUSED(verbose
), bool first
, int delayMilliSecs
, bool loop
,
212 const wxRGB
*pal
, int palCount
, int maskIndex
)
214 const unsigned long colorcount
= image
.CountColours(256+1);
215 bool ok
= colorcount
&& (colorcount
<= 256);
221 int width
= image
.GetWidth();
222 int height
= image
.GetHeight();
223 int width_even
= width
+ ((width
% 2) ? 1 : 0);
227 ok
= wxGIFHandler_WriteHeader(stream
, width
, height
, loop
,
228 pal
, palCount
, image
.GetOption(wxIMAGE_OPTION_GIF_COMMENT
));
231 ok
= ok
&& wxGIFHandler_WriteControl(stream
, maskIndex
, delayMilliSecs
)
232 && wxGIFHandler_WriteByte(stream
, GIF_MARKER_SEP
)
233 && wxGIFHandler_WriteRect(stream
, width
, height
);
238 // we already saved the (global) palette
239 ok
= ok
&& wxGIFHandler_WriteZero(stream
);
243 const int bpp
= wxGIFHandler_BitSize(palCount
);
249 b
&=~0x40; // clear interlaced
251 ok
= ok
&& wxGIFHandler_WriteByte(stream
, b
)
252 && wxGIFHandler_WritePalette(stream
, pal
, palCount
, bpp
);
260 if (!InitHashTable())
262 wxLogError(_("Couldn't initialize GIF hash table."));
266 const wxUint8
*src
= image
.GetData();
267 wxUint8
*eightBitData
= new wxUint8
[width
];
269 SetupCompress(stream
, 8);
271 m_pixelCount
= height
* width_even
;
272 for (int y
= 0; y
< height
; y
++)
274 m_pixelCount
-= width_even
;
275 for (int x
= 0; x
< width
; x
++)
281 int index
= wxGIFHandler_PaletteFind(rgb
, pal
, palCount
);
282 wxASSERT(index
!= wxNOT_FOUND
);
283 eightBitData
[x
] = (wxUint8
)index
;
287 ok
= CompressLine(stream
, eightBitData
, width
);
294 delete [] eightBitData
;
296 wxDELETE(m_hashTable
);
301 bool wxGIFHandler::SaveAnimation(const wxImageArray
& images
,
302 wxOutputStream
*stream
, bool verbose
, int delayMilliSecs
)
309 for (i
= 0; (i
< images
.GetCount()) && ok
; i
++)
311 const wxImage
& image
= images
.Item(i
);
312 wxSize
temp(image
.GetWidth(), image
.GetHeight());
313 ok
= ok
&& image
.HasPalette();
316 ok
= ok
&& (size
== temp
);
324 for (i
= 0; (i
< images
.GetCount()) && ok
; i
++)
326 const wxImage
& image
= images
.Item(i
);
332 ok
= wxGIFHandler_GetPalette(image
, pal
, &palCount
, &maskIndex
)
333 && DoSaveFile(image
, stream
, verbose
, i
== 0 /*first?*/, delayMilliSecs
,
334 true /*loop?*/, pal
, palCount
, maskIndex
);
337 return ok
&& wxGIFHandler_WriteTerm(stream
);
341 wxUnusedVar(verbose
);
342 wxUnusedVar(delayMilliSecs
);
348 bool wxGIFHandler::CompressOutput(wxOutputStream
*stream
, int code
)
350 if (code
== FLUSH_OUTPUT
)
352 while (m_crntShiftState
> 0)
354 // Get rid of what is left in DWord, and flush it.
355 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
,
356 m_crntShiftDWord
& 0xff))
360 m_crntShiftDWord
>>= 8;
361 m_crntShiftState
-= 8;
363 m_crntShiftState
= 0; // For next time.
364 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
, FLUSH_OUTPUT
))
371 m_crntShiftDWord
|= ((long) code
) << m_crntShiftState
;
372 m_crntShiftState
+= m_runningBits
;
373 while (m_crntShiftState
>= 8)
375 // Dump out full bytes:
376 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
,
377 m_crntShiftDWord
& 0xff))
381 m_crntShiftDWord
>>= 8;
382 m_crntShiftState
-= 8;
386 // If code can't fit into RunningBits bits, must raise its size. Note
387 // however that codes above LZ_MAX_CODE are used for special signaling.
388 if ( (m_runningCode
>= m_maxCode1
) && (code
<= LZ_MAX_CODE
))
390 m_maxCode1
= 1 << ++m_runningBits
;
395 bool wxGIFHandler::SetupCompress(wxOutputStream
*stream
, int bpp
)
397 m_LZBuf
[0] = 0; // Nothing was output yet.
398 m_clearCode
= (1 << bpp
);
399 m_EOFCode
= m_clearCode
+ 1;
400 m_runningCode
= m_EOFCode
+ 1;
401 m_runningBits
= bpp
+ 1; // Number of bits per code.
402 m_maxCode1
= 1 << m_runningBits
; // Max. code + 1.
403 m_crntCode
= FIRST_CODE
; // Signal that this is first one!
404 m_crntShiftState
= 0; // No information in CrntShiftDWord.
405 m_crntShiftDWord
= 0;
407 // Clear hash table and send Clear to make sure the decoder does the same.
410 return wxGIFHandler_WriteByte(stream
, (wxUint8
)bpp
)
411 && CompressOutput(stream
, m_clearCode
);
414 bool wxGIFHandler::CompressLine(wxOutputStream
*stream
,
415 const wxUint8
*line
, int lineLen
)
417 int i
= 0, crntCode
, newCode
;
418 unsigned long newKey
;
420 if (m_crntCode
== FIRST_CODE
) // It's first time!
421 crntCode
= line
[i
++];
423 crntCode
= m_crntCode
; // Get last code in compression.
427 // Decode lineLen items.
428 pixel
= line
[i
++]; // Get next pixel from stream.
429 // Form a new unique key to search hash table for the code combines
430 // crntCode as Prefix string with Pixel as postfix char.
431 newKey
= (((unsigned long) crntCode
) << 8) + pixel
;
432 if ((newCode
= ExistsHashTable(newKey
)) >= 0)
434 // This Key is already there, or the string is old one, so
435 // simply take new code as our crntCode:
440 // Put it in hash table, output the prefix code, and make our
441 // crntCode equal to Pixel.
442 if (!CompressOutput(stream
, crntCode
))
449 // If however the HashTable is full, we send a clear first and
450 // Clear the hash table.
451 if (m_runningCode
>= LZ_MAX_CODE
)
453 // Time to do some clearance:
454 if (!CompressOutput(stream
, m_clearCode
))
459 m_runningCode
= m_EOFCode
+ 1;
460 m_runningBits
= 8 + 1;
461 m_maxCode1
= 1 << m_runningBits
;
466 // Put this unique key with its relative Code in hash table:
467 InsertHashTable(newKey
, m_runningCode
++);
471 // Preserve the current state of the compression algorithm:
472 m_crntCode
= crntCode
;
473 if (m_pixelCount
== 0)
475 // We are done - output last Code and flush output buffers:
476 if (!CompressOutput(stream
, crntCode
)
477 || !CompressOutput(stream
, m_EOFCode
)
478 || !CompressOutput(stream
, FLUSH_OUTPUT
))
487 #endif // wxUSE_STREAMS
489 bool wxGIFHandler::InitHashTable()
493 m_hashTable
= new GifHashTableType();
506 void wxGIFHandler::ClearHashTable()
509 wxUint32
*HTable
= m_hashTable
->HTable
;
513 HTable
[index
] = 0xfffffffful
;
517 void wxGIFHandler::InsertHashTable(unsigned long key
, int code
)
519 int hKey
= wxGIFHandler_KeyItem(key
);
520 wxUint32
*HTable
= m_hashTable
->HTable
;
522 while (HT_GET_KEY(HTable
[hKey
]) != 0xFFFFFL
)
524 hKey
= (hKey
+ 1) & HT_KEY_MASK
;
526 HTable
[hKey
] = HT_PUT_KEY(key
) | HT_PUT_CODE(code
);
530 int wxGIFHandler::ExistsHashTable(unsigned long key
)
532 int hKey
= wxGIFHandler_KeyItem(key
);
533 wxUint32
*HTable
= m_hashTable
->HTable
, HTKey
;
535 while ((HTKey
= HT_GET_KEY(HTable
[hKey
])) != 0xFFFFFL
)
539 return HT_GET_CODE(HTable
[hKey
]);
541 hKey
= (hKey
+ 1) & HT_KEY_MASK
;
546 // ---------------------------------------------------------------------------
547 // implementation of global private functions
548 // ---------------------------------------------------------------------------
550 int wxGIFHandler_KeyItem(unsigned long item
)
552 return ((item
>> 12) ^ item
) & HT_KEY_MASK
;
557 int wxGIFHandler_BitSize(int n
)
560 for (i
= 1; i
<= 8; i
++)
571 bool wxGIFHandler_GetPalette(const wxImage
& image
,
572 wxRGB
*pal
, int *pPalCount
, int *pMaskIndex
)
574 if (!image
.HasPalette())
579 const wxPalette
& palette
= image
.GetPalette();
580 int palCount
= palette
.GetColoursCount();
582 for (int i
= 0; i
< palCount
; ++i
)
584 if (!palette
.GetRGB(i
, &pal
[i
].red
, &pal
[i
].green
, &pal
[i
].blue
))
593 mask
.red
= image
.GetMaskRed();
594 mask
.green
= image
.GetMaskGreen();
595 mask
.blue
= image
.GetMaskBlue();
596 *pMaskIndex
= wxGIFHandler_PaletteFind(mask
, pal
, palCount
);
597 if ( (*pMaskIndex
== wxNOT_FOUND
) && (palCount
< 256))
599 *pMaskIndex
= palCount
;
600 pal
[palCount
++] = mask
;
605 *pMaskIndex
= wxNOT_FOUND
;
607 *pPalCount
= palCount
;
611 #endif // wxUSE_PALETTE
613 int wxGIFHandler_PaletteFind(const wxRGB
& clr
, const wxRGB
*array
, int count
)
615 for (int i
= 0; i
< count
; i
++)
617 if ( (clr
.red
== array
[i
].red
)
618 && (clr
.green
== array
[i
].green
)
619 && (clr
.blue
== array
[i
].blue
))
628 bool wxGIFHandler_Write(wxOutputStream
*stream
, const void *buf
, size_t len
)
630 return (len
== stream
->Write(buf
, len
).LastWrite());
633 bool wxGIFHandler_WriteByte(wxOutputStream
*stream
, wxUint8 byte
)
635 return wxGIFHandler_Write(stream
, &byte
, sizeof(byte
));
638 bool wxGIFHandler_WriteWord(wxOutputStream
*stream
, wxUint16 word
)
642 buf
[0] = word
& 0xff;
643 buf
[1] = (word
>> 8) & 0xff;
644 return wxGIFHandler_Write(stream
, &word
, sizeof(word
));
647 bool wxGIFHandler_WriteHeader(wxOutputStream
*stream
, int width
, int height
,
648 bool loop
, const wxRGB
*pal
, int palCount
, const wxString
& comment
)
650 const int bpp
= wxGIFHandler_BitSize(palCount
);
653 bool ok
= wxGIFHandler_Write(stream
, GIF89_HDR
, sizeof(GIF89_HDR
)-1)
654 && wxGIFHandler_WriteWord(stream
, (wxUint16
) width
)
655 && wxGIFHandler_WriteWord(stream
, (wxUint16
) height
);
658 buf
[0] |=(bpp
- 1) << 5;
660 buf
[1] = 0; // background color == entry 0
661 buf
[2] = 0; // aspect ratio 1:1
662 ok
= ok
&& wxGIFHandler_Write(stream
, buf
, sizeof(buf
))
663 && wxGIFHandler_WritePalette(stream
, pal
, palCount
, bpp
);
667 ok
= ok
&& wxGIFHandler_WriteLoop(stream
);
670 return ok
&& wxGIFHandler_WriteComment(stream
, comment
);
673 bool wxGIFHandler_WriteRect(wxOutputStream
*stream
, int width
, int height
)
675 return wxGIFHandler_WriteWord(stream
, 0) // left
676 && wxGIFHandler_WriteWord(stream
, 0) // top
677 && wxGIFHandler_WriteWord(stream
, (wxUint16
) width
)
678 && wxGIFHandler_WriteWord(stream
, (wxUint16
) height
);
682 bool wxGIFHandler_WriteTerm(wxOutputStream
*stream
)
684 return wxGIFHandler_WriteByte(stream
, GIF_MARKER_ENDOFDATA
);
688 bool wxGIFHandler_WriteZero(wxOutputStream
*stream
)
690 return wxGIFHandler_WriteByte(stream
, 0);
693 bool wxGIFHandler_WritePalette(wxOutputStream
*stream
,
694 const wxRGB
*array
, size_t count
, int bpp
)
697 for (int i
= 0; (i
< (1 << bpp
)); i
++)
701 buf
[0] = array
[i
].red
;
702 buf
[1] = array
[i
].green
;
703 buf
[2] = array
[i
].blue
;
707 buf
[0] = buf
[1] = buf
[2] = 0;
710 if ( !wxGIFHandler_Write(stream
, buf
, sizeof(buf
)) )
719 bool wxGIFHandler_WriteControl(wxOutputStream
*stream
,
720 int maskIndex
, int delayMilliSecs
)
724 buf
[0] = GIF_MARKER_EXT
; // extension marker
725 buf
[1] = GIF_MARKER_EXT_GRAPHICS_CONTROL
;
726 buf
[2] = 4; // length of block
727 buf
[3] = (maskIndex
!= wxNOT_FOUND
) ? 1 : 0; // has transparency
728 buf
[4] = delayMilliSecs
/ 10; // delay time
730 buf
[6] = (maskIndex
!= wxNOT_FOUND
) ? (wxUint8
) maskIndex
: 0;
732 return wxGIFHandler_Write(stream
, buf
, sizeof(buf
));
735 bool wxGIFHandler_WriteComment(wxOutputStream
*stream
, const wxString
& comment
)
737 if ( comment
.empty() )
742 // Write comment header.
744 buf
[0] = GIF_MARKER_EXT
;
745 buf
[1] = GIF_MARKER_EXT_COMMENT
;
746 if ( !wxGIFHandler_Write(stream
, buf
, sizeof(buf
)) )
752 If comment is longer than 255 bytes write it in blocks of maximum 255
755 wxCharBuffer
text( comment
.mb_str() );
757 size_t pos
= 0, fullLength
= text
.length();
761 size_t blockLength
= wxMin(fullLength
- pos
, 255);
763 if ( !wxGIFHandler_WriteByte(stream
, (wxUint8
) blockLength
)
764 || !wxGIFHandler_Write(stream
, &text
.data()[pos
], blockLength
) )
770 }while (pos
< fullLength
);
773 // Write comment footer.
774 return wxGIFHandler_WriteZero(stream
);
777 bool wxGIFHandler_WriteLoop(wxOutputStream
*stream
)
780 const int loopcount
= 0; // infinite
782 buf
[0] = GIF_MARKER_EXT
;
783 buf
[1] = GIF_MARKER_EXT_APP
;
785 bool ok
= wxGIFHandler_Write(stream
, buf
, 3)
786 && wxGIFHandler_Write(stream
, NETSCAPE_LOOP
, sizeof(NETSCAPE_LOOP
)-1);
790 buf
[2] = loopcount
& 0xFF;
791 buf
[3] = loopcount
>> 8;
793 return ok
&& wxGIFHandler_Write(stream
, buf
, 4)
794 && wxGIFHandler_WriteZero(stream
);
797 bool wxGIFHandler_BufferedOutput(wxOutputStream
*stream
, wxUint8
*buf
, int c
)
801 if (c
== FLUSH_OUTPUT
)
803 // Flush everything out.
806 ok
= wxGIFHandler_Write(stream
, buf
, buf
[0]+1);
808 // Mark end of compressed data, by an empty block (see GIF doc):
809 wxGIFHandler_WriteZero(stream
);
815 // Dump out this buffer - it is full:
816 ok
= wxGIFHandler_Write(stream
, buf
, buf
[0] + 1);
825 #endif // wxUSE_STREAMS
827 #endif // wxUSE_IMAGE && wxUSE_GIF