]> git.saurik.com Git - wxWidgets.git/blame - src/common/imaggif.cpp
skip apple options
[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
77b83d0a 5// Copyright: (c) 1999-2011 Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K
65571936 6// Licence: wxWindows licence
b59bf2db
JS
7/////////////////////////////////////////////////////////////////////////////
8
b59bf2db 9// For compilers that support precompilation, includes "wx.h".
3096bd2f 10#include "wx/wxprec.h"
b59bf2db
JS
11
12#ifdef __BORLANDC__
8898456d 13 #pragma hdrstop
b59bf2db
JS
14#endif
15
8898456d
WS
16#if wxUSE_IMAGE && wxUSE_GIF
17
ce4169a4 18#ifndef WX_PRECOMP
8898456d
WS
19 #include "wx/intl.h"
20 #include "wx/log.h"
77b83d0a
DS
21 #include "wx/palette.h"
22 #include "wx/utils.h"
ce4169a4 23#endif
b59bf2db 24
8f493002 25#include "wx/imaggif.h"
464122b6 26#include "wx/gifdecod.h"
77b83d0a
DS
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
36enum
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
59struct wxRGB
60{
61 wxUint8 red;
62 wxUint8 green;
63 wxUint8 blue;
64};
65
66struct GifHashTableType
67{
68 wxUint32 HTable[HT_SIZE];
69};
70
ce4169a4
RR
71IMPLEMENT_DYNAMIC_CLASS(wxGIFHandler,wxImageHandler)
72
77b83d0a
DS
73//----------------------------------------------------------------------------
74// Forward declarations
75//----------------------------------------------------------------------------
76
77static int wxGIFHandler_KeyItem(unsigned long item);
78
79#if wxUSE_STREAMS
80
81static int wxGIFHandler_BitSize(int n);
82
83#if wxUSE_PALETTE
84static bool wxGIFHandler_GetPalette(const wxImage& image,
85 wxRGB *pal, int *palCount, int *mask_index);
86#endif
87static
88int wxGIFHandler_PaletteFind(const wxRGB& clr, const wxRGB *array, int count);
89
90static bool wxGIFHandler_Write(wxOutputStream *, const void *buf, size_t len);
91static bool wxGIFHandler_WriteByte(wxOutputStream *, wxUint8);
92static bool wxGIFHandler_WriteWord(wxOutputStream *, wxUint16);
93static bool wxGIFHandler_WriteHeader(wxOutputStream *, int width, int height,
64a3ee76 94 bool loop, const wxRGB *pal, int palCount);
77b83d0a
DS
95static bool wxGIFHandler_WriteRect(wxOutputStream *, int width, int height);
96#if wxUSE_PALETTE
97static bool wxGIFHandler_WriteTerm(wxOutputStream *);
98#endif
99static bool wxGIFHandler_WriteZero(wxOutputStream *);
100static bool wxGIFHandler_WritePalette(wxOutputStream *,
101 const wxRGB *pal, size_t palCount, int bpp);
102static bool wxGIFHandler_WriteControl(wxOutputStream *,
103 int maskIndex, int delayMilliSecs);
104static bool wxGIFHandler_WriteComment(wxOutputStream *, const wxString&);
105static bool wxGIFHandler_WriteLoop(wxOutputStream *);
106
107static bool wxGIFHandler_BufferedOutput(wxOutputStream *, wxUint8 *buf, int c);
108#endif // wxUSE_STREAMS
109
b59bf2db
JS
110//-----------------------------------------------------------------------------
111// wxGIFHandler
112//-----------------------------------------------------------------------------
113
9ab6ee85
GRG
114#if wxUSE_STREAMS
115
b931f7ee 116bool wxGIFHandler::LoadFile(wxImage *image, wxInputStream& stream,
77b83d0a 117 bool verbose, int index)
b59bf2db 118{
464122b6 119 wxGIFDecoder *decod;
72045d57 120 wxGIFErrorCode error;
7beb59f3 121 bool ok = true;
b59bf2db 122
0141d2c9 123// image->Destroy();
72045d57
VZ
124 decod = new wxGIFDecoder();
125 error = decod->LoadGIF(stream);
8a68d8b6 126
8141573c 127 if ((error != wxGIF_OK) && (error != wxGIF_TRUNCATED))
995612e2 128 {
e08e239b
GRG
129 if (verbose)
130 {
131 switch (error)
132 {
8141573c 133 case wxGIF_INVFORMAT:
add95ac3 134 wxLogError(_("GIF: error in GIF image format."));
8141573c
GRG
135 break;
136 case wxGIF_MEMERR:
add95ac3 137 wxLogError(_("GIF: not enough memory."));
8141573c
GRG
138 break;
139 default:
add95ac3 140 wxLogError(_("GIF: unknown error!!!"));
8141573c 141 break;
e08e239b
GRG
142 }
143 }
b59bf2db 144 delete decod;
7beb59f3 145 return false;
b59bf2db 146 }
b59bf2db 147
8141573c
GRG
148 if ((error == wxGIF_TRUNCATED) && verbose)
149 {
add95ac3 150 wxLogError(_("GIF: data stream seems to be truncated."));
77b83d0a 151 // go on; image data is OK
8141573c
GRG
152 }
153
b931f7ee
VS
154 if (ok)
155 {
72045d57 156 ok = decod->ConvertToImage(index != -1 ? (size_t)index : 0, image);
b931f7ee
VS
157 }
158 else
159 {
160 wxLogError(_("GIF: Invalid gif index."));
161 }
162
464122b6 163 delete decod;
0141d2c9 164
464122b6 165 return ok;
b59bf2db
JS
166}
167
77b83d0a
DS
168bool wxGIFHandler::SaveFile(wxImage *image,
169 wxOutputStream& stream, bool verbose)
b59bf2db 170{
77b83d0a
DS
171#if wxUSE_PALETTE
172 wxRGB pal[256];
173 int palCount;
174 int maskIndex;
8a68d8b6 175
77b83d0a
DS
176 return wxGIFHandler_GetPalette(*image, pal, &palCount, &maskIndex)
177 && DoSaveFile(*image, &stream, verbose, true /*first?*/, 0,
491da411 178 false /*loop?*/, pal, palCount, maskIndex)
77b83d0a
DS
179 && wxGIFHandler_WriteTerm(&stream);
180#else
181 wxUnusedVar(image);
182 wxUnusedVar(stream);
183 wxUnusedVar(verbose);
7beb59f3 184 return false;
77b83d0a 185#endif
b59bf2db
JS
186}
187
995612e2 188bool wxGIFHandler::DoCanRead( wxInputStream& stream )
0828c087 189{
72045d57
VZ
190 wxGIFDecoder decod;
191 return decod.CanRead(stream);
8faef7cc 192 // it's ok to modify the stream position here
0828c087
VS
193}
194
8faef7cc 195int wxGIFHandler::DoGetImageCount( wxInputStream& stream )
85fcb94f
VZ
196{
197 wxGIFDecoder decod;
198 wxGIFErrorCode error = decod.LoadGIF(stream);
199 if ( (error != wxGIF_OK) && (error != wxGIF_TRUNCATED) )
200 return -1;
03647350 201
8faef7cc
FM
202 // NOTE: this function modifies the current stream position but it's ok
203 // (see wxImageHandler::GetImageCount)
85fcb94f
VZ
204
205 return decod.GetFrameCount();
206}
207
77b83d0a
DS
208bool wxGIFHandler::DoSaveFile(const wxImage& image, wxOutputStream *stream,
209 bool WXUNUSED(verbose), bool first, int delayMilliSecs, bool loop,
491da411 210 const wxRGB *pal, int palCount, int maskIndex)
77b83d0a
DS
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,
64a3ee76 226 pal, palCount);
77b83d0a
DS
227 }
228
64a3ee76
DS
229 ok = ok
230 && wxGIFHandler_WriteComment(stream,
231 image.GetOption(wxIMAGE_OPTION_GIF_COMMENT))
232 && wxGIFHandler_WriteControl(stream, maskIndex, delayMilliSecs)
77b83d0a
DS
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
302bool wxGIFHandler::SaveAnimation(const wxImageArray& images,
491da411 303 wxOutputStream *stream, bool verbose, int delayMilliSecs)
77b83d0a
DS
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,
491da411 335 true /*loop?*/, pal, palCount, maskIndex);
77b83d0a
DS
336 }
337
338 return ok && wxGIFHandler_WriteTerm(stream);
339#else
340 wxUnusedVar(images);
341 wxUnusedVar(stream);
342 wxUnusedVar(verbose);
343 wxUnusedVar(delayMilliSecs);
77b83d0a
DS
344
345 return false;
346#endif
347}
348
349bool 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
396bool 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
415bool 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
9ab6ee85
GRG
488#endif // wxUSE_STREAMS
489
77b83d0a
DS
490bool 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
507void 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
518void 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
531int 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
551int wxGIFHandler_KeyItem(unsigned long item)
552{
553 return ((item >> 12) ^ item) & HT_KEY_MASK;
554}
555
556#if wxUSE_STREAMS
557
558int 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
572bool 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
614int 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
629bool wxGIFHandler_Write(wxOutputStream *stream, const void *buf, size_t len)
630{
631 return (len == stream->Write(buf, len).LastWrite());
632}
633
634bool wxGIFHandler_WriteByte(wxOutputStream *stream, wxUint8 byte)
635{
636 return wxGIFHandler_Write(stream, &byte, sizeof(byte));
637}
638
639bool wxGIFHandler_WriteWord(wxOutputStream *stream, wxUint16 word)
640{
641 wxUint8 buf[2];
642
643 buf[0] = word & 0xff;
644 buf[1] = (word >> 8) & 0xff;
ff5bf31c 645 return wxGIFHandler_Write(stream, &buf, sizeof(buf));
77b83d0a
DS
646}
647
648bool wxGIFHandler_WriteHeader(wxOutputStream *stream, int width, int height,
64a3ee76 649 bool loop, const wxRGB *pal, int palCount)
77b83d0a
DS
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
64a3ee76 671 return ok;
77b83d0a
DS
672}
673
674bool 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
683bool wxGIFHandler_WriteTerm(wxOutputStream *stream)
684{
685 return wxGIFHandler_WriteByte(stream, GIF_MARKER_ENDOFDATA);
686}
687#endif
688
689bool wxGIFHandler_WriteZero(wxOutputStream *stream)
690{
691 return wxGIFHandler_WriteByte(stream, 0);
692}
693
694bool 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
720bool wxGIFHandler_WriteControl(wxOutputStream *stream,
721 int maskIndex, int delayMilliSecs)
722{
723 wxUint8 buf[8];
724
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
730 buf[5] = 0;
731 buf[6] = (maskIndex != wxNOT_FOUND) ? (wxUint8) maskIndex : 0;
732 buf[7] = 0;
733 return wxGIFHandler_Write(stream, buf, sizeof(buf));
734}
735
736bool wxGIFHandler_WriteComment(wxOutputStream *stream, const wxString& comment)
737{
da60600b
DS
738 if ( comment.empty() )
739 {
740 return true;
741 }
77b83d0a 742
da60600b
DS
743 // Write comment header.
744 wxUint8 buf[2];
77b83d0a
DS
745 buf[0] = GIF_MARKER_EXT;
746 buf[1] = GIF_MARKER_EXT_COMMENT;
da60600b
DS
747 if ( !wxGIFHandler_Write(stream, buf, sizeof(buf)) )
748 {
749 return false;
750 }
751
752 /*
753 If comment is longer than 255 bytes write it in blocks of maximum 255
754 bytes each.
755 */
756 wxCharBuffer text( comment.mb_str() );
757
758 size_t pos = 0, fullLength = text.length();
759
760 do
761 {
762 size_t blockLength = wxMin(fullLength - pos, 255);
763
764 if ( !wxGIFHandler_WriteByte(stream, (wxUint8) blockLength)
765 || !wxGIFHandler_Write(stream, &text.data()[pos], blockLength) )
766 {
767 return false;
768 }
769
770 pos += blockLength;
771 }while (pos < fullLength);
772
77b83d0a 773
da60600b
DS
774 // Write comment footer.
775 return wxGIFHandler_WriteZero(stream);
77b83d0a
DS
776}
777
778bool wxGIFHandler_WriteLoop(wxOutputStream *stream)
779{
780 wxUint8 buf[4];
781 const int loopcount = 0; // infinite
782
783 buf[0] = GIF_MARKER_EXT;
784 buf[1] = GIF_MARKER_EXT_APP;
785 buf[2] = 0x0B;
786 bool ok = wxGIFHandler_Write(stream, buf, 3)
787 && wxGIFHandler_Write(stream, NETSCAPE_LOOP, sizeof(NETSCAPE_LOOP)-1);
788
789 buf[0] = 3;
790 buf[1] = 1;
791 buf[2] = loopcount & 0xFF;
792 buf[3] = loopcount >> 8;
793
794 return ok && wxGIFHandler_Write(stream, buf, 4)
795 && wxGIFHandler_WriteZero(stream);
796}
797
798bool wxGIFHandler_BufferedOutput(wxOutputStream *stream, wxUint8 *buf, int c)
799{
800 bool ok = true;
801
802 if (c == FLUSH_OUTPUT)
803 {
804 // Flush everything out.
805 if (buf[0])
806 {
807 ok = wxGIFHandler_Write(stream, buf, buf[0]+1);
808 }
809 // Mark end of compressed data, by an empty block (see GIF doc):
810 wxGIFHandler_WriteZero(stream);
811 }
812 else
813 {
814 if (buf[0] == 255)
815 {
816 // Dump out this buffer - it is full:
817 ok = wxGIFHandler_Write(stream, buf, buf[0] + 1);
818 buf[0] = 0;
819 }
820 buf[++buf[0]] = c;
821 }
822
823 return ok;
824}
825
826#endif // wxUSE_STREAMS
827
828#endif // wxUSE_IMAGE && wxUSE_GIF