00001 // $Id: outptgif.cpp 1348 2006-06-21 12:47:25Z luke $ 00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE 00003 ================================XARAHEADERSTART=========================== 00004 00005 Xara LX, a vector drawing and manipulation program. 00006 Copyright (C) 1993-2006 Xara Group Ltd. 00007 Copyright on certain contributions may be held in joint with their 00008 respective authors. See AUTHORS file for details. 00009 00010 LICENSE TO USE AND MODIFY SOFTWARE 00011 ---------------------------------- 00012 00013 This file is part of Xara LX. 00014 00015 Xara LX is free software; you can redistribute it and/or modify it 00016 under the terms of the GNU General Public License version 2 as published 00017 by the Free Software Foundation. 00018 00019 Xara LX and its component source files are distributed in the hope 00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the 00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 See the GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License along 00025 with Xara LX (see the file GPL in the root directory of the 00026 distribution); if not, write to the Free Software Foundation, Inc., 51 00027 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00028 00029 00030 ADDITIONAL RIGHTS 00031 ----------------- 00032 00033 Conditional upon your continuing compliance with the GNU General Public 00034 License described above, Xara Group Ltd grants to you certain additional 00035 rights. 00036 00037 The additional rights are to use, modify, and distribute the software 00038 together with the wxWidgets library, the wxXtra library, and the "CDraw" 00039 library and any other such library that any version of Xara LX relased 00040 by Xara Group Ltd requires in order to compile and execute, including 00041 the static linking of that library to XaraLX. In the case of the 00042 "CDraw" library, you may satisfy obligation under the GNU General Public 00043 License to provide source code by providing a binary copy of the library 00044 concerned and a copy of the license accompanying it. 00045 00046 Nothing in this section restricts any of the rights you have under 00047 the GNU General Public License. 00048 00049 00050 SCOPE OF LICENSE 00051 ---------------- 00052 00053 This license applies to this program (XaraLX) and its constituent source 00054 files only, and does not necessarily apply to other Xara products which may 00055 in part share the same code base, and are subject to their own licensing 00056 terms. 00057 00058 This license does not apply to files in the wxXtra directory, which 00059 are built into a separate library, and are subject to the wxWindows 00060 license contained within that directory in the file "WXXTRA-LICENSE". 00061 00062 This license does not apply to the binary libraries (if any) within 00063 the "libs" directory, which are subject to a separate license contained 00064 within that directory in the file "LIBS-LICENSE". 00065 00066 00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS 00068 ---------------------------------------------- 00069 00070 Subject to the terms of the GNU Public License (see above), you are 00071 free to do whatever you like with your modifications. However, you may 00072 (at your option) wish contribute them to Xara's source tree. You can 00073 find details of how to do this at: 00074 http://www.xaraxtreme.org/developers/ 00075 00076 Prior to contributing your modifications, you will need to complete our 00077 contributor agreement. This can be found at: 00078 http://www.xaraxtreme.org/developers/contribute/ 00079 00080 Please note that Xara will not accept modifications which modify any of 00081 the text between the start and end of this header (marked 00082 XARAHEADERSTART and XARAHEADEREND). 00083 00084 00085 MARKS 00086 ----- 00087 00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara 00089 designs are registered or unregistered trademarks, design-marks, and/or 00090 service marks of Xara Group Ltd. All rights in these marks are reserved. 00091 00092 00093 Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. 00094 http://www.xara.com/ 00095 00096 =================================XARAHEADEREND============================ 00097 */ 00098 00099 /* 00100 */ 00101 00102 #include "camtypes.h" 00103 //#include "outptgif.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00104 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00105 //#include "dibutil.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00106 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00107 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00108 //#include "andy.h" 00109 //#include "dibconv.h" - in camtypes.h [AUTOMATICALLY REMOVED] 00110 #include "progress.h" // For hourglass stuff 00111 #include "gifutil.h" // GIF header definitions 00112 //#include "string.h" // memcpy 00113 //#include "camfiltr.h" // BaseCamelotFilter - in camtypes.h [AUTOMATICALLY REMOVED] 00114 00115 #define new CAM_DEBUG_NEW 00116 00117 /******************************************************************************************** 00118 > OutputGIF::OutputGIF() 00119 00120 Author: Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> 00121 Created: 24/4/95 00122 Purpose: Default constructor for the class. Just sets up the pointers to our buffers 00123 that may be used to be null so that the TidyUp function can do its job. 00124 SeeAlso: OutputGIF::TidyUp 00125 SeeAlso: OutputDIB; 00126 00127 ********************************************************************************************/ 00128 00129 OutputGIF::OutputGIF() 00130 { 00131 OutputFile = NULL; 00132 lpBitmap = NULL; 00133 ExportBuffer = NULL; 00134 DoExportConvert = NULL; 00135 OutputForward = FALSE; 00136 00137 DestBitmapInfo = NULL; 00138 DestBitmapBytes = NULL; 00139 00140 InterlacingOn = FALSE; 00141 00142 pCamFilter = NULL; 00143 } 00144 00145 00146 /******************************************************************************************** 00147 00148 > void Putword( INT32 word, CCLexFile* pFile ) 00149 00150 Author: Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> 00151 Created: 24/4/95 00152 Inputs: Word INT32 to write to file as 16bit value 00153 File file to write to 00154 Returns: - 00155 Purpose: Write out a word to the GIF file 00156 Scope: private 00157 00158 ********************************************************************************************/ 00159 00160 void Putword( INT32 Word, CCLexFile *File) 00161 { 00162 File->put( (Word & 0xff)); 00163 File->put( ((Word / 256) & 0xff)); 00164 } 00165 00166 00167 00168 /******************************************************************************************** 00169 00170 > virtual BOOL OutputGIF::StartFile( LPBITMAPINFOHEADER lpHeader, LPLOGPALETTE Palette, UINT32 OutputDepth, 00171 DWORD CompressionType, UINT32 FinalHeight, INT32 ExportSize, UINT32 DitherType = XARADITHER_ORDERED_GREY); 00172 00173 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 00174 Created: 5/6/96 00175 Inputs: lpHeader contains width & DPI of source & dest. biHeight & depth ignored. 00176 Palette pointer to bmiColor struct, or NULL if not used (can be temporary) 00177 OutputDepth depth of bitmap required on disk 00178 CompressionType in this case is used to pass in the transparency and interlace 00179 information as GIFs are always compressed. Passed in as a number between 00180 0 .. 3 which maps onto the TI_GIF filter types. 00181 00182 FinalHeight output of entire bitmap on disk 00183 ExportSize The set progress bar size 00184 DitherType 00185 00186 Returns: TRUE if worked, FALSE if failed (error will be set accordingly but not reported) 00187 Purpose: Prepare to start the export of a GIF. 00188 SeeAlso: OutputGIF::WriteBlock; OutputGIF::TidyUp 00189 00190 ********************************************************************************************/ 00191 00192 BOOL OutputGIF::StartFile ( LPBITMAPINFOHEADER lpHeader, 00193 LPLOGPALETTE Palette, 00194 UINT32 OutputDepth, 00195 DWORD CompressionType, 00196 UINT32 FinalHeight, 00197 INT32 ExportSize, 00198 UINT32 DitherType ) 00199 { 00200 ERROR2IF( lpHeader==NULL, FALSE, "NULL lpHeader param"); 00201 ERROR2IF( Palette==NULL, FALSE, "NULL Palette param"); 00202 00203 // Set member variables 00204 if (DestBitmapInfo && DestBitmapBytes) 00205 { 00206 FreeDIB( DestBitmapInfo, DestBitmapBytes ); 00207 DestBitmapInfo = NULL; 00208 DestBitmapBytes = NULL; 00209 } 00210 00211 if (OutputPalette) 00212 { 00213 CCFree(OutputPalette); 00214 OutputPalette = NULL; 00215 } 00216 00217 IsFirstStrip = TRUE; 00218 HeightWritten = 0; 00219 00220 // remember input args 00221 BitmapInfo = *lpHeader; // take a copy of user's header 00222 CurrentExportSize = ExportSize; // size set up for the progress bar 00223 HeightWanted = FinalHeight; // the actual height of the export required 00224 Dither = DitherType; 00225 00226 // We will need to have the entire image present before writing out so that we can 00227 // cope with interlacing and transparency, so create that DIB 00228 // Set up the information header for the dib which we hold during export 00229 UINT32 LineWidth = DIBUtil::ScanlineSize( BitmapInfo.biWidth, OutputDepth ); 00230 INT32 PalSize = 0; 00231 BOOL ok = SetUpInfoHeader(lpHeader, OutputDepth, CompressionType, LineWidth, FinalHeight, &PalSize); 00232 00233 // Claim memory for the bitmap 00234 if (ok) 00235 { 00236 DestBitmapInfo = AllocDIB( BitmapInfo.biWidth, FinalHeight, OutputDepth, &DestBitmapBytes ); 00237 ok = (DestBitmapInfo != NULL) && (DestBitmapBytes != NULL); 00238 } 00239 00240 // Transfer across the required other bits of info 00241 if (ok) 00242 { 00243 DestBitmapInfo->bmiHeader.biXPelsPerMeter = BitmapInfo.biXPelsPerMeter; 00244 DestBitmapInfo->bmiHeader.biYPelsPerMeter = BitmapInfo.biYPelsPerMeter; 00245 DestBitmapInfo->bmiHeader.biClrUsed = PalSize; 00246 } 00247 00248 // Point the place to put the next strip of data at the start ready for the first strip 00249 pNextStrip = DestBitmapBytes; 00250 00251 // Take a copy of the palette 00252 if (ok) 00253 { 00254 const size_t TotalPal = sizeof(LOGPALETTE) + ( sizeof(PALETTEENTRY) * PalSize ); 00255 OutputPalette = (LPLOGPALETTE)CCMalloc( TotalPal ); 00256 if (OutputPalette != NULL) 00257 memcpy( OutputPalette, Palette, TotalPal ); 00258 else 00259 ok = FALSE; 00260 } 00261 00262 // Clean up if an error happened 00263 if (!ok) 00264 { 00265 // Free up the DIB that we have just created 00266 FreeDIB( DestBitmapInfo, DestBitmapBytes ); 00267 DestBitmapInfo = NULL; 00268 DestBitmapBytes = NULL; 00269 if (OutputPalette != NULL) 00270 { 00271 CCFree(OutputPalette); 00272 OutputPalette = NULL; 00273 } 00274 } 00275 00276 return ok; 00277 } 00278 00279 00280 00281 /******************************************************************************************** 00282 > BOOL OutputGIF::ReStartFile(LOGPALETTE* pNewPal) 00283 00284 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 00285 Created: 13/6/96 00286 Inputs: pNewPal - New (optimised) palette to use 00287 Returns: TRUE if worked, FALSE if failed 00288 Purpose: Called to reset output before outputing another in a run of multiple images 00289 SeeAlso: OutputGIF::StartFile 00290 ********************************************************************************************/ 00291 BOOL OutputGIF::ReStartFile(LOGPALETTE* pNewPal) 00292 { 00293 // Reset member variables 00294 IsFirstStrip = TRUE; 00295 HeightWritten = 0; 00296 pNextStrip = DestBitmapBytes; 00297 00298 if (pNewPal!=NULL) 00299 { 00300 ERROR2IF(OutputPalette==NULL, FALSE, "No output palette"); 00301 ERROR3IF(pNewPal->palNumEntries != OutputPalette->palNumEntries, "Different sized palettes"); 00302 00303 const size_t PalSize = sizeof(PALETTEENTRY) * pNewPal->palNumEntries-1; 00304 memcpy( OutputPalette->palPalEntry, pNewPal->palPalEntry, PalSize ); 00305 } 00306 00307 return TRUE; 00308 } 00309 00310 00311 /******************************************************************************************** 00312 00313 > BOOL OutputGIF::SetUpInfoHeader(const LPBITMAPINFOHEADER lpHeader, const UINT32 OutputDepth, 00314 const DWORD CompressionType, 00315 const UINT32 LineWidth, const UINT32 FinalHeight, 00316 INT32 * pPalSize) 00317 00318 Author: Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> 00319 Created: 9/11/94 00320 Inputs: lpHeader contains width & DPI of source & dest. biHeight & depth ignored. 00321 OutputDepth depth of bitmap required on disk 00322 CompressionType BI_RGB only supported (or CC_BMPTYPE for 32-bit with trans) 00323 LineWidth destination line width 00324 FinalHeight output of entire bitmap on disk 00325 Outputs: pPalSize pointer to an integer PalSize variable which is the palette size 00326 Returns: TRUE if worked, FALSE if failed (error will be set accordingly but not reported) 00327 Purpose: Set up the information header for the DIB. 00328 Errors: Calls SetError on FALSE returns. 00329 SeeAlso: OutputGIF::StartFile; OutputGIF::WriteBlock; OutputGIF::TidyUp 00330 Scope: Public 00331 00332 ********************************************************************************************/ 00333 BOOL OutputGIF::SetUpInfoHeader(const LPBITMAPINFOHEADER lpHeader, const UINT32 OutputDepth, const DWORD CompressionType, 00334 const UINT32 LineWidth, const UINT32 FinalHeight, INT32 * pPalSize) 00335 { 00336 SourceBitmapDepth = lpHeader->biBitCount; 00337 BitmapInfo.biBitCount = OutputDepth; 00338 00339 const WORD DestDepth = BitmapInfo.biBitCount; 00340 00341 // source bitmap not necessarily same as required file depth 00342 // make sure we can handle conversion 00343 BOOL FormatOK = FALSE; 00344 00345 switch (SourceBitmapDepth) 00346 { 00347 case 32: 00348 { 00349 switch (DestDepth) 00350 { 00351 case 8: 00352 { 00353 // 32->8 is OK 00354 FormatOK = TRUE; 00355 *pPalSize = 256; 00356 } 00357 break; 00358 case 4: 00359 { 00360 // 32->4 is OK 00361 FormatOK = TRUE; 00362 *pPalSize = 16; 00363 } 00364 break; 00365 case 1: 00366 { 00367 // 32->1 is OK 00368 FormatOK = TRUE; 00369 *pPalSize = 2; 00370 } 00371 break; 00372 default: 00373 // other source formats here 00374 break; 00375 } 00376 00377 } 00378 break; 00379 case 8: 00380 if (DestDepth==8) 00381 { 00382 // 8->8 is OK 00383 FormatOK = TRUE; 00384 *pPalSize = 256; 00385 } 00386 break; 00387 default: 00388 // other source formats here 00389 break; 00390 } 00391 00392 if (!FormatOK) 00393 { 00394 Error::SetError( _R(IDE_FORMATNOTSUPPORTED) ); 00395 return FALSE; 00396 } 00397 00398 BitmapInfo.biSizeImage = LineWidth * FinalHeight; 00399 00400 return TRUE; 00401 } 00402 00403 00404 00405 /******************************************************************************************** 00406 > BOOL OutputGIF::OutputGifFileHeader(CCLexFile *File, BOOL Enhanced, INT32 TransColour) 00407 00408 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 00409 Created: 12/6/96 00410 Inputs: File - output file object 00411 Enhanced - TRUE if enhanced GIF features are required (eg transpacency) 00412 TransColour - the index of the transparent colour (-1 for no transparency) 00413 Outputs: - 00414 Returns: TRUE if worked, FALSE if failed 00415 Purpose: Writes out the file header for the cached bitmap within this class 00416 SeeAlso: OutputGIF::OutputGifFileHeader (the other one) 00417 ********************************************************************************************/ 00418 BOOL OutputGIF::OutputGifFileHeader(CCLexFile *File, BOOL Enhanced, INT32 TransColour) 00419 { 00420 return OutputGifFileHeader(File, &(DestBitmapInfo->bmiHeader), Enhanced, TransColour, OutputPalette); 00421 } 00422 00423 00424 /******************************************************************************************** 00425 00426 > BOOL OutputGIF::OutputGifFileHeader(CCLexFile *, LPBITMAPINFOHEADER, BOOL Enhanced, INT32 TransColour, PLOGPALETTE = NULL, LPRGBQUAD pQuadPalette = NULL) 00427 00428 Author: Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> 00429 Created: 28/4/95 00430 Inputs: File file to output to 00431 pInfo pointer to the bitmap info header 00432 Enhanced TRUE if useing advanced GIF features such as transparency or animation 00433 TransColour The transparent index or -1 if no transparency 00434 pPalette pointer to a palette in LOGPALETTE form (defaults to NULL) 00435 OR 00436 pQuadPalette pointer to a palette in RGBQUAD form (defaults to NULL) 00437 Outputs: - 00438 Returns: TRUE if worked, FALSE if failed (error will be set accordingly but not reported) 00439 Purpose: Output a GIF file header for the specified bitmap to the file. 00440 Errors: Calls SetError on FALSE returns. 00441 SeeAlso: OutputGIF::StartFile; OutputGIF::WriteBlock; OutputGIF::TidyUp 00442 Scope: static 00443 00444 ********************************************************************************************/ 00445 BOOL OutputGIF::OutputGifFileHeader(CCLexFile *File, LPBITMAPINFOHEADER pInfo, BOOL Enhanced, INT32 TransColour, LPLOGPALETTE pPalette, LPRGBQUAD pQuadPalette) 00446 { 00447 ERROR2IF(File==NULL,FALSE,"OutputGIF::OutputGifHeader File pointer is null"); 00448 ERROR2IF(pInfo==NULL,FALSE,"OutputGIF::OutputGifHeader BitmapInfo pointer is null"); 00449 ERROR2IF(pPalette==NULL && pQuadPalette==NULL,FALSE,"OutputGIF::OutputGifHeader Bitmap palette pointer is null"); 00450 00451 // Note file in our class variable as used by all the low level routines 00452 OutputFile = File; 00453 00454 // Must set the exception throwing flag to True and force reporting of errors to False. 00455 // This means that the caller must report an error if the function returns False. 00456 // Any calls to CCFile::GotError will now throw a file exception and should fall into 00457 // the catch handler at the end of the function. 00458 // Replaces the goto's that handled this before. 00459 BOOL OldThrowingState = File->SetThrowExceptions( TRUE ); 00460 BOOL OldReportingState = File->SetReportErrors( FALSE ); 00461 00462 try 00463 { 00464 // Set up the class variables 00465 // First the width/height of the bitmap 00466 Width = pInfo->biWidth; 00467 Height = pInfo->biHeight; 00468 INT32 PalSize = pInfo->biClrUsed; 00469 // Translate this into a bits per pixel which is not limited to 8, 4 and 1. 00470 INT32 ColourDepth = GetColourDepth(PalSize); 00471 // ColourDepth will be rounded to nearest power of two so need to output 00472 // that many palette entries 00473 INT32 NewPalSize = 1 << ColourDepth; 00474 InitCodeSize = ColourDepth; 00475 00476 BitsPerPixel = pInfo->biBitCount; 00477 00478 TRACEUSER( "Neville", _T("OutputGifFileHeader - Colour depth = %d\n"),ColourDepth); 00479 00480 // Write out the unique GIF header 00481 GIFINFOHEADER Header; 00482 if (Enhanced) 00483 memcpy(Header.giName, "GIF89a", 6); 00484 else 00485 memcpy(Header.giName, "GIF87a", 6); 00486 00487 // Indicate that there is a global colour map 00488 BYTE Flags = 0; 00489 // global colour table flag, colour resoltion sort flag and 00490 // size of global colour table 00491 if ((pPalette || pQuadPalette) && (PalSize > 0)) 00492 Flags = 0x80; // Flag there is a colour map 00493 // 3 bit colour resolution value = number of bits per primary colour - 1 00494 Flags |= ((BitsPerPixel - 1) & 0x07) << 4; // OR in the colour resolution 00495 // 3 bit size of global colour table = number of bits - 1 00496 Flags |= (ColourDepth - 1) & 0x07; // OR in global colour table size 00497 00498 Header.giWidth = (WORD)Width; 00499 Header.giHeight = (WORD)Height; 00500 Header.giFlags = Flags; 00501 Header.giBackground = TransColour==-1 ? 0 : TransColour; 00502 Header.giAspect = 0; 00503 // This is really sizeof(GIFINFOHEADER) but this returns 14 instead of 13 00504 // as it rounds to the nearest word boundary 00505 const size_t HeaderSize = sizeof(char)* 6 + sizeof(WORD) * 2 + sizeof(BYTE) * 3; 00506 File->write( &Header, HeaderSize ); 00507 00508 if (pQuadPalette && NewPalSize > 0) 00509 { 00510 // Palette supplied in RGBQUAD form 00511 // write it to disk in GIFRGBTRIPLE format 00512 GIFRGBTRIPLE TempRGB; 00513 for (INT32 i = 0; i < NewPalSize; i++) 00514 { 00515 // If we are using transparency then bodge the transparent colour to 00516 // be White. Otherwise, if we load it back it then we could have a strange 00517 // colour being shown as we don't support transparency on loading. 00518 if (TransColour >= 0 && i == TransColour) 00519 { 00520 // Make transparent colour white 00521 TempRGB.grgbtRed = 0xFF; 00522 TempRGB.grgbtGreen = 0xFF; 00523 TempRGB.grgbtBlue = 0xFF; 00524 } 00525 else if (i > PalSize) 00526 { 00527 // Make blank palette entries black 00528 TempRGB.grgbtRed = 0x00; 00529 TempRGB.grgbtGreen = 0x00; 00530 TempRGB.grgbtBlue = 0x00; 00531 } 00532 else 00533 { 00534 TempRGB.grgbtRed = pQuadPalette->rgbRed; 00535 TempRGB.grgbtGreen = pQuadPalette->rgbGreen; 00536 TempRGB.grgbtBlue = pQuadPalette->rgbBlue; 00537 } 00538 00539 File->write( &TempRGB, sizeof(GIFRGBTRIPLE) ); 00540 // skip to the next palette entry 00541 pQuadPalette++; 00542 } 00543 } 00544 else if (pPalette && NewPalSize > 0) 00545 { 00546 // Palette supplied in LOGPALETTE form 00547 // write it to disk in GIFRGBTRIPLE format 00548 GIFRGBTRIPLE TempRGB; 00549 for (INT32 i = 0; i < NewPalSize; i++) 00550 { 00551 // If we are using transparency then bodge the transparent colour to 00552 // be White. Otherwise, if we load it back it then we could have a strange 00553 // colour being shown as we don't support transparency on loading. 00554 if (TransColour >= 0 && i == TransColour) 00555 { 00556 // Make transparent colour white 00557 TempRGB.grgbtRed = 0xFF; 00558 TempRGB.grgbtGreen = 0xFF; 00559 TempRGB.grgbtBlue = 0xFF; 00560 } 00561 else if (i > PalSize) 00562 { 00563 // Make blank palette entries black 00564 TempRGB.grgbtRed = 0x00; 00565 TempRGB.grgbtGreen = 0x00; 00566 TempRGB.grgbtBlue = 0x00; 00567 } 00568 else 00569 { 00570 TempRGB.grgbtRed = pPalette->palPalEntry[i].peRed; 00571 TempRGB.grgbtGreen = pPalette->palPalEntry[i].peGreen; 00572 TempRGB.grgbtBlue = pPalette->palPalEntry[i].peBlue; 00573 } 00574 File->write( &TempRGB, sizeof(GIFRGBTRIPLE) ); 00575 } 00576 } 00577 else 00578 ERROR3("OutputGIF::OutputGifHeader No palette specified"); 00579 00580 // Must set the exception throwing and reporting flags back to their entry states 00581 File->SetThrowExceptions( OldThrowingState ); 00582 File->SetReportErrors( OldReportingState ); 00583 00584 // er, we seem to have finished OK so say so 00585 return TRUE; 00586 } 00587 catch(...) 00588 { 00589 // catch our form of a file exception 00590 TRACE( _T("OutputGIF::OutputGifHeader CC catch handler\n")); 00591 00592 // Must set the exception throwing and reporting flags back to their entry states 00593 File->SetThrowExceptions( OldThrowingState ); 00594 File->SetReportErrors( OldReportingState ); 00595 00596 return FALSE; 00597 } 00598 00599 ERROR2( FALSE, "Escaped exception clause somehow" ); 00600 } 00601 00602 00603 00604 /******************************************************************************************** 00605 > BOOL OutputGIF::OutputGifImageExtensionHeader(CCLexFile *File, BOOL InterlaceState, INT32 TransparentColour, UINT32 Delay, UINT32 RestoreType) 00606 00607 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> - from Neville code 00608 Created: 14/6/96 00609 Inputs: File file to output to 00610 InterlaceState TRUE if image should be interlaced 00611 TransparentColour index of transparent colour or -1 for no transparency 00612 Delay Delay in millseconds between this frame and the previous one 00613 RestoreType GIF image restore type (0-3) 00614 Outputs: - 00615 Returns: TRUE if worked, FALSE if failed 00616 Purpose: Output a GIF image header for the specified bitmap to the file. 00617 ********************************************************************************************/ 00618 BOOL OutputGIF::OutputGifImageExtensionHeader(CCLexFile *File, BOOL InterlaceState, INT32 TransparentColour, UINT32 Delay, UINT32 RestoreType) 00619 { 00620 ERROR2IF(File==NULL,FALSE,"OutputGIF::OutputGifHeader File pointer is null"); 00621 ERROR3IF(RestoreType > 3, "Unknown GIF image restore type"); 00622 00623 // Note file in our class variable as used by all the low level routines 00624 OutputFile = File; 00625 InterlacingOn = InterlaceState; 00626 00627 UINT32 TranspFlag = (TransparentColour >= 0) ? 1 : 0; 00628 00629 // Must set the exception throwing flag to True and force reporting of errors to False. 00630 // This means that the caller must report an error if the function returns False. 00631 // Any calls to CCFile::GotError will now throw a file exception and should fall into 00632 // the catch handler at the end of the function. 00633 // Replaces the goto's that handled this before. 00634 BOOL OldThrowingState = File->SetThrowExceptions( TRUE ); 00635 BOOL OldReportingState = File->SetReportErrors( FALSE ); 00636 try 00637 { 00638 GIFTRANSBLOCK TransBlock; 00639 TransBlock.gtbBlockStart = EXTENSIONBLOCK; 00640 TransBlock.gtbIdentifier = TRANSPARENTBLOCK; 00641 TransBlock.gtbBlockSize = 4; 00642 TransBlock.gtbFlags = TranspFlag | ((RestoreType & 0x03) << 2); 00643 TransBlock.gtbDelay = Delay; // 00644 TransBlock.gtbTransparency = TranspFlag ? TransparentColour : 0; 00645 TransBlock.gtbTerminator = 0; 00646 00647 // This is really sizeof(GIFTRANSBLOCK) but this returns 14 instead of 13 00648 // as it rounds to the nearest word boundary 00649 const size_t TransBlockSize = sizeof(WORD) * 1 + sizeof(BYTE) * 6; 00650 File->write( &TransBlock, TransBlockSize ); 00651 00652 // Must set the exception throwing and reporting flags back to their entry states 00653 File->SetThrowExceptions( OldThrowingState ); 00654 File->SetReportErrors( OldReportingState ); 00655 00656 return TRUE; 00657 } 00658 catch(...) 00659 { 00660 // catch our form of a file exception 00661 TRACE( _T("OutputGIF::OutputGifHeader CC catch handler\n")); 00662 00663 // Must set the exception throwing and reporting flags back to their entry states 00664 File->SetThrowExceptions( OldThrowingState ); 00665 File->SetReportErrors( OldReportingState ); 00666 00667 return FALSE; 00668 } 00669 00670 ERROR2( FALSE, "Escaped exception clause somehow" ); 00671 } 00672 00673 00674 00675 /******************************************************************************************** 00676 > BOOL OutputGIF::OutputAnimationControl(CCLexFile *File, unsigned short Repeats) 00677 00678 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 00679 Created: 13/6/96 00680 Inputs: File - file to output to 00681 Outputs: - 00682 Returns: TRUE if worked, FALSE if failed 00683 Purpose: Outputs a Netscape Animation Control block into the GIF file 00684 NOTE Must be inserted after file header but before image data 00685 ********************************************************************************************/ 00686 BOOL OutputGIF::OutputAnimationControl(CCLexFile *File, unsigned short Repeats) 00687 { 00688 ERROR2IF(File==NULL,FALSE,"OutputGIF::OutputAnimationControl File pointer is null"); 00689 00690 BOOL OldThrowingState = File->SetThrowExceptions( TRUE ); 00691 BOOL OldReportingState = File->SetReportErrors( FALSE ); 00692 try 00693 { 00694 // Generate the Netscape Extension block 00695 BYTE Block[19]; 00696 Block[0] = 0x21; // GIF extension code 00697 Block[1] = 0xFF; // Application extension 00698 Block[2] = 0x0B; // Length of application string 00699 memcpy(Block+3, "NETSCAPE2.0", 0x0B); // Application string 00700 Block[14] = 0x03; // Length of data sub-block 00701 Block[15] = 0x01; // ?? 00702 Block[16] = Repeats & 0xFF; // Low byte of repeats 00703 Block[17] = Repeats >> 8; // High byte of repeats 00704 Block[18] = 0; // Terminator 00705 00706 File->write(Block, 19); 00707 00708 File->SetThrowExceptions( OldThrowingState ); 00709 File->SetReportErrors( OldReportingState ); 00710 00711 return TRUE; 00712 } 00713 catch(...) 00714 { 00715 File->SetThrowExceptions( OldThrowingState ); 00716 File->SetReportErrors( OldReportingState ); 00717 00718 return FALSE; 00719 } 00720 00721 ERROR2( FALSE, "Escaped exception clause somehow" ); 00722 } 00723 00724 /****************************************************************************************** 00725 00726 > LPBITMAPINFO OutputGIF::GetDestBitmapInfo ( void ) 00727 00728 Author: Graeme_Sutherland (Xara Group Ltd) <camelotdev@xara.com> 00729 Created: 29/6/00 00730 Inputs: pPalette - Palette information that we want to copy from 00731 Returns: A pointer to the bitmap information structure. 00732 Purpose: Access function to DestBitmapInfo. 00733 00734 ******************************************************************************************/ 00735 00736 LPBITMAPINFO OutputGIF::GetDestBitmapInfo ( void ) 00737 { 00738 return DestBitmapInfo; 00739 } 00740 00741 /****************************************************************************************** 00742 00743 > LPBYTE OutputGIF::GetDestBitmapBits ( void ) 00744 00745 Author: Graeme_Sutherland (Xara Group Ltd) <camelotdev@xara.com> 00746 Created: 29/6/00 00747 Inputs: pPalette - Palette information that we want to copy from 00748 Returns: A pointer to the bitmap itself. 00749 Purpose: Access function to DestBitmapBytes. 00750 00751 ******************************************************************************************/ 00752 00753 LPBYTE OutputGIF::GetDestBitmapBits ( void ) 00754 { 00755 return DestBitmapBytes; 00756 } 00757 00758 /******************************************************************************************** 00759 00760 > INT32 OutputGIF::GetColourDepth(INT32 NumberOfColours) 00761 00762 Author: Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> 00763 Created: 27/5/97 00764 Inputs: NumberOfColours The number of colours in the palette of the bitmap 00765 Outputs: - 00766 Returns: The colour depth 00767 Purpose: Converts the number of colours given into the colour depth that is required 00768 to output that number. This is not restricted to the colour depths of Window's 00769 DIBs i.e. 8, 4 and 1 but can be any colour depth less than 8 bpps. 00770 00771 ********************************************************************************************/ 00772 00773 INT32 OutputGIF::GetColourDepth(INT32 NumberOfColours) 00774 { 00775 INT32 ColourDepth = 8; 00776 if (NumberOfColours <= 2) 00777 // Changed by Craig Hamilton 14/9/00. 00778 ColourDepth = 1; 00779 // End changed. 00780 else if (NumberOfColours <= 4) 00781 ColourDepth = 2; 00782 else if (NumberOfColours <= 8) 00783 ColourDepth = 3; 00784 else if (NumberOfColours <= 16) 00785 ColourDepth = 4; 00786 else if (NumberOfColours <= 32) 00787 ColourDepth = 5; 00788 else if (NumberOfColours <= 64) 00789 ColourDepth = 6; 00790 else if (NumberOfColours <= 128) 00791 ColourDepth = 7; 00792 else if (NumberOfColours <= 256) 00793 ColourDepth = 8; 00794 00795 return ColourDepth; 00796 } 00797 00798 00799 /******************************************************************************************** 00800 00801 > BOOL OutputGIF::OutputGifImageBits(CCLexFile *File, LPBYTE pBlockStart, BOOL Interlace, BOOL LocalColourTable, 00802 BaseCamelotFilter *pFilter = NULL, 00803 UINT32 NewWidth = 0, UINT32 NewHeight = 0, UINT32 LeftOffset = 0, UINT32 TopOffset = 0, 00804 LPRGBQUAD pDestPalette = NULL, UINT32 PaletteColourDepth = 0, UINT32 NewBitsPerPixel = 0) 00805 00806 00807 Author: Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> 00808 Created: 28/4/95 00809 Inputs: File pointer to file to output to 00810 pBlockStart actual bitmap data to write out 00811 Interlace TRUE if image should be interlaced 00812 LocalColourTable True if need to specify a local colour table, 00813 False if the global one is all that is required 00814 pFilter is an alternative way of handling the progress bar, assume the 00815 progress bar has been start and just call the IncProgressBarCount in 00816 BaseCamelotFilter to do the progress bar update. 00817 Defaults to NULL i.e. no progress bar. 00818 NewWidth The new width of this bitmap, defaults to 0 meaning none 00819 NewHeight The new height of this bitmap, defaults to 0 meaning none 00820 LeftOffset The offset of this bitmap from the left hand side, defaults to 0 00821 TopOffset The offset of this bitmap from the top hand side, defaults to 0 00822 pDestPalette The local palette to output 00823 NumberOfPalColours The number of colours in this local palette 00824 NewBitsPerPixel The new bits per pixel of the bitmap, defaults to 0 meaning none 00825 Outputs: - 00826 Returns: TRUE if worked, FALSE if failed (error will be set accordingly but not reported) 00827 Purpose: Output the actual bits of a bitmap as compressed GIF data. Assumes that a 00828 header has already been output, possibly using OutputGifHeader. Assumes that 00829 all the data is present. 00830 The GIF specifcation allows us to output the next bitmap in an animation such 00831 that it is a subregion of a previous image. In this case we need to specify a 00832 new width and height plus a left and top offset. 00833 Errors: Calls SetError on FALSE returns. 00834 SeeAlso: OutputGIF::OutputGifHeader; 00835 SeeAlo: OutputGIF::StartFile; OutputGIF::WriteBlock; OutputGIF::TidyUp 00836 Scope: static 00837 00838 Note: If we use the NewWidth and NewHeight input variables then we must ALWAYS 00839 input the height and width of the bitmap. 00840 00841 ********************************************************************************************/ 00842 BOOL OutputGIF::OutputGifImageBits(CCLexFile *File, LPBYTE pBlockStart, BOOL Interlace, BOOL LocalColourTable, 00843 BaseCamelotFilter *pFilter, 00844 UINT32 NewWidth, UINT32 NewHeight, UINT32 LeftOffset, UINT32 TopOffset, 00845 LPRGBQUAD pDestPalette, UINT32 PaletteColourDepth, UINT32 NewBitsPerPixel) 00846 00847 { 00848 ERROR2IF(File==NULL,FALSE,"OutputGIF::OutputGifHeader File pointer is null"); 00849 ERROR2IF(pBlockStart==NULL,FALSE,"OutputGIF::OutputGifHeader BitmapInfo pointer is null"); 00850 00851 // Note file in our class variable as used by all the low level routines 00852 OutputFile = File; 00853 InterlacingOn = Interlace; 00854 00855 // Set up our pointer to the alternative way of handling the progress bar 00856 // If it is the NULL default then do not use. 00857 pCamFilter = pFilter; 00858 00859 if (NewWidth > 0 && NewHeight > 0) 00860 { 00861 // Set the statics to the new values. If we use this method then we must ALWAYS input 00862 // the height and width of the bitmap. 00863 Width = NewWidth; 00864 Height = NewHeight; 00865 } 00866 00867 ERROR3IF(NewBitsPerPixel > 8,"Bad BPP in OutputGifImageBits"); 00868 if (NewBitsPerPixel > 0 && NewBitsPerPixel <= 8) 00869 BitsPerPixel = NewBitsPerPixel; 00870 00871 // Must set the exception throwing flag to True and force reporting of errors to False. 00872 // This means that the caller must report an error if the function returns False. 00873 // Any calls to CCFile::GotError will now throw a file exception and should fall into 00874 // the catch handler at the end of the function. 00875 // Replaces the goto's that handled this before. 00876 BOOL OldThrowingState = File->SetThrowExceptions( TRUE ); 00877 BOOL OldReportingState = File->SetReportErrors( FALSE ); 00878 00879 try 00880 { 00881 // Write an Image separator 00882 File->put(0x2C); 00883 00884 // The initial code size 00885 if( BitsPerPixel <= 1 ) 00886 InitCodeSize = 2; 00887 else 00888 InitCodeSize = BitsPerPixel; 00889 00890 UINT32 ColourDepth = 1; 00891 00892 if (pDestPalette && PaletteColourDepth > 0) 00893 { 00894 // Translate the number of colours into a bits per pixel which is not limited to 8, 4 and 1. 00895 ColourDepth = GetColourDepth(PaletteColourDepth); 00896 InitCodeSize = ColourDepth; 00897 } 00898 else 00899 { 00900 // Direct saving a bitmap has this set to null 00901 //ERROR3IF(DestBitmapInfo == NULL,"Bad DestBitmapInfo in OutputGifImageBits"); 00902 if (DestBitmapInfo) 00903 { 00904 ColourDepth = DestBitmapInfo->bmiHeader.biBitCount; 00905 InitCodeSize = GetColourDepth(DestBitmapInfo->bmiHeader.biClrUsed); 00906 } 00907 else 00908 ColourDepth = BitsPerPixel; 00909 } 00910 00911 // Sanity check on the code size 00912 if (InitCodeSize > 8) 00913 InitCodeSize = 8; 00914 if (InitCodeSize < 2) 00915 InitCodeSize = 2; 00916 00917 TRACEUSER( "Neville", _T("OutputGifImageBits - Colour depth = %d\n"),ColourDepth); 00918 00919 // Write the Image header 00920 GIFIMAGEBLOCK ImageHeader; 00921 ImageHeader.gibLeft = (WORD)LeftOffset; // Left Offset defaults to zero 00922 ImageHeader.gibTop = (WORD)TopOffset; // Top Offset defaults to zero 00923 ImageHeader.gibWidth = (WORD)Width; 00924 ImageHeader.gibDepth = (WORD)Height; 00925 // Flags consists of:- 00926 // Local Color Table Flag 1 Bit 00927 // Interlace Flag 1 Bit 00928 // Sort Flag 1 Bit 00929 // Reserved 2 Bits 00930 // Size of Local Color Table 3 Bits 00931 ImageHeader.gibFlags = (LocalColourTable ? 0x80 : 0) | (Interlace ? 0x40 : 0) | 00932 (LocalColourTable ? (ColourDepth - 1) : 0); 00933 File->write(&ImageHeader, 9); 00934 00935 // Save the local colour table - it follows straight on 00936 if (LocalColourTable) 00937 { 00938 if (pDestPalette && PaletteColourDepth > 0) 00939 { 00940 TRACEUSER( "Neville", _T("OutputGifImageBits - Output local palette table (Extended)\n")); 00941 // Somebody supplied a palette in RGBQUAD form for us to output so do so. 00942 // write it to disk in GIFRGBTRIPLE format 00943 GIFRGBTRIPLE TempRGB; 00944 for (INT32 loop = 0; loop < ((1 << ColourDepth)); loop++) 00945 { 00946 TempRGB.grgbtRed = pDestPalette->rgbRed; 00947 TempRGB.grgbtGreen = pDestPalette->rgbGreen; 00948 TempRGB.grgbtBlue = pDestPalette->rgbBlue; 00949 pDestPalette++; 00950 00951 File->write( &TempRGB, sizeof(GIFRGBTRIPLE) ); 00952 } 00953 } 00954 else if (OutputPalette && ColourDepth > 0) 00955 { 00956 TRACEUSER( "Neville", _T("OutputGifImageBits - Output local palette table\n")); 00957 //ERROR3IF(OutputPalette == NULL,"Bad OutputPalette in OutputGifImageBits") 00958 // Use the palette stored inside the DestBitmapInfo and OutputPalette class variables 00959 GIFRGBTRIPLE TempRGB; 00960 for (INT32 loop = 0; loop < ((1 << ColourDepth)); loop++) 00961 { 00962 TempRGB.grgbtRed = OutputPalette->palPalEntry[loop].peRed; 00963 TempRGB.grgbtGreen = OutputPalette->palPalEntry[loop].peGreen; 00964 TempRGB.grgbtBlue = OutputPalette->palPalEntry[loop].peBlue; 00965 00966 File->write( &TempRGB, sizeof(GIFRGBTRIPLE) ); 00967 } 00968 } 00969 else 00970 ERROR3("Trying to output local palette when none specified!"); 00971 } 00972 00973 // Write out the initial code size 00974 File->put( InitCodeSize ); 00975 00976 // It is now ready to go and put that data onto the disc 00977 // so wait for the bitmap data to be prepared 00978 00979 // Go and actually compress the data 00980 CompressBlockToFile( pBlockStart, InitCodeSize + 1 ); 00981 00982 // Write out a Zero-length packet (to end the series) 00983 File->put( 0 ); 00984 00985 // Must set the exception throwing and reporting flags back to their entry states 00986 File->SetThrowExceptions( OldThrowingState ); 00987 File->SetReportErrors( OldReportingState ); 00988 00989 return TRUE; 00990 } 00991 00992 catch(...) 00993 { 00994 // catch our form of a file exception 00995 TRACE( _T("OutputGIF::OutputGifHeader CC catch handler\n")); 00996 00997 // Must set the exception throwing and reporting flags back to their entry states 00998 File->SetThrowExceptions( OldThrowingState ); 00999 File->SetReportErrors( OldReportingState ); 01000 01001 return FALSE; 01002 } 01003 01004 ERROR2( FALSE, "Escaped exception clause somehow" ); 01005 } 01006 01007 01008 01009 /******************************************************************************************** 01010 > BOOL OutputGIF::OutputGifTerminator(CCLexFile *File) 01011 01012 Author: Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com> 01013 Created: 28/4/95 01014 Inputs: File pointer to file to output to 01015 Outputs: - 01016 Returns: TRUE if worked, FALSE if failed 01017 Purpose: Outputs the GIF terminator sequence 01018 ********************************************************************************************/ 01019 01020 BOOL OutputGIF::OutputGifTerminator(CCLexFile *File) 01021 { 01022 ERROR2IF(File==NULL,FALSE,"OutputGIF::OutputGifTerminator File pointer is null"); 01023 01024 BOOL OldThrowingState = File->SetThrowExceptions( TRUE ); 01025 BOOL OldReportingState = File->SetReportErrors( FALSE ); 01026 01027 try 01028 { 01029 // Write the GIF file terminator 01030 File->put( ';' ); 01031 01032 // Must set the exception throwing and reporting flags back to their entry states 01033 File->SetThrowExceptions( OldThrowingState ); 01034 File->SetReportErrors( OldReportingState ); 01035 01036 // er, we seem to have finished OK so say so 01037 return TRUE; 01038 } 01039 01040 catch(...) 01041 { 01042 // Must set the exception throwing and reporting flags back to their entry states 01043 File->SetThrowExceptions( OldThrowingState ); 01044 File->SetReportErrors( OldReportingState ); 01045 01046 return FALSE; 01047 } 01048 01049 ERROR2( FALSE, "Escaped exception clause somehow" ); 01050 } 01051 01052 01053 01054 01055 01056 /******************************************************************************************** 01057 01058 > BOOL OutputGIF::WriteBlock( UINT32 YPos, UINT32 Height, LPBYTE BlockStart, UINT32 InputBPP = 32, 01059 INT32 ProgressOffset = 0) 01060 01061 Author: Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> 01062 Created: 24/4/95 01063 Inputs: YPos pixel co-ord of the chunk of DIB we are about to write out (0=bottom, 01064 increasing as it goes up). 01065 Height height in pixels of this chunk 01066 BlockStart the start address of the bytes of this chunk 01067 (optional) InputBPP BPP of the input image (assumed 32 by default - 8 also works) 01068 ProgressOffset value to add to value passed to ContinueSlowJob (default 0). 01069 Returns: TRUE if worked, FALSE if failed (error will be set accordingly but not reported) 01070 Purpose: Writes a chunk of bitmap data to the device. 01071 Assumes a progress hourglass is required and the caller has started an hourglass 01072 with a size of 100 and will end it. 01073 Notes: Originally this routine assumed that it was being given a 32bpp bmp, but using 01074 the InputBPP param it will now handle 8bpp bmps as well... 01075 Errors: Calls SetError on FALSE returns. 01076 Scope: Public 01077 01078 ********************************************************************************************/ 01079 01080 BOOL OutputGIF::WriteBlock( UINT32 YPos, UINT32 Height, LPBYTE BlockStart, UINT32 InputBPP, 01081 INT32 ProgressOffset) 01082 { 01083 ERROR2IF(DestBitmapInfo == NULL, FALSE,"OutputGIF::WriteBlock destination bitmap info null"); 01084 ERROR2IF(DestBitmapBytes == NULL, FALSE,"OutputGIF::WriteBlock destination bitmap bits null"); 01085 ERROR2IF(pNextStrip == NULL, FALSE,"OutputGIF::WriteBlock next strip pointer is null"); 01086 01087 FNPTR_SCANLINE ConvertFn = NULL; 01088 LPBYTE Buffer = NULL; 01089 size_t BufSize = 0L; 01090 size_t ChunkHeight = 1; 01091 DIBConvert *DoConvert = NULL; 01092 01093 // Set up the size and other information for the dib block that we require. This is 01094 // dependent on the export depth or bpp required. 01095 if ( !SetUpBlock( &BufSize, &ChunkHeight, &DoConvert, &ConvertFn ) ) 01096 return FALSE; // Error details already set up 01097 01098 if (BufSize) 01099 { 01100 Buffer = (LPBYTE)CCMalloc( BufSize ); 01101 if (Buffer==NULL) 01102 return FALSE; 01103 } 01104 01105 if ( DoConvert ) 01106 { 01107 // use new classes to do it 01108 // 8bpp, 4bpp and 1bpp conversion 01109 INT32 h = Height; 01110 INT32 count = 0; 01111 LPBYTE Data = BlockStart; 01112 const size_t SourceWidth = DIBUtil::ScanlineSize( BitmapInfo.biWidth, InputBPP ) * ChunkHeight; 01113 const size_t DestWidth = DIBUtil::ScanlineSize( BitmapInfo.biWidth, BitmapInfo.biBitCount ); 01114 01115 while (h) 01116 { 01117 ENSURE(h >= 0, "bad looping"); 01118 01119 const size_t ThisBit = min( h, (INT32)ChunkHeight ); 01120 if (!DoConvert->Convert( Data, Buffer, ThisBit, IsFirstStrip )) 01121 break; // stop if conversion failed 01122 01123 IsFirstStrip = FALSE; 01124 01125 // Copy this block to our destination bitmap 01126 // pNextStrip should be pointing at the next place to copy the data to 01127 memcpy(pNextStrip, Buffer, ThisBit * DestWidth); 01128 01129 Data += SourceWidth; 01130 h -= ThisBit; 01131 pNextStrip += ThisBit * DestWidth; 01132 01133 // now update the progress display, started with CurrentExportSize 01134 // CurrentExport size is now the point to go from in the export 01135 count++; 01136 //ContinueSlowJob( (INT32)(ProgressOffset + count) ); 01137 //ContinueSlowJob( (INT32)(100*count/(Height)) ); 01138 } 01139 } 01140 else if ( ConvertFn && Buffer ) 01141 { 01142 // Write via conversion function 01143 // 24 bpp convert 01144 ERROR2(FALSE,"OutputGIF::WriteBlock trying to write using ConvertFunction"); 01145 } 01146 else 01147 { 01148 ERROR2(FALSE,"OutputGIF::WriteBlock trying to write in one go"); 01149 } 01150 01151 // If present, get rid of our export function 01152 if (DoConvert) 01153 { 01154 delete DoConvert; 01155 DoConvert = NULL; 01156 } 01157 01158 CCFree( Buffer ); 01159 Buffer = NULL; 01160 01161 HeightWritten += Height; // remember we wrote this lot 01162 01163 return TRUE; 01164 } 01165 01166 /******************************************************************************************** 01167 01168 > BOOL OutputDIB::TidyUp() 01169 01170 Author: Andy_Pennell (Xara Group Ltd) <camelotdev@xara.com> 01171 Created: 4/8/94 01172 Returns: TRUE if worked, FALSE if failed (error will be set accordingly but not reported) 01173 Purpose: When everything has been done via WriteBlock, call this to update the header 01174 etc. The caller is responsible for closing the file. The class CANNOT be used 01175 for further output until StartFile is called again. 01176 Errors: Calls SetError on FALSE returns. 01177 Scope: Public 01178 01179 ********************************************************************************************/ 01180 01181 BOOL OutputGIF::TidyUp() 01182 { 01183 // Free up the DIB that we have just created 01184 if (DestBitmapInfo && DestBitmapBytes) 01185 { 01186 FreeDIB( DestBitmapInfo, DestBitmapBytes ); 01187 DestBitmapInfo = NULL; 01188 DestBitmapBytes = NULL; 01189 } 01190 01191 // Call the baseclass version to do its stuff 01192 const BOOL ok = OutputDIB::TidyUp(); 01193 01194 return ok; 01195 } 01196 01197 01198 /******************************************************************************************** 01199 * 01200 * GIFCOMPR.C - GIF Image compression routines 01201 * 01202 * Lempel-Ziv compression based on 'compress'. GIF modifications by 01203 * David Rowley (mgardi@watdcsu.waterloo.edu) 01204 * 01205 ********************************************************************************************/ 01206 01207 /* 01208 * GIF Image compression - modified 'compress' 01209 * 01210 * Based on: compress.c - File compression ala IEEE Computer, June 1984. 01211 * 01212 * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) 01213 * Jim McKie (decvax!mcvax!jim) 01214 * Steve Davies (decvax!vax135!petsd!peora!srd) 01215 * Ken Turkowski (decvax!decwrl!turtlevax!ken) 01216 * James A. Woods (decvax!ihnp4!ames!jaw) 01217 * Joe Orost (decvax!vax135!petsd!joe) 01218 * 01219 * To save much memory, we overlay the table used by compress() with those 01220 * used by decompress(). The tab_prefix table is the same size and type 01221 * as the codetab. The tab_suffix table needs 2**GIFBITS characters. We 01222 * get this from the beginning of htab. The output stack uses the rest 01223 * of htab, and contains characters. There is plenty of room for any 01224 * possible stack (stack used to be 8000 characters). 01225 * 01226 * Algorithm: use open addressing double hashing (no chaining) on the 01227 * prefix code / next character combination. We do a variant of Knuth's 01228 * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime 01229 * secondary probe. Here, the modular division first probe is gives way 01230 * to a faster exclusive-or manipulation. Also do block compression with 01231 * an adaptive reset, whereby the code table is cleared when the compression 01232 * ratio decreases, but after the table fills. The variable-length output 01233 * codes are re-sized at this point, and a special CLEAR code is generated 01234 * for the decompressor. Late addition: construct the table according to 01235 * file size for noticeable speed improvement on small files. Please direct 01236 * questions about this implementation to ames!jaw. 01237 */ 01238 01239 /******************************************************************************************** 01240 01241 > void OutputGIF::CompressBlockToFile( LPBYTE Buffer, INT32 init_bits ) 01242 01243 Inputs: Buffer pointer to the bits to be compressed 01244 init_bits initial number of bits 01245 Outputs: - 01246 Purpose: Compress the specified bitmap out to the file specified in the class variable 01247 OutputFile. 01248 Assumes that variables such as Width, Height, WidthOfLine have been set up 01249 by the caller from the bitmap we are about to compress. This is done by the 01250 usual calling function of this OutputGifBits and OutputGifHeader. 01251 SeeAlso: OutputGIF::OutputGifHeader; OutputGIF::OutputGifBits; 01252 01253 ********************************************************************************************/ 01254 01255 void OutputGIF::CompressBlockToFile( LPBYTE Buffer, INT32 init_bits ) 01256 { 01257 // These used to have register before them 01258 INT32 fcode; 01259 code_int i; //= 0; 01260 INT32 c; 01261 code_int ent; 01262 code_int disp; 01263 code_int hsize_reg; 01264 INT32 hshift; 01265 01266 // Note the passed in buffer and size in our class variables 01267 DataBuffer = Buffer; 01268 01269 // Calculate number of bits we are expecting 01270 CountDown = (INT32)Width * (INT32)Height; 01271 01272 // Work out the word/byte rounded line width rather than the pixel width 01273 WidthOfLine = DIBUtil::ScanlineSize( Width, BitsPerPixel ); 01274 01275 // Set up the globals: g_init_bits - initial number of bits 01276 g_init_bits = init_bits; 01277 01278 // Set up the necessary values 01279 offset = 0; 01280 out_count = 0; 01281 clear_flg = 0; 01282 in_count = 1; 01283 maxcode = MAXCODE(n_bits = init_bits); 01284 ClearCode = (1 << (init_bits - 1)); 01285 EOFCode = ClearCode + 1; 01286 free_ent = ClearCode + 2; 01287 01288 // Indicate which pass we are on (if interlace) 01289 Pass = 0; 01290 01291 // Set up the current x and y position 01292 curx = 0; 01293 cury = 0; 01294 CurrentLine = 0; 01295 01296 cur_accum = 0; 01297 cur_bits = 0; 01298 01299 CharacterInit(); // a_count = 0; 01300 01301 ent = GIFNextPixel(); 01302 01303 hshift = 0; 01304 for ( fcode = (INT32) hsize; fcode < 65536L; fcode *= 2L ) 01305 ++hshift; 01306 hshift = 8 - hshift; // set hash code range bound 01307 01308 // clear hash table 01309 hsize_reg = hsize; 01310 ClearHashTable( (count_int) hsize_reg); 01311 01312 OutputCode( (code_int)ClearCode ); 01313 01314 while ( (c = GIFNextPixel()) != EOF ) 01315 { 01316 ++in_count; 01317 01318 fcode = (INT32) (((INT32) c << maxbits) + ent); 01319 i = (((code_int)c << hshift) ^ ent); // xor hashing 01320 01321 if ( HashTabOf(i) == fcode ) 01322 { 01323 ent = CodeTabOf(i); 01324 continue; 01325 } 01326 else if ( (INT32)HashTabOf(i) < 0 ) // empty slot 01327 goto nomatch; 01328 01329 disp = hsize_reg - i; // secondary hash (after G. Knott) 01330 if ( i == 0 ) 01331 disp = 1; 01332 01333 probe: 01334 if ( (i -= disp) < 0 ) 01335 i += hsize_reg; 01336 01337 if ( HashTabOf(i) == fcode ) 01338 { 01339 ent = CodeTabOf(i); 01340 continue; 01341 } 01342 if ( (INT32)HashTabOf(i) > 0 ) 01343 goto probe; 01344 01345 nomatch: 01346 OutputCode( (code_int) ent ); 01347 ++out_count; 01348 ent = c; 01349 if ( free_ent < maxmaxcode ) 01350 { 01351 CodeTabOf(i) = free_ent++; /* code -> hashtable */ 01352 HashTabOf(i) = fcode; 01353 } 01354 else 01355 ClearBlock(); 01356 } 01357 // Put out the final code. 01358 OutputCode( (code_int) ent ); 01359 ++out_count; 01360 OutputCode( (code_int) EOFCode ); 01361 } 01362 01363 /******************************************************************************************** 01364 01365 > void OutputGIF::OutputCode( code_int code ) 01366 01367 Inputs: code A n_bits-bit integer. If == -1, then EOF. 01368 This assumes that n_bits =< (INT32)wordsize - 1. 01369 Outputs: Outputs code to the file. 01370 Purpose: Output the given code. 01371 This assumes that chars are 8 bits long. 01372 It maintains a GIFBITS character INT32 buffer (so that 8 codes will fit in it 01373 exactly). When the buffer fills up empty it and start over. 01374 01375 ********************************************************************************************/ 01376 01377 void OutputGIF::OutputCode( code_int code ) 01378 { 01379 cur_accum &= masks[ cur_bits ]; 01380 01381 if( cur_bits > 0 ) 01382 cur_accum |= ((INT32)code << cur_bits); 01383 else 01384 cur_accum = code; 01385 01386 cur_bits += n_bits; 01387 01388 while( cur_bits >= 8 ) 01389 { 01390 OutputCharacter( (UINT32)(cur_accum & 0xff) ); 01391 cur_accum >>= 8; 01392 cur_bits -= 8; 01393 } 01394 01395 // If the next entry is going to be too big for the code size, then increase it, 01396 // if possible. 01397 if ( free_ent > maxcode || clear_flg ) 01398 { 01399 if( clear_flg ) 01400 { 01401 maxcode = MAXCODE(n_bits = g_init_bits); 01402 clear_flg = 0; 01403 } 01404 else 01405 { 01406 ++n_bits; 01407 if ( n_bits == maxbits ) 01408 maxcode = maxmaxcode; 01409 else 01410 maxcode = MAXCODE(n_bits); 01411 } 01412 } 01413 01414 if( code == EOFCode ) 01415 { 01416 // At EOF, write the rest of the buffer. 01417 while( cur_bits > 0 ) 01418 { 01419 OutputCharacter( (UINT32)(cur_accum & 0xff) ); 01420 cur_accum >>= 8; 01421 cur_bits -= 8; 01422 } 01423 01424 FlushCharacters(); 01425 01426 OutputFile->flush(); 01427 } 01428 } 01429 01430 /******************************************************************************************** 01431 01432 > void OutputGIF::ClearBlock() 01433 01434 Inputs: - 01435 Outputs: - 01436 Returns: - 01437 Purpose: Clear out the hash table for block compress 01438 01439 ********************************************************************************************/ 01440 01441 void OutputGIF::ClearBlock() 01442 { 01443 ClearHashTable( (count_int) hsize ); 01444 free_ent = ClearCode + 2; 01445 clear_flg = 1; 01446 01447 OutputCode( (code_int)ClearCode ); 01448 } 01449 01450 /******************************************************************************************** 01451 01452 > void OutputGIF::ClearHashTable(count_int hsize) 01453 01454 Inputs: hsize size of the maximum code size 01455 Outputs: - 01456 Returns: - 01457 Purpose: reset the code table back to the default values 01458 01459 ********************************************************************************************/ 01460 01461 void OutputGIF::ClearHashTable(count_int hsize) 01462 { 01463 // These used to have register in front of them 01464 count_int *htab_p = htab + hsize; 01465 INT32 i; 01466 INT32 m1 = -1; 01467 01468 i = hsize - 16; 01469 do 01470 { 01471 *(htab_p - 16) = m1; 01472 *(htab_p - 15) = m1; 01473 *(htab_p - 14) = m1; 01474 *(htab_p - 13) = m1; 01475 *(htab_p - 12) = m1; 01476 *(htab_p - 11) = m1; 01477 *(htab_p - 10) = m1; 01478 *(htab_p - 9) = m1; 01479 *(htab_p - 8) = m1; 01480 *(htab_p - 7) = m1; 01481 *(htab_p - 6) = m1; 01482 *(htab_p - 5) = m1; 01483 *(htab_p - 4) = m1; 01484 *(htab_p - 3) = m1; 01485 *(htab_p - 2) = m1; 01486 *(htab_p - 1) = m1; 01487 htab_p -= 16; 01488 } while ((i -= 16) >= 0); 01489 01490 for ( i += 16; i > 0; --i ) 01491 *--htab_p = m1; 01492 } 01493 01494 /******************************************************************************************** 01495 * 01496 * GIF Specific routines 01497 * 01498 ********************************************************************************************/ 01499 01500 /******************************************************************************************** 01501 01502 > void OutputGIF::CharacterInit() 01503 01504 Inputs: - 01505 Outputs: - 01506 Returns: - 01507 Purpose: Set up the 'byte output' routine. 01508 01509 ********************************************************************************************/ 01510 01511 void OutputGIF::CharacterInit() 01512 { 01513 a_count = 0; // set accumlator to zero 01514 } 01515 01516 /******************************************************************************************** 01517 01518 > void OutputGIF::OutputCharacter( INT32 c ) 01519 01520 Inputs: c character to add to the output stream 01521 Outputs: - 01522 Returns: - 01523 Purpose: Add a character to the end of the current packet, and if it is 254 01524 characters, flush the packet to disk. 01525 01526 ********************************************************************************************/ 01527 01528 void OutputGIF::OutputCharacter( INT32 c ) 01529 { 01530 accum[ a_count++ ] = c; 01531 if( a_count >= 255 ) // was 254 until 16/4/97 - Gavin suggested tweak 01532 FlushCharacters(); 01533 } 01534 01535 /******************************************************************************************** 01536 01537 > void OutputGIF::FlushCharacters() 01538 01539 Inputs: - 01540 Outputs: - 01541 Returns: - 01542 Purpose: Flush the packet to disk, and reset the accumulator (class variable a_count). 01543 01544 ********************************************************************************************/ 01545 01546 void OutputGIF::FlushCharacters() 01547 { 01548 if( a_count > 0 ) 01549 { 01550 OutputFile->put( a_count ); 01551 OutputFile->write( &accum, a_count); 01552 a_count = 0; 01553 } 01554 } 01555 01556 /******************************************************************************************** 01557 01558 > void OutputGIF::init_statics() 01559 01560 Inputs: - 01561 Outputs: - 01562 Returns: - 01563 Purpose: - 01564 01565 ********************************************************************************************/ 01566 01567 /* 01568 void OutputGIF::init_statics() 01569 { 01570 */ 01571 // Some of these are properly initialized later. What I'm doing here is making sure 01572 // code that depends on C's initialization of statics doesn't break when the code 01573 // gets called more than once. 01574 /* 01575 curx = 0; 01576 cury = 0; 01577 CountDown = 0; 01578 Pass = 0; 01579 a_count = 0; 01580 cur_accum = 0; 01581 cur_bits = 0; 01582 g_init_bits = 0; 01583 ClearCode = 0; 01584 EOFCode = 0; 01585 free_ent = 0; 01586 clear_flg = 0; 01587 offset = 0; 01588 in_count = 1; 01589 out_count = 0; 01590 n_bits = 0; 01591 maxcode = 0; 01592 } */ 01593 01594 /******************************************************************************************** 01595 01596 > INT32 OutputGIF::GIFNextPixel() 01597 01598 Inputs: - 01599 Outputs: - 01600 Returns: the value of the specified pixel or 0 if badly specified 01601 Purpose: Return the next pixel from the image using the class variables curx, cury 01602 as the current positions and afterwards moving them to the next positions. 01603 Takes into account if interlacing is on. 01604 01605 ********************************************************************************************/ 01606 01607 INT32 OutputGIF::GIFNextPixel() 01608 { 01609 INT32 Pixel; 01610 01611 // First, check whether we have done all the pixels in the image 01612 if( CountDown == 0 ) 01613 return EOF; 01614 01615 // Decrement our pixel count 01616 --CountDown; 01617 01618 //r = GetPixel(curx, cury); 01619 if ( !(((cury < 0) || (cury >= Height)) || ((curx < 0) || (curx >= Width))) && DataBuffer ) 01620 { 01621 // Our DIBs are the wrong way up so we must output the data from the last 01622 // line of the image and go up to the start 01623 // -1 as height = 1 .. Height whereas y goes from 0 .. Height - 1 01624 // Use the word/byte rounded line width rather than the pixel width 01625 // always compresses a byte regardless of colour depth. 01626 switch (BitsPerPixel) 01627 { 01628 case 8: 01629 { 01630 // If 8 bpp then just use the whole byte straight 01631 LPBYTE pData = DataBuffer + curx + ((Height - 1 - cury) * WidthOfLine); 01632 Pixel = *(pData); 01633 } 01634 break; 01635 01636 case 4: 01637 { 01638 // 4bpp so we must get the high or low nibble. This will be dependent on 01639 // whether we are on an odd or even pixel. So test the LSBit of the curx, 01640 // if set we will be odd. Only move onto next byte every other pixel hence 01641 // curx/2. 01642 LPBYTE pData = DataBuffer + curx/2 + ((Height - 1 - cury) * WidthOfLine); 01643 Pixel = *(pData); 01644 if (curx & 1) 01645 Pixel = (Pixel & 0x0F); // take low nibble 01646 else 01647 Pixel = (Pixel & 0xF0) >> 4; // take top nibble 01648 } 01649 break; 01650 case 1: 01651 { 01652 // 1bpp - pixels are stored in bits. 01653 LPBYTE pData = DataBuffer + curx/8 + ((Height - 1 - cury) * WidthOfLine); 01654 BYTE Mask = 0x80 >> (curx % 8); 01655 if (((*pData) & Mask) == 0) 01656 Pixel = 0; 01657 else 01658 Pixel = 1; 01659 } 01660 break; 01661 default: 01662 ERROR3("Unknown export depth"); 01663 Pixel = 0; // bad bpp but should not get here 01664 } 01665 } 01666 else 01667 { 01668 ERROR3("Something bad has happened"); 01669 Pixel = 0; 01670 } 01671 01672 // Move the curx, cury to the next pixel on the image taking into account interlacing 01673 BumpPixel(); 01674 01675 // Check that the pixel is not outside of the allowed range 01676 // If it is then what do we do with it? Set it 0 for now. 01677 ERROR3IF(Pixel >= ClearCode,"Trying to output pixel >= ClearCode"); 01678 if (Pixel >= ClearCode) 01679 Pixel = 0; 01680 01681 return Pixel; 01682 } 01683 01684 /******************************************************************************************** 01685 01686 > void OutputGIF::BumpPixel() 01687 01688 Inputs: - 01689 Outputs: - 01690 Returns: - 01691 Purpose: Bump the class variables 'curx' and 'cury' to point to the next pixel 01692 taking into account interlacing if it is on. 01693 01694 ********************************************************************************************/ 01695 01696 void OutputGIF::BumpPixel() 01697 { 01698 // Bump the current X position 01699 ++curx; 01700 01701 // If we are at the end of a scan line, set curx back to the beginning 01702 // If we are interlaced, bump the cury to the appropriate spot, 01703 // otherwise, just increment it. 01704 if( curx == Width ) 01705 { 01706 // Reset x to start of next line 01707 curx = 0; 01708 01709 CurrentLine++; 01710 01711 // If Interlacing off then just increment the line we are on 01712 if( !InterlacingOn ) 01713 ++cury; 01714 else 01715 { 01716 // Otherwise get the next interlaced line in the sequence 01717 // Use the same style of code that we use for import in GIFUtil 01718 switch (Pass) 01719 { 01720 case 0: 01721 case 1: 01722 cury += 8; 01723 break; 01724 case 2: 01725 cury += 4; 01726 break; 01727 case 3: 01728 cury += 2; 01729 break; 01730 } 01731 01732 while (cury >= Height && Pass <= 3) 01733 { 01734 ++Pass; 01735 switch (Pass) 01736 { 01737 case 1: 01738 cury = 4; 01739 break; 01740 case 2: 01741 cury = 2; 01742 break; 01743 case 3: 01744 cury = 1; 01745 break; 01746 default: 01747 cury = 0; 01748 break; 01749 } 01750 } 01751 } 01752 01753 // Show a progress bar 01754 if (pCamFilter == NULL) 01755 { 01756 // Andy, 13-12-00: We don't want another progress bar if we're exporting 01757 //ContinueSlowJob( (INT32)(100 * CurrentLine/(Height)) ); 01758 } 01759 else 01760 pCamFilter->IncProgressBarCount(1); 01761 } 01762 } 01763 01764 /******************************************************************************************** 01765 01766 > BOOL OutputGIF::BoundsSafe(INT32 x, INT32 y) 01767 01768 Inputs: x horizontal pixel offset into the image 01769 y vertical pixel(line) offset into the image 01770 Outputs: - 01771 Returns: True if specified pixel bounds ok otherwise False 01772 Purpose: Check that the pixel we are about to get is within the bounds of the image 01773 01774 ********************************************************************************************/ 01775 01776 //BOOL OutputGIF::BoundsSafe(INT32 x, INT32 y) 01777 //{ 01778 // return ( !(((y < 0) || (y >= Height)) || ((x < 0) || (x >= Width))) ); 01779 //} 01780 01781 /******************************************************************************************** 01782 01783 > INT32 OutputGIF::GetPixel(INT32 x, INT32 y) 01784 01785 Inputs: x horizontal pixel offset into the image 01786 y vertical pixel(line) offset into the image 01787 Outputs: - 01788 Returns: the value of teh specified pixel or 0 if badly specified 01789 Purpose: Get the specified pixel from the image buffer checking that it is within 01790 the image bounds otherwise return 0. 01791 01792 ********************************************************************************************/ 01793 01794 //INT32 OutputGIF::GetPixel(INT32 x, INT32 y) 01795 //{ 01796 // if (BoundsSafe(x, y) && DataBuffer) 01797 // { 01798 // // Our DIBs are the wrong way up so we must output the data from the last 01799 // // line of the image and go up to the start 01800 // // -1 as height = 1 .. Height whereas y goes from 0 .. Height - 1 01801 // LPBYTE pData = DataBuffer + x + ((Height - 1 - y) * Width); 01802 // //return pixels[x][y]; 01803 // // always compresses a byte regardless of colour depth. 01804 // return *(pData); 01805 // } 01806 // else 01807 // return 0; 01808 //} 01809 01810