| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| #include <vcl/salbtype.hxx> |
| #include <vcl/dibtools.hxx> |
| #include <tools/zcodec.hxx> |
| #include <tools/stream.hxx> |
| #include <vcl/bitmapex.hxx> |
| #include <vcl/bmpacc.hxx> |
| #include <vcl/outdev.hxx> |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // - Defines - |
| |
| #define DIBCOREHEADERSIZE ( 12UL ) |
| #define DIBINFOHEADERSIZE ( sizeof(DIBInfoHeader) ) |
| #define DIBV5HEADERSIZE ( sizeof(DIBV5Header) ) |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // - 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 and DIBV5Header |
| |
| typedef sal_Int32 FXPT2DOT30; |
| |
| struct CIEXYZ |
| { |
| FXPT2DOT30 aXyzX; |
| FXPT2DOT30 aXyzY; |
| FXPT2DOT30 aXyzZ; |
| |
| CIEXYZ() |
| : aXyzX(0L), |
| aXyzY(0L), |
| aXyzZ(0L) |
| {} |
| |
| ~CIEXYZ() |
| {} |
| }; |
| |
| struct CIEXYZTriple |
| { |
| CIEXYZ aXyzRed; |
| CIEXYZ aXyzGreen; |
| CIEXYZ aXyzBlue; |
| |
| CIEXYZTriple() |
| : aXyzRed(), |
| aXyzGreen(), |
| aXyzBlue() |
| {} |
| |
| ~CIEXYZTriple() |
| {} |
| }; |
| |
| 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() |
| {} |
| }; |
| |
| struct DIBV5Header : public DIBInfoHeader |
| { |
| sal_uInt32 nV5RedMask; |
| sal_uInt32 nV5GreenMask; |
| sal_uInt32 nV5BlueMask; |
| sal_uInt32 nV5AlphaMask; |
| sal_uInt32 nV5CSType; |
| CIEXYZTriple aV5Endpoints; |
| sal_uInt32 nV5GammaRed; |
| sal_uInt32 nV5GammaGreen; |
| sal_uInt32 nV5GammaBlue; |
| sal_uInt32 nV5Intent; |
| sal_uInt32 nV5ProfileData; |
| sal_uInt32 nV5ProfileSize; |
| sal_uInt32 nV5Reserved; |
| |
| DIBV5Header() |
| : DIBInfoHeader(), |
| nV5RedMask(0UL), |
| nV5GreenMask(0UL), |
| nV5BlueMask(0UL), |
| nV5AlphaMask(0UL), |
| nV5CSType(0UL), |
| aV5Endpoints(), |
| nV5GammaRed(0UL), |
| nV5GammaGreen(0UL), |
| nV5GammaBlue(0UL), |
| nV5Intent(0UL), |
| nV5ProfileData(0UL), |
| nV5ProfileSize(0UL), |
| nV5Reserved(0UL) |
| {} |
| |
| ~DIBV5Header() |
| {} |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| namespace |
| { |
| inline sal_uInt16 discretizeBitcount( sal_uInt16 nInputCount ) |
| { |
| return ( nInputCount <= 1 ) ? 1 : |
| ( nInputCount <= 4 ) ? 4 : |
| ( nInputCount <= 8 ) ? 8 : 24; |
| } |
| |
| inline bool isBitfieldCompression( sal_uLong nScanlineFormat ) |
| { |
| return (BMP_FORMAT_16BIT_TC_LSB_MASK == nScanlineFormat) || (BMP_FORMAT_32BIT_TC_MASK == nScanlineFormat); |
| } |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| bool ImplReadDIBInfoHeader(SvStream& rIStm, DIBV5Header& rHeader, bool& bTopDown) |
| { |
| // BITMAPINFOHEADER or BITMAPCOREHEADER or BITMAPV5HEADER |
| const sal_Size aStartPos(rIStm.Tell()); |
| 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 |
| { |
| // BITMAPCOREHEADER, BITMAPV5HEADER or unknown. Read as far as possible |
| sal_Size nUsed(sizeof(rHeader.nSize)); |
| |
| // read DIBInfoHeader entries |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nWidth; nUsed += sizeof(rHeader.nWidth); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nHeight; nUsed += sizeof(rHeader.nHeight); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nPlanes; nUsed += sizeof(rHeader.nPlanes); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nBitCount; nUsed += sizeof(rHeader.nBitCount); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nCompression; nUsed += sizeof(rHeader.nCompression); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nSizeImage; nUsed += sizeof(rHeader.nSizeImage); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nXPelsPerMeter; nUsed += sizeof(rHeader.nXPelsPerMeter); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nYPelsPerMeter; nUsed += sizeof(rHeader.nYPelsPerMeter); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nColsUsed; nUsed += sizeof(rHeader.nColsUsed); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nColsImportant; nUsed += sizeof(rHeader.nColsImportant); } |
| |
| // read DIBV5HEADER members |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5RedMask; nUsed += sizeof(rHeader.nV5RedMask); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5GreenMask; nUsed += sizeof(rHeader.nV5GreenMask); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5BlueMask; nUsed += sizeof(rHeader.nV5BlueMask); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5AlphaMask; nUsed += sizeof(rHeader.nV5AlphaMask); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5CSType; nUsed += sizeof(rHeader.nV5CSType); } |
| |
| // read contained CIEXYZTriple's |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzRed.aXyzX; nUsed += sizeof(rHeader.aV5Endpoints.aXyzRed.aXyzX); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzRed.aXyzY; nUsed += sizeof(rHeader.aV5Endpoints.aXyzRed.aXyzY); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzRed.aXyzZ; nUsed += sizeof(rHeader.aV5Endpoints.aXyzRed.aXyzZ); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzGreen.aXyzX; nUsed += sizeof(rHeader.aV5Endpoints.aXyzGreen.aXyzX); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzGreen.aXyzY; nUsed += sizeof(rHeader.aV5Endpoints.aXyzGreen.aXyzY); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzGreen.aXyzZ; nUsed += sizeof(rHeader.aV5Endpoints.aXyzGreen.aXyzZ); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzBlue.aXyzX; nUsed += sizeof(rHeader.aV5Endpoints.aXyzBlue.aXyzX); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzBlue.aXyzY; nUsed += sizeof(rHeader.aV5Endpoints.aXyzBlue.aXyzY); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.aV5Endpoints.aXyzBlue.aXyzZ; nUsed += sizeof(rHeader.aV5Endpoints.aXyzBlue.aXyzZ); } |
| |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5GammaRed; nUsed += sizeof(rHeader.nV5GammaRed); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5GammaGreen; nUsed += sizeof(rHeader.nV5GammaGreen); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5GammaBlue; nUsed += sizeof(rHeader.nV5GammaBlue); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5Intent; nUsed += sizeof(rHeader.nV5Intent); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5ProfileData; nUsed += sizeof(rHeader.nV5ProfileData); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5ProfileSize; nUsed += sizeof(rHeader.nV5ProfileSize); } |
| if(nUsed < rHeader.nSize) { rIStm >> rHeader.nV5Reserved; nUsed += sizeof(rHeader.nV5Reserved); } |
| |
| // seek to EndPos |
| rIStm.Seek(aStartPos + rHeader.nSize); |
| } |
| |
| if ( rHeader.nHeight < 0 ) |
| { |
| bTopDown = true; |
| rHeader.nHeight *= -1; |
| } |
| else |
| { |
| bTopDown = 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 ImplReadDIBPalette( SvStream& rIStm, BitmapWriteAccess& rAcc, bool bQuad ) |
| { |
| const sal_uInt16 nColors = rAcc.GetPaletteEntryCount(); |
| const sal_uLong nPalSize = nColors * ( bQuad ? 4UL : 3UL ); |
| BitmapColor aPalColor; |
| |
| sal_uInt8* pEntries = new sal_uInt8[ nPalSize ]; |
| rIStm.Read( pEntries, nPalSize ); |
| |
| sal_uInt8* pTmpEntry = pEntries; |
| for( sal_uInt16 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 ); |
| } |
| |
| void ImplDecodeRLE( sal_uInt8* pBuffer, DIBV5Header& rHeader, BitmapWriteAccess& rAcc, bool bRLE4 ) |
| { |
| Scanline pRLE = pBuffer; |
| long nY = rHeader.nHeight - 1L; |
| const sal_uLong nWidth = rAcc.Width(); |
| sal_uLong nCountByte; |
| sal_uLong nRunByte; |
| sal_uLong nX = 0UL; |
| sal_uInt8 cTmp; |
| bool bEndDecoding = false; |
| |
| do |
| { |
| if( ( nCountByte = *pRLE++ ) == 0 ) |
| { |
| nRunByte = *pRLE++; |
| |
| if( nRunByte > 2 ) |
| { |
| if( bRLE4 ) |
| { |
| nCountByte = nRunByte >> 1; |
| |
| for( sal_uLong i = 0UL; i < nCountByte; i++ ) |
| { |
| cTmp = *pRLE++; |
| |
| if( nX < nWidth ) |
| rAcc.SetPixelIndex( nY, nX++, cTmp >> 4 ); |
| |
| if( nX < nWidth ) |
| rAcc.SetPixelIndex( nY, nX++, cTmp & 0x0f ); |
| } |
| |
| if( nRunByte & 1 ) |
| { |
| if( nX < nWidth ) |
| rAcc.SetPixelIndex( nY, nX++, *pRLE >> 4 ); |
| |
| pRLE++; |
| } |
| |
| if( ( ( nRunByte + 1 ) >> 1 ) & 1 ) |
| pRLE++; |
| } |
| else |
| { |
| for( sal_uLong i = 0UL; i < nRunByte; i++ ) |
| { |
| if( nX < nWidth ) |
| rAcc.SetPixelIndex( 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( sal_uLong i = 0UL; i < nRunByte; i++ ) |
| { |
| if( nX < nWidth ) |
| rAcc.SetPixelIndex( nY, nX++, cTmp >> 4 ); |
| |
| if( nX < nWidth ) |
| rAcc.SetPixelIndex( nY, nX++, cTmp & 0x0f ); |
| } |
| |
| if( ( nCountByte & 1 ) && ( nX < nWidth ) ) |
| rAcc.SetPixelIndex( nY, nX++, cTmp >> 4 ); |
| } |
| else |
| { |
| for( sal_uLong i = 0UL; ( i < nCountByte ) && ( nX < nWidth ); i++ ) |
| rAcc.SetPixelIndex( nY, nX++, cTmp ); |
| } |
| } |
| } |
| while ( !bEndDecoding && ( nY >= 0L ) ); |
| } |
| |
| bool ImplReadDIBBits(SvStream& rIStm, DIBV5Header& rHeader, BitmapWriteAccess& rAcc, BitmapWriteAccess* pAccAlpha, bool bTopDown, bool& rAlphaUsed) |
| { |
| const sal_Int64 nBitsPerLine (static_cast<sal_Int64>(rHeader.nWidth) * static_cast<sal_Int64>(rHeader.nBitCount)); |
| if (nBitsPerLine > SAL_MAX_UINT32) |
| return false; |
| |
| const sal_uLong nAlignedWidth = AlignedWidth4Bytes(static_cast<sal_uLong>(nBitsPerLine)); |
| sal_uInt32 nRMask(( rHeader.nBitCount == 16 ) ? 0x00007c00UL : 0x00ff0000UL); |
| sal_uInt32 nGMask(( rHeader.nBitCount == 16 ) ? 0x000003e0UL : 0x0000ff00UL); |
| sal_uInt32 nBMask(( rHeader.nBitCount == 16 ) ? 0x0000001fUL : 0x000000ffUL); |
| bool bNative(false); |
| bool bTCMask(!pAccAlpha && ((16 == rHeader.nBitCount) || (32 == rHeader.nBitCount))); |
| bool bRLE((RLE_8 == rHeader.nCompression && 8 == rHeader.nBitCount) || (RLE_4 == rHeader.nCompression && 4 == rHeader.nBitCount)); |
| |
| // 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 = ( ( static_cast< bool >(rAcc.IsBottomUp()) != bTopDown ) && !bRLE && !bTCMask && ( rAcc.GetScanlineSize() == nAlignedWidth ) ); |
| break; |
| } |
| |
| default: |
| { |
| break; |
| } |
| } |
| |
| // Read data |
| if(bNative) |
| { |
| rIStm.Read(rAcc.GetBuffer(), rHeader.nHeight * nAlignedWidth); |
| } |
| else |
| { |
| // Read color mask |
| if(bTCMask && BITFIELDS == rHeader.nCompression) |
| { |
| rIStm.SeekRel( -12L ); |
| rIStm >> nRMask; |
| rIStm >> nGMask; |
| rIStm >> nBMask; |
| } |
| |
| if(bRLE) |
| { |
| if(!rHeader.nSizeImage) |
| { |
| const sal_uLong nOldPos(rIStm.Tell()); |
| |
| rIStm.Seek(STREAM_SEEK_TO_END); |
| rHeader.nSizeImage = rIStm.Tell() - nOldPos; |
| rIStm.Seek(nOldPos); |
| } |
| |
| sal_uInt8* pBuffer = (sal_uInt8*)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); |
| sal_uInt8* pBuf = new sal_uInt8[nAlignedWidth]; |
| |
| const long nI(bTopDown ? 1 : -1); |
| long nY(bTopDown ? 0 : nHeight - 1); |
| long nCount(nHeight); |
| |
| switch(rHeader.nBitCount) |
| { |
| case( 1 ): |
| { |
| sal_uInt8* pTmp; |
| sal_uInt8 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.SetPixelIndex( nY, nX, (cTmp >> --nShift) & 1); |
| } |
| } |
| } |
| break; |
| |
| case( 4 ): |
| { |
| sal_uInt8* pTmp; |
| sal_uInt8 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.SetPixelIndex( nY, nX, (cTmp >> ( --nShift << 2UL ) ) & 0x0f); |
| } |
| } |
| } |
| break; |
| |
| case( 8 ): |
| { |
| sal_uInt8* pTmp; |
| |
| for( ; nCount--; nY += nI ) |
| { |
| rIStm.Read( pTmp = pBuf, nAlignedWidth ); |
| |
| for( long nX = 0L; nX < nWidth; nX++ ) |
| rAcc.SetPixelIndex( nY, nX, *pTmp++ ); |
| } |
| } |
| break; |
| |
| case( 16 ): |
| { |
| ColorMask aMask( nRMask, nGMask, nBMask ); |
| BitmapColor aColor; |
| sal_uInt16* pTmp16; |
| |
| for( ; nCount--; nY += nI ) |
| { |
| rIStm.Read( (char*)( pTmp16 = (sal_uInt16*) pBuf ), nAlignedWidth ); |
| |
| for( long nX = 0L; nX < nWidth; nX++ ) |
| { |
| aMask.GetColorFor16BitLSB( aColor, (sal_uInt8*) pTmp16++ ); |
| rAcc.SetPixel( nY, nX, aColor ); |
| } |
| } |
| } |
| break; |
| |
| case( 24 ): |
| { |
| BitmapColor aPixelColor; |
| sal_uInt8* 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; |
| sal_uInt32* pTmp32; |
| |
| if(pAccAlpha) |
| { |
| sal_uInt8 aAlpha; |
| |
| for( ; nCount--; nY += nI ) |
| { |
| rIStm.Read( (char*)( pTmp32 = (sal_uInt32*) pBuf ), nAlignedWidth ); |
| |
| for( long nX = 0L; nX < nWidth; nX++ ) |
| { |
| aMask.GetColorAndAlphaFor32Bit( aColor, aAlpha, (sal_uInt8*) pTmp32++ ); |
| rAcc.SetPixel( nY, nX, aColor ); |
| pAccAlpha->SetPixelIndex(nY, nX, sal_uInt8(0xff) - aAlpha); |
| rAlphaUsed |= bool(0xff != aAlpha); |
| } |
| } |
| } |
| else |
| { |
| for( ; nCount--; nY += nI ) |
| { |
| rIStm.Read( (char*)( pTmp32 = (sal_uInt32*) pBuf ), nAlignedWidth ); |
| |
| for( long nX = 0L; nX < nWidth; nX++ ) |
| { |
| aMask.GetColorFor32Bit( aColor, (sal_uInt8*) pTmp32++ ); |
| rAcc.SetPixel( nY, nX, aColor ); |
| } |
| } |
| } |
| } |
| } |
| |
| delete[] pBuf; |
| } |
| } |
| |
| return( rIStm.GetError() == 0UL ); |
| } |
| |
| bool ImplReadDIBBody( SvStream& rIStm, Bitmap& rBmp, Bitmap* pBmpAlpha, sal_uLong nOffset ) |
| { |
| DIBV5Header aHeader; |
| const sal_uLong nStmPos = rIStm.Tell(); |
| bool bRet( false ); |
| bool bTopDown( false ); |
| |
| if ( ImplReadDIBInfoHeader( rIStm, aHeader, bTopDown ) |
| && aHeader.nWidth != 0 |
| && aHeader.nHeight != 0 |
| && aHeader.nBitCount != 0 ) |
| { |
| if ( nOffset > 0 && aHeader.nSize > nOffset ) |
| { |
| // Header size claims to extend into the image data. |
| // Looks like an error. |
| return false; |
| } |
| |
| const sal_uInt16 nBitCount(discretizeBitcount(aHeader.nBitCount)); |
| const Size aSizePixel(aHeader.nWidth, aHeader.nHeight); |
| BitmapPalette aDummyPal; |
| Bitmap aNewBmp(aSizePixel, nBitCount, &aDummyPal); |
| Bitmap aNewBmpAlpha; |
| BitmapWriteAccess* pAcc = aNewBmp.AcquireWriteAccess(); |
| BitmapWriteAccess* pAccAlpha = 0; |
| bool bAlphaPossible(pBmpAlpha && aHeader.nBitCount == 32); |
| |
| if(bAlphaPossible) |
| { |
| const bool bRedSet(0 != aHeader.nV5RedMask); |
| const bool bGreenSet(0 != aHeader.nV5GreenMask); |
| const bool bBlueSet(0 != aHeader.nV5BlueMask); |
| |
| // some clipboard entries have alpha mask on zero to say that there is |
| // no alpha; do only use this when the other masks are set. The MS docu |
| // says that that masks are only to be set when bV5Compression is set to |
| // BI_BITFIELDS, but there seem to exist a wild variety of usages... |
| if((bRedSet || bGreenSet || bBlueSet) && (0 == aHeader.nV5AlphaMask)) |
| { |
| bAlphaPossible = false; |
| } |
| } |
| |
| if(bAlphaPossible) |
| { |
| aNewBmpAlpha = Bitmap(aSizePixel, 8); |
| pAccAlpha = aNewBmpAlpha.AcquireWriteAccess(); |
| } |
| |
| if(pAcc) |
| { |
| sal_uInt16 nColors(0); |
| SvStream* pIStm; |
| SvMemoryStream* pMemStm = NULL; |
| sal_uInt8* pData = NULL; |
| |
| if(nBitCount <= 8) |
| { |
| if(aHeader.nColsUsed) |
| { |
| nColors = (sal_uInt16)aHeader.nColsUsed; |
| } |
| else |
| { |
| nColors = ( 1 << aHeader.nBitCount ); |
| } |
| } |
| |
| if(ZCOMPRESS == aHeader.nCompression) |
| { |
| ZCodec aCodec; |
| sal_uInt32 nCodedSize(0); |
| sal_uInt32 nUncodedSize(0); |
| sal_uLong nCodedPos(0); |
| |
| // read coding information |
| rIStm >> nCodedSize >> nUncodedSize >> aHeader.nCompression; |
| pData = (sal_uInt8*) 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 |
| bool bAlphaUsed(false); |
| |
| if(!pIStm->GetError()) |
| { |
| if(nOffset) |
| { |
| pIStm->SeekRel(nOffset - (pIStm->Tell() - nStmPos)); |
| } |
| |
| bRet = ImplReadDIBBits(*pIStm, aHeader, *pAcc, pAccAlpha, bTopDown, bAlphaUsed); |
| |
| 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(bAlphaPossible) |
| { |
| aNewBmpAlpha.ReleaseAccess(pAccAlpha); |
| |
| if(!bAlphaUsed) |
| { |
| bAlphaPossible = false; |
| } |
| } |
| |
| if(bRet) |
| { |
| rBmp = aNewBmp; |
| |
| if(bAlphaPossible) |
| { |
| *pBmpAlpha = aNewBmpAlpha; |
| } |
| } |
| } |
| } |
| |
| return bRet; |
| } |
| |
| bool ImplReadDIBFileHeader( SvStream& rIStm, sal_uLong& rOffset ) |
| { |
| bool bRet = false; |
| |
| const sal_Int64 nSavedStreamPos( rIStm.Tell() ); |
| const sal_Int64 nStreamLength( rIStm.Seek( STREAM_SEEK_TO_END ) ); |
| rIStm.Seek( nSavedStreamPos ); |
| |
| sal_uInt16 nTmp16 = 0; |
| rIStm >> nTmp16; |
| |
| if ( ( 0x4D42 == nTmp16 ) || ( 0x4142 == nTmp16 ) ) |
| { |
| sal_uInt32 nTmp32; |
| if ( 0x4142 == nTmp16 ) |
| { |
| rIStm.SeekRel( 12L ); |
| rIStm >> nTmp16; |
| rIStm.SeekRel( 8L ); |
| rIStm >> nTmp32; |
| rOffset = nTmp32 - 28UL; |
| bRet = ( 0x4D42 == nTmp16 ); |
| } |
| else // 0x4D42 == nTmp16, 'MB' from BITMAPFILEHEADER |
| { |
| rIStm.SeekRel( 8L ); // we are on bfSize member of BITMAPFILEHEADER, forward to bfOffBits |
| rIStm >> nTmp32; // read bfOffBits |
| rOffset = nTmp32 - 14UL; // adapt offset by sizeof(BITMAPFILEHEADER) |
| bRet = ( rIStm.GetError() == 0UL ); |
| } |
| |
| if ( rOffset >= nStreamLength ) |
| { |
| // Offset claims that image starts past the end of the |
| // stream. Unlikely. |
| rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); |
| bRet = false; |
| } |
| } |
| else |
| rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR ); |
| |
| return bRet; |
| } |
| |
| bool ImplWriteDIBPalette( SvStream& rOStm, BitmapReadAccess& rAcc ) |
| { |
| const sal_uInt16 nColors = rAcc.GetPaletteEntryCount(); |
| const sal_uLong nPalSize = nColors * 4UL; |
| sal_uInt8* pEntries = new sal_uInt8[ nPalSize ]; |
| sal_uInt8* pTmpEntry = pEntries; |
| BitmapColor aPalColor; |
| |
| for( sal_uInt16 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 ImplWriteRLE( SvStream& rOStm, BitmapReadAccess& rAcc, bool bRLE4 ) |
| { |
| const sal_uLong nWidth = rAcc.Width(); |
| const sal_uLong nHeight = rAcc.Height(); |
| sal_uLong nX; |
| sal_uLong nSaveIndex; |
| sal_uLong nCount; |
| sal_uLong nBufCount; |
| sal_uInt8* pBuf = new sal_uInt8[ ( nWidth << 1 ) + 2 ]; |
| sal_uInt8* pTmp; |
| sal_uInt8 cPix; |
| sal_uInt8 cLast; |
| bool bFound; |
| |
| for ( long nY = nHeight - 1L; nY >= 0L; nY-- ) |
| { |
| pTmp = pBuf; |
| nX = nBufCount = 0UL; |
| |
| while( nX < nWidth ) |
| { |
| nCount = 1L; |
| cPix = rAcc.GetPixelIndex( nY, nX++ ); |
| |
| while( ( nX < nWidth ) && ( nCount < 255L ) |
| && ( cPix == rAcc.GetPixelIndex( nY, nX ) ) ) |
| { |
| nX++; |
| nCount++; |
| } |
| |
| if ( nCount > 1 ) |
| { |
| *pTmp++ = (sal_uInt8) nCount; |
| *pTmp++ = ( bRLE4 ? ( ( cPix << 4 ) | cPix ) : cPix ); |
| nBufCount += 2; |
| } |
| else |
| { |
| cLast = cPix; |
| nSaveIndex = nX - 1UL; |
| bFound = false; |
| |
| while( ( nX < nWidth ) && ( nCount < 256L ) |
| && ( cPix = rAcc.GetPixelIndex( nY, nX ) ) != cLast ) |
| { |
| nX++; nCount++; |
| cLast = cPix; |
| bFound = true; |
| } |
| |
| if ( bFound ) |
| nX--; |
| |
| if ( nCount > 3 ) |
| { |
| *pTmp++ = 0; |
| *pTmp++ = (sal_uInt8) --nCount; |
| |
| if( bRLE4 ) |
| { |
| for ( sal_uLong i = 0; i < nCount; i++, pTmp++ ) |
| { |
| *pTmp = rAcc.GetPixelIndex( nY, nSaveIndex++ ) << 4; |
| |
| if ( ++i < nCount ) |
| *pTmp |= rAcc.GetPixelIndex( nY, nSaveIndex++ ); |
| } |
| |
| nCount = ( nCount + 1 ) >> 1; |
| } |
| else |
| { |
| for( sal_uLong i = 0UL; i < nCount; i++ ) |
| *pTmp++ = rAcc.GetPixelIndex( nY, nSaveIndex++ ); |
| } |
| |
| if ( nCount & 1 ) |
| { |
| *pTmp++ = 0; |
| nBufCount += ( nCount + 3 ); |
| } |
| else |
| nBufCount += ( nCount + 2 ); |
| } |
| else |
| { |
| *pTmp++ = 1; |
| *pTmp++ = rAcc.GetPixelIndex( nY, nSaveIndex ) << (bRLE4 ? 4 : 0); |
| |
| if ( nCount == 3 ) |
| { |
| *pTmp++ = 1; |
| *pTmp++ = rAcc.GetPixelIndex( nY, ++nSaveIndex ) << ( bRLE4 ? 4 : 0 ); |
| nBufCount += 4; |
| } |
| else |
| nBufCount += 2; |
| } |
| } |
| } |
| |
| pBuf[ nBufCount++ ] = 0; |
| pBuf[ nBufCount++ ] = 0; |
| |
| rOStm.Write( pBuf, nBufCount ); |
| } |
| |
| rOStm << (sal_uInt8) 0; |
| rOStm << (sal_uInt8) 1; |
| |
| delete[] pBuf; |
| |
| return( rOStm.GetError() == 0UL ); |
| } |
| |
| bool ImplWriteDIBBits(SvStream& rOStm, BitmapReadAccess& rAcc, BitmapReadAccess* pAccAlpha, sal_uLong nCompression, sal_uInt32& rImageSize) |
| { |
| if(!pAccAlpha && BITFIELDS == nCompression) |
| { |
| const ColorMask& rMask = rAcc.GetColorMask(); |
| SVBT32 aVal32; |
| |
| UInt32ToSVBT32( rMask.GetRedMask(), aVal32 ); |
| rOStm.Write( (sal_uInt8*) aVal32, 4UL ); |
| |
| UInt32ToSVBT32( rMask.GetGreenMask(), aVal32 ); |
| rOStm.Write( (sal_uInt8*) aVal32, 4UL ); |
| |
| UInt32ToSVBT32( rMask.GetBlueMask(), aVal32 ); |
| rOStm.Write( (sal_uInt8*) 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(!pAccAlpha && ((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 sal_uInt16 nBitCount(pAccAlpha ? 32 : discretizeBitcount(static_cast< sal_uInt16 >(rAcc.GetBitCount()))); |
| const sal_uLong nAlignedWidth(AlignedWidth4Bytes(rAcc.Width() * 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(!pAccAlpha && 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()); |
| sal_uInt8* pBuf = new sal_uInt8[ nAlignedWidth ]; |
| sal_uInt8* pTmp(0); |
| sal_uInt8 cTmp(0); |
| |
| 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 |= rAcc.GetPixelIndex( 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 |= rAcc.GetPixelIndex( 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.GetPixelIndex( 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; |
| const bool bWriteAlpha(32 == nBitCount && pAccAlpha); |
| |
| for( long nY = nHeight - 1; nY >= 0L; nY-- ) |
| { |
| pTmp = pBuf; |
| |
| for( long nX = 0L; nX < nWidth; nX++ ) |
| { |
| // when alpha is used, this may be non-24bit main bitmap, so use GetColor |
| // instead of GetPixel to ensure RGB value |
| aPixelColor = rAcc.GetColor( nY, nX ); |
| |
| *pTmp++ = aPixelColor.GetBlue(); |
| *pTmp++ = aPixelColor.GetGreen(); |
| *pTmp++ = aPixelColor.GetRed(); |
| |
| if(bWriteAlpha) |
| { |
| if(pAccAlpha) |
| { |
| *pTmp++ = (sal_uInt8)0xff - (sal_uInt8)pAccAlpha->GetPixelIndex( nY, nX ); |
| } |
| else |
| { |
| *pTmp++ = (sal_uInt8)0xff; |
| } |
| } |
| } |
| |
| rOStm.Write( pBuf, nAlignedWidth ); |
| } |
| } |
| break; |
| } |
| |
| delete[] pBuf; |
| } |
| } |
| |
| rImageSize = rOStm.Tell() - rImageSize; |
| |
| return (!rOStm.GetError()); |
| } |
| |
| bool ImplWriteDIBBody(const Bitmap& rBitmap, SvStream& rOStm, BitmapReadAccess& rAcc, BitmapReadAccess* pAccAlpha, bool bCompressed) |
| { |
| const MapMode aMapPixel(MAP_PIXEL); |
| DIBV5Header aHeader; |
| sal_uLong nImageSizePos(0); |
| sal_uLong nEndPos(0); |
| sal_uInt32 nCompression(COMPRESS_NONE); |
| bool bRet(false); |
| |
| aHeader.nSize = pAccAlpha ? DIBV5HEADERSIZE : DIBINFOHEADERSIZE; // size dependent on CF_DIB type to use |
| aHeader.nWidth = rAcc.Width(); |
| aHeader.nHeight = rAcc.Height(); |
| aHeader.nPlanes = 1; |
| |
| if(!pAccAlpha && isBitfieldCompression(rAcc.GetScanlineFormat())) |
| { |
| aHeader.nBitCount = (BMP_FORMAT_16BIT_TC_LSB_MASK == rAcc.GetScanlineFormat()) ? 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 sal_uInt16 nBitCount(pAccAlpha ? 32 : discretizeBitcount(static_cast< sal_uInt16 >(rAcc.GetBitCount()))); |
| aHeader.nBitCount = 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; |
| } |
| } |
| } |
| |
| if((rOStm.GetCompressMode() & COMPRESSMODE_ZBITMAP) && (rOStm.GetVersion() >= SOFFICE_FILEFORMAT_40)) |
| { |
| aHeader.nCompression = ZCOMPRESS; |
| } |
| else |
| { |
| aHeader.nCompression = nCompression; |
| } |
| |
| if(rBitmap.GetPrefSize().Width() && rBitmap.GetPrefSize().Height() && (rBitmap.GetPrefMapMode() != 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, rBitmap.GetPrefMapMode())); |
| const double fBmpWidthM((double)rBitmap.GetPrefSize().Width() / aScale100000.Width()); |
| const double fBmpHeightM((double)rBitmap.GetPrefSize().Height() / aScale100000.Height()); |
| |
| if(!basegfx::fTools::equalZero(fBmpWidthM) && !basegfx::fTools::equalZero(fBmpHeightM)) |
| { |
| aHeader.nXPelsPerMeter = basegfx::fround(rAcc.Width() / fabs(fBmpWidthM)); |
| aHeader.nYPelsPerMeter = basegfx::fround(rAcc.Height() / fabs(fBmpHeightM)); |
| } |
| } |
| |
| aHeader.nColsUsed = ((!pAccAlpha && 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(pAccAlpha) // only write DIBV5 when asked to do so |
| { |
| aHeader.nV5CSType = 0x57696E20; // LCS_WINDOWS_COLOR_SPACE |
| aHeader.nV5Intent = 0x00000004; // LCS_GM_IMAGES |
| |
| rOStm << aHeader.nV5RedMask; |
| rOStm << aHeader.nV5GreenMask; |
| rOStm << aHeader.nV5BlueMask; |
| rOStm << aHeader.nV5AlphaMask; |
| rOStm << aHeader.nV5CSType; |
| |
| rOStm << aHeader.aV5Endpoints.aXyzRed.aXyzX; |
| rOStm << aHeader.aV5Endpoints.aXyzRed.aXyzY; |
| rOStm << aHeader.aV5Endpoints.aXyzRed.aXyzZ; |
| rOStm << aHeader.aV5Endpoints.aXyzGreen.aXyzX; |
| rOStm << aHeader.aV5Endpoints.aXyzGreen.aXyzY; |
| rOStm << aHeader.aV5Endpoints.aXyzGreen.aXyzZ; |
| rOStm << aHeader.aV5Endpoints.aXyzBlue.aXyzX; |
| rOStm << aHeader.aV5Endpoints.aXyzBlue.aXyzY; |
| rOStm << aHeader.aV5Endpoints.aXyzBlue.aXyzZ; |
| |
| rOStm << aHeader.nV5GammaRed; |
| rOStm << aHeader.nV5GammaGreen; |
| rOStm << aHeader.nV5GammaBlue; |
| rOStm << aHeader.nV5Intent; |
| rOStm << aHeader.nV5ProfileData; |
| rOStm << aHeader.nV5ProfileSize; |
| rOStm << aHeader.nV5Reserved; |
| } |
| |
| if(ZCOMPRESS == aHeader.nCompression) |
| { |
| ZCodec aCodec; |
| SvMemoryStream aMemStm(aHeader.nSizeImage + 4096, 65535); |
| sal_uLong nCodedPos(rOStm.Tell()); |
| sal_uLong nLastPos(0); |
| sal_uInt32 nCodedSize(0); |
| sal_uInt32 nUncodedSize(0); |
| |
| // write uncoded data palette |
| if(aHeader.nColsUsed) |
| { |
| ImplWriteDIBPalette(aMemStm, rAcc); |
| } |
| |
| // write uncoded bits |
| bRet = ImplWriteDIBBits(aMemStm, rAcc, pAccAlpha, 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, (sal_uInt8*)aMemStm.GetData(), nUncodedSize); |
| aCodec.EndCompression(); |
| |
| // update compress info ( coded size, uncoded size, uncoded compression ) |
| nLastPos = rOStm.Tell(); |
| nCodedSize = nLastPos - nCodedPos - 12; |
| rOStm.Seek(nCodedPos); |
| rOStm << nCodedSize << nUncodedSize << nCompression; |
| rOStm.Seek(nLastPos); |
| |
| if(bRet) |
| { |
| bRet = (ERRCODE_NONE == rOStm.GetError()); |
| } |
| } |
| else |
| { |
| if(aHeader.nColsUsed) |
| { |
| ImplWriteDIBPalette(rOStm, rAcc); |
| } |
| |
| bRet = ImplWriteDIBBits(rOStm, rAcc, pAccAlpha, aHeader.nCompression, aHeader.nSizeImage); |
| } |
| |
| nEndPos = rOStm.Tell(); |
| rOStm.Seek(nImageSizePos); |
| rOStm << aHeader.nSizeImage; |
| rOStm.Seek(nEndPos); |
| |
| return bRet; |
| } |
| |
| bool ImplWriteDIBFileHeader(SvStream& rOStm, BitmapReadAccess& rAcc, bool bUseDIBV5) |
| { |
| const sal_uInt32 nPalCount((rAcc.HasPalette() ? rAcc.GetPaletteEntryCount() : isBitfieldCompression(rAcc.GetScanlineFormat()) ? 3UL : 0UL)); |
| const sal_uInt32 nOffset(14 + (bUseDIBV5 ? DIBV5HEADERSIZE : DIBINFOHEADERSIZE) + nPalCount * 4UL); |
| |
| rOStm << (sal_uInt16)0x4D42; // 'MB' from BITMAPFILEHEADER |
| rOStm << (sal_uInt32)(nOffset + (rAcc.Height() * rAcc.GetScanlineSize())); |
| rOStm << (sal_uInt16)0; |
| rOStm << (sal_uInt16)0; |
| rOStm << nOffset; |
| |
| return( rOStm.GetError() == 0UL ); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| bool ImplReadDIB( |
| Bitmap& rTarget, Bitmap* |
| pTargetAlpha, |
| SvStream& rIStm, |
| bool bFileHeader) |
| { |
| const sal_uInt16 nOldFormat(rIStm.GetNumberFormatInt()); |
| const sal_uLong nOldPos(rIStm.Tell()); |
| sal_uLong nOffset(0UL); |
| bool bRet(false); |
| |
| rIStm.SetNumberFormatInt(NUMBERFORMAT_INT_LITTLEENDIAN); |
| |
| if(bFileHeader) |
| { |
| if(ImplReadDIBFileHeader(rIStm, nOffset)) |
| { |
| bRet = ImplReadDIBBody(rIStm, rTarget, nOffset >= DIBV5HEADERSIZE ? pTargetAlpha : 0, nOffset); |
| } |
| } |
| else |
| { |
| bRet = ImplReadDIBBody(rIStm, rTarget, 0, nOffset); |
| } |
| |
| if(!bRet) |
| { |
| if(!rIStm.GetError()) |
| { |
| rIStm.SetError(SVSTREAM_GENERALERROR); |
| } |
| |
| rIStm.Seek(nOldPos); |
| } |
| |
| rIStm.SetNumberFormatInt(nOldFormat); |
| |
| return bRet; |
| } |
| |
| bool ImplWriteDIB( |
| const Bitmap& rSource, |
| const Bitmap* pSourceAlpha, |
| SvStream& rOStm, |
| bool bCompressed, |
| bool bFileHeader) |
| { |
| const Size aSizePix(rSource.GetSizePixel()); |
| bool bRet(false); |
| |
| if(aSizePix.Width() && aSizePix.Height()) |
| { |
| BitmapReadAccess* pAcc = const_cast< Bitmap& >(rSource).AcquireReadAccess(); |
| BitmapReadAccess* pAccAlpha = 0; |
| const sal_uInt16 nOldFormat(rOStm.GetNumberFormatInt()); |
| const sal_uLong nOldPos(rOStm.Tell()); |
| |
| if(pSourceAlpha) |
| { |
| const Size aSizePixAlpha(pSourceAlpha->GetSizePixel()); |
| |
| if(aSizePixAlpha == aSizePix) |
| { |
| pAccAlpha = const_cast< Bitmap* >(pSourceAlpha)->AcquireReadAccess(); |
| } |
| else |
| { |
| OSL_ENSURE(false, "WriteDIB got an alpha channel, but it's pixel size differs from the base bitmap (!)"); |
| } |
| } |
| |
| rOStm.SetNumberFormatInt(NUMBERFORMAT_INT_LITTLEENDIAN); |
| |
| if(pAcc) |
| { |
| if(bFileHeader) |
| { |
| if(ImplWriteDIBFileHeader(rOStm, *pAcc, 0 != pSourceAlpha)) |
| { |
| bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, pAccAlpha, bCompressed); |
| } |
| } |
| else |
| { |
| bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, pAccAlpha, bCompressed); |
| } |
| |
| const_cast< Bitmap& >(rSource).ReleaseAccess(pAcc); |
| |
| if(pAccAlpha) |
| { |
| const_cast< Bitmap* >(pSourceAlpha)->ReleaseAccess(pAccAlpha); |
| } |
| } |
| |
| if(!bRet) |
| { |
| rOStm.SetError(SVSTREAM_GENERALERROR); |
| rOStm.Seek(nOldPos); |
| } |
| |
| rOStm.SetNumberFormatInt(nOldFormat); |
| } |
| |
| return bRet; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| bool ReadDIB( |
| Bitmap& rTarget, |
| SvStream& rIStm, |
| bool bFileHeader) |
| { |
| return ImplReadDIB(rTarget, 0, rIStm, bFileHeader); |
| } |
| |
| bool ReadDIBBitmapEx( |
| BitmapEx& rTarget, |
| SvStream& rIStm) |
| { |
| Bitmap aBmp; |
| bool bRetval(ImplReadDIB(aBmp, 0, rIStm, true) && !rIStm.GetError()); |
| |
| if(bRetval) |
| { |
| // base bitmap was read, set as return value and try to read alpha extra-data |
| const sal_uLong nStmPos(rIStm.Tell()); |
| sal_uInt32 nMagic1(0); |
| sal_uInt32 nMagic2(0); |
| |
| rTarget = BitmapEx(aBmp); |
| rIStm >> nMagic1 >> nMagic2; |
| bRetval = (0x25091962 == nMagic1) && (0xACB20201 == nMagic2) && !rIStm.GetError(); |
| |
| if(bRetval) |
| { |
| sal_uInt8 bTransparent(false); |
| |
| rIStm >> bTransparent; |
| bRetval = !rIStm.GetError(); |
| |
| if(bRetval) |
| { |
| if((sal_uInt8)TRANSPARENT_BITMAP == bTransparent) |
| { |
| Bitmap aMask; |
| |
| bRetval = ImplReadDIB(aMask, 0, rIStm, true); |
| |
| if(bRetval) |
| { |
| if(!!aMask) |
| { |
| // do we have an alpha mask? |
| if((8 == aMask.GetBitCount()) && aMask.HasGreyPalette()) |
| { |
| AlphaMask aAlpha; |
| |
| // create alpha mask quickly (without greyscale conversion) |
| aAlpha.ImplSetBitmap(aMask); |
| rTarget = BitmapEx(aBmp, aAlpha); |
| } |
| else |
| { |
| rTarget = BitmapEx(aBmp, aMask); |
| } |
| } |
| } |
| } |
| else if((sal_uInt8)TRANSPARENT_COLOR == bTransparent) |
| { |
| Color aTransparentColor; |
| |
| rIStm >> aTransparentColor; |
| bRetval = !rIStm.GetError(); |
| |
| if(bRetval) |
| { |
| rTarget = BitmapEx(aBmp, aTransparentColor); |
| } |
| } |
| } |
| } |
| |
| if(!bRetval) |
| { |
| // alpha extra data could not be read; reset, but use base bitmap as result |
| rIStm.ResetError(); |
| rIStm.Seek(nStmPos); |
| bRetval = true; |
| } |
| } |
| |
| return bRetval; |
| } |
| |
| bool ReadDIBV5( |
| Bitmap& rTarget, |
| Bitmap& rTargetAlpha, |
| SvStream& rIStm) |
| { |
| return ImplReadDIB(rTarget, &rTargetAlpha, rIStm, true); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| bool WriteDIB( |
| const Bitmap& rSource, |
| SvStream& rOStm, |
| bool bCompressed, |
| bool bFileHeader) |
| { |
| return ImplWriteDIB(rSource, 0, rOStm, bCompressed, bFileHeader); |
| } |
| |
| bool WriteDIBBitmapEx( |
| const BitmapEx& rSource, |
| SvStream& rOStm) |
| { |
| if(ImplWriteDIB(rSource.GetBitmap(), 0, rOStm, true, true)) |
| { |
| rOStm << (sal_uInt32)0x25091962; |
| rOStm << (sal_uInt32)0xACB20201; |
| rOStm << (sal_uInt8)rSource.eTransparent; |
| |
| if(TRANSPARENT_BITMAP == rSource.eTransparent) |
| { |
| return ImplWriteDIB(rSource.aMask, 0, rOStm, true, true); |
| } |
| else if(TRANSPARENT_COLOR == rSource.eTransparent) |
| { |
| rOStm << rSource.aTransparentColor; |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool WriteDIBV5( |
| const Bitmap& rSource, |
| const Bitmap& rSourceAlpha, |
| SvStream& rOStm) |
| { |
| return ImplWriteDIB(rSource, &rSourceAlpha, rOStm, false, true); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // eof |