outptgif.cpp

Go to the documentation of this file.
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 

Generated on Sat Nov 10 03:48:45 2007 for Camelot by  doxygen 1.4.4