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 /////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 #ifdef __BORLANDC__
13     #pragma hdrstop
14 #endif
15 
16 #if wxUSE_IMAGE && wxUSE_GIF
17 
18 #ifndef WX_PRECOMP
19     #include "wx/intl.h"
20     #include "wx/log.h"
21     #include "wx/palette.h"
22     #include "wx/utils.h"
23 #endif
24 
25 #include "wx/imaggif.h"
26 #include "wx/gifdecod.h"
27 #include "wx/stream.h"
28 #include "wx/anidecod.h" // wxImageArray
29 
30 #define GIF89_HDR     "GIF89a"
31 #define NETSCAPE_LOOP "NETSCAPE2.0"
32 
33 // see members.aol.com/royalef/gifabout.htm
34 //     members.aol.com/royalef/gif89a.txt
35 
36 enum
37 {
38     GIF_MARKER_EXT       = '!', // 0x21
39     GIF_MARKER_SEP       = ',', // 0x2C
40     GIF_MARKER_ENDOFDATA = ';', // 0x3B
41 
42     GIF_MARKER_EXT_GRAPHICS_CONTROL = 0xF9,
43     GIF_MARKER_EXT_COMMENT          = 0xFE,
44     GIF_MARKER_EXT_APP              = 0xFF
45 };
46 
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.
50 
51 #define HT_SIZE         8192        // 12bits = 4096 or twice as big!
52 #define HT_KEY_MASK     0x1FFF      // 13bits keys
53 
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)
58 
59 struct wxRGB
60 {
61     wxUint8 red;
62     wxUint8 green;
63     wxUint8 blue;
64 };
65 
66 struct GifHashTableType
67 {
68     wxUint32 HTable[HT_SIZE];
69 };
70 
71 IMPLEMENT_DYNAMIC_CLASS(wxGIFHandler,wxImageHandler)
72 
73 //----------------------------------------------------------------------------
74 // Forward declarations
75 //----------------------------------------------------------------------------
76 
77 static int wxGIFHandler_KeyItem(unsigned long item);
78 
79 #if wxUSE_STREAMS
80 
81 static int wxGIFHandler_BitSize(int n);
82 
83 #if wxUSE_PALETTE
84 static bool wxGIFHandler_GetPalette(const wxImage& image,
85     wxRGB *pal, int *palCount, int *mask_index);
86 #endif
87 static
88 int wxGIFHandler_PaletteFind(const wxRGB& clr, const wxRGB *array, int count);
89 
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);
96 #if wxUSE_PALETTE
97 static bool wxGIFHandler_WriteTerm(wxOutputStream *);
98 #endif
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 *);
106 
107 static bool wxGIFHandler_BufferedOutput(wxOutputStream *, wxUint8 *buf, int c);
108 #endif // wxUSE_STREAMS
109 
110 //-----------------------------------------------------------------------------
111 // wxGIFHandler
112 //-----------------------------------------------------------------------------
113 
114 #if wxUSE_STREAMS
115 
LoadFile(wxImage * image,wxInputStream & stream,bool verbose,int index)116 bool wxGIFHandler::LoadFile(wxImage *image, wxInputStream& stream,
117     bool verbose, int index)
118 {
119     wxGIFDecoder *decod;
120     wxGIFErrorCode error;
121     bool ok = true;
122 
123 //    image->Destroy();
124     decod = new wxGIFDecoder();
125     error = decod->LoadGIF(stream);
126 
127     if ((error != wxGIF_OK) && (error != wxGIF_TRUNCATED))
128     {
129         if (verbose)
130         {
131             switch (error)
132             {
133                 case wxGIF_INVFORMAT:
134                     wxLogError(_("GIF: error in GIF image format."));
135                     break;
136                 case wxGIF_MEMERR:
137                     wxLogError(_("GIF: not enough memory."));
138                     break;
139                 default:
140                     wxLogError(_("GIF: unknown error!!!"));
141                     break;
142             }
143         }
144         delete decod;
145         return false;
146     }
147 
148     if ((error == wxGIF_TRUNCATED) && verbose)
149     {
150         wxLogError(_("GIF: data stream seems to be truncated."));
151         // go on; image data is OK
152     }
153 
154     if (ok)
155     {
156         ok = decod->ConvertToImage(index != -1 ? (size_t)index : 0, image);
157     }
158     else
159     {
160         wxLogError(_("GIF: Invalid gif index."));
161     }
162 
163     delete decod;
164 
165     return ok;
166 }
167 
SaveFile(wxImage * image,wxOutputStream & stream,bool verbose)168 bool wxGIFHandler::SaveFile(wxImage *image,
169     wxOutputStream& stream, bool verbose)
170 {
171 #if wxUSE_PALETTE
172     wxRGB pal[256];
173     int palCount;
174     int maskIndex;
175 
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);
180 #else
181     wxUnusedVar(image);
182     wxUnusedVar(stream);
183     wxUnusedVar(verbose);
184     return false;
185 #endif
186 }
187 
DoCanRead(wxInputStream & stream)188 bool wxGIFHandler::DoCanRead( wxInputStream& stream )
189 {
190     wxGIFDecoder decod;
191     return decod.CanRead(stream);
192          // it's ok to modify the stream position here
193 }
194 
DoGetImageCount(wxInputStream & stream)195 int wxGIFHandler::DoGetImageCount( wxInputStream& stream )
196 {
197     wxGIFDecoder decod;
198     wxGIFErrorCode error = decod.LoadGIF(stream);
199     if ( (error != wxGIF_OK) && (error != wxGIF_TRUNCATED) )
200         return -1;
201 
202     // NOTE: this function modifies the current stream position but it's ok
203     //       (see wxImageHandler::GetImageCount)
204 
205     return decod.GetFrameCount();
206 }
207 
DoSaveFile(const wxImage & image,wxOutputStream * stream,bool WXUNUSED (verbose),bool first,int delayMilliSecs,bool loop,const wxRGB * pal,int palCount,int maskIndex)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)
211 {
212     const unsigned long colorcount = image.CountColours(256+1);
213     bool ok = colorcount && (colorcount <= 256);
214     if (!ok)
215     {
216         return false;
217     }
218 
219     int width = image.GetWidth();
220     int height = image.GetHeight();
221     int width_even = width + ((width % 2) ? 1 : 0);
222 
223     if (first)
224     {
225         ok = wxGIFHandler_WriteHeader(stream, width, height, loop,
226             pal, palCount);
227     }
228 
229     ok = ok
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);
235 
236     // local palette
237     if (first)
238     {
239         // we already saved the (global) palette
240         ok = ok && wxGIFHandler_WriteZero(stream);
241     }
242     else
243     {
244         const int bpp = wxGIFHandler_BitSize(palCount);
245         wxUint8 b;
246 
247         b = 0x80;
248         b |=(bpp - 1) << 5;
249         b |=(bpp - 1);
250         b &=~0x40; // clear interlaced
251 
252         ok = ok && wxGIFHandler_WriteByte(stream, b)
253             && wxGIFHandler_WritePalette(stream, pal, palCount, bpp);
254     }
255 
256     if (!ok)
257     {
258         return false;
259     }
260 
261     if (!InitHashTable())
262     {
263         wxLogError(_("Couldn't initialize GIF hash table."));
264         return false;
265     }
266 
267     const wxUint8 *src = image.GetData();
268     wxUint8 *eightBitData = new wxUint8[width];
269 
270     SetupCompress(stream, 8);
271 
272     m_pixelCount = height * width_even;
273     for (int y = 0; y < height; y++)
274     {
275         m_pixelCount -= width_even;
276         for (int x = 0; x < width; x++)
277         {
278             wxRGB rgb;
279             rgb.red   = src[0];
280             rgb.green = src[1];
281             rgb.blue  = src[2];
282             int index = wxGIFHandler_PaletteFind(rgb, pal, palCount);
283             wxASSERT(index != wxNOT_FOUND);
284             eightBitData[x] = (wxUint8)index;
285             src+=3;
286         }
287 
288         ok = CompressLine(stream, eightBitData, width);
289         if (!ok)
290         {
291             break;
292         }
293     }
294 
295     delete [] eightBitData;
296 
297     wxDELETE(m_hashTable);
298 
299     return ok;
300 }
301 
SaveAnimation(const wxImageArray & images,wxOutputStream * stream,bool verbose,int delayMilliSecs)302 bool wxGIFHandler::SaveAnimation(const wxImageArray& images,
303     wxOutputStream *stream, bool verbose, int delayMilliSecs)
304 {
305 #if wxUSE_PALETTE
306     bool ok = true;
307     size_t i;
308 
309     wxSize size(0,0);
310     for (i = 0; (i < images.GetCount()) && ok; i++)
311     {
312         const wxImage& image = images.Item(i);
313         wxSize temp(image.GetWidth(), image.GetHeight());
314         ok = ok && image.HasPalette();
315         if (i)
316         {
317            ok = ok && (size == temp);
318         }
319         else
320         {
321            size = temp;
322         }
323     }
324 
325     for (i = 0; (i < images.GetCount()) && ok; i++)
326     {
327         const wxImage& image = images.Item(i);
328 
329         wxRGB pal[256];
330         int palCount;
331         int maskIndex;
332 
333         ok = wxGIFHandler_GetPalette(image, pal, &palCount, &maskIndex)
334           && DoSaveFile(image, stream, verbose, i == 0 /*first?*/, delayMilliSecs,
335             true /*loop?*/, pal, palCount, maskIndex);
336     }
337 
338     return ok && wxGIFHandler_WriteTerm(stream);
339 #else
340     wxUnusedVar(images);
341     wxUnusedVar(stream);
342     wxUnusedVar(verbose);
343     wxUnusedVar(delayMilliSecs);
344 
345     return false;
346 #endif
347 }
348 
CompressOutput(wxOutputStream * stream,int code)349 bool wxGIFHandler::CompressOutput(wxOutputStream *stream, int code)
350 {
351     if (code == FLUSH_OUTPUT)
352     {
353         while (m_crntShiftState > 0)
354         {
355             // Get rid of what is left in DWord, and flush it.
356             if (!wxGIFHandler_BufferedOutput(stream, m_LZBuf,
357                 m_crntShiftDWord & 0xff))
358             {
359                 return false;
360             }
361             m_crntShiftDWord >>= 8;
362             m_crntShiftState -= 8;
363         }
364         m_crntShiftState = 0;                       // For next time.
365         if (!wxGIFHandler_BufferedOutput(stream, m_LZBuf, FLUSH_OUTPUT))
366         {
367             return false;
368         }
369     }
370     else
371     {
372         m_crntShiftDWord |= ((long) code) << m_crntShiftState;
373         m_crntShiftState += m_runningBits;
374         while (m_crntShiftState >= 8)
375         {
376             // Dump out full bytes:
377             if (!wxGIFHandler_BufferedOutput(stream, m_LZBuf,
378                 m_crntShiftDWord & 0xff))
379             {
380                 return false;
381             }
382             m_crntShiftDWord >>= 8;
383             m_crntShiftState -= 8;
384         }
385     }
386 
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))
390     {
391         m_maxCode1 = 1 << ++m_runningBits;
392     }
393     return true;
394 }
395 
SetupCompress(wxOutputStream * stream,int bpp)396 bool wxGIFHandler::SetupCompress(wxOutputStream *stream, int bpp)
397 {
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;
407 
408     // Clear hash table and send Clear to make sure the decoder does the same.
409     ClearHashTable();
410 
411     return wxGIFHandler_WriteByte(stream, (wxUint8)bpp)
412         && CompressOutput(stream, m_clearCode);
413 }
414 
CompressLine(wxOutputStream * stream,const wxUint8 * line,int lineLen)415 bool wxGIFHandler::CompressLine(wxOutputStream *stream,
416     const wxUint8 *line, int lineLen)
417 {
418     int i = 0, crntCode, newCode;
419     unsigned long newKey;
420     wxUint8 pixel;
421     if (m_crntCode == FIRST_CODE)                  // It's first time!
422         crntCode = line[i++];
423     else
424         crntCode = m_crntCode;     // Get last code in compression.
425 
426     while (i < lineLen)
427     {
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)
434         {
435             // This Key is already there, or the string is old one, so
436             // simply take new code as our crntCode:
437             crntCode = newCode;
438         }
439         else
440         {
441             // Put it in hash table, output the prefix code, and make our
442             // crntCode equal to Pixel.
443             if (!CompressOutput(stream, crntCode))
444             {
445                 return false;
446             }
447 
448             crntCode = pixel;
449 
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)
453             {
454                 // Time to do some clearance:
455                 if (!CompressOutput(stream, m_clearCode))
456                 {
457                     return false;
458                 }
459 
460                 m_runningCode = m_EOFCode + 1;
461                 m_runningBits = 8 + 1;
462                 m_maxCode1 = 1 << m_runningBits;
463                 ClearHashTable();
464             }
465             else
466             {
467                 // Put this unique key with its relative Code in hash table:
468                 InsertHashTable(newKey, m_runningCode++);
469             }
470         }
471     }
472     // Preserve the current state of the compression algorithm:
473     m_crntCode = crntCode;
474     if (m_pixelCount == 0)
475     {
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))
480         {
481             return false;
482         }
483     }
484 
485     return true;
486 }
487 
488 #endif  // wxUSE_STREAMS
489 
InitHashTable()490 bool wxGIFHandler::InitHashTable()
491 {
492     if (!m_hashTable)
493     {
494         m_hashTable = new GifHashTableType();
495     }
496 
497     if (!m_hashTable)
498     {
499         return false;
500     }
501 
502     ClearHashTable();
503 
504     return true;
505 }
506 
ClearHashTable()507 void wxGIFHandler::ClearHashTable()
508 {
509     int index = HT_SIZE;
510     wxUint32 *HTable = m_hashTable->HTable;
511 
512     while (--index>=0)
513     {
514         HTable[index] = 0xfffffffful;
515     }
516 }
517 
InsertHashTable(unsigned long key,int code)518 void wxGIFHandler::InsertHashTable(unsigned long key, int code)
519 {
520     int hKey = wxGIFHandler_KeyItem(key);
521     wxUint32 *HTable = m_hashTable->HTable;
522 
523     while (HT_GET_KEY(HTable[hKey]) != 0xFFFFFL)
524     {
525         hKey = (hKey + 1) & HT_KEY_MASK;
526     }
527     HTable[hKey] = HT_PUT_KEY(key) | HT_PUT_CODE(code);
528 }
529 
530 
ExistsHashTable(unsigned long key)531 int wxGIFHandler::ExistsHashTable(unsigned long key)
532 {
533     int hKey = wxGIFHandler_KeyItem(key);
534     wxUint32 *HTable = m_hashTable->HTable, HTKey;
535 
536     while ((HTKey = HT_GET_KEY(HTable[hKey])) != 0xFFFFFL)
537     {
538         if (key == HTKey)
539         {
540             return HT_GET_CODE(HTable[hKey]);
541         }
542         hKey = (hKey + 1) & HT_KEY_MASK;
543     }
544     return -1;
545 }
546 
547 // ---------------------------------------------------------------------------
548 // implementation of global private functions
549 // ---------------------------------------------------------------------------
550 
wxGIFHandler_KeyItem(unsigned long item)551 int wxGIFHandler_KeyItem(unsigned long item)
552 {
553     return ((item >> 12) ^ item) & HT_KEY_MASK;
554 }
555 
556 #if wxUSE_STREAMS
557 
wxGIFHandler_BitSize(int n)558 int wxGIFHandler_BitSize(int n)
559 {
560     int i;
561     for (i = 1; i <= 8; i++)
562     {
563         if ((1 << i) >= n)
564         {
565             break;
566         }
567     }
568     return i;
569 }
570 
571 #if wxUSE_PALETTE
wxGIFHandler_GetPalette(const wxImage & image,wxRGB * pal,int * pPalCount,int * pMaskIndex)572 bool wxGIFHandler_GetPalette(const wxImage& image,
573     wxRGB *pal, int *pPalCount, int *pMaskIndex)
574 {
575     if (!image.HasPalette())
576     {
577         return false;
578     }
579 
580     const wxPalette& palette = image.GetPalette();
581     int palCount = palette.GetColoursCount();
582 
583     for (int i = 0; i < palCount; ++i)
584     {
585         if (!palette.GetRGB(i, &pal[i].red, &pal[i].green, &pal[i].blue))
586         {
587             break;
588         }
589     }
590     if (image.HasMask())
591     {
592         wxRGB mask;
593 
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))
599         {
600             *pMaskIndex = palCount;
601             pal[palCount++] = mask;
602         }
603     }
604     else
605     {
606         *pMaskIndex = wxNOT_FOUND;
607     }
608     *pPalCount = palCount;
609 
610     return true;
611 }
612 #endif // wxUSE_PALETTE
613 
wxGIFHandler_PaletteFind(const wxRGB & clr,const wxRGB * array,int count)614 int wxGIFHandler_PaletteFind(const wxRGB& clr, const wxRGB *array, int count)
615 {
616     for (int i = 0; i < count; i++)
617     {
618         if (   (clr.red   == array[i].red)
619             && (clr.green == array[i].green)
620             && (clr.blue  == array[i].blue))
621         {
622             return i;
623         }
624     }
625 
626     return wxNOT_FOUND;
627 }
628 
wxGIFHandler_Write(wxOutputStream * stream,const void * buf,size_t len)629 bool wxGIFHandler_Write(wxOutputStream *stream, const void *buf, size_t len)
630 {
631     return (len == stream->Write(buf, len).LastWrite());
632 }
633 
wxGIFHandler_WriteByte(wxOutputStream * stream,wxUint8 byte)634 bool wxGIFHandler_WriteByte(wxOutputStream *stream, wxUint8 byte)
635 {
636     return wxGIFHandler_Write(stream, &byte, sizeof(byte));
637 }
638 
wxGIFHandler_WriteWord(wxOutputStream * stream,wxUint16 word)639 bool wxGIFHandler_WriteWord(wxOutputStream *stream, wxUint16 word)
640 {
641     wxUint8 buf[2];
642 
643     buf[0] = word & 0xff;
644     buf[1] = (word >> 8) & 0xff;
645     return wxGIFHandler_Write(stream, &buf, sizeof(buf));
646 }
647 
wxGIFHandler_WriteHeader(wxOutputStream * stream,int width,int height,bool loop,const wxRGB * pal,int palCount)648 bool wxGIFHandler_WriteHeader(wxOutputStream *stream, int width, int height,
649     bool loop, const wxRGB *pal, int palCount)
650 {
651     const int bpp = wxGIFHandler_BitSize(palCount);
652     wxUint8 buf[3];
653 
654     bool ok = wxGIFHandler_Write(stream, GIF89_HDR, sizeof(GIF89_HDR)-1)
655         && wxGIFHandler_WriteWord(stream, (wxUint16) width)
656         && wxGIFHandler_WriteWord(stream, (wxUint16) height);
657 
658     buf[0] = 0x80;
659     buf[0] |=(bpp - 1) << 5;
660     buf[0] |=(bpp - 1);
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);
665 
666     if (loop)
667     {
668        ok = ok && wxGIFHandler_WriteLoop(stream);
669     }
670 
671     return ok;
672 }
673 
wxGIFHandler_WriteRect(wxOutputStream * stream,int width,int height)674 bool wxGIFHandler_WriteRect(wxOutputStream *stream, int width, int height)
675 {
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);
680 }
681 
682 #if wxUSE_PALETTE
wxGIFHandler_WriteTerm(wxOutputStream * stream)683 bool wxGIFHandler_WriteTerm(wxOutputStream *stream)
684 {
685     return wxGIFHandler_WriteByte(stream, GIF_MARKER_ENDOFDATA);
686 }
687 #endif
688 
wxGIFHandler_WriteZero(wxOutputStream * stream)689 bool wxGIFHandler_WriteZero(wxOutputStream *stream)
690 {
691     return wxGIFHandler_WriteByte(stream, 0);
692 }
693 
wxGIFHandler_WritePalette(wxOutputStream * stream,const wxRGB * array,size_t count,int bpp)694 bool wxGIFHandler_WritePalette(wxOutputStream *stream,
695     const wxRGB *array, size_t count, int bpp)
696 {
697     wxUint8 buf[3];
698     for (int i = 0; (i < (1 << bpp)); i++)
699     {
700         if (i < (int)count)
701         {
702             buf[0] = array[i].red;
703             buf[1] = array[i].green;
704             buf[2] = array[i].blue;
705         }
706         else
707         {
708             buf[0] = buf[1] = buf[2] = 0;
709         }
710 
711         if ( !wxGIFHandler_Write(stream, buf, sizeof(buf)) )
712         {
713             return false;
714         }
715     }
716 
717     return true;
718 }
719 
wxGIFHandler_WriteControl(wxOutputStream * stream,int maskIndex,int delayMilliSecs)720 bool wxGIFHandler_WriteControl(wxOutputStream *stream,
721     int maskIndex, int delayMilliSecs)
722 {
723     wxUint8 buf[8];
724     const wxUint16 delay = delayMilliSecs / 10;
725 
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] = delay & 0xff;  // delay time
731     buf[5] = (delay >> 8) & 0xff;   // delay time second byte
732     buf[6] = (maskIndex != wxNOT_FOUND) ? (wxUint8) maskIndex : 0;
733     buf[7] = 0;
734     return wxGIFHandler_Write(stream, buf, sizeof(buf));
735 }
736 
wxGIFHandler_WriteComment(wxOutputStream * stream,const wxString & comment)737 bool wxGIFHandler_WriteComment(wxOutputStream *stream, const wxString& comment)
738 {
739     if ( comment.empty() )
740     {
741         return true;
742     }
743 
744     // Write comment header.
745     wxUint8 buf[2];
746     buf[0] = GIF_MARKER_EXT;
747     buf[1] = GIF_MARKER_EXT_COMMENT;
748     if ( !wxGIFHandler_Write(stream, buf, sizeof(buf)) )
749     {
750         return false;
751     }
752 
753     /*
754     If comment is longer than 255 bytes write it in blocks of maximum 255
755     bytes each.
756     */
757     wxCharBuffer text( comment.mb_str() );
758 
759     size_t pos = 0, fullLength = text.length();
760 
761     do
762     {
763         size_t blockLength = wxMin(fullLength - pos, 255);
764 
765         if ( !wxGIFHandler_WriteByte(stream, (wxUint8) blockLength)
766             || !wxGIFHandler_Write(stream, &text.data()[pos], blockLength) )
767         {
768             return false;
769         }
770 
771         pos += blockLength;
772     }while (pos < fullLength);
773 
774 
775     // Write comment footer.
776     return wxGIFHandler_WriteZero(stream);
777 }
778 
wxGIFHandler_WriteLoop(wxOutputStream * stream)779 bool wxGIFHandler_WriteLoop(wxOutputStream *stream)
780 {
781     wxUint8 buf[4];
782     const int loopcount = 0; // infinite
783 
784     buf[0] = GIF_MARKER_EXT;
785     buf[1] = GIF_MARKER_EXT_APP;
786     buf[2] = 0x0B;
787     bool ok = wxGIFHandler_Write(stream, buf, 3)
788         && wxGIFHandler_Write(stream, NETSCAPE_LOOP, sizeof(NETSCAPE_LOOP)-1);
789 
790     buf[0] = 3;
791     buf[1] = 1;
792     buf[2] = loopcount & 0xFF;
793     buf[3] = loopcount >> 8;
794 
795     return ok && wxGIFHandler_Write(stream, buf, 4)
796         && wxGIFHandler_WriteZero(stream);
797 }
798 
wxGIFHandler_BufferedOutput(wxOutputStream * stream,wxUint8 * buf,int c)799 bool wxGIFHandler_BufferedOutput(wxOutputStream *stream, wxUint8 *buf, int c)
800 {
801     bool ok = true;
802 
803     if (c == FLUSH_OUTPUT)
804     {
805         // Flush everything out.
806         if (buf[0])
807         {
808             ok = wxGIFHandler_Write(stream, buf, buf[0]+1);
809         }
810         // Mark end of compressed data, by an empty block (see GIF doc):
811         wxGIFHandler_WriteZero(stream);
812     }
813     else
814     {
815         if (buf[0] == 255)
816         {
817             // Dump out this buffer - it is full:
818             ok = wxGIFHandler_Write(stream, buf, buf[0] + 1);
819             buf[0] = 0;
820         }
821         buf[++buf[0]] = c;
822     }
823 
824     return ok;
825 }
826 
827 #endif // wxUSE_STREAMS
828 
829 #endif  // wxUSE_IMAGE && wxUSE_GIF
830