Bugfix : Zooming works correct, no more errors on screen.
[xara-cairo.git] / wxOil / gifutil.cpp
blobbfc912ad86654401fe0b7f4a4b5597a4900e47bb
1 // $Header: /Camelot/winoil/gifutil.cpp 19 25/03/99 16:16 Markn $
3 // Contains useful routines for compressing a bitmap out to a GIF format file and
4 // routines to load that file back in.
6 /*
7 */
9 #include "camtypes.h"
10 #include "ensure.h"
11 #include "fixmem.h"
12 #include "errors.h"
13 #include "progress.h" // For hourglass stuff
14 //#include "resource.h" // IDS_OUTOFMEMORY
15 //#include "accures.h" // IDW_CANCELLEDBMPIMPORT
16 #include "gifutil.h"
17 //#include "outptgif.h"
18 //#include "andy.h"
19 //#include "dibconv.h"
20 //#include "string.h" // memcpy
21 #include "camfiltr.h" // BaseCamelotFilter
23 #define new CAM_DEBUG_NEW
26 CC_IMPLEMENT_MEMDUMP(Palette, CC_CLASS_MEMDUMP)
27 Palette::~Palette()
29 if (m_pColours)
30 delete [] m_pColours;
31 m_pColours = NULL; // ensure state correct
32 m_nSize = 0;
37 Palette::Palette(const Palette& otherPalette)
39 if (&otherPalette == this) return;
41 if (otherPalette.m_nSize > m_nSize)
42 delete [] m_pColours;
43 m_pColours = new RGBQUAD[otherPalette.m_nSize];
44 if (m_pColours == NULL)
46 m_bInitOK = FALSE;
47 return;
49 m_nSize = otherPalette.m_nSize;
50 memcpy(m_pColours, otherPalette.m_pColours, m_nSize);
51 m_bInitOK = TRUE;
54 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
56 // define the statics that we require
57 BOOL GIFUtil::Interlace = FALSE; // Use interlace or not
58 int GIFUtil::Transparent = -1; // colour or -1 = no transparency
59 UINT32 GIFUtil::m_Delay = 0; // Data member, to recieve a bitmaps Animation delay value
60 BOOL GIFUtil::ZeroDataBlock = FALSE; // flag to say whether we have a zero length block or not
61 int GIFUtil::m_nCurrentBitmap = 0;
62 GIFDisposalMethod GIFUtil::m_Restore = GDM_LEAVE; // Data member, to recieve a bitmaps' Animation Restore value.
64 LPRGBQUAD GIFUtil::lpGlobalPalette = NULL; // pointer to temporary palette store
65 int GIFUtil::GlobalPaletteSize = 0; // size of the global palette found
66 FilePos GIFUtil::m_NextImageStartPosition = 0; // Position in file of next image
67 BOOL GIFUtil::m_bImageRead = FALSE; // Did we get something?
69 WORD GIFUtil::m_GlobalWidth = 0; // The overall width from the GIF header
70 WORD GIFUtil::m_GlobalHeight = 0; // The overall height from the GIF header
72 /********************************************************************************************
74 > GIFUtil::GIFUtil()
76 Author: Neville Humphrys
77 Created: 29/6/95
78 Purpose: Default constructor for the class.
79 SeeAlso:
81 ********************************************************************************************/
83 //GIFUtil::GIFUtil()
84 //{
85 //}
87 /********************************************************************************************
88 > GIFUtil::GIFUtil()
90 Author: Neville Humphrys
91 Created: 29/6/95
92 Purpose: Default destructor for the class.
93 SeeAlso:
95 ********************************************************************************************/
97 //GIFUtil::~GIFUtil()
98 //{
99 //}
101 /********************************************************************************************
103 > static BOOL GIFUtil::ReadFromFile( CCLexFile *File, LPBITMAPINFO *Info, LPBYTE *Bits,
104 int *TransColour, int& nBitmapToRead,
105 String_64 *ProgressString, BaseCamelotFilter *pFilter = NULL )
107 Author: Neville Humphrys
108 Created: 29/6/95
109 Inputs: File A opened CCLexFile that can be read from. It should be positioned at the
110 start. Caller is responsible for closing it. The file needs to be in
111 Binary mode.
112 nBitmapToRead The number of the bitmap in the GIF that you wish to read.
113 If 1 then the GIF will be read from the beginning obtaining the
114 header information.
115 For any number other than 1 it should be noted that bitmaps can only
116 be read in an incremental sequence and that the first one must have
117 been read prior to any call with a non-1 value.
118 ProgressString allows the user to specify whether they require a progress hourglass or
119 not. If NULL then none is shown, otherwise an progress bar is shown
120 using the text supplied. Defaults to NULL i.e. no progress bar.
121 pFilter is an alternative way of handling the progress bar, assume the
122 progress bar has been start and just call the IncProgressBarCount in
123 BaseCamelotFilter to do the progress bar update.
124 Defaults to NULL i.e. no progress bar.
125 Outputs: Info points to a new LPBITMAPINFO struct and Bits points to the bytes.
126 These can be freed up with FreeDIB.
127 TransColour is either -1 == none or equal to the transparency colour found
128 nBitmapToRead Returns the number of the next bitmap that can be read from the GIF
129 or -1 if no further bitmaps exist.
130 Returns: TRUE if worked, FALSE if failed (error will be set accordingly but not reported)
131 Purpose: Reads a .gif file into memory decompressing it as it goes.
132 ***Errors on 16-bit builds***
133 A progress hourglass can be shown if required.
134 Errors: Calls SetError on FALSE returns.
135 Scope: Static, Public
136 SeeAlso: DIBUtil::ReadFromFile; AccusoftFilters::ReadFromFile;
138 ********************************************************************************************/
139 BOOL GIFUtil::ReadFromFile( CCLexFile *File, LPBITMAPINFO *Info, LPBYTE *Bits,
140 int *TransColour, String_64 *ProgressString,
141 BaseCamelotFilter *pFilter )
143 int nBitmap = 1;
145 return (ReadFromFile( File, Info, Bits, TransColour, nBitmap, ProgressString, pFilter));
148 /**********************************************************************************************
150 static BOOL GIFUtil::ReadFromFile( CCLexFile *File, LPBITMAPINFO *Info, LPBYTE *Bits,
151 int *TransColour, int& nBitmapToRead, String_64 *ProgressString = NULL,
152 BaseCamelotFilter *pFilter = NULL, UINT32* Delay=NULL,
153 GIFDisposalMethod *Restore=NULL,
154 UINT32 * pLeftOffset = NULL, UINT32 * pTopOffset = NULL,
155 BOOL * pLocalPalette = NULL)
156 Author: -
157 Created: -
158 Inputs/Outputs Refer to the above Function Header for a full explanation.
160 *************************************************************************************************/
162 BOOL GIFUtil::ReadFromFile( CCLexFile *File, LPBITMAPINFO *Info, LPBYTE *Bits,
163 int *TransColour, int& nBitmapToRead, String_64 *ProgressString,
164 BaseCamelotFilter *pFilter, UINT32* Delay, GIFDisposalMethod *Restore,
165 UINT32 * pLeftOffset, UINT32 * pTopOffset,
166 BOOL * pLocalPalette)
168 if ((nBitmapToRead > 1 && nBitmapToRead == m_nCurrentBitmap) || nBitmapToRead < 1)
170 ERROR3("GIFUtil::ReadFromFile() - can't read that bitmap");
171 return FALSE;
174 *Info = NULL; // in case of early exit
175 *Bits = NULL;
176 Transparent = -1;
177 *TransColour = -1; // in case of early exit set to none
178 int Background = 0; // background colour number !!! not used !!!
179 Interlace = FALSE; // set interlace to false by default
181 // Must set the exception throwing flag to True and force reporting of errors to False.
182 // This means that the caller must report an error if the function returns False.
183 // Any calls to CCLexFile::GotError will now throw a file exception and should fall into
184 // the catch handler at the end of the function.
185 BOOL OldThrowingState = File->SetThrowExceptions( TRUE );
186 BOOL OldReportingState = File->SetReportErrors( FALSE );
188 // If the caller has specified a string then assume they require a progress bar
189 // Start it up.
190 if (ProgressString != NULL)
191 BeginSlowJob(100, FALSE, ProgressString);
195 // If we want the first bitmap we'll assume we're at the start of the file & read the header
196 if (nBitmapToRead == 1)
198 // place to store the global palette, if present, for later use
199 lpGlobalPalette = NULL; // pointer to temporary palette store
200 GlobalPaletteSize = 0; // size of the global palette found
202 GIFINFOHEADER Header;
203 // This is really sizeof(GIFINFOHEADER) but this returns 14 instead of 13
204 // as it rounds to the nearest word boundary
205 const size_t HeaderSize = sizeof(char)* 6 + sizeof(WORD) * 2 + sizeof(BYTE) * 3;
207 File->read( &Header, HeaderSize );
208 if (File->bad())
209 File->GotError( _R(IDE_FORMATNOTSUPPORTED) );
211 // Just double check that the signature is correct, if not then fail immediately
212 if (
213 (strncmp(Header.giName, "GIF89a", 6) != 0) &&
214 (strncmp(Header.giName, "GIF87a", 6) != 0)
216 File->GotError( _R(IDE_BADFORMAT) );
218 // Note the overall size of the GIF from the header, may be the animation size
219 m_GlobalWidth = Header.giWidth;
220 m_GlobalHeight = Header.giHeight;
221 // flags word consists of:-
222 // - bit 7 colour table flag = set if global colour table follows
223 // - bits 6-4 colour resolution = bpps - 1
224 // - bit 3 sort flag = set global colour table sorted
225 // - bits 3-0 size of global colour table = size is 2^(value+1)
226 int ColorResolution = (((Header.giFlags >> 4) & 0x07) + 1);
227 GlobalPaletteSize = 2 << (Header.giFlags & 0x07);
228 Background = Header.giBackground;
229 // int AspectRatio = Header.giAspect;
231 TRACEUSER("Neville",_T("Gif Global Width = %d Height = %d\n"), m_GlobalWidth, m_GlobalHeight);
232 TRACEUSER("Neville",_T("Gif ColorResolution = %d\n"), ColorResolution);
234 // Check if we have a global colour map present or not, if so then read it in.
235 if (BitSet(Header.giFlags, GLOBALCOLOURMAP))
237 TRACEUSER("Neville",_T("Gif read global colour table size = %d\n"), GlobalPaletteSize);
238 // Read in the global colour map into a palette structure for possible later use
239 const size_t TotalPal = sizeof(RGBQUAD) * GlobalPaletteSize;
240 lpGlobalPalette = (LPRGBQUAD)CCMalloc( TotalPal );
241 if (lpGlobalPalette==NULL)
242 return FALSE;
244 ReadColourMap(File, GlobalPaletteSize, lpGlobalPalette);
246 m_nCurrentBitmap = 0;
247 m_NextImageStartPosition = 0;
250 // We now need to go through and process the data in this file which
251 // should now consist off GIF blocks until we hit the end of file or
252 // we have processed the lot.
253 BOOL FileProcessed = FALSE;
255 m_bImageRead = FALSE;
256 unsigned char c;
257 // Set up bad values so that the DIB alloc claim will fail if there
258 // has been no GIFIMAGEBLOCK found
259 // int Width = 0; // Width of bitmap
260 // int Height = 0; // Height of bitmap
261 // int BitsPerPixel = 0; //ColorResolution; // Colour depth required
263 if (m_NextImageStartPosition != 0)
265 File->seekIn(m_NextImageStartPosition, ios::beg);
266 m_NextImageStartPosition = 0;
269 while (!File->eof() && !File->bad() && !FileProcessed)
271 // Get the next character in the stream and see what it indicates
272 // comes next in the file
273 // Use Get as this will read the EOF character (255) and exit via the
274 // File->eof() test rather than Read which will throw an exception.
275 //File->read( &c, 1 );
276 File->get( (char&)c );
278 switch (c)
280 case ';':
282 // GIF terminator
283 // Cannot assume that the terminator immediately follows the image data
284 // Might have some extensions following the image data.
285 FileProcessed = TRUE;
286 nBitmapToRead = -1;
287 break;
290 case '!': //EXTENSIONBLOCK:
292 // Extension
293 ProcessExtension(File);
294 break;
297 case ',':
299 // start of image character
300 if (m_bImageRead) // another bitmap - save it for the next call to ReadFromFile()
302 if (m_NextImageStartPosition == 0)
304 // No transparency info for the next one so start here
305 m_NextImageStartPosition = File->tellIn() - 1;
307 ++nBitmapToRead;
308 FileProcessed = TRUE;
309 break;
312 UINT32 LeftOffset = 0;
313 UINT32 TopOffset = 0;
314 BOOL LocalPalette = FALSE;
316 // Should be followed by a GIFIMAGE BLOCK
317 ProcessImageBlock(File, Info, Bits, &LeftOffset, &TopOffset, &LocalPalette);
318 ++m_nCurrentBitmap;
320 if (nBitmapToRead == m_nCurrentBitmap)
322 m_bImageRead = TRUE;
325 // return the values back to the caller if it desired them
326 if (pLeftOffset)
327 *pLeftOffset = LeftOffset;
328 if (pTopOffset)
329 *pTopOffset = TopOffset;
330 if (pLocalPalette)
331 *pLocalPalette = LocalPalette;
332 break;
335 default:
336 // We have found something other than what we are expecting
337 // so fail with an error
338 //File->GotError( IDE_BADFORMAT );
339 // Cannot do this as some files have random bits
340 // e.g. galileo.gif has a random zero before the ;
341 // colsec.gif has a rampant > at the end instead of ;
342 if (m_bImageRead)
344 // We've already got something so ignore anything else in case it goes
345 // completely wrong.
346 FileProcessed = TRUE;
347 nBitmapToRead = -1;
349 TRACE( _T("Unrecognized Character %x at %d\n"), (int)c, File->tellIn());
351 break;
355 // If we reach here and the bitmap allocations are still null then no valid image
356 // was found and so we should error now.
357 // Might have just been a GIF file with extension tags in and no images!
358 if (*Info == NULL || *Bits == NULL)
359 File->GotError( _R(IDE_BADFORMAT) );
361 // We read the desired bitmap but the EOF came along before we could try anything else
362 // Signal this is the last bitmap
363 if (m_bImageRead && File->eof())
365 TRACE( _T("GIF: Premature end of file") );
366 nBitmapToRead = -1;
369 // Return the transparency/delay/restore found to the caller.
370 *TransColour = Transparent;
371 if (Delay)
373 *Delay = m_Delay;
375 if (Restore)
377 *Restore = m_Restore;
380 // Free up the bit of memory for a palette we grabbed, if present & no more images to read
381 if (lpGlobalPalette && nBitmapToRead == -1)
383 CCFree(lpGlobalPalette);
384 lpGlobalPalette = NULL;
387 // If started, then stop then progress bar
388 if (ProgressString != NULL)
389 EndSlowJob();
391 // Must set the exception throwing and reporting flags back to their entry states
392 File->SetThrowExceptions( OldThrowingState );
393 File->SetReportErrors( OldReportingState );
395 // er, we seem to have finished OK so say so
396 return TRUE;
398 catch( CFileException e )
400 // catch our form of a file exception
401 TRACE( _T("GIFUtil::ReadFromFile CC catch handler\n") );
403 FreeDIB( *Info, *Bits ); // free any alloced memory
404 *Info = NULL; // and NULL the pointers
405 *Bits = NULL;
407 // Free up the bit of memory for a palette we grabbed, if present
408 if (lpGlobalPalette)
410 CCFree(lpGlobalPalette);
411 lpGlobalPalette = NULL;
414 // If started, then stop then progress bar
415 if (ProgressString != NULL)
416 EndSlowJob();
418 // Must set the exception throwing and reporting flags back to their entry states
419 File->SetThrowExceptions( OldThrowingState );
420 File->SetReportErrors( OldReportingState );
422 return FALSE;
425 ERROR2( FALSE, "Escaped exception clause somehow" );
429 /********************************************************************************************
431 > static BOOL GIFUtil::ProcessExtension(CCLexFile *fd)
433 Author: Neville Humphrys
434 Created: 29/6/95
435 Inputs: fd pointer to a CCLexFile to read the data from
436 Outputs: -
437 Returns: True if worked ok, False otherwise.
438 Purpose:
439 SeeAlso:
441 ********************************************************************************************/
443 BOOL GIFUtil::ProcessExtension(CCLexFile *fd)
445 static char buf[256];
447 // Get the type of this extension and then process it
448 unsigned char ExtensionType;
449 fd->read( &ExtensionType, 1 );
451 switch (ExtensionType)
453 case TRANSPARENTBLOCK:
455 // The next image in the file might need to start here to get transparency
456 // information. So remember the position
457 m_NextImageStartPosition = fd->tellIn() - 2;
459 // Graphic Control Extension
460 // We have read in the identfier and the type of extension block so we
461 // really need to read the data from that point onwards
462 GIFTRANSBLOCK TransBlock;
464 // This is really sizeof(GIFTRANSBLOCK) but this returns 14 instead of 13
465 // as it rounds to the nearest word boundary
466 const size_t TransBlockSize = sizeof(WORD) * 1 + sizeof(BYTE) * 6;
467 fd->read( &(TransBlock.gtbBlockSize), TransBlockSize - 2);
469 // Contains lots of random rubbish we are not interested in.
470 // Just skip to the crux of the matter and read in the transparency colour,
471 // if the transparency flag is set.
473 if (!m_bImageRead)
475 BOOL fTrans = TransBlock.gtbFlags & 0x1;
476 if (fTrans != 0)
477 Transparent = TransBlock.gtbTransparency;
478 else
480 Transparent = -1; // no transparency
483 TRACEUSER("Neville", _T("ProcessExtension - transparent fTrans = %d Transparent = %d\n"), fTrans, Transparent );
485 // Animation Restore\Delay values.
486 m_Delay = TransBlock.gtbDelay;
487 m_Restore = GIFDisposalMethod((TransBlock.gtbFlags >> 2) & 0x03);
490 return TRUE;
493 case PLAINTEXTBLOCK:
495 // We need to bin any transparency information we have since
496 // "The scope of this [Graphic Control (TRANSPARENTBLOCK)] Extension is the
497 // graphic rendering block that follows it"
499 if (m_bImageRead)
501 // We've read the image but don't need to start with any TRANSPARENT block
502 // that might have been read after the image, so make sure we don't
503 m_NextImageStartPosition = 0;
505 else
507 // We haven't read any image yet so just make sure no transparency
508 Transparent = -1;
510 // ...and drop through
513 default:
514 break;
517 // Must set this as otherwise, if this is after we have loaded an image then we have
518 // the possibility that the zero block has been encountered and GetDataBlock will
519 // always return 0, so force it not to.
520 ZeroDataBlock = FALSE;
522 // If not recognised then read the rest of that extension
523 while (GetDataBlock(fd, (unsigned char*) buf) != 0)
526 return FALSE;
529 /********************************************************************************************
531 > static BOOL GIFUtil::ReadColourMap(CCLexFile *fd, int number, LPRGBQUAD lpPalette)
533 Author: Neville Humphrys
534 Created: 29/6/95
535 Inputs: fd pointer to a CCLexFile to read the data from
536 Outputs: -
537 Returns: True if worked ok, False otherwise.
538 Purpose:
539 SeeAlso:
541 ********************************************************************************************/
543 BOOL GIFUtil::ReadColourMap(CCLexFile *fd, int number, LPRGBQUAD lpPalette)
545 GIFRGBTRIPLE rgb;
546 for (int i = 0; i < number; ++i)
548 fd->read( &rgb, sizeof(GIFRGBTRIPLE) );
549 lpPalette->rgbBlue = rgb.grgbtBlue;
550 lpPalette->rgbGreen = rgb.grgbtGreen;
551 lpPalette->rgbRed = rgb.grgbtRed;
552 lpPalette->rgbReserved = 0;
553 lpPalette++;
556 return TRUE;
561 /********************************************************************************************
563 > static int GIFUtil::GetDataBlock(CCLexFile *fd, unsigned char *buf)
565 Author: Neville Humphrys
566 Created: 29/6/95
567 Inputs: fd pointer to a CCLexFile to read the data from
568 Outputs: -
569 Returns: Number of bytes read
570 Purpose: To read in a block of data from the specifed file. Reads a 1 byte count
571 and then reads that many bytes in, as long as the count is non-zero.
572 Any non-image calls must set ZeroDataBlock to False before calling this as
573 otherwise it may just return zero.
574 SeeAlso:
576 ********************************************************************************************/
578 int GIFUtil::GetDataBlock(CCLexFile *fd, unsigned char *buf)
580 unsigned char count;
582 // Added, as if we encounter the zero block then that is the end of this image
583 // and we should never read any more info from that image. (added 14/8/95)
584 if (ZeroDataBlock)
585 return 0;
587 fd->read( &count, 1 );
589 // If there is a count then read that many bytes in
590 // If zero then this is a terminator so return that
591 if (count == 0)
593 ZeroDataBlock = TRUE;
594 // Fall through to return 0
596 else
598 ZeroDataBlock = FALSE;
599 fd->read( buf, count );
600 // fall through to return the number of bytes found
603 return count;
606 /********************************************************************************************
608 > static int GIFUtil::GetCode(CCLexFile *fd, int code_size, int flag)
610 Author: Neville Humphrys
611 Created: 29/6/95
612 Inputs: fd pointer to a CCLexFile to read the data from
613 code_size
614 flag
615 Outputs: -
616 Returns: The code found.
617 Purpose: Returns the next code in the stream.
618 SeeAlso:
620 ********************************************************************************************/
622 int GIFUtil::GetCode(CCLexFile *fd, int code_size, int flag)
624 static unsigned char buf[280];
625 static int curbit;
626 static int lastbit;
627 static int done;
628 static int last_byte;
629 int i;
630 int j;
631 int ret;
632 unsigned char count;
634 if (flag)
636 curbit = 0;
637 lastbit = 0;
638 done = FALSE;
639 return 0;
642 if ( (curbit+code_size) >= lastbit)
644 if (done)
646 if (curbit >= lastbit)
648 /* Oh well */
650 return -1;
652 buf[0] = buf[last_byte-2];
653 buf[1] = buf[last_byte-1];
655 if ((count = GetDataBlock(fd, &buf[2])) == 0)
656 done = TRUE;
658 last_byte = 2 + count;
659 curbit = (curbit - lastbit) + 16;
660 lastbit = (2+count)*8 ;
663 ret = 0;
664 for (i = curbit, j = 0; j < code_size; ++i, ++j)
665 ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
667 curbit += code_size;
669 return ret;
673 /********************************************************************************************
675 > static int GIFUtil::LWZReadByte(CCLexFile *fd, int flag, int input_code_size)
677 Author: Neville Humphrys
678 Created: 29/6/95
679 Inputs: fd pointer to a CCLexFile to read the data from
680 flag
681 input_code_size
682 Outputs: -
683 Returns: The next LZW byte in the stream.
684 Purpose: To read the next LZW byte from the stream.
685 SeeAlso:
687 ********************************************************************************************/
689 int GIFUtil::LWZReadByte(CCLexFile *fd, int flag, int input_code_size)
691 static int fresh = FALSE;
692 int code;
693 int incode;
694 static int code_size;
695 static int set_code_size;
696 static int max_code;
697 static int max_code_size;
698 static int firstcode;
699 static int oldcode;
700 static int clear_code;
701 static int end_code;
702 static int table[2][(1<< MAX_LWZ_BITS)];
703 static int stack[(1<<(MAX_LWZ_BITS))*2];
704 static int *sp;
705 int i;
707 if (flag)
709 set_code_size = input_code_size;
710 code_size = set_code_size+1;
711 clear_code = 1 << set_code_size ;
712 end_code = clear_code + 1;
713 max_code_size = 2*clear_code;
714 max_code = clear_code+2;
716 GetCode(fd, 0, TRUE);
718 fresh = TRUE;
720 for (i = 0; i < clear_code; ++i)
722 table[0][i] = 0;
723 table[1][i] = i;
725 for (; i < (1<<MAX_LWZ_BITS); ++i)
726 table[0][i] = table[1][0] = 0;
728 sp = stack;
730 return 0;
732 else if (fresh)
734 fresh = FALSE;
737 firstcode = oldcode = GetCode(fd, code_size, FALSE);
738 } while (firstcode == clear_code);
740 return firstcode;
743 if (sp > stack)
744 return *--sp;
746 while ((code = GetCode(fd, code_size, FALSE)) >= 0)
748 if (code == clear_code)
750 for (i = 0; i < clear_code; ++i)
752 table[0][i] = 0;
753 table[1][i] = i;
756 for (; i < (1<<MAX_LWZ_BITS); ++i)
757 table[0][i] = table[1][i] = 0;
759 code_size = set_code_size+1;
760 max_code_size = 2*clear_code;
761 max_code = clear_code+2;
762 sp = stack;
763 firstcode = oldcode = GetCode(fd, code_size, FALSE);
765 return firstcode;
767 else if (code == end_code)
769 int count;
770 unsigned char buf[260];
772 if (ZeroDataBlock)
773 return -2;
775 while ((count = GetDataBlock(fd, buf)) > 0)
778 if (count != 0)
779 return -2;
782 incode = code;
784 if (code >= max_code)
786 *sp++ = firstcode;
787 code = oldcode;
790 while (code >= clear_code)
792 *sp++ = table[1][code];
793 if (code == table[0][code])
795 /* Oh well */
797 code = table[0][code];
800 *sp++ = firstcode = table[1][code];
802 if ((code = max_code) <(1<<MAX_LWZ_BITS))
804 table[0][code] = oldcode;
805 table[1][code] = firstcode;
806 ++max_code;
807 if ((max_code >= max_code_size) && (max_code_size < (1<<MAX_LWZ_BITS)))
809 max_code_size *= 2;
810 ++code_size;
814 oldcode = incode;
816 if (sp > stack)
817 return *--sp;
819 return code;
822 /********************************************************************************************
824 > static BOOL GIFUtil::ReadImage(CCLexFile *fd, LPBYTE pBitsData, int width, int height,
825 BOOL interlace, BaseCamelotFilter *pFilter = NULL )
827 Author: Neville Humphrys
828 Created: 29/6/95
829 Inputs: fd pointer to a CCLexFile to read the data from
830 pBitsData pointer to the place to put the bitmap data, should have been
831 claimed so that it is the correct size to contain it.
832 width width of the bitmap in pixels
833 height height of the bitmap in pixels
834 bpp colour depth of the bitmap in bits per pixel
835 interlace flag to say if this data is interlaced or not.
836 pFilter is an alternative way of handling the progress bar, assume the
837 progress bar has been start and just call the IncProgressBarCount in
838 BaseCamelotFilter to do the progress bar update.
839 Defaults to NULL i.e. no progress bar.
840 Purpose: The read the bitmap data into the specified buffer. It decompresses the
841 data from the specified file and then puts the pixel data into the buffer.
842 Copes with interlaced GIFs.
843 Assumes progress hourglass has been started with 100.
844 SeeAlso: GIFUtil::ReadFromFile;
846 ********************************************************************************************/
848 BOOL GIFUtil::ReadImage(CCLexFile *fd, LPBYTE pBitsData, int width, int height, int bpp,
849 BOOL interlace, BaseCamelotFilter *pFilter)
851 ERROR2IF(pBitsData == NULL, FALSE, "GIFUtil::ReadImage given null pointer to bits data");
852 ERROR2IF(bpp != 8 && bpp != 4 && bpp != 1, FALSE, "GIFUtil::ReadImage - bpp invalid");
854 unsigned char c;
855 int v = 0;
856 int xpos = 0;
857 int ypos = 0;
858 int pass = 0;
859 int yposcount = 0;
861 ZeroDataBlock = FALSE;
863 // Work out how often we need to update the progress bar
864 int UpdateEvery = 1;
865 INT32 UpdateValue = 1;
866 if (pFilter == NULL)
867 UpdateEvery = height/100 + 1;
868 else
870 // Ask the filter what the record size for this bitmap is and hence
871 // what the allocation we have for progress bar updates are.
872 // We will then have to update our progress bar by
873 // current scanline/total number of scanlines * allocation
874 // so that we get update by a proportion of the value.
875 // We can assume no interlacing as in native/web files this is turned off.
876 INT32 RecordSize = pFilter->GetCurrentRecordSize();
877 if (RecordSize == 0)
878 RecordSize = 1;
879 UpdateValue = RecordSize/height;
880 UpdateEvery = 0; // So it updates every time round the loop
883 int LastProgressUpdate = 0;
885 // Work out the word/byte rounded line width rather than the pixel width
886 INT32 WidthOfLine = DIBUtil::ScanlineSize( width, bpp );
888 // Initialize the Compression routines
889 fd->read( &c, 1 );
890 ERROR3IF(c > GIFBITS,"Bad code size in GIFUtil ReadImage");
892 if (LWZReadByte(fd, TRUE, c) < 0)
894 return FALSE;
897 while ((v = LWZReadByte(fd, FALSE, c)) >= 0 )
899 //GIFSetPixel(pBitsData, xpos, ypos, v);
900 // Check that the specified pixel is within the bounds specified and that we
901 // have a buffer to write to
903 // if ( !(((ypos < 0) || (ypos >= height)) || ((xpos < 0) || (xpos >= width))) && pBitsData )
904 // {
905 // Our DIBs are the wrong way up so we must output the data from the last
906 // line of the image and go up to the start
907 // -1 as height = 1 .. Height whereas y goes from 0 .. Height - 1
908 // Use the word/byte rounded line width rather than the pixel width
909 if (bpp == 8)
911 // If 8 bpp then just use the whole byte straight
912 LPBYTE pData = pBitsData + xpos + ((height - 1 - ypos) * WidthOfLine);
913 *(pData) = v;
915 else if (bpp == 4)
917 // 4bpp so we must put the data into either the high or low nibble.
918 // This will be dependent on whether we are on an odd or even pixel.
919 // So test the LSBit of the curx, if set we will be odd.
920 // Only move onto next byte every other pixel hence curx/2.
921 LPBYTE pData = pBitsData + xpos/2 + ((height - 1 - ypos) * WidthOfLine);
922 // Get whole present byte
923 if (xpos & 1)
924 *(pData) = ((*(pData)) & 0xF0) | (v & 0x0F); // add into low nibble
925 else
926 *(pData) = ((*(pData)) & 0x0F) | ((v << 4) & 0xF0); // add into top nibble
928 else if (bpp == 1)
930 // 1bpp so we must put the data into either the high or low nibble.
931 // This will be dependent on whether we are on an odd or even pixel.
932 // So test the LSBit of the curx, if set we will be odd.
933 // Only move onto next byte every other pixel hence curx/2.
934 LPBYTE pOutputBitmap = pBitsData + (xpos / 8) + ((height - 1 - ypos) * WidthOfLine);
935 // Get whole present byte
936 UINT32 BitPosition = 7 - (xpos % 8);
937 BYTE SetBit = v << BitPosition;
938 BYTE Mask = ~(1 << BitPosition);
940 *pOutputBitmap = ((*pOutputBitmap) & Mask) | SetBit;
942 else
944 ERROR3IF(TRUE, "GIFUtil::ReadImage() - Invalid bpp");
945 // bad bpp but should not get here so do nothing
948 // }
950 ++xpos;
952 BOOL JobState = TRUE;
953 // if we have reached the end of the current line then work out the next
954 // line that we must do, if interlacing is on
955 if (xpos >= width)
957 xpos = 0;
959 // Do different filing methods depending on whether interlaced on non-interlaced.
960 if (interlace)
962 // We are interlaced so learn not to count properly!
963 switch (pass)
965 case 0:
966 case 1:
967 ypos += 8; break;
968 case 2:
969 ypos += 4; break;
970 case 3:
971 ypos += 2; break;
974 while (ypos >= height && pass <= 3)
976 ++pass;
977 switch (pass)
979 case 1:
980 ypos = 4;
981 break;
982 case 2:
983 ypos = 2;
984 break;
985 case 3:
986 ypos = 1;
987 break;
988 default:
989 goto finish;
993 else
995 // Non-interlaced case
996 ++ypos;
999 yposcount++;
1001 if (yposcount > (LastProgressUpdate + UpdateEvery))
1003 // Note the update point so that we know the next one
1004 LastProgressUpdate = yposcount;
1006 // Now update the progress display, started with 100
1007 if (pFilter == NULL)
1008 JobState = ContinueSlowJob( (long)(100 * yposcount/height) );
1009 else
1010 JobState = pFilter->IncProgressBarCount(UpdateValue);
1012 // If JobState is False then the user has probably pressed escape and we should
1013 // immediately stop what we are doing.
1014 if (!JobState)
1016 fd->GotError( _R(IDW_CANCELLEDBMPIMPORT) ); // Expects error set on cancel
1017 return FALSE;
1022 if (ypos >= height)
1023 break;
1026 finish:
1027 if (LWZReadByte(fd, FALSE, c) >= 0)
1029 /* Ignore extra */
1032 // Everything seemed to go ok
1033 return TRUE;
1037 /********************************************************************************************
1039 > static void GIFUtil::GIFSetPixel(LPBYTE pBitsData, int x, int y, int colour)
1041 Author: Neville Humphrys
1042 Created: 29/6/95
1043 Inputs:
1044 Outputs:
1045 Returns:
1046 Purpose:
1047 SeeAlso:
1049 ********************************************************************************************/
1051 //void GIFUtil::GIFSetPixel(LPBYTE pBitsData, int x, int y, int colour)
1053 // // Check that the specified pixel is within the bounds specified and that we
1054 // // have a buffer to write to
1055 // if ( !(((y < 0) || (y >= Height)) || ((x < 0) || (x >= Width))) && pBitsData )
1056 // {
1057 // // Our DIBs are the wrong way up so we must output the data from the last
1058 // // line of the image and go up to the start
1059 // // -1 as height = 1 .. Height whereas y goes from 0 .. Height - 1
1060 // // Use the word/byte rounded line width rather than the pixel width
1061 // LPBYTE pData = pBitsData + x + ((Height - 1 - y) * WidthOfLine);
1062 // *(pData) = colour;
1063 // }
1066 /********************************************************************************************
1068 > static BOOL ReadColourMap(CCLexFile *fd, Palette& NewPalette)
1070 Author: Colin
1071 Created: 29/6/95
1072 Inputs: fd pointer to a CCLexFile to read the data from
1073 Outputs: -
1074 Returns: True if worked ok, False otherwise.
1075 Purpose:
1076 SeeAlso:
1078 ********************************************************************************************/
1079 BOOL Palette::SetSize(const UINT32 nNewSize)
1081 if (nNewSize == m_nSize) return TRUE;
1082 if (m_pColours != NULL)
1084 delete [] m_pColours;
1086 if (nNewSize == 0)
1088 m_pColours = NULL;
1089 return TRUE;
1091 m_pColours = new RGBQUAD[nNewSize];
1092 // m_StartIterator;
1093 // m_EndIterator;
1094 return (m_pColours == NULL) ? FALSE : TRUE;
1098 static BOOL ReadColourMap(CCLexFile *fd, Palette& NewPalette)
1100 GIFRGBTRIPLE rgb;
1101 PaletteIterator PalColour;
1102 for (PalColour = NewPalette.Start(); PalColour < NewPalette.End(); ++PalColour)
1104 fd->read(&rgb, sizeof(GIFRGBTRIPLE));
1106 PalColour.SetBlue(rgb.grgbtBlue);
1107 PalColour.SetGreen(rgb.grgbtGreen);
1108 PalColour.SetRed(rgb.grgbtRed);
1109 PalColour.SetTransparent(0);
1112 return TRUE;
1115 /********************************************************************************************
1117 > BOOL GIFUtil::ProcessHeader()
1119 Author: Colin
1120 Created: 11/06/96
1121 Inputs:
1122 Outputs:
1123 Returns:
1124 Purpose:
1125 SeeAlso:
1127 ********************************************************************************************/
1128 BOOL GIFUtil::ProcessHeader()
1130 ERROR3("GIFUtil::ProcessHeader : not implemented");
1131 if (TRUE) return FALSE;
1133 CCLexFile* File;
1134 GIFINFOHEADER Header;
1135 // This is really sizeof(GIFINFOHEADER) but this returns 14 instead of 13
1136 // as it rounds to the nearest word boundary
1137 const size_t HeaderSize = sizeof(char)* 6 + sizeof(WORD) * 2 + sizeof(BYTE) * 3;
1138 // const size_t HeaderSize = (sizeof(GIFINFOHEADER)* sizeof(WORD)) / sizeof(WORD);
1140 File->read( &Header, HeaderSize );
1141 if (File->bad())
1142 File->GotError( _R(IDE_FORMATNOTSUPPORTED) );
1144 // Just double check that the signature is correct, if not then fail immediately
1145 if (
1146 ( strncmp( Header.giName, "GIF89a", 6 ) != 0 ) &&
1147 ( strncmp( Header.giName, "GIF87a", 6 ) != 0 )
1149 File->GotError( _R(IDE_BADFORMAT) );
1151 // Note the overall size of the GIF from the header, may be the animation size
1152 m_GlobalWidth = Header.giWidth;
1153 m_GlobalHeight = Header.giHeight;
1154 // int ColorResolution = (((Header.giFlags >> 4) & 0x07) + 1);
1155 // int AspectRatio = Header.giAspect;
1157 UINT32 nGlobalPaletteSize = 2 << (Header.giFlags & 0x07);
1158 // m_nBackground = Header.giBackground;
1160 // Check if we have a global colour map present or not, if so then read it in.
1161 if (BitSet(Header.giFlags, GLOBALCOLOURMAP))
1163 // Read in the global colour map into a palette structure for possible later use
1164 if (m_GlobalPalette.SetSize(nGlobalPaletteSize) == FALSE)
1166 ERROR3("GIFUtil::ProcessHeader() - can't renew palette");
1167 return FALSE;
1169 ::ReadColourMap(File, m_GlobalPalette);
1171 m_nCurrentBitmap = 0;
1175 /********************************************************************************************
1177 > BOOL GIFUtil::ProcessImageBlock(CCLexFile* File, LPBITMAPINFO *Info, LPBYTE *Bits,
1178 UINT32 * pLeftOffset = NULL, UINT32 * pTopOffset = NULL,
1179 BOOL * pLocalPalette = NULL)
1181 Author: Colin
1182 Created: 11/06/96
1183 Inputs:
1184 Outputs:
1185 Returns:
1186 Purpose:
1187 SeeAlso:
1189 ********************************************************************************************/
1190 BOOL GIFUtil::ProcessImageBlock(CCLexFile* File, LPBITMAPINFO *Info, LPBYTE *Bits,
1191 UINT32 * pLeftOffset, UINT32 * pTopOffset, BOOL * pLocalPalette)
1193 GIFIMAGEBLOCK ImageBlock;
1194 // This is really sizeof(GIFIMAGEBLOCK) but this returns 10 instead of 9 as it rounds to the nearest word boundary
1195 const size_t ImageHeaderSize = sizeof(WORD) * 4 + sizeof(BYTE) * 1;
1196 File->read( &ImageBlock, ImageHeaderSize );
1198 // Check whether we there is a local colour map or not
1199 // If there is use that otherwise use the global one read in earlier
1200 BOOL UseLocalColourMap = BitSet(ImageBlock.gibFlags, LOCALCOLOURMAP);
1201 int LocalColourMapSize = 1 << ((ImageBlock.gibFlags & 0x07) + 1);
1203 int Width = ImageBlock.gibWidth; //LM_to_uint(buf[4],buf[5]);
1204 int Height = ImageBlock.gibDepth; //LM_to_uint(buf[6],buf[7]);
1206 // If the user asked for them and they are within the animation's boundaries
1207 // Then return the offsets to the user
1208 if (pLeftOffset && ImageBlock.gibLeft < m_GlobalWidth)
1209 *pLeftOffset = ImageBlock.gibLeft;
1210 if (pTopOffset && ImageBlock.gibTop < m_GlobalHeight)
1211 *pTopOffset = ImageBlock.gibTop;
1212 if (pLocalPalette)
1213 *pLocalPalette = UseLocalColourMap;
1215 Interlace = BitSet(ImageBlock.gibFlags, INTERLACE);
1217 int ColoursInPalette = UseLocalColourMap ? LocalColourMapSize : GlobalPaletteSize;
1218 int BitsPerPixel;
1219 if (ColoursInPalette > 16)
1220 BitsPerPixel = 8;
1221 else if (ColoursInPalette > 2)
1222 BitsPerPixel = 4;
1223 else
1224 BitsPerPixel = 1;
1226 TRACEUSER("Neville", _T("Colours in palette = %d bpp = %d\n"), ColoursInPalette, BitsPerPixel );
1228 // Allocate the space that we require for this bitmap
1229 // Sanity checks on the file that we have been asked to load.
1230 if ((BitsPerPixel != 8) && (BitsPerPixel != 4) && (BitsPerPixel != 1) && (Width != 0) && (Height != 0))
1231 File->GotError( _R(IDE_FORMATNOTSUPPORTED) );
1233 // we know what sort of bitmap we are - lets allocate a new LPBITMAPINFO and some bytes
1234 *Info = AllocDIB( Width, Height, BitsPerPixel, Bits, NULL );
1236 if (*Info == NULL || *Bits == NULL)
1237 File->GotError( _R(IDS_OUT_OF_MEMORY) );
1239 // if the clrUsed field is zero, put a sensible value in it
1241 BOOL ok = FALSE;
1242 if (UseLocalColourMap)
1244 // There is a local colour table specified so read this directly into
1245 // the palette of the DIB
1246 if (LocalColourMapSize > (1 << BitsPerPixel))
1247 File->GotError( _R(IDE_BADFORMAT) );
1249 ReadColourMap(File, LocalColourMapSize, (*Info)->bmiColors);
1251 else
1253 // No local palette so copy the global palette into the palette of the DIB, if
1254 if (lpGlobalPalette && ((*Info)->bmiColors))
1256 const size_t TotalPal = sizeof(RGBQUAD) * GlobalPaletteSize;
1257 memcpy((*Info)->bmiColors, lpGlobalPalette, TotalPal);
1261 // Note how many colours are in the palette
1262 (*Info)->bmiHeader.biClrUsed = ColoursInPalette;
1264 ok = ReadImage(File, *Bits, Width, Height, BitsPerPixel, Interlace);
1265 // Now read all that lovely data in
1266 if (!ok)
1267 File->GotError( _R(IDE_BADFORMAT) );
1269 return TRUE;