1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imaggif.cpp
3 // Purpose: wxGIFHandler
4 // Author: Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K
5 // Copyright: (c) 1999-2011 Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
16 #if wxUSE_IMAGE && wxUSE_GIF
21 #include "wx/palette.h"
25 #include "wx/imaggif.h"
26 #include "wx/gifdecod.h"
27 #include "wx/stream.h"
28 #include "wx/anidecod.h" // wxImageArray
30 #define GIF89_HDR "GIF89a"
31 #define NETSCAPE_LOOP "NETSCAPE2.0"
33 // see members.aol.com/royalef/gifabout.htm
34 // members.aol.com/royalef/gif89a.txt
38 GIF_MARKER_EXT
= '!', // 0x21
39 GIF_MARKER_SEP
= ',', // 0x2C
40 GIF_MARKER_ENDOFDATA
= ';', // 0x3B
42 GIF_MARKER_EXT_GRAPHICS_CONTROL
= 0xF9,
43 GIF_MARKER_EXT_COMMENT
= 0xFE,
44 GIF_MARKER_EXT_APP
= 0xFF
47 #define LZ_MAX_CODE 4095 // Biggest code possible in 12 bits.
48 #define FLUSH_OUTPUT 4096 // Impossible code, to signal flush.
49 #define FIRST_CODE 4097 // Impossible code, to signal first.
51 #define HT_SIZE 8192 // 12bits = 4096 or twice as big!
52 #define HT_KEY_MASK 0x1FFF // 13bits keys
54 #define HT_GET_KEY(l) (l >> 12)
55 #define HT_GET_CODE(l) (l & 0x0FFF)
56 #define HT_PUT_KEY(l) (l << 12)
57 #define HT_PUT_CODE(l) (l & 0x0FFF)
66 struct GifHashTableType
68 wxUint32 HTable
[HT_SIZE
];
71 IMPLEMENT_DYNAMIC_CLASS(wxGIFHandler
,wxImageHandler
)
73 //----------------------------------------------------------------------------
74 // Forward declarations
75 //----------------------------------------------------------------------------
77 static int wxGIFHandler_KeyItem(unsigned long item
);
81 static int wxGIFHandler_BitSize(int n
);
84 static bool wxGIFHandler_GetPalette(const wxImage
& image
,
85 wxRGB
*pal
, int *palCount
, int *mask_index
);
88 int wxGIFHandler_PaletteFind(const wxRGB
& clr
, const wxRGB
*array
, int count
);
90 static bool wxGIFHandler_Write(wxOutputStream
*, const void *buf
, size_t len
);
91 static bool wxGIFHandler_WriteByte(wxOutputStream
*, wxUint8
);
92 static bool wxGIFHandler_WriteWord(wxOutputStream
*, wxUint16
);
93 static bool wxGIFHandler_WriteHeader(wxOutputStream
*, int width
, int height
,
94 bool loop
, const wxRGB
*pal
, int palCount
);
95 static bool wxGIFHandler_WriteRect(wxOutputStream
*, int width
, int height
);
97 static bool wxGIFHandler_WriteTerm(wxOutputStream
*);
99 static bool wxGIFHandler_WriteZero(wxOutputStream
*);
100 static bool wxGIFHandler_WritePalette(wxOutputStream
*,
101 const wxRGB
*pal
, size_t palCount
, int bpp
);
102 static bool wxGIFHandler_WriteControl(wxOutputStream
*,
103 int maskIndex
, int delayMilliSecs
);
104 static bool wxGIFHandler_WriteComment(wxOutputStream
*, const wxString
&);
105 static bool wxGIFHandler_WriteLoop(wxOutputStream
*);
107 static bool wxGIFHandler_BufferedOutput(wxOutputStream
*, wxUint8
*buf
, int c
);
108 #endif // wxUSE_STREAMS
110 //-----------------------------------------------------------------------------
112 //-----------------------------------------------------------------------------
116 bool wxGIFHandler::LoadFile(wxImage
*image
, wxInputStream
& stream
,
117 bool verbose
, int index
)
120 wxGIFErrorCode error
;
124 decod
= new wxGIFDecoder();
125 error
= decod
->LoadGIF(stream
);
127 if ((error
!= wxGIF_OK
) && (error
!= wxGIF_TRUNCATED
))
133 case wxGIF_INVFORMAT
:
134 wxLogError(_("GIF: error in GIF image format."));
137 wxLogError(_("GIF: not enough memory."));
140 wxLogError(_("GIF: unknown error!!!"));
148 if ((error
== wxGIF_TRUNCATED
) && verbose
)
150 wxLogError(_("GIF: data stream seems to be truncated."));
151 // go on; image data is OK
156 ok
= decod
->ConvertToImage(index
!= -1 ? (size_t)index
: 0, image
);
160 wxLogError(_("GIF: Invalid gif index."));
168 bool wxGIFHandler::SaveFile(wxImage
*image
,
169 wxOutputStream
& stream
, bool verbose
)
176 return wxGIFHandler_GetPalette(*image
, pal
, &palCount
, &maskIndex
)
177 && DoSaveFile(*image
, &stream
, verbose
, true /*first?*/, 0,
178 false /*loop?*/, pal
, palCount
, maskIndex
)
179 && wxGIFHandler_WriteTerm(&stream
);
183 wxUnusedVar(verbose
);
188 bool wxGIFHandler::DoCanRead( wxInputStream
& stream
)
191 return decod
.CanRead(stream
);
192 // it's ok to modify the stream position here
195 int wxGIFHandler::DoGetImageCount( wxInputStream
& stream
)
198 wxGIFErrorCode error
= decod
.LoadGIF(stream
);
199 if ( (error
!= wxGIF_OK
) && (error
!= wxGIF_TRUNCATED
) )
202 // NOTE: this function modifies the current stream position but it's ok
203 // (see wxImageHandler::GetImageCount)
205 return decod
.GetFrameCount();
208 bool wxGIFHandler::DoSaveFile(const wxImage
& image
, wxOutputStream
*stream
,
209 bool WXUNUSED(verbose
), bool first
, int delayMilliSecs
, bool loop
,
210 const wxRGB
*pal
, int palCount
, int maskIndex
)
212 const unsigned long colorcount
= image
.CountColours(256+1);
213 bool ok
= colorcount
&& (colorcount
<= 256);
219 int width
= image
.GetWidth();
220 int height
= image
.GetHeight();
221 int width_even
= width
+ ((width
% 2) ? 1 : 0);
225 ok
= wxGIFHandler_WriteHeader(stream
, width
, height
, loop
,
230 && wxGIFHandler_WriteComment(stream
,
231 image
.GetOption(wxIMAGE_OPTION_GIF_COMMENT
))
232 && wxGIFHandler_WriteControl(stream
, maskIndex
, delayMilliSecs
)
233 && wxGIFHandler_WriteByte(stream
, GIF_MARKER_SEP
)
234 && wxGIFHandler_WriteRect(stream
, width
, height
);
239 // we already saved the (global) palette
240 ok
= ok
&& wxGIFHandler_WriteZero(stream
);
244 const int bpp
= wxGIFHandler_BitSize(palCount
);
250 b
&=~0x40; // clear interlaced
252 ok
= ok
&& wxGIFHandler_WriteByte(stream
, b
)
253 && wxGIFHandler_WritePalette(stream
, pal
, palCount
, bpp
);
261 if (!InitHashTable())
263 wxLogError(_("Couldn't initialize GIF hash table."));
267 const wxUint8
*src
= image
.GetData();
268 wxUint8
*eightBitData
= new wxUint8
[width
];
270 SetupCompress(stream
, 8);
272 m_pixelCount
= height
* width_even
;
273 for (int y
= 0; y
< height
; y
++)
275 m_pixelCount
-= width_even
;
276 for (int x
= 0; x
< width
; x
++)
282 int index
= wxGIFHandler_PaletteFind(rgb
, pal
, palCount
);
283 wxASSERT(index
!= wxNOT_FOUND
);
284 eightBitData
[x
] = (wxUint8
)index
;
288 ok
= CompressLine(stream
, eightBitData
, width
);
295 delete [] eightBitData
;
297 wxDELETE(m_hashTable
);
302 bool wxGIFHandler::SaveAnimation(const wxImageArray
& images
,
303 wxOutputStream
*stream
, bool verbose
, int delayMilliSecs
)
310 for (i
= 0; (i
< images
.GetCount()) && ok
; i
++)
312 const wxImage
& image
= images
.Item(i
);
313 wxSize
temp(image
.GetWidth(), image
.GetHeight());
314 ok
= ok
&& image
.HasPalette();
317 ok
= ok
&& (size
== temp
);
325 for (i
= 0; (i
< images
.GetCount()) && ok
; i
++)
327 const wxImage
& image
= images
.Item(i
);
333 ok
= wxGIFHandler_GetPalette(image
, pal
, &palCount
, &maskIndex
)
334 && DoSaveFile(image
, stream
, verbose
, i
== 0 /*first?*/, delayMilliSecs
,
335 true /*loop?*/, pal
, palCount
, maskIndex
);
338 return ok
&& wxGIFHandler_WriteTerm(stream
);
342 wxUnusedVar(verbose
);
343 wxUnusedVar(delayMilliSecs
);
349 bool wxGIFHandler::CompressOutput(wxOutputStream
*stream
, int code
)
351 if (code
== FLUSH_OUTPUT
)
353 while (m_crntShiftState
> 0)
355 // Get rid of what is left in DWord, and flush it.
356 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
,
357 m_crntShiftDWord
& 0xff))
361 m_crntShiftDWord
>>= 8;
362 m_crntShiftState
-= 8;
364 m_crntShiftState
= 0; // For next time.
365 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
, FLUSH_OUTPUT
))
372 m_crntShiftDWord
|= ((long) code
) << m_crntShiftState
;
373 m_crntShiftState
+= m_runningBits
;
374 while (m_crntShiftState
>= 8)
376 // Dump out full bytes:
377 if (!wxGIFHandler_BufferedOutput(stream
, m_LZBuf
,
378 m_crntShiftDWord
& 0xff))
382 m_crntShiftDWord
>>= 8;
383 m_crntShiftState
-= 8;
387 // If code can't fit into RunningBits bits, must raise its size. Note
388 // however that codes above LZ_MAX_CODE are used for special signaling.
389 if ( (m_runningCode
>= m_maxCode1
) && (code
<= LZ_MAX_CODE
))
391 m_maxCode1
= 1 << ++m_runningBits
;
396 bool wxGIFHandler::SetupCompress(wxOutputStream
*stream
, int bpp
)
398 m_LZBuf
[0] = 0; // Nothing was output yet.
399 m_clearCode
= (1 << bpp
);
400 m_EOFCode
= m_clearCode
+ 1;
401 m_runningCode
= m_EOFCode
+ 1;
402 m_runningBits
= bpp
+ 1; // Number of bits per code.
403 m_maxCode1
= 1 << m_runningBits
; // Max. code + 1.
404 m_crntCode
= FIRST_CODE
; // Signal that this is first one!
405 m_crntShiftState
= 0; // No information in CrntShiftDWord.
406 m_crntShiftDWord
= 0;
408 // Clear hash table and send Clear to make sure the decoder does the same.
411 return wxGIFHandler_WriteByte(stream
, (wxUint8
)bpp
)
412 && CompressOutput(stream
, m_clearCode
);
415 bool wxGIFHandler::CompressLine(wxOutputStream
*stream
,
416 const wxUint8
*line
, int lineLen
)
418 int i
= 0, crntCode
, newCode
;
419 unsigned long newKey
;
421 if (m_crntCode
== FIRST_CODE
) // It's first time!
422 crntCode
= line
[i
++];
424 crntCode
= m_crntCode
; // Get last code in compression.
428 // Decode lineLen items.
429 pixel
= line
[i
++]; // Get next pixel from stream.
430 // Form a new unique key to search hash table for the code combines
431 // crntCode as Prefix string with Pixel as postfix char.
432 newKey
= (((unsigned long) crntCode
) << 8) + pixel
;
433 if ((newCode
= ExistsHashTable(newKey
)) >= 0)
435 // This Key is already there, or the string is old one, so
436 // simply take new code as our crntCode:
441 // Put it in hash table, output the prefix code, and make our
442 // crntCode equal to Pixel.
443 if (!CompressOutput(stream
, crntCode
))
450 // If however the HashTable is full, we send a clear first and
451 // Clear the hash table.
452 if (m_runningCode
>= LZ_MAX_CODE
)
454 // Time to do some clearance:
455 if (!CompressOutput(stream
, m_clearCode
))
460 m_runningCode
= m_EOFCode
+ 1;
461 m_runningBits
= 8 + 1;
462 m_maxCode1
= 1 << m_runningBits
;
467 // Put this unique key with its relative Code in hash table:
468 InsertHashTable(newKey
, m_runningCode
++);
472 // Preserve the current state of the compression algorithm:
473 m_crntCode
= crntCode
;
474 if (m_pixelCount
== 0)
476 // We are done - output last Code and flush output buffers:
477 if (!CompressOutput(stream
, crntCode
)
478 || !CompressOutput(stream
, m_EOFCode
)
479 || !CompressOutput(stream
, FLUSH_OUTPUT
))
488 #endif // wxUSE_STREAMS
490 bool wxGIFHandler::InitHashTable()
494 m_hashTable
= new GifHashTableType();
507 void wxGIFHandler::ClearHashTable()
510 wxUint32
*HTable
= m_hashTable
->HTable
;
514 HTable
[index
] = 0xfffffffful
;
518 void wxGIFHandler::InsertHashTable(unsigned long key
, int code
)
520 int hKey
= wxGIFHandler_KeyItem(key
);
521 wxUint32
*HTable
= m_hashTable
->HTable
;
523 while (HT_GET_KEY(HTable
[hKey
]) != 0xFFFFFL
)
525 hKey
= (hKey
+ 1) & HT_KEY_MASK
;
527 HTable
[hKey
] = HT_PUT_KEY(key
) | HT_PUT_CODE(code
);
531 int wxGIFHandler::ExistsHashTable(unsigned long key
)
533 int hKey
= wxGIFHandler_KeyItem(key
);
534 wxUint32
*HTable
= m_hashTable
->HTable
, HTKey
;
536 while ((HTKey
= HT_GET_KEY(HTable
[hKey
])) != 0xFFFFFL
)
540 return HT_GET_CODE(HTable
[hKey
]);
542 hKey
= (hKey
+ 1) & HT_KEY_MASK
;
547 // ---------------------------------------------------------------------------
548 // implementation of global private functions
549 // ---------------------------------------------------------------------------
551 int wxGIFHandler_KeyItem(unsigned long item
)
553 return ((item
>> 12) ^ item
) & HT_KEY_MASK
;
558 int wxGIFHandler_BitSize(int n
)
561 for (i
= 1; i
<= 8; i
++)
572 bool wxGIFHandler_GetPalette(const wxImage
& image
,
573 wxRGB
*pal
, int *pPalCount
, int *pMaskIndex
)
575 if (!image
.HasPalette())
580 const wxPalette
& palette
= image
.GetPalette();
581 int palCount
= palette
.GetColoursCount();
583 for (int i
= 0; i
< palCount
; ++i
)
585 if (!palette
.GetRGB(i
, &pal
[i
].red
, &pal
[i
].green
, &pal
[i
].blue
))
594 mask
.red
= image
.GetMaskRed();
595 mask
.green
= image
.GetMaskGreen();
596 mask
.blue
= image
.GetMaskBlue();
597 *pMaskIndex
= wxGIFHandler_PaletteFind(mask
, pal
, palCount
);
598 if ( (*pMaskIndex
== wxNOT_FOUND
) && (palCount
< 256))
600 *pMaskIndex
= palCount
;
601 pal
[palCount
++] = mask
;
606 *pMaskIndex
= wxNOT_FOUND
;
608 *pPalCount
= palCount
;
612 #endif // wxUSE_PALETTE
614 int wxGIFHandler_PaletteFind(const wxRGB
& clr
, const wxRGB
*array
, int count
)
616 for (int i
= 0; i
< count
; i
++)
618 if ( (clr
.red
== array
[i
].red
)
619 && (clr
.green
== array
[i
].green
)
620 && (clr
.blue
== array
[i
].blue
))
629 bool wxGIFHandler_Write(wxOutputStream
*stream
, const void *buf
, size_t len
)
631 return (len
== stream
->Write(buf
, len
).LastWrite());
634 bool wxGIFHandler_WriteByte(wxOutputStream
*stream
, wxUint8 byte
)
636 return wxGIFHandler_Write(stream
, &byte
, sizeof(byte
));
639 bool wxGIFHandler_WriteWord(wxOutputStream
*stream
, wxUint16 word
)
643 buf
[0] = word
& 0xff;
644 buf
[1] = (word
>> 8) & 0xff;
645 return wxGIFHandler_Write(stream
, &buf
, sizeof(buf
));
648 bool wxGIFHandler_WriteHeader(wxOutputStream
*stream
, int width
, int height
,
649 bool loop
, const wxRGB
*pal
, int palCount
)
651 const int bpp
= wxGIFHandler_BitSize(palCount
);
654 bool ok
= wxGIFHandler_Write(stream
, GIF89_HDR
, sizeof(GIF89_HDR
)-1)
655 && wxGIFHandler_WriteWord(stream
, (wxUint16
) width
)
656 && wxGIFHandler_WriteWord(stream
, (wxUint16
) height
);
659 buf
[0] |=(bpp
- 1) << 5;
661 buf
[1] = 0; // background color == entry 0
662 buf
[2] = 0; // aspect ratio 1:1
663 ok
= ok
&& wxGIFHandler_Write(stream
, buf
, sizeof(buf
))
664 && wxGIFHandler_WritePalette(stream
, pal
, palCount
, bpp
);
668 ok
= ok
&& wxGIFHandler_WriteLoop(stream
);
674 bool wxGIFHandler_WriteRect(wxOutputStream
*stream
, int width
, int height
)
676 return wxGIFHandler_WriteWord(stream
, 0) // left
677 && wxGIFHandler_WriteWord(stream
, 0) // top
678 && wxGIFHandler_WriteWord(stream
, (wxUint16
) width
)
679 && wxGIFHandler_WriteWord(stream
, (wxUint16
) height
);
683 bool wxGIFHandler_WriteTerm(wxOutputStream
*stream
)
685 return wxGIFHandler_WriteByte(stream
, GIF_MARKER_ENDOFDATA
);
689 bool wxGIFHandler_WriteZero(wxOutputStream
*stream
)
691 return wxGIFHandler_WriteByte(stream
, 0);
694 bool wxGIFHandler_WritePalette(wxOutputStream
*stream
,
695 const wxRGB
*array
, size_t count
, int bpp
)
698 for (int i
= 0; (i
< (1 << bpp
)); i
++)
702 buf
[0] = array
[i
].red
;
703 buf
[1] = array
[i
].green
;
704 buf
[2] = array
[i
].blue
;
708 buf
[0] = buf
[1] = buf
[2] = 0;
711 if ( !wxGIFHandler_Write(stream
, buf
, sizeof(buf
)) )
720 bool wxGIFHandler_WriteControl(wxOutputStream
*stream
,
721 int maskIndex
, int delayMilliSecs
)
725 buf
[0] = GIF_MARKER_EXT
; // extension marker
726 buf
[1] = GIF_MARKER_EXT_GRAPHICS_CONTROL
;
727 buf
[2] = 4; // length of block
728 buf
[3] = (maskIndex
!= wxNOT_FOUND
) ? 1 : 0; // has transparency
729 buf
[4] = delayMilliSecs
/ 10; // delay time
731 buf
[6] = (maskIndex
!= wxNOT_FOUND
) ? (wxUint8
) maskIndex
: 0;
733 return wxGIFHandler_Write(stream
, buf
, sizeof(buf
));
736 bool wxGIFHandler_WriteComment(wxOutputStream
*stream
, const wxString
& comment
)
738 if ( comment
.empty() )
743 // Write comment header.
745 buf
[0] = GIF_MARKER_EXT
;
746 buf
[1] = GIF_MARKER_EXT_COMMENT
;
747 if ( !wxGIFHandler_Write(stream
, buf
, sizeof(buf
)) )
753 If comment is longer than 255 bytes write it in blocks of maximum 255
756 wxCharBuffer
text( comment
.mb_str() );
758 size_t pos
= 0, fullLength
= text
.length();
762 size_t blockLength
= wxMin(fullLength
- pos
, 255);
764 if ( !wxGIFHandler_WriteByte(stream
, (wxUint8
) blockLength
)
765 || !wxGIFHandler_Write(stream
, &text
.data()[pos
], blockLength
) )
771 }while (pos
< fullLength
);
774 // Write comment footer.
775 return wxGIFHandler_WriteZero(stream
);
778 bool wxGIFHandler_WriteLoop(wxOutputStream
*stream
)
781 const int loopcount
= 0; // infinite
783 buf
[0] = GIF_MARKER_EXT
;
784 buf
[1] = GIF_MARKER_EXT_APP
;
786 bool ok
= wxGIFHandler_Write(stream
, buf
, 3)
787 && wxGIFHandler_Write(stream
, NETSCAPE_LOOP
, sizeof(NETSCAPE_LOOP
)-1);
791 buf
[2] = loopcount
& 0xFF;
792 buf
[3] = loopcount
>> 8;
794 return ok
&& wxGIFHandler_Write(stream
, buf
, 4)
795 && wxGIFHandler_WriteZero(stream
);
798 bool wxGIFHandler_BufferedOutput(wxOutputStream
*stream
, wxUint8
*buf
, int c
)
802 if (c
== FLUSH_OUTPUT
)
804 // Flush everything out.
807 ok
= wxGIFHandler_Write(stream
, buf
, buf
[0]+1);
809 // Mark end of compressed data, by an empty block (see GIF doc):
810 wxGIFHandler_WriteZero(stream
);
816 // Dump out this buffer - it is full:
817 ok
= wxGIFHandler_Write(stream
, buf
, buf
[0] + 1);
826 #endif // wxUSE_STREAMS
828 #endif // wxUSE_IMAGE && wxUSE_GIF