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