]> git.saurik.com Git - wxWidgets.git/blame - src/common/imaggif.cpp
Implement undo and redo for the ie and gtk webkit backends. Extend the sample to...
[wxWidgets.git] / src / common / imaggif.cpp
CommitLineData
b59bf2db 1/////////////////////////////////////////////////////////////////////////////
8898456d 2// Name: src/common/imaggif.cpp
b59bf2db 3// Purpose: wxGIFHandler
77b83d0a 4// Author: Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K
464122b6 5// RCS-ID: $Id$
77b83d0a 6// Copyright: (c) 1999-2011 Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K
65571936 7// Licence: wxWindows licence
b59bf2db
JS
8/////////////////////////////////////////////////////////////////////////////
9
b59bf2db 10// For compilers that support precompilation, includes "wx.h".
3096bd2f 11#include "wx/wxprec.h"
b59bf2db
JS
12
13#ifdef __BORLANDC__
8898456d 14 #pragma hdrstop
b59bf2db
JS
15#endif
16
8898456d
WS
17#if wxUSE_IMAGE && wxUSE_GIF
18
ce4169a4 19#ifndef WX_PRECOMP
8898456d
WS
20 #include "wx/intl.h"
21 #include "wx/log.h"
77b83d0a
DS
22 #include "wx/palette.h"
23 #include "wx/utils.h"
ce4169a4 24#endif
b59bf2db 25
8f493002 26#include "wx/imaggif.h"
464122b6 27#include "wx/gifdecod.h"
77b83d0a
DS
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
37enum
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
60struct wxRGB
61{
62 wxUint8 red;
63 wxUint8 green;
64 wxUint8 blue;
65};
66
67struct GifHashTableType
68{
69 wxUint32 HTable[HT_SIZE];
70};
71
ce4169a4
RR
72IMPLEMENT_DYNAMIC_CLASS(wxGIFHandler,wxImageHandler)
73
77b83d0a
DS
74//----------------------------------------------------------------------------
75// Forward declarations
76//----------------------------------------------------------------------------
77
78static int wxGIFHandler_KeyItem(unsigned long item);
79
80#if wxUSE_STREAMS
81
82static int wxGIFHandler_BitSize(int n);
83
84#if wxUSE_PALETTE
85static bool wxGIFHandler_GetPalette(const wxImage& image,
86 wxRGB *pal, int *palCount, int *mask_index);
87#endif
88static
89int wxGIFHandler_PaletteFind(const wxRGB& clr, const wxRGB *array, int count);
90
91static bool wxGIFHandler_Write(wxOutputStream *, const void *buf, size_t len);
92static bool wxGIFHandler_WriteByte(wxOutputStream *, wxUint8);
93static bool wxGIFHandler_WriteWord(wxOutputStream *, wxUint16);
94static bool wxGIFHandler_WriteHeader(wxOutputStream *, int width, int height,
64a3ee76 95 bool loop, const wxRGB *pal, int palCount);
77b83d0a
DS
96static bool wxGIFHandler_WriteRect(wxOutputStream *, int width, int height);
97#if wxUSE_PALETTE
98static bool wxGIFHandler_WriteTerm(wxOutputStream *);
99#endif
100static bool wxGIFHandler_WriteZero(wxOutputStream *);
101static bool wxGIFHandler_WritePalette(wxOutputStream *,
102 const wxRGB *pal, size_t palCount, int bpp);
103static bool wxGIFHandler_WriteControl(wxOutputStream *,
104 int maskIndex, int delayMilliSecs);
105static bool wxGIFHandler_WriteComment(wxOutputStream *, const wxString&);
106static bool wxGIFHandler_WriteLoop(wxOutputStream *);
107
108static bool wxGIFHandler_BufferedOutput(wxOutputStream *, wxUint8 *buf, int c);
109#endif // wxUSE_STREAMS
110
b59bf2db
JS
111//-----------------------------------------------------------------------------
112// wxGIFHandler
113//-----------------------------------------------------------------------------
114
9ab6ee85
GRG
115#if wxUSE_STREAMS
116
b931f7ee 117bool wxGIFHandler::LoadFile(wxImage *image, wxInputStream& stream,
77b83d0a 118 bool verbose, int index)
b59bf2db 119{
464122b6 120 wxGIFDecoder *decod;
72045d57 121 wxGIFErrorCode error;
7beb59f3 122 bool ok = true;
b59bf2db 123
0141d2c9 124// image->Destroy();
72045d57
VZ
125 decod = new wxGIFDecoder();
126 error = decod->LoadGIF(stream);
8a68d8b6 127
8141573c 128 if ((error != wxGIF_OK) && (error != wxGIF_TRUNCATED))
995612e2 129 {
e08e239b
GRG
130 if (verbose)
131 {
132 switch (error)
133 {
8141573c 134 case wxGIF_INVFORMAT:
add95ac3 135 wxLogError(_("GIF: error in GIF image format."));
8141573c
GRG
136 break;
137 case wxGIF_MEMERR:
add95ac3 138 wxLogError(_("GIF: not enough memory."));
8141573c
GRG
139 break;
140 default:
add95ac3 141 wxLogError(_("GIF: unknown error!!!"));
8141573c 142 break;
e08e239b
GRG
143 }
144 }
b59bf2db 145 delete decod;
7beb59f3 146 return false;
b59bf2db 147 }
b59bf2db 148
8141573c
GRG
149 if ((error == wxGIF_TRUNCATED) && verbose)
150 {
add95ac3 151 wxLogError(_("GIF: data stream seems to be truncated."));
77b83d0a 152 // go on; image data is OK
8141573c
GRG
153 }
154
b931f7ee
VS
155 if (ok)
156 {
72045d57 157 ok = decod->ConvertToImage(index != -1 ? (size_t)index : 0, image);
b931f7ee
VS
158 }
159 else
160 {
161 wxLogError(_("GIF: Invalid gif index."));
162 }
163
464122b6 164 delete decod;
0141d2c9 165
464122b6 166 return ok;
b59bf2db
JS
167}
168
77b83d0a
DS
169bool wxGIFHandler::SaveFile(wxImage *image,
170 wxOutputStream& stream, bool verbose)
b59bf2db 171{
77b83d0a
DS
172#if wxUSE_PALETTE
173 wxRGB pal[256];
174 int palCount;
175 int maskIndex;
8a68d8b6 176
77b83d0a
DS
177 return wxGIFHandler_GetPalette(*image, pal, &palCount, &maskIndex)
178 && DoSaveFile(*image, &stream, verbose, true /*first?*/, 0,
491da411 179 false /*loop?*/, pal, palCount, maskIndex)
77b83d0a
DS
180 && wxGIFHandler_WriteTerm(&stream);
181#else
182 wxUnusedVar(image);
183 wxUnusedVar(stream);
184 wxUnusedVar(verbose);
7beb59f3 185 return false;
77b83d0a 186#endif
b59bf2db
JS
187}
188
995612e2 189bool wxGIFHandler::DoCanRead( wxInputStream& stream )
0828c087 190{
72045d57
VZ
191 wxGIFDecoder decod;
192 return decod.CanRead(stream);
8faef7cc 193 // it's ok to modify the stream position here
0828c087
VS
194}
195
8faef7cc 196int wxGIFHandler::DoGetImageCount( wxInputStream& stream )
85fcb94f
VZ
197{
198 wxGIFDecoder decod;
199 wxGIFErrorCode error = decod.LoadGIF(stream);
200 if ( (error != wxGIF_OK) && (error != wxGIF_TRUNCATED) )
201 return -1;
03647350 202
8faef7cc
FM
203 // NOTE: this function modifies the current stream position but it's ok
204 // (see wxImageHandler::GetImageCount)
85fcb94f
VZ
205
206 return decod.GetFrameCount();
207}
208
77b83d0a
DS
209bool wxGIFHandler::DoSaveFile(const wxImage& image, wxOutputStream *stream,
210 bool WXUNUSED(verbose), bool first, int delayMilliSecs, bool loop,
491da411 211 const wxRGB *pal, int palCount, int maskIndex)
77b83d0a
DS
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,
64a3ee76 227 pal, palCount);
77b83d0a
DS
228 }
229
64a3ee76
DS
230 ok = ok
231 && wxGIFHandler_WriteComment(stream,
232 image.GetOption(wxIMAGE_OPTION_GIF_COMMENT))
233 && wxGIFHandler_WriteControl(stream, maskIndex, delayMilliSecs)
77b83d0a
DS
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
303bool wxGIFHandler::SaveAnimation(const wxImageArray& images,
491da411 304 wxOutputStream *stream, bool verbose, int delayMilliSecs)
77b83d0a
DS
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,
491da411 336 true /*loop?*/, pal, palCount, maskIndex);
77b83d0a
DS
337 }
338
339 return ok && wxGIFHandler_WriteTerm(stream);
340#else
341 wxUnusedVar(images);
342 wxUnusedVar(stream);
343 wxUnusedVar(verbose);
344 wxUnusedVar(delayMilliSecs);
77b83d0a
DS
345
346 return false;
347#endif
348}
349
350bool 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
397bool 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
416bool 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
9ab6ee85
GRG
489#endif // wxUSE_STREAMS
490
77b83d0a
DS
491bool 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
508void 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
519void 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
532int 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
552int wxGIFHandler_KeyItem(unsigned long item)
553{
554 return ((item >> 12) ^ item) & HT_KEY_MASK;
555}
556
557#if wxUSE_STREAMS
558
559int 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
573bool 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
615int 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
630bool wxGIFHandler_Write(wxOutputStream *stream, const void *buf, size_t len)
631{
632 return (len == stream->Write(buf, len).LastWrite());
633}
634
635bool wxGIFHandler_WriteByte(wxOutputStream *stream, wxUint8 byte)
636{
637 return wxGIFHandler_Write(stream, &byte, sizeof(byte));
638}
639
640bool 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, &word, sizeof(word));
647}
648
649bool wxGIFHandler_WriteHeader(wxOutputStream *stream, int width, int height,
64a3ee76 650 bool loop, const wxRGB *pal, int palCount)
77b83d0a
DS
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
64a3ee76 672 return ok;
77b83d0a
DS
673}
674
675bool 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
684bool wxGIFHandler_WriteTerm(wxOutputStream *stream)
685{
686 return wxGIFHandler_WriteByte(stream, GIF_MARKER_ENDOFDATA);
687}
688#endif
689
690bool wxGIFHandler_WriteZero(wxOutputStream *stream)
691{
692 return wxGIFHandler_WriteByte(stream, 0);
693}
694
695bool 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
721bool 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
737bool wxGIFHandler_WriteComment(wxOutputStream *stream, const wxString& comment)
738{
da60600b
DS
739 if ( comment.empty() )
740 {
741 return true;
742 }
77b83d0a 743
da60600b
DS
744 // Write comment header.
745 wxUint8 buf[2];
77b83d0a
DS
746 buf[0] = GIF_MARKER_EXT;
747 buf[1] = GIF_MARKER_EXT_COMMENT;
da60600b
DS
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
77b83d0a 774
da60600b
DS
775 // Write comment footer.
776 return wxGIFHandler_WriteZero(stream);
77b83d0a
DS
777}
778
779bool 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
799bool 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