diff options
Diffstat (limited to 'vcl/source/gdi/bitmap2.cxx')
-rw-r--r-- | vcl/source/gdi/bitmap2.cxx | 1277 |
1 files changed, 1277 insertions, 0 deletions
diff --git a/vcl/source/gdi/bitmap2.cxx b/vcl/source/gdi/bitmap2.cxx new file mode 100644 index 000000000000..de926a0446ca --- /dev/null +++ b/vcl/source/gdi/bitmap2.cxx @@ -0,0 +1,1277 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <tools/zcodec.hxx> +#ifndef _TOOLS_STREAM_HXX +#include <tools/stream.hxx> +#endif +#include <vcl/salbtype.hxx> +#include <vcl/bmpacc.hxx> +#include <vcl/outdev.hxx> +#include <vcl/bitmap.hxx> + +#include <utility> + + +// ----------- +// - Defines - +// ----------- + +#define DIBCOREHEADERSIZE ( 12UL ) +#define DIBINFOHEADERSIZE ( sizeof( DIBInfoHeader ) ) +#define SETPIXEL4( pBuf, nX, cChar )( (pBuf)[ (nX) >> 1 ] |= ( (nX) & 1 ) ? ( cChar ): (cChar) << 4 ); + +// ---------------------- +// - Compression defines +// ---------------------- + +#define COMPRESS_OWN ('S'|('D'<<8UL)) +#define COMPRESS_NONE ( 0UL ) +#define RLE_8 ( 1UL ) +#define RLE_4 ( 2UL ) +#define BITFIELDS ( 3UL ) +#define ZCOMPRESS ( COMPRESS_OWN | 0x01000000UL ) /* == 'SD01' (binary) */ + +// ----------------- +// - DIBInfoHeader - +// ----------------- + +struct DIBInfoHeader +{ + sal_uInt32 nSize; + sal_Int32 nWidth; + sal_Int32 nHeight; + sal_uInt16 nPlanes; + sal_uInt16 nBitCount; + sal_uInt32 nCompression; + sal_uInt32 nSizeImage; + sal_Int32 nXPelsPerMeter; + sal_Int32 nYPelsPerMeter; + sal_uInt32 nColsUsed; + sal_uInt32 nColsImportant; + + DIBInfoHeader() : + nSize( 0UL ), + nWidth( 0UL ), + nHeight( 0UL ), + nPlanes( 0 ), + nBitCount( 0 ), + nCompression( 0 ), + nSizeImage( 0 ), + nXPelsPerMeter( 0UL ), + nYPelsPerMeter( 0UL ), + nColsUsed( 0UL ), + nColsImportant( 0UL ) {} + + ~DIBInfoHeader() {} +}; + +namespace +{ + inline USHORT discretizeBitcount( UINT16 nInputCount ) + { + return ( nInputCount <= 1 ) ? 1 : + ( nInputCount <= 4 ) ? 4 : + ( nInputCount <= 8 ) ? 8 : 24; + } + + inline bool isBitfieldCompression( ULONG nScanlineFormat ) + { + return nScanlineFormat == BMP_FORMAT_16BIT_TC_LSB_MASK || + nScanlineFormat == BMP_FORMAT_32BIT_TC_MASK; + } +} + +// ---------- +// - Bitmap - +// ---------- + +SvStream& operator>>( SvStream& rIStm, Bitmap& rBitmap ) +{ + rBitmap.Read( rIStm, TRUE ); + return rIStm; +} + +// ------------------------------------------------------------------ + +SvStream& operator<<( SvStream& rOStm, const Bitmap& rBitmap ) +{ + rBitmap.Write( rOStm, FALSE, TRUE ); + return rOStm; +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::Read( SvStream& rIStm, BOOL bFileHeader ) +{ + const USHORT nOldFormat = rIStm.GetNumberFormatInt(); + const ULONG nOldPos = rIStm.Tell(); + ULONG nOffset = 0UL; + BOOL bRet = FALSE; + + rIStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + if( bFileHeader ) + { + if( ImplReadDIBFileHeader( rIStm, nOffset ) ) + bRet = ImplReadDIB( rIStm, *this, nOffset ); + } + else + bRet = ImplReadDIB( rIStm, *this, nOffset ); + + if( !bRet ) + { + if( !rIStm.GetError() ) + rIStm.SetError( SVSTREAM_GENERALERROR ); + + rIStm.Seek( nOldPos ); + } + + rIStm.SetNumberFormatInt( nOldFormat ); + + return bRet; +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplReadDIB( SvStream& rIStm, Bitmap& rBmp, ULONG nOffset ) +{ + DIBInfoHeader aHeader; + const ULONG nStmPos = rIStm.Tell(); + BOOL bRet = FALSE; + sal_Bool bTopDown = sal_False; + + if( ImplReadDIBInfoHeader( rIStm, aHeader, bTopDown ) && aHeader.nWidth && aHeader.nHeight && aHeader.nBitCount ) + { + const USHORT nBitCount( discretizeBitcount(aHeader.nBitCount) ); + + const Size aSizePixel( aHeader.nWidth, aHeader.nHeight ); + BitmapPalette aDummyPal; + Bitmap aNewBmp( aSizePixel, nBitCount, &aDummyPal ); + BitmapWriteAccess* pAcc = aNewBmp.AcquireWriteAccess(); + + if( pAcc ) + { + USHORT nColors; + SvStream* pIStm; + SvMemoryStream* pMemStm = NULL; + BYTE* pData = NULL; + + if( nBitCount <= 8 ) + { + if( aHeader.nColsUsed ) + nColors = (USHORT) aHeader.nColsUsed; + else + nColors = ( 1 << aHeader.nBitCount ); + } + else + nColors = 0; + + if( ZCOMPRESS == aHeader.nCompression ) + { + ZCodec aCodec; + sal_uInt32 nCodedSize, nUncodedSize; + ULONG nCodedPos; + + // read coding information + rIStm >> nCodedSize >> nUncodedSize >> aHeader.nCompression; + pData = (BYTE*) rtl_allocateMemory( nUncodedSize ); + + // decode buffer + nCodedPos = rIStm.Tell(); + aCodec.BeginCompression(); + aCodec.Read( rIStm, pData, nUncodedSize ); + aCodec.EndCompression(); + + // skip unread bytes from coded buffer + rIStm.SeekRel( nCodedSize - ( rIStm.Tell() - nCodedPos ) ); + + // set decoded bytes to memory stream, + // from which we will read the bitmap data + pIStm = pMemStm = new SvMemoryStream; + pMemStm->SetBuffer( (char*) pData, nUncodedSize, FALSE, nUncodedSize ); + nOffset = 0; + } + else + pIStm = &rIStm; + + // read palette + if( nColors ) + { + pAcc->SetPaletteEntryCount( nColors ); + ImplReadDIBPalette( *pIStm, *pAcc, aHeader.nSize != DIBCOREHEADERSIZE ); + } + + // read bits + if( !pIStm->GetError() ) + { + if( nOffset ) + pIStm->SeekRel( nOffset - ( pIStm->Tell() - nStmPos ) ); + + bRet = ImplReadDIBBits( *pIStm, aHeader, *pAcc, bTopDown ); + + if( bRet && aHeader.nXPelsPerMeter && aHeader.nYPelsPerMeter ) + { + MapMode aMapMode( MAP_MM, Point(), + Fraction( 1000, aHeader.nXPelsPerMeter ), + Fraction( 1000, aHeader.nYPelsPerMeter ) ); + + aNewBmp.SetPrefMapMode( aMapMode ); + aNewBmp.SetPrefSize( Size( aHeader.nWidth, aHeader.nHeight ) ); + } + } + + if( pData ) + rtl_freeMemory( pData ); + + delete pMemStm; + aNewBmp.ReleaseAccess( pAcc ); + + if( bRet ) + rBmp = aNewBmp; + } + } + + return bRet; +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplReadDIBFileHeader( SvStream& rIStm, ULONG& rOffset ) +{ + UINT32 nTmp32; + UINT16 nTmp16 = 0; + BOOL bRet = FALSE; + + rIStm >> nTmp16; + + if ( ( 0x4D42 == nTmp16 ) || ( 0x4142 == nTmp16 ) ) + { + if ( 0x4142 == nTmp16 ) + { + rIStm.SeekRel( 12L ); + rIStm >> nTmp16; + rIStm.SeekRel( 8L ); + rIStm >> nTmp32; + rOffset = nTmp32 - 28UL;; + bRet = ( 0x4D42 == nTmp16 ); + } + else + { + rIStm.SeekRel( 8L ); + rIStm >> nTmp32; + rOffset = nTmp32 - 14UL; + bRet = ( rIStm.GetError() == 0UL ); + } + } + else + rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); + + return bRet; +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplReadDIBInfoHeader( SvStream& rIStm, DIBInfoHeader& rHeader, sal_Bool& bTopDown ) +{ + // BITMAPINFOHEADER or BITMAPCOREHEADER + rIStm >> rHeader.nSize; + + // BITMAPCOREHEADER + if ( rHeader.nSize == DIBCOREHEADERSIZE ) + { + sal_Int16 nTmp16; + + rIStm >> nTmp16; rHeader.nWidth = nTmp16; + rIStm >> nTmp16; rHeader.nHeight = nTmp16; + rIStm >> rHeader.nPlanes; + rIStm >> rHeader.nBitCount; + } + else + { + // unknown Header + if( rHeader.nSize < DIBINFOHEADERSIZE ) + { + ULONG nUnknownSize = sizeof( rHeader.nSize ); + + rIStm >> rHeader.nWidth; nUnknownSize += sizeof( rHeader.nWidth ); + rIStm >> rHeader.nHeight; nUnknownSize += sizeof( rHeader.nHeight ); + rIStm >> rHeader.nPlanes; nUnknownSize += sizeof( rHeader.nPlanes ); + rIStm >> rHeader.nBitCount; nUnknownSize += sizeof( rHeader.nBitCount ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nCompression; + nUnknownSize += sizeof( rHeader.nCompression ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nSizeImage; + nUnknownSize += sizeof( rHeader.nSizeImage ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nXPelsPerMeter; + nUnknownSize += sizeof( rHeader.nXPelsPerMeter ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nYPelsPerMeter; + nUnknownSize += sizeof( rHeader.nYPelsPerMeter ); + } + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nColsUsed; + nUnknownSize += sizeof( rHeader.nColsUsed ); + + if( nUnknownSize < rHeader.nSize ) + { + rIStm >> rHeader.nColsImportant; + nUnknownSize += sizeof( rHeader.nColsImportant ); + } + } + } + } + } + } + else + { + rIStm >> rHeader.nWidth; + rIStm >> rHeader.nHeight; + rIStm >> rHeader.nPlanes; + rIStm >> rHeader.nBitCount; + rIStm >> rHeader.nCompression; + rIStm >> rHeader.nSizeImage; + rIStm >> rHeader.nXPelsPerMeter; + rIStm >> rHeader.nYPelsPerMeter; + rIStm >> rHeader.nColsUsed; + rIStm >> rHeader.nColsImportant; + } + + // Eventuell bis zur Palette ueberlesen + if ( rHeader.nSize > DIBINFOHEADERSIZE ) + rIStm.SeekRel( rHeader.nSize - DIBINFOHEADERSIZE ); + } + if ( rHeader.nHeight < 0 ) + { + bTopDown = sal_True; + rHeader.nHeight *= -1; + } + else + bTopDown = sal_False; + + if ( rHeader.nWidth < 0 ) + rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); + + // #144105# protect a little against damaged files + if( rHeader.nSizeImage > ( 16 * static_cast< sal_uInt32 >( rHeader.nWidth * rHeader.nHeight ) ) ) + rHeader.nSizeImage = 0; + + return( ( rHeader.nPlanes == 1 ) && ( rIStm.GetError() == 0UL ) ); +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplReadDIBPalette( SvStream& rIStm, BitmapWriteAccess& rAcc, BOOL bQuad ) +{ + const USHORT nColors = rAcc.GetPaletteEntryCount(); + const ULONG nPalSize = nColors * ( bQuad ? 4UL : 3UL ); + BitmapColor aPalColor; + + BYTE* pEntries = new BYTE[ nPalSize ]; + rIStm.Read( pEntries, nPalSize ); + + BYTE* pTmpEntry = pEntries; + for( USHORT i = 0; i < nColors; i++ ) + { + aPalColor.SetBlue( *pTmpEntry++ ); + aPalColor.SetGreen( *pTmpEntry++ ); + aPalColor.SetRed( *pTmpEntry++ ); + + if( bQuad ) + pTmpEntry++; + + rAcc.SetPaletteColor( i, aPalColor ); + } + + delete[] pEntries; + + return( rIStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplReadDIBBits( SvStream& rIStm, DIBInfoHeader& rHeader, BitmapWriteAccess& rAcc, sal_Bool bTopDown ) +{ + const ULONG nAlignedWidth = AlignedWidth4Bytes( rHeader.nWidth * rHeader.nBitCount ); + UINT32 nRMask = 0; + UINT32 nGMask = 0; + UINT32 nBMask = 0; + BOOL bNative; + BOOL bTCMask = ( rHeader.nBitCount == 16 ) || ( rHeader.nBitCount == 32 ); + BOOL bRLE = ( RLE_8 == rHeader.nCompression && rHeader.nBitCount == 8 ) || + ( RLE_4 == rHeader.nCompression && rHeader.nBitCount == 4 ); + + // Is native format? + switch( rAcc.GetScanlineFormat() ) + { + case( BMP_FORMAT_1BIT_MSB_PAL ): + case( BMP_FORMAT_4BIT_MSN_PAL ): + case( BMP_FORMAT_8BIT_PAL ): + case( BMP_FORMAT_24BIT_TC_BGR ): + bNative = ( ( rAcc.IsBottomUp() != bTopDown ) && !bRLE && !bTCMask && ( rAcc.GetScanlineSize() == nAlignedWidth ) ); + break; + + default: + bNative = FALSE; + break; + } + // Read data + if( bNative ) + { + // true color DIB's can have a (optimization) palette + if( rHeader.nColsUsed && rHeader.nBitCount > 8 ) + rIStm.SeekRel( rHeader.nColsUsed * ( ( rHeader.nSize != DIBCOREHEADERSIZE ) ? 4 : 3 ) ); + + rIStm.Read( rAcc.GetBuffer(), rHeader.nHeight * nAlignedWidth ); + } + else + { + // Read color mask + if( bTCMask ) + { + if( rHeader.nCompression == BITFIELDS ) + { + rIStm.SeekRel( -12L ); + rIStm >> nRMask; + rIStm >> nGMask; + rIStm >> nBMask; + } + else + { + nRMask = ( rHeader.nBitCount == 16 ) ? 0x00007c00UL : 0x00ff0000UL; + nGMask = ( rHeader.nBitCount == 16 ) ? 0x000003e0UL : 0x0000ff00UL; + nBMask = ( rHeader.nBitCount == 16 ) ? 0x0000001fUL : 0x000000ffUL; + } + } + + if( bRLE ) + { + if ( !rHeader.nSizeImage ) + { + const ULONG nOldPos = rIStm.Tell(); + + rIStm.Seek( STREAM_SEEK_TO_END ); + rHeader.nSizeImage = rIStm.Tell() - nOldPos; + rIStm.Seek( nOldPos ); + } + + BYTE* pBuffer = (BYTE*) rtl_allocateMemory( rHeader.nSizeImage ); + + rIStm.Read( (char*) pBuffer, rHeader.nSizeImage ); + ImplDecodeRLE( pBuffer, rHeader, rAcc, RLE_4 == rHeader.nCompression ); + + rtl_freeMemory( pBuffer ); + } + else + { + const long nWidth = rHeader.nWidth; + const long nHeight = rHeader.nHeight; + BYTE* pBuf = new BYTE[ nAlignedWidth ]; + + // true color DIB's can have a (optimization) palette + if( rHeader.nColsUsed && rHeader.nBitCount > 8 ) + rIStm.SeekRel( rHeader.nColsUsed * ( ( rHeader.nSize != DIBCOREHEADERSIZE ) ? 4 : 3 ) ); + + const long nI = bTopDown ? 1 : -1; + long nY = bTopDown ? 0 : nHeight - 1; + long nCount = nHeight; + + switch( rHeader.nBitCount ) + { + case( 1 ): + { + BYTE* pTmp; + BYTE cTmp; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( pTmp = pBuf, nAlignedWidth ); + cTmp = *pTmp++; + + for( long nX = 0L, nShift = 8L; nX < nWidth; nX++ ) + { + if( !nShift ) + { + nShift = 8L, + cTmp = *pTmp++; + } + + rAcc.SetPixel( nY, nX, sal::static_int_cast<BYTE>(( cTmp >> --nShift ) & 1) ); + } + } + } + break; + + case( 4 ): + { + BYTE* pTmp; + BYTE cTmp; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( pTmp = pBuf, nAlignedWidth ); + cTmp = *pTmp++; + + for( long nX = 0L, nShift = 2L; nX < nWidth; nX++ ) + { + if( !nShift ) + { + nShift = 2UL, + cTmp = *pTmp++; + } + + rAcc.SetPixel( nY, nX, sal::static_int_cast<BYTE>(( cTmp >> ( --nShift << 2UL ) ) & 0x0f) ); + } + } + } + break; + + case( 8 ): + { + BYTE* pTmp; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( pTmp = pBuf, nAlignedWidth ); + + for( long nX = 0L; nX < nWidth; nX++ ) + rAcc.SetPixel( nY, nX, *pTmp++ ); + } + } + break; + + case( 16 ): + { + ColorMask aMask( nRMask, nGMask, nBMask ); + BitmapColor aColor; + UINT16* pTmp16; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( (char*)( pTmp16 = (UINT16*) pBuf ), nAlignedWidth ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aMask.GetColorFor16BitLSB( aColor, (BYTE*) pTmp16++ ); + rAcc.SetPixel( nY, nX, aColor ); + } + } + } + break; + + case( 24 ): + { + BitmapColor aPixelColor; + BYTE* pTmp; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( pTmp = pBuf, nAlignedWidth ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aPixelColor.SetBlue( *pTmp++ ); + aPixelColor.SetGreen( *pTmp++ ); + aPixelColor.SetRed( *pTmp++ ); + rAcc.SetPixel( nY, nX, aPixelColor ); + } + } + } + break; + + case( 32 ): + { + ColorMask aMask( nRMask, nGMask, nBMask ); + BitmapColor aColor; + UINT32* pTmp32; + + for( ; nCount--; nY += nI ) + { + rIStm.Read( (char*)( pTmp32 = (UINT32*) pBuf ), nAlignedWidth ); + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aMask.GetColorFor32Bit( aColor, (BYTE*) pTmp32++ ); + rAcc.SetPixel( nY, nX, aColor ); + } + } + } + } + + delete[] pBuf; + } + } + + return( rIStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::Write( SvStream& rOStm, BOOL bCompressed, BOOL bFileHeader ) const +{ + DBG_ASSERT( mpImpBmp, "Empty Bitmaps can't be saved" ); + + const Size aSizePix( GetSizePixel() ); + BOOL bRet = FALSE; + + if( mpImpBmp && aSizePix.Width() && aSizePix.Height() ) + { + BitmapReadAccess* pAcc = ( (Bitmap*) this)->AcquireReadAccess(); + const USHORT nOldFormat = rOStm.GetNumberFormatInt(); + const ULONG nOldPos = rOStm.Tell(); + + rOStm.SetNumberFormatInt( NUMBERFORMAT_INT_LITTLEENDIAN ); + + if( pAcc ) + { + if( bFileHeader ) + { + if( ImplWriteDIBFileHeader( rOStm, *pAcc ) ) + bRet = ImplWriteDIB( rOStm, *pAcc, bCompressed ); + } + else + bRet = ImplWriteDIB( rOStm, *pAcc, bCompressed ); + + ( (Bitmap*) this)->ReleaseAccess( pAcc ); + } + + if( !bRet ) + { + rOStm.SetError( SVSTREAM_GENERALERROR ); + rOStm.Seek( nOldPos ); + } + + rOStm.SetNumberFormatInt( nOldFormat ); + } + + return bRet; +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplWriteDIB( SvStream& rOStm, BitmapReadAccess& rAcc, BOOL bCompressed ) const +{ + const MapMode aMapPixel( MAP_PIXEL ); + DIBInfoHeader aHeader; + ULONG nImageSizePos; + ULONG nEndPos; + sal_uInt32 nCompression = 0; + BOOL bRet = FALSE; + + aHeader.nSize = DIBINFOHEADERSIZE; + aHeader.nWidth = rAcc.Width(); + aHeader.nHeight = rAcc.Height(); + aHeader.nPlanes = 1; + + if( isBitfieldCompression( rAcc.GetScanlineFormat() ) ) + { + aHeader.nBitCount = ( rAcc.GetScanlineFormat() == BMP_FORMAT_16BIT_TC_LSB_MASK ) ? 16 : 32; + aHeader.nSizeImage = rAcc.Height() * rAcc.GetScanlineSize(); + + nCompression = BITFIELDS; + } + else + { + // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are + // not handled properly below (would have to set color + // masks, and nCompression=BITFIELDS - but color mask is + // not set for formats != *_TC_*). Note that this very + // problem might cause trouble at other places - the + // introduction of 32 bit RGBA bitmaps is relatively + // recent. + // #i59239# discretize bitcount to 1,4,8,24 (other cases + // are not written below) + const UINT16 nBitCount( sal::static_int_cast<UINT16>(rAcc.GetBitCount()) ); + + aHeader.nBitCount = discretizeBitcount( nBitCount ); + aHeader.nSizeImage = rAcc.Height() * + AlignedWidth4Bytes( rAcc.Width()*aHeader.nBitCount ); + + if( bCompressed ) + { + if( 4 == nBitCount ) + nCompression = RLE_4; + else if( 8 == nBitCount ) + nCompression = RLE_8; + } + else + nCompression = COMPRESS_NONE; + } + + if( ( rOStm.GetCompressMode() & COMPRESSMODE_ZBITMAP ) && + ( rOStm.GetVersion() >= SOFFICE_FILEFORMAT_40 ) ) + { + aHeader.nCompression = ZCOMPRESS; + } + else + aHeader.nCompression = nCompression; + + if( maPrefSize.Width() && maPrefSize.Height() && ( maPrefMapMode != aMapPixel ) ) + { + // #i48108# Try to recover xpels/ypels as previously stored on + // disk. The problem with just converting maPrefSize to 100th + // mm and then relating that to the bitmap pixel size is that + // MapMode is integer-based, and suffers from roundoffs, + // especially if maPrefSize is small. Trying to circumvent + // that by performing part of the math in floating point. + const Size aScale100000( + OutputDevice::LogicToLogic( Size(100000L, + 100000L), + MAP_100TH_MM, + maPrefMapMode ) ); + const double fBmpWidthM((double)maPrefSize.Width() / aScale100000.Width() ); + const double fBmpHeightM((double)maPrefSize.Height() / aScale100000.Height() ); + if( fabs(fBmpWidthM) > 0.000000001 && + fabs(fBmpHeightM) > 0.000000001 ) + { + aHeader.nXPelsPerMeter = (UINT32)(rAcc.Width() / fBmpWidthM + .5); + aHeader.nYPelsPerMeter = (UINT32)(rAcc.Height() / fBmpHeightM + .5); + } + } + + aHeader.nColsUsed = ( ( aHeader.nBitCount <= 8 ) ? rAcc.GetPaletteEntryCount() : 0 ); + aHeader.nColsImportant = 0; + + rOStm << aHeader.nSize; + rOStm << aHeader.nWidth; + rOStm << aHeader.nHeight; + rOStm << aHeader.nPlanes; + rOStm << aHeader.nBitCount; + rOStm << aHeader.nCompression; + + nImageSizePos = rOStm.Tell(); + rOStm.SeekRel( sizeof( aHeader.nSizeImage ) ); + + rOStm << aHeader.nXPelsPerMeter; + rOStm << aHeader.nYPelsPerMeter; + rOStm << aHeader.nColsUsed; + rOStm << aHeader.nColsImportant; + + if( aHeader.nCompression == ZCOMPRESS ) + { + ZCodec aCodec; + SvMemoryStream aMemStm( aHeader.nSizeImage + 4096, 65535 ); + ULONG nCodedPos = rOStm.Tell(), nLastPos; + sal_uInt32 nCodedSize, nUncodedSize; + + // write uncoded data palette + if( aHeader.nColsUsed ) + ImplWriteDIBPalette( aMemStm, rAcc ); + + // write uncoded bits + bRet = ImplWriteDIBBits( aMemStm, rAcc, nCompression, aHeader.nSizeImage ); + + // get uncoded size + nUncodedSize = aMemStm.Tell(); + + // seek over compress info + rOStm.SeekRel( 12 ); + + // write compressed data + aCodec.BeginCompression( 3 ); + aCodec.Write( rOStm, (BYTE*) aMemStm.GetData(), nUncodedSize ); + aCodec.EndCompression(); + + // update compress info ( coded size, uncoded size, uncoded compression ) + nCodedSize = ( nLastPos = rOStm.Tell() ) - nCodedPos - 12; + rOStm.Seek( nCodedPos ); + rOStm << nCodedSize << nUncodedSize << nCompression; + rOStm.Seek( nLastPos ); + + if( bRet ) + bRet = ( rOStm.GetError() == ERRCODE_NONE ); + } + else + { + if( aHeader.nColsUsed ) + ImplWriteDIBPalette( rOStm, rAcc ); + + bRet = ImplWriteDIBBits( rOStm, rAcc, aHeader.nCompression, aHeader.nSizeImage ); + } + + nEndPos = rOStm.Tell(); + rOStm.Seek( nImageSizePos ); + rOStm << aHeader.nSizeImage; + rOStm.Seek( nEndPos ); + + return bRet; +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplWriteDIBFileHeader( SvStream& rOStm, BitmapReadAccess& rAcc ) +{ + UINT32 nPalCount = ( rAcc.HasPalette() ? rAcc.GetPaletteEntryCount() : + isBitfieldCompression( rAcc.GetScanlineFormat() ) ? 3UL : 0UL ); + UINT32 nOffset = 14 + DIBINFOHEADERSIZE + nPalCount * 4UL; + + rOStm << (UINT16) 0x4D42; + rOStm << (UINT32) ( nOffset + ( rAcc.Height() * rAcc.GetScanlineSize() ) ); + rOStm << (UINT16) 0; + rOStm << (UINT16) 0; + rOStm << nOffset; + + return( rOStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplWriteDIBPalette( SvStream& rOStm, BitmapReadAccess& rAcc ) +{ + const USHORT nColors = rAcc.GetPaletteEntryCount(); + const ULONG nPalSize = nColors * 4UL; + BYTE* pEntries = new BYTE[ nPalSize ]; + BYTE* pTmpEntry = pEntries; + BitmapColor aPalColor; + + for( USHORT i = 0; i < nColors; i++ ) + { + const BitmapColor& rPalColor = rAcc.GetPaletteColor( i ); + + *pTmpEntry++ = rPalColor.GetBlue(); + *pTmpEntry++ = rPalColor.GetGreen(); + *pTmpEntry++ = rPalColor.GetRed(); + *pTmpEntry++ = 0; + } + + rOStm.Write( pEntries, nPalSize ); + delete[] pEntries; + + return( rOStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplWriteDIBBits( SvStream& rOStm, BitmapReadAccess& rAcc, + ULONG nCompression, sal_uInt32& rImageSize ) +{ + if( BITFIELDS == nCompression ) + { + const ColorMask& rMask = rAcc.GetColorMask(); + SVBT32 aVal32; + + UInt32ToSVBT32( rMask.GetRedMask(), aVal32 ); + rOStm.Write( (BYTE*) aVal32, 4UL ); + + UInt32ToSVBT32( rMask.GetGreenMask(), aVal32 ); + rOStm.Write( (BYTE*) aVal32, 4UL ); + + UInt32ToSVBT32( rMask.GetBlueMask(), aVal32 ); + rOStm.Write( (BYTE*) aVal32, 4UL ); + + rImageSize = rOStm.Tell(); + + if( rAcc.IsBottomUp() ) + rOStm.Write( rAcc.GetBuffer(), rAcc.Height() * rAcc.GetScanlineSize() ); + else + { + for( long nY = rAcc.Height() - 1, nScanlineSize = rAcc.GetScanlineSize(); nY >= 0L; nY-- ) + rOStm.Write( rAcc.GetScanline( nY ), nScanlineSize ); + } + } + else if( ( RLE_4 == nCompression ) || ( RLE_8 == nCompression ) ) + { + rImageSize = rOStm.Tell(); + ImplWriteRLE( rOStm, rAcc, RLE_4 == nCompression ); + } + else if( !nCompression ) + { + // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are not + // handled properly below (would have to set color masks, and + // nCompression=BITFIELDS - but color mask is not set for + // formats != *_TC_*). Note that this very problem might cause + // trouble at other places - the introduction of 32 bit RGBA + // bitmaps is relatively recent. + // #i59239# discretize bitcount for aligned width to 1,4,8,24 + // (other cases are not written below) + const USHORT nBitCount( sal::static_int_cast<USHORT>(rAcc.GetBitCount()) ); + const ULONG nAlignedWidth = AlignedWidth4Bytes( rAcc.Width() * + discretizeBitcount(nBitCount)); + BOOL bNative = FALSE; + + switch( rAcc.GetScanlineFormat() ) + { + case( BMP_FORMAT_1BIT_MSB_PAL ): + case( BMP_FORMAT_4BIT_MSN_PAL ): + case( BMP_FORMAT_8BIT_PAL ): + case( BMP_FORMAT_24BIT_TC_BGR ): + { + if( rAcc.IsBottomUp() && ( rAcc.GetScanlineSize() == nAlignedWidth ) ) + bNative = TRUE; + } + break; + + default: + break; + } + + rImageSize = rOStm.Tell(); + + if( bNative ) + rOStm.Write( rAcc.GetBuffer(), nAlignedWidth * rAcc.Height() ); + else + { + const long nWidth = rAcc.Width(); + const long nHeight = rAcc.Height(); + BYTE* pBuf = new BYTE[ nAlignedWidth ]; + BYTE* pTmp; + BYTE cTmp; + + switch( nBitCount ) + { + case( 1 ): + { + for( long nY = nHeight - 1; nY >= 0L; nY-- ) + { + pTmp = pBuf; + cTmp = 0; + + for( long nX = 0L, nShift = 8L; nX < nWidth; nX++ ) + { + if( !nShift ) + { + nShift = 8L; + *pTmp++ = cTmp; + cTmp = 0; + } + + cTmp |= ( (BYTE) rAcc.GetPixel( nY, nX ) << --nShift ); + } + + *pTmp = cTmp; + rOStm.Write( pBuf, nAlignedWidth ); + } + } + break; + + case( 4 ): + { + for( long nY = nHeight - 1; nY >= 0L; nY-- ) + { + pTmp = pBuf; + cTmp = 0; + + for( long nX = 0L, nShift = 2L; nX < nWidth; nX++ ) + { + if( !nShift ) + { + nShift = 2L; + *pTmp++ = cTmp; + cTmp = 0; + } + + cTmp |= ( (BYTE) rAcc.GetPixel( nY, nX ) << ( --nShift << 2L ) ); + } + *pTmp = cTmp; + rOStm.Write( pBuf, nAlignedWidth ); + } + } + break; + + case( 8 ): + { + for( long nY = nHeight - 1; nY >= 0L; nY-- ) + { + pTmp = pBuf; + + for( long nX = 0L; nX < nWidth; nX++ ) + *pTmp++ = rAcc.GetPixel( nY, nX ); + + rOStm.Write( pBuf, nAlignedWidth ); + } + } + break; + + // #i59239# fallback to 24 bit format, if bitcount is non-default + default: + // FALLTHROUGH intended + case( 24 ): + { + BitmapColor aPixelColor; + + for( long nY = nHeight - 1; nY >= 0L; nY-- ) + { + pTmp = pBuf; + + for( long nX = 0L; nX < nWidth; nX++ ) + { + aPixelColor = rAcc.GetPixel( nY, nX ); + *pTmp++ = aPixelColor.GetBlue(); + *pTmp++ = aPixelColor.GetGreen(); + *pTmp++ = aPixelColor.GetRed(); + } + + rOStm.Write( pBuf, nAlignedWidth ); + } + } + break; + } + + delete[] pBuf; + } + } + + rImageSize = rOStm.Tell() - rImageSize; + + return( rOStm.GetError() == 0UL ); +} + +// ------------------------------------------------------------------ + +void Bitmap::ImplDecodeRLE( BYTE* pBuffer, DIBInfoHeader& rHeader, + BitmapWriteAccess& rAcc, BOOL bRLE4 ) +{ + Scanline pRLE = pBuffer; + long nY = rHeader.nHeight - 1L; + const ULONG nWidth = rAcc.Width(); + ULONG nCountByte; + ULONG nRunByte; + ULONG nX = 0UL; + BYTE cTmp; + BOOL bEndDecoding = FALSE; + + do + { + if( ( nCountByte = *pRLE++ ) == 0 ) + { + nRunByte = *pRLE++; + + if( nRunByte > 2 ) + { + if( bRLE4 ) + { + nCountByte = nRunByte >> 1; + + for( ULONG i = 0UL; i < nCountByte; i++ ) + { + cTmp = *pRLE++; + + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, cTmp >> 4 ); + + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, cTmp & 0x0f ); + } + + if( nRunByte & 1 ) + { + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, *pRLE >> 4 ); + + pRLE++; + } + + if( ( ( nRunByte + 1 ) >> 1 ) & 1 ) + pRLE++; + } + else + { + for( ULONG i = 0UL; i < nRunByte; i++ ) + { + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, *pRLE ); + + pRLE++; + } + + if( nRunByte & 1 ) + pRLE++; + } + } + else if( !nRunByte ) + { + nY--; + nX = 0UL; + } + else if( nRunByte == 1 ) + bEndDecoding = TRUE; + else + { + nX += *pRLE++; + nY -= *pRLE++; + } + } + else + { + cTmp = *pRLE++; + + if( bRLE4 ) + { + nRunByte = nCountByte >> 1; + + for( ULONG i = 0UL; i < nRunByte; i++ ) + { + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, cTmp >> 4 ); + + if( nX < nWidth ) + rAcc.SetPixel( nY, nX++, cTmp & 0x0f ); + } + + if( ( nCountByte & 1 ) && ( nX < nWidth ) ) + rAcc.SetPixel( nY, nX++, cTmp >> 4 ); + } + else + { + for( ULONG i = 0UL; ( i < nCountByte ) && ( nX < nWidth ); i++ ) + rAcc.SetPixel( nY, nX++, cTmp ); + } + } + } + while ( !bEndDecoding && ( nY >= 0L ) ); +} + +// ------------------------------------------------------------------ + +BOOL Bitmap::ImplWriteRLE( SvStream& rOStm, BitmapReadAccess& rAcc, BOOL bRLE4 ) +{ + const ULONG nWidth = rAcc.Width(); + const ULONG nHeight = rAcc.Height(); + ULONG nX; + ULONG nSaveIndex; + ULONG nCount; + ULONG nBufCount; + BYTE* pBuf = new BYTE[ ( nWidth << 1 ) + 2 ]; + BYTE* pTmp; + BYTE cPix; + BYTE cLast; + BOOL bFound; + + for ( long nY = nHeight - 1L; nY >= 0L; nY-- ) + { + pTmp = pBuf; + nX = nBufCount = 0UL; + + while( nX < nWidth ) + { + nCount = 1L; + cPix = rAcc.GetPixel( nY, nX++ ); + + while( ( nX < nWidth ) && ( nCount < 255L ) && ( cPix == rAcc.GetPixel( nY, nX ) ) ) + { + nX++; + nCount++; + } + + if ( nCount > 1 ) + { + *pTmp++ = (BYTE) nCount; + *pTmp++ = ( bRLE4 ? ( ( cPix << 4 ) | cPix ) : cPix ); + nBufCount += 2; + } + else + { + cLast = cPix; + nSaveIndex = nX - 1UL; + bFound = FALSE; + + while( ( nX < nWidth ) && ( nCount < 256L ) && ( cPix = rAcc.GetPixel( nY, nX ) ) != cLast ) + { + nX++; nCount++; + cLast = cPix; + bFound = TRUE; + } + + if ( bFound ) + nX--; + + if ( nCount > 3 ) + { + *pTmp++ = 0; + *pTmp++ = (BYTE) --nCount; + + if( bRLE4 ) + { + for ( ULONG i = 0; i < nCount; i++, pTmp++ ) + { + *pTmp = (BYTE) rAcc.GetPixel( nY, nSaveIndex++ ) << 4; + + if ( ++i < nCount ) + *pTmp |= rAcc.GetPixel( nY, nSaveIndex++ ); + } + + nCount = ( nCount + 1 ) >> 1; + } + else + { + for( ULONG i = 0UL; i < nCount; i++ ) + *pTmp++ = rAcc.GetPixel( nY, nSaveIndex++ ); + } + + if ( nCount & 1 ) + { + *pTmp++ = 0; + nBufCount += ( nCount + 3 ); + } + else + nBufCount += ( nCount + 2 ); + } + else + { + *pTmp++ = 1; + *pTmp++ = (BYTE) rAcc.GetPixel( nY, nSaveIndex ) << ( bRLE4 ? 4 : 0 ); + + if ( nCount == 3 ) + { + *pTmp++ = 1; + *pTmp++ = (BYTE) rAcc.GetPixel( nY, ++nSaveIndex ) << ( bRLE4 ? 4 : 0 ); + nBufCount += 4; + } + else + nBufCount += 2; + } + } + } + + pBuf[ nBufCount++ ] = 0; + pBuf[ nBufCount++ ] = 0; + + rOStm.Write( pBuf, nBufCount ); + } + + rOStm << (BYTE) 0; + rOStm << (BYTE) 1; + + delete[] pBuf; + + return( rOStm.GetError() == 0UL ); +} |