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.
13 #include "progress.h" // For hourglass stuff
14 //#include "resource.h" // IDS_OUTOFMEMORY
15 //#include "accures.h" // IDW_CANCELLEDBMPIMPORT
17 //#include "outptgif.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
)
31 m_pColours
= NULL
; // ensure state correct
37 Palette::Palette(const Palette
& otherPalette
)
39 if (&otherPalette
== this) return;
41 if (otherPalette
.m_nSize
> m_nSize
)
43 m_pColours
= new RGBQUAD
[otherPalette
.m_nSize
];
44 if (m_pColours
== NULL
)
49 m_nSize
= otherPalette
.m_nSize
;
50 memcpy(m_pColours
, otherPalette
.m_pColours
, m_nSize
);
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 /********************************************************************************************
76 Author: Neville Humphrys
78 Purpose: Default constructor for the class.
81 ********************************************************************************************/
87 /********************************************************************************************
90 Author: Neville Humphrys
92 Purpose: Default destructor for the class.
95 ********************************************************************************************/
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
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
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
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
)
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)
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");
174 *Info
= NULL
; // in case of early exit
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
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
);
209 File
->GotError( _R(IDE_FORMATNOTSUPPORTED
) );
211 // Just double check that the signature is correct, if not then fail immediately
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
)
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
;
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
);
283 // Cannot assume that the terminator immediately follows the image data
284 // Might have some extensions following the image data.
285 FileProcessed
= TRUE
;
290 case '!': //EXTENSIONBLOCK:
293 ProcessExtension(File
);
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;
308 FileProcessed
= TRUE
;
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
);
320 if (nBitmapToRead
== m_nCurrentBitmap
)
325 // return the values back to the caller if it desired them
327 *pLeftOffset
= LeftOffset
;
329 *pTopOffset
= TopOffset
;
331 *pLocalPalette
= LocalPalette
;
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 ;
344 // We've already got something so ignore anything else in case it goes
346 FileProcessed
= TRUE
;
349 TRACE( _T("Unrecognized Character %x at %d\n"), (int)c
, File
->tellIn());
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") );
369 // Return the transparency/delay/restore found to the caller.
370 *TransColour
= Transparent
;
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
)
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
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
407 // Free up the bit of memory for a palette we grabbed, if present
410 CCFree(lpGlobalPalette
);
411 lpGlobalPalette
= NULL
;
414 // If started, then stop then progress bar
415 if (ProgressString
!= NULL
)
418 // Must set the exception throwing and reporting flags back to their entry states
419 File
->SetThrowExceptions( OldThrowingState
);
420 File
->SetReportErrors( OldReportingState
);
425 ERROR2( FALSE
, "Escaped exception clause somehow" );
429 /********************************************************************************************
431 > static BOOL GIFUtil::ProcessExtension(CCLexFile *fd)
433 Author: Neville Humphrys
435 Inputs: fd pointer to a CCLexFile to read the data from
437 Returns: True if worked ok, False otherwise.
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.
475 BOOL fTrans
= TransBlock
.gtbFlags
& 0x1;
477 Transparent
= TransBlock
.gtbTransparency
;
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);
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"
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;
507 // We haven't read any image yet so just make sure no transparency
510 // ...and drop through
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)
529 /********************************************************************************************
531 > static BOOL GIFUtil::ReadColourMap(CCLexFile *fd, int number, LPRGBQUAD lpPalette)
533 Author: Neville Humphrys
535 Inputs: fd pointer to a CCLexFile to read the data from
537 Returns: True if worked ok, False otherwise.
541 ********************************************************************************************/
543 BOOL
GIFUtil::ReadColourMap(CCLexFile
*fd
, int number
, LPRGBQUAD lpPalette
)
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;
561 /********************************************************************************************
563 > static int GIFUtil::GetDataBlock(CCLexFile *fd, unsigned char *buf)
565 Author: Neville Humphrys
567 Inputs: fd pointer to a CCLexFile to read the data from
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.
576 ********************************************************************************************/
578 int GIFUtil::GetDataBlock(CCLexFile
*fd
, unsigned char *buf
)
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)
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
593 ZeroDataBlock
= TRUE
;
594 // Fall through to return 0
598 ZeroDataBlock
= FALSE
;
599 fd
->read( buf
, count
);
600 // fall through to return the number of bytes found
606 /********************************************************************************************
608 > static int GIFUtil::GetCode(CCLexFile *fd, int code_size, int flag)
610 Author: Neville Humphrys
612 Inputs: fd pointer to a CCLexFile to read the data from
616 Returns: The code found.
617 Purpose: Returns the next code in the stream.
620 ********************************************************************************************/
622 int GIFUtil::GetCode(CCLexFile
*fd
, int code_size
, int flag
)
624 static unsigned char buf
[280];
628 static int last_byte
;
642 if ( (curbit
+code_size
) >= lastbit
)
646 if (curbit
>= lastbit
)
652 buf
[0] = buf
[last_byte
-2];
653 buf
[1] = buf
[last_byte
-1];
655 if ((count
= GetDataBlock(fd
, &buf
[2])) == 0)
658 last_byte
= 2 + count
;
659 curbit
= (curbit
- lastbit
) + 16;
660 lastbit
= (2+count
)*8 ;
664 for (i
= curbit
, j
= 0; j
< code_size
; ++i
, ++j
)
665 ret
|= ((buf
[ i
/ 8 ] & (1 << (i
% 8))) != 0) << j
;
673 /********************************************************************************************
675 > static int GIFUtil::LWZReadByte(CCLexFile *fd, int flag, int input_code_size)
677 Author: Neville Humphrys
679 Inputs: fd pointer to a CCLexFile to read the data from
683 Returns: The next LZW byte in the stream.
684 Purpose: To read the next LZW byte from the stream.
687 ********************************************************************************************/
689 int GIFUtil::LWZReadByte(CCLexFile
*fd
, int flag
, int input_code_size
)
691 static int fresh
= FALSE
;
694 static int code_size
;
695 static int set_code_size
;
697 static int max_code_size
;
698 static int firstcode
;
700 static int clear_code
;
702 static int table
[2][(1<< MAX_LWZ_BITS
)];
703 static int stack
[(1<<(MAX_LWZ_BITS
))*2];
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
);
720 for (i
= 0; i
< clear_code
; ++i
)
725 for (; i
< (1<<MAX_LWZ_BITS
); ++i
)
726 table
[0][i
] = table
[1][0] = 0;
737 firstcode
= oldcode
= GetCode(fd
, code_size
, FALSE
);
738 } while (firstcode
== clear_code
);
746 while ((code
= GetCode(fd
, code_size
, FALSE
)) >= 0)
748 if (code
== clear_code
)
750 for (i
= 0; i
< clear_code
; ++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;
763 firstcode
= oldcode
= GetCode(fd
, code_size
, FALSE
);
767 else if (code
== end_code
)
770 unsigned char buf
[260];
775 while ((count
= GetDataBlock(fd
, buf
)) > 0)
784 if (code
>= max_code
)
790 while (code
>= clear_code
)
792 *sp
++ = table
[1][code
];
793 if (code
== table
[0][code
])
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
;
807 if ((max_code
>= max_code_size
) && (max_code_size
< (1<<MAX_LWZ_BITS
)))
822 /********************************************************************************************
824 > static BOOL GIFUtil::ReadImage(CCLexFile *fd, LPBYTE pBitsData, int width, int height,
825 BOOL interlace, BaseCamelotFilter *pFilter = NULL )
827 Author: Neville Humphrys
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");
861 ZeroDataBlock
= FALSE
;
863 // Work out how often we need to update the progress bar
865 INT32 UpdateValue
= 1;
867 UpdateEvery
= height
/100 + 1;
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();
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
890 ERROR3IF(c
> GIFBITS
,"Bad code size in GIFUtil ReadImage");
892 if (LWZReadByte(fd
, TRUE
, c
) < 0)
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 )
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
911 // If 8 bpp then just use the whole byte straight
912 LPBYTE pData
= pBitsData
+ xpos
+ ((height
- 1 - ypos
) * WidthOfLine
);
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
924 *(pData
) = ((*(pData
)) & 0xF0) | (v
& 0x0F); // add into low nibble
926 *(pData
) = ((*(pData
)) & 0x0F) | ((v
<< 4) & 0xF0); // add into top nibble
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
;
944 ERROR3IF(TRUE
, "GIFUtil::ReadImage() - Invalid bpp");
945 // bad bpp but should not get here so do nothing
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
959 // Do different filing methods depending on whether interlaced on non-interlaced.
962 // We are interlaced so learn not to count properly!
974 while (ypos
>= height
&& pass
<= 3)
995 // Non-interlaced case
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
) );
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.
1016 fd
->GotError( _R(IDW_CANCELLEDBMPIMPORT
) ); // Expects error set on cancel
1027 if (LWZReadByte(fd
, FALSE
, c
) >= 0)
1032 // Everything seemed to go ok
1037 /********************************************************************************************
1039 > static void GIFUtil::GIFSetPixel(LPBYTE pBitsData, int x, int y, int colour)
1041 Author: Neville Humphrys
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 )
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;
1066 /********************************************************************************************
1068 > static BOOL ReadColourMap(CCLexFile *fd, Palette& NewPalette)
1072 Inputs: fd pointer to a CCLexFile to read the data from
1074 Returns: True if worked ok, False otherwise.
1078 ********************************************************************************************/
1079 BOOL
Palette::SetSize(const UINT32 nNewSize
)
1081 if (nNewSize
== m_nSize
) return TRUE
;
1082 if (m_pColours
!= NULL
)
1084 delete [] m_pColours
;
1091 m_pColours
= new RGBQUAD
[nNewSize
];
1094 return (m_pColours
== NULL
) ? FALSE
: TRUE
;
1098 static BOOL
ReadColourMap(CCLexFile
*fd
, Palette
& NewPalette
)
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);
1115 /********************************************************************************************
1117 > BOOL GIFUtil::ProcessHeader()
1127 ********************************************************************************************/
1128 BOOL
GIFUtil::ProcessHeader()
1130 ERROR3("GIFUtil::ProcessHeader : not implemented");
1131 if (TRUE
) return FALSE
;
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
);
1142 File
->GotError( _R(IDE_FORMATNOTSUPPORTED
) );
1144 // Just double check that the signature is correct, if not then fail immediately
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");
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)
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
;
1213 *pLocalPalette
= UseLocalColourMap
;
1215 Interlace
= BitSet(ImageBlock
.gibFlags
, INTERLACE
);
1217 int ColoursInPalette
= UseLocalColourMap
? LocalColourMapSize
: GlobalPaletteSize
;
1219 if (ColoursInPalette
> 16)
1221 else if (ColoursInPalette
> 2)
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
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
);
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
1267 File
->GotError( _R(IDE_BADFORMAT
) );