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 static bool wxGIFHandler_WriteRect(wxOutputStream
*, int width
, int height
);
98 static bool wxGIFHandler_WriteTerm(wxOutputStream
*);
100 static bool wxGIFHandler_WriteZero(wxOutputStream
*);
101 static bool wxGIFHandler_WritePalette(wxOutputStream
*,
102 const wxRGB
*pal
, size_t palCount
, int bpp
);
103 static bool wxGIFHandler_WriteControl(wxOutputStream
*,
104 int maskIndex
, int delayMilliSecs
);
105 static bool wxGIFHandler_WriteComment(wxOutputStream
*, const wxString
&);
106 static bool wxGIFHandler_WriteLoop(wxOutputStream
*);
108 static bool wxGIFHandler_BufferedOutput(wxOutputStream
*, wxUint8
*buf
, int c
);
109 #endif // wxUSE_STREAMS
111 //-----------------------------------------------------------------------------
113 //-----------------------------------------------------------------------------
117 bool wxGIFHandler::LoadFile(wxImage
*image
, wxInputStream
& stream
,
118 bool verbose
, int index
)
121 wxGIFErrorCode error
;
125 decod
= new wxGIFDecoder();
126 error
= decod
->LoadGIF(stream
);
128 if ((error
!= wxGIF_OK
) && (error
!= wxGIF_TRUNCATED
))
134 case wxGIF_INVFORMAT
:
135 wxLogError(_("GIF: error in GIF image format."));
138 wxLogError(_("GIF: not enough memory."));
141 wxLogError(_("GIF: unknown error!!!"));
149 if ((error
== wxGIF_TRUNCATED
) && verbose
)
151 wxLogError(_("GIF: data stream seems to be truncated."));
152 // go on; image data is OK
157 ok
= decod
->ConvertToImage(index
!= -1 ? (size_t)index
: 0, image
);
161 wxLogError(_("GIF: Invalid gif index."));
169 bool wxGIFHandler::SaveFile(wxImage
*image
,
170 wxOutputStream
& stream
, bool verbose
)
177 return wxGIFHandler_GetPalette(*image
, pal
, &palCount
, &maskIndex
)
178 && DoSaveFile(*image
, &stream
, verbose
, true /*first?*/, 0,
179 false /*loop?*/, pal
, palCount
, maskIndex
)
180 && wxGIFHandler_WriteTerm(&stream
);
184 wxUnusedVar(verbose
);
189 bool wxGIFHandler::DoCanRead( wxInputStream
& stream
)
192 return decod
.CanRead(stream
);
193 // it's ok to modify the stream position here
196 int wxGIFHandler::DoGetImageCount( wxInputStream
& stream
)
199 wxGIFErrorCode error
= decod
.LoadGIF(stream
);
200 if ( (error
!= wxGIF_OK
) && (error
!= wxGIF_TRUNCATED
) )
203 // NOTE: this function modifies the current stream position but it's ok
204 // (see wxImageHandler::GetImageCount)
206 return decod
.GetFrameCount();
209 bool wxGIFHandler::DoSaveFile(const wxImage
& image
, wxOutputStream
*stream
,
210 bool WXUNUSED(verbose
), bool first
, int delayMilliSecs
, bool loop
,
211 const wxRGB
*pal
, int palCount
, int maskIndex
)
213 const unsigned long colorcount
= image
.CountColours(256+1);
214 bool ok
= colorcount
&& (colorcount
<= 256);
220 int width
= image
.GetWidth();
221 int height
= image
.GetHeight();
222 int width_even
= width
+ ((width
% 2) ? 1 : 0);
226 ok
= wxGIFHandler_WriteHeader(stream
, width
, height
, loop
,
231 && wxGIFHandler_WriteComment(stream
,
232 image
.GetOption(wxIMAGE_OPTION_GIF_COMMENT
))
233 && 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
)
311 for (i
= 0; (i
< images
.GetCount()) && ok
; i
++)
313 const wxImage
& image
= images
.Item(i
);
314 wxSize
temp(image
.GetWidth(), image
.GetHeight());
315 ok
= ok
&& image
.HasPalette();
318 ok
= ok
&& (size
== temp
);
326 for (i
= 0; (i
< images
.GetCount()) && ok
; i
++)
328 const wxImage
& image
= images
.Item(i
);
334 ok
= wxGIFHandler_GetPalette(image
, pal
, &palCount
, &maskIndex
)
335 && DoSaveFile(image
, stream
, verbose
, i
== 0 /*first?*/, delayMilliSecs
,
336 true /*loop?*/, pal
, palCount
, maskIndex
);
339 return ok
&& wxGIFHandler_WriteTerm(stream
);
343 wxUnusedVar(verbose
);
344 wxUnusedVar(delayMilliSecs
);
350 bool wxGIFHandler::CompressOutput(wxOutputStream
*stream
, int code
)
352 if (code
== FLUSH_OUTPUT
)
354 while (m_crntShiftState
> 0)
356 // Get rid of what is left in DWord, and flush it.
357 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
,
358 m_crntShiftDWord
& 0xff))
362 m_crntShiftDWord
>>= 8;
363 m_crntShiftState
-= 8;
365 m_crntShiftState
= 0; // For next time.
366 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
, FLUSH_OUTPUT
))
373 m_crntShiftDWord
|= ((long) code
) << m_crntShiftState
;
374 m_crntShiftState
+= m_runningBits
;
375 while (m_crntShiftState
>= 8)
377 // Dump out full bytes:
378 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
,
379 m_crntShiftDWord
& 0xff))
383 m_crntShiftDWord
>>= 8;
384 m_crntShiftState
-= 8;
388 // If code can't fit into RunningBits bits, must raise its size. Note
389 // however that codes above LZ_MAX_CODE are used for special signaling.
390 if ( (m_runningCode
>= m_maxCode1
) && (code
<= LZ_MAX_CODE
))
392 m_maxCode1
= 1 << ++m_runningBits
;
397 bool wxGIFHandler::SetupCompress(wxOutputStream
*stream
, int bpp
)
399 m_LZBuf
[0] = 0; // Nothing was output yet.
400 m_clearCode
= (1 << bpp
);
401 m_EOFCode
= m_clearCode
+ 1;
402 m_runningCode
= m_EOFCode
+ 1;
403 m_runningBits
= bpp
+ 1; // Number of bits per code.
404 m_maxCode1
= 1 << m_runningBits
; // Max. code + 1.
405 m_crntCode
= FIRST_CODE
; // Signal that this is first one!
406 m_crntShiftState
= 0; // No information in CrntShiftDWord.
407 m_crntShiftDWord
= 0;
409 // Clear hash table and send Clear to make sure the decoder does the same.
412 return wxGIFHandler_WriteByte(stream
, (wxUint8
)bpp
)
413 && CompressOutput(stream
, m_clearCode
);
416 bool wxGIFHandler::CompressLine(wxOutputStream
*stream
,
417 const wxUint8
*line
, int lineLen
)
419 int i
= 0, crntCode
, newCode
;
420 unsigned long newKey
;
422 if (m_crntCode
== FIRST_CODE
) // It's first time!
423 crntCode
= line
[i
++];
425 crntCode
= m_crntCode
; // Get last code in compression.
429 // Decode lineLen items.
430 pixel
= line
[i
++]; // Get next pixel from stream.
431 // Form a new unique key to search hash table for the code combines
432 // crntCode as Prefix string with Pixel as postfix char.
433 newKey
= (((unsigned long) crntCode
) << 8) + pixel
;
434 if ((newCode
= ExistsHashTable(newKey
)) >= 0)
436 // This Key is already there, or the string is old one, so
437 // simply take new code as our crntCode:
442 // Put it in hash table, output the prefix code, and make our
443 // crntCode equal to Pixel.
444 if (!CompressOutput(stream
, crntCode
))
451 // If however the HashTable is full, we send a clear first and
452 // Clear the hash table.
453 if (m_runningCode
>= LZ_MAX_CODE
)
455 // Time to do some clearance:
456 if (!CompressOutput(stream
, m_clearCode
))
461 m_runningCode
= m_EOFCode
+ 1;
462 m_runningBits
= 8 + 1;
463 m_maxCode1
= 1 << m_runningBits
;
468 // Put this unique key with its relative Code in hash table:
469 InsertHashTable(newKey
, m_runningCode
++);
473 // Preserve the current state of the compression algorithm:
474 m_crntCode
= crntCode
;
475 if (m_pixelCount
== 0)
477 // We are done - output last Code and flush output buffers:
478 if (!CompressOutput(stream
, crntCode
)
479 || !CompressOutput(stream
, m_EOFCode
)
480 || !CompressOutput(stream
, FLUSH_OUTPUT
))
489 #endif // wxUSE_STREAMS
491 bool wxGIFHandler::InitHashTable()
495 m_hashTable
= new GifHashTableType();
508 void wxGIFHandler::ClearHashTable()
511 wxUint32
*HTable
= m_hashTable
->HTable
;
515 HTable
[index
] = 0xfffffffful
;
519 void wxGIFHandler::InsertHashTable(unsigned long key
, int code
)
521 int hKey
= wxGIFHandler_KeyItem(key
);
522 wxUint32
*HTable
= m_hashTable
->HTable
;
524 while (HT_GET_KEY(HTable
[hKey
]) != 0xFFFFFL
)
526 hKey
= (hKey
+ 1) & HT_KEY_MASK
;
528 HTable
[hKey
] = HT_PUT_KEY(key
) | HT_PUT_CODE(code
);
532 int wxGIFHandler::ExistsHashTable(unsigned long key
)
534 int hKey
= wxGIFHandler_KeyItem(key
);
535 wxUint32
*HTable
= m_hashTable
->HTable
, HTKey
;
537 while ((HTKey
= HT_GET_KEY(HTable
[hKey
])) != 0xFFFFFL
)
541 return HT_GET_CODE(HTable
[hKey
]);
543 hKey
= (hKey
+ 1) & HT_KEY_MASK
;
548 // ---------------------------------------------------------------------------
549 // implementation of global private functions
550 // ---------------------------------------------------------------------------
552 int wxGIFHandler_KeyItem(unsigned long item
)
554 return ((item
>> 12) ^ item
) & HT_KEY_MASK
;
559 int wxGIFHandler_BitSize(int n
)
562 for (i
= 1; i
<= 8; i
++)
573 bool wxGIFHandler_GetPalette(const wxImage
& image
,
574 wxRGB
*pal
, int *pPalCount
, int *pMaskIndex
)
576 if (!image
.HasPalette())
581 const wxPalette
& palette
= image
.GetPalette();
582 int palCount
= palette
.GetColoursCount();
584 for (int i
= 0; i
< palCount
; ++i
)
586 if (!palette
.GetRGB(i
, &pal
[i
].red
, &pal
[i
].green
, &pal
[i
].blue
))
595 mask
.red
= image
.GetMaskRed();
596 mask
.green
= image
.GetMaskGreen();
597 mask
.blue
= image
.GetMaskBlue();
598 *pMaskIndex
= wxGIFHandler_PaletteFind(mask
, pal
, palCount
);
599 if ( (*pMaskIndex
== wxNOT_FOUND
) && (palCount
< 256))
601 *pMaskIndex
= palCount
;
602 pal
[palCount
++] = mask
;
607 *pMaskIndex
= wxNOT_FOUND
;
609 *pPalCount
= palCount
;
613 #endif // wxUSE_PALETTE
615 int wxGIFHandler_PaletteFind(const wxRGB
& clr
, const wxRGB
*array
, int count
)
617 for (int i
= 0; i
< count
; i
++)
619 if ( (clr
.red
== array
[i
].red
)
620 && (clr
.green
== array
[i
].green
)
621 && (clr
.blue
== array
[i
].blue
))
630 bool wxGIFHandler_Write(wxOutputStream
*stream
, const void *buf
, size_t len
)
632 return (len
== stream
->Write(buf
, len
).LastWrite());
635 bool wxGIFHandler_WriteByte(wxOutputStream
*stream
, wxUint8 byte
)
637 return wxGIFHandler_Write(stream
, &byte
, sizeof(byte
));
640 bool wxGIFHandler_WriteWord(wxOutputStream
*stream
, wxUint16 word
)
644 buf
[0] = word
& 0xff;
645 buf
[1] = (word
>> 8) & 0xff;
646 return wxGIFHandler_Write(stream
, &word
, sizeof(word
));
649 bool wxGIFHandler_WriteHeader(wxOutputStream
*stream
, int width
, int height
,
650 bool loop
, const wxRGB
*pal
, int palCount
)
652 const int bpp
= wxGIFHandler_BitSize(palCount
);
655 bool ok
= wxGIFHandler_Write(stream
, GIF89_HDR
, sizeof(GIF89_HDR
)-1)
656 && wxGIFHandler_WriteWord(stream
, (wxUint16
) width
)
657 && wxGIFHandler_WriteWord(stream
, (wxUint16
) height
);
660 buf
[0] |=(bpp
- 1) << 5;
662 buf
[1] = 0; // background color == entry 0
663 buf
[2] = 0; // aspect ratio 1:1
664 ok
= ok
&& wxGIFHandler_Write(stream
, buf
, sizeof(buf
))
665 && wxGIFHandler_WritePalette(stream
, pal
, palCount
, bpp
);
669 ok
= ok
&& wxGIFHandler_WriteLoop(stream
);
675 bool wxGIFHandler_WriteRect(wxOutputStream
*stream
, int width
, int height
)
677 return wxGIFHandler_WriteWord(stream
, 0) // left
678 && wxGIFHandler_WriteWord(stream
, 0) // top
679 && wxGIFHandler_WriteWord(stream
, (wxUint16
) width
)
680 && wxGIFHandler_WriteWord(stream
, (wxUint16
) height
);
684 bool wxGIFHandler_WriteTerm(wxOutputStream
*stream
)
686 return wxGIFHandler_WriteByte(stream
, GIF_MARKER_ENDOFDATA
);
690 bool wxGIFHandler_WriteZero(wxOutputStream
*stream
)
692 return wxGIFHandler_WriteByte(stream
, 0);
695 bool wxGIFHandler_WritePalette(wxOutputStream
*stream
,
696 const wxRGB
*array
, size_t count
, int bpp
)
699 for (int i
= 0; (i
< (1 << bpp
)); i
++)
703 buf
[0] = array
[i
].red
;
704 buf
[1] = array
[i
].green
;
705 buf
[2] = array
[i
].blue
;
709 buf
[0] = buf
[1] = buf
[2] = 0;
712 if ( !wxGIFHandler_Write(stream
, buf
, sizeof(buf
)) )
721 bool wxGIFHandler_WriteControl(wxOutputStream
*stream
,
722 int maskIndex
, int delayMilliSecs
)
726 buf
[0] = GIF_MARKER_EXT
; // extension marker
727 buf
[1] = GIF_MARKER_EXT_GRAPHICS_CONTROL
;
728 buf
[2] = 4; // length of block
729 buf
[3] = (maskIndex
!= wxNOT_FOUND
) ? 1 : 0; // has transparency
730 buf
[4] = delayMilliSecs
/ 10; // delay time
732 buf
[6] = (maskIndex
!= wxNOT_FOUND
) ? (wxUint8
) maskIndex
: 0;
734 return wxGIFHandler_Write(stream
, buf
, sizeof(buf
));
737 bool wxGIFHandler_WriteComment(wxOutputStream
*stream
, const wxString
& comment
)
739 if ( comment
.empty() )
744 // Write comment header.
746 buf
[0] = GIF_MARKER_EXT
;
747 buf
[1] = GIF_MARKER_EXT_COMMENT
;
748 if ( !wxGIFHandler_Write(stream
, buf
, sizeof(buf
)) )
754 If comment is longer than 255 bytes write it in blocks of maximum 255
757 wxCharBuffer
text( comment
.mb_str() );
759 size_t pos
= 0, fullLength
= text
.length();
763 size_t blockLength
= wxMin(fullLength
- pos
, 255);
765 if ( !wxGIFHandler_WriteByte(stream
, (wxUint8
) blockLength
)
766 || !wxGIFHandler_Write(stream
, &text
.data()[pos
], blockLength
) )
772 }while (pos
< fullLength
);
775 // Write comment footer.
776 return wxGIFHandler_WriteZero(stream
);
779 bool wxGIFHandler_WriteLoop(wxOutputStream
*stream
)
782 const int loopcount
= 0; // infinite
784 buf
[0] = GIF_MARKER_EXT
;
785 buf
[1] = GIF_MARKER_EXT_APP
;
787 bool ok
= wxGIFHandler_Write(stream
, buf
, 3)
788 && wxGIFHandler_Write(stream
, NETSCAPE_LOOP
, sizeof(NETSCAPE_LOOP
)-1);
792 buf
[2] = loopcount
& 0xFF;
793 buf
[3] = loopcount
>> 8;
795 return ok
&& wxGIFHandler_Write(stream
, buf
, 4)
796 && wxGIFHandler_WriteZero(stream
);
799 bool wxGIFHandler_BufferedOutput(wxOutputStream
*stream
, wxUint8
*buf
, int c
)
803 if (c
== FLUSH_OUTPUT
)
805 // Flush everything out.
808 ok
= wxGIFHandler_Write(stream
, buf
, buf
[0]+1);
810 // Mark end of compressed data, by an empty block (see GIF doc):
811 wxGIFHandler_WriteZero(stream
);
817 // Dump out this buffer - it is full:
818 ok
= wxGIFHandler_Write(stream
, buf
, buf
[0] + 1);
827 #endif // wxUSE_STREAMS
829 #endif // wxUSE_IMAGE && wxUSE_GIF