/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 .
*/
// Is native format? switch(rAcc.GetScanlineFormat())
{ case ScanlineFormat::N1BitMsbPal: case ScanlineFormat::N24BitTcBgr:
{ // we can't trust arbitrary-sourced index based formats to have correct indexes, so we exclude the pal formats // from raw read and force checking their colormap indexes
bNative = ( ( rAcc.IsBottomUp() != bTopDown ) && !bRLE && !bTCMask && ( rAcc.GetScanlineSize() == nAlignedWidth ) ); break;
}
default:
{ break;
}
}
// Read data if (bNative)
{ if (nAlignedWidth
> std::numeric_limits<std::size_t>::max() / rHeader.nHeight)
{ returnfalse;
}
std::size_t n = nAlignedWidth * rHeader.nHeight; if (rIStm.ReadBytes(rAcc.GetBuffer(), n) != n)
{ returnfalse;
}
} else
{ if (rHeader.nV5RedMask > 0)
nRMask = rHeader.nV5RedMask; if (rHeader.nV5GreenMask > 0)
nGMask = rHeader.nV5GreenMask; if (rHeader.nV5BlueMask > 0)
nBMask = rHeader.nV5BlueMask;
if (bRLE)
{ if(!rHeader.nSizeImage)
{
rHeader.nSizeImage = rIStm.remainingSize();
}
if (rHeader.nSizeImage > rIStm.remainingSize()) returnfalse;
std::vector<sal_uInt8> aBuffer(rHeader.nSizeImage); if (rIStm.ReadBytes(aBuffer.data(), rHeader.nSizeImage) != rHeader.nSizeImage) returnfalse; if (!ImplDecodeRLE(aBuffer.data(), rHeader, rAcc, rPalette, RLE_4 == rHeader.nCompression)) returnfalse;
} else
{ if (nAlignedWidth > rIStm.remainingSize())
{ // ofz#11188 avoid timeout // all following paths will enter a case statement, and nCount // is always at least 1, so we can check here before allocation // if at least one row can be read returnfalse;
}
std::vector<sal_uInt8> aBuf(nAlignedWidth);
case 16:
{
ColorMaskElement aRedMask(nRMask); if (!aRedMask.CalcMaskShift()) returnfalse;
ColorMaskElement aGreenMask(nGMask); if (!aGreenMask.CalcMaskShift()) returnfalse;
ColorMaskElement aBlueMask(nBMask); if (!aBlueMask.CalcMaskShift()) returnfalse;
if (!ImplReadDIBInfoHeader(rIStm, aHeader, bTopDown, bMSOFormat)) returnfalse;
//BI_BITCOUNT_0 jpeg/png is unsupported if (aHeader.nBitCount == 0) returnfalse;
if (aHeader.nWidth <= 0 || aHeader.nHeight <= 0) returnfalse;
// In case ImplReadDIB() didn't call ImplReadDIBFileHeader() before // this method, nOffset is 0, that's OK. if (nOffset && aHeader.nSize > nOffset)
{ // Header size claims to extend into the image data. // Looks like an error. returnfalse;
}
if (ZCOMPRESS == aHeader.nCompression)
{
sal_uInt32 nCodedSize(0);
sal_uInt32 nUncodedSize(0);
// read coding information
rIStm.ReadUInt32( nCodedSize ).ReadUInt32( nUncodedSize ).ReadUInt32( aHeader.nCompression ); if (nCodedSize > rIStm.remainingSize())
nCodedSize = sal_uInt32(rIStm.remainingSize());
pMemStm.reset(new SvMemoryStream); // There may be bytes left over or the codec might read more than // necessary. So to preserve the correctness of the source stream copy // the encoded block
pMemStm->WriteStream(rIStm, nCodedSize);
pMemStm->Seek(0);
if (nSizeInc > 0)
{ // decode buffer
ZCodec aCodec;
aCodec.BeginCompression();
aData.resize(nSizeInc);
size_t nDataPos(0); while (nUncodedSize > nDataPos)
{
assert(aData.size() > nDataPos); const size_t nToRead(std::min<size_t>(nUncodedSize - nDataPos, aData.size() - nDataPos));
assert(nToRead > 0);
assert(!aData.empty()); const tools::Long nRead = aCodec.Read(*pMemStm, aData.data() + nDataPos, sal_uInt32(nToRead)); if (nRead > 0)
{
nDataPos += static_cast<tools::ULong>(nRead); // we haven't read everything yet: resize buffer and continue if (nDataPos < nUncodedSize)
aData.resize(aData.size() + nSizeInc);
} else
{ break;
}
} // truncate the data buffer to actually read size
aData.resize(nDataPos); // set the real uncoded size
nUncodedSize = sal_uInt32(aData.size());
aCodec.EndCompression();
}
if (aData.empty())
{ // add something so we can take address of the first element
aData.resize(1);
nUncodedSize = 0;
}
// set decoded bytes to memory stream, // from which we will read the bitmap data
pMemStm.reset(new SvMemoryStream);
pIStm = pMemStm.get();
assert(!aData.empty());
pMemStm->SetBuffer(aData.data(), nUncodedSize, nUncodedSize);
nOffset = 0;
} else
{
pIStm = &rIStm;
}
if (nOffset)
{ // It is problematic to seek backwards. We are at the // end of BITMAPINFOHEADER or 12 bytes further in case // of WinBMPv3-NT format. It is possible to seek forward // though because a gap may be there.
sal_Int64 nSeekRel = nOffset - (pIStm->Tell() - nStmPos); if (nSeekRel > 0)
pIStm->SeekRel(nSeekRel);
}
switch (aHeader.nCompression)
{ case RLE_8:
{ if (aHeader.nBitCount != 8) returnfalse; // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
sal_uInt64 nMaxWidth = pIStm->remainingSize();
nMaxWidth *= 256; //assume generous compression ratio
nMaxWidth /= aHeader.nHeight; if (nMaxWidth < o3tl::make_unsigned(aHeader.nWidth)) returnfalse; break;
} case RLE_4:
{ if (aHeader.nBitCount != 4) returnfalse;
sal_uInt64 nMaxWidth = pIStm->remainingSize();
nMaxWidth *= 512; //assume generous compression ratio
nMaxWidth /= aHeader.nHeight; if (nMaxWidth < o3tl::make_unsigned(aHeader.nWidth)) returnfalse; break;
} default: // tdf#122958 invalid compression value used if (aHeader.nCompression & 0x000F)
{ // let's assume that there was an error in the generating application // and allow through as COMPRESS_NONE if the bottom byte is 0
SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader.nCompression << ", rejecting bmp"); returnfalse;
} else
SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader.nCompression << ", assuming meant to be COMPRESS_NONE");
[[fallthrough]]; case BITFIELDS: case ZCOMPRESS: case COMPRESS_NONE:
{ // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
sal_uInt64 nMaxWidth = pIStm->remainingSize();
nMaxWidth /= aHeader.nHeight; if (nMaxWidth < nAlignedWidth) returnfalse; break;
}
}
// 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 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 = AlphaMask(aSizePixel);
pAccAlpha = aNewBmpAlpha;
}
vcl::PixelFormat ePixelFormat(convertToBPP(aHeader.nBitCount)); const BitmapPalette* pPal = &aPalette; //ofz#948 match the surrounding logic of case TransparentType::Bitmap of //ReadDIBBitmapEx but do it while reading for performance
Bitmap aNewBmp(aSizePixel, ePixelFormat, pPal);
BitmapScopedWriteAccess pAcc(aNewBmp); if (!pAcc) returnfalse; if (pAcc->Width() != aHeader.nWidth || pAcc->Height() != aHeader.nHeight)
{ returnfalse;
}
if ( ( 0x4D42 == nTmp16 ) || ( 0x4142 == nTmp16 ) )
{
sal_uInt32 nTmp32(0); if ( 0x4142 == nTmp16 )
{
rIStm.SeekRel( 12 );
rIStm.ReadUInt16( nTmp16 );
rIStm.SeekRel( 8 );
rIStm.ReadUInt32( nTmp32 ); if (nTmp32 < 28)
rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR); else
{
rOffset = nTmp32 - 28;
bRet = ( 0x4D42 == nTmp16 );
}
} else// 0x4D42 == nTmp16, 'MB' from BITMAPFILEHEADER
{
rIStm.SeekRel( 8 ); // we are on bfSize member of BITMAPFILEHEADER, forward to bfOffBits
rIStm.ReadUInt32( nTmp32 ); // read bfOffBits if (nTmp32 < 14)
rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR); else
{
rOffset = nTmp32 - 14; // adapt offset by sizeof(BITMAPFILEHEADER)
bRet = rIStm.GetError() == ERRCODE_NONE;
}
}
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 );
if( rAcc.IsBottomUp() )
rOStm.WriteBytes(rAcc.GetBuffer(), rAcc.Height() * rAcc.GetScanlineSize()); else
{ for( tools::Long nY = rAcc.Height() - 1, nScanlineSize = rAcc.GetScanlineSize(); nY >= 0; nY-- )
rOStm.WriteBytes( rAcc.GetScanline(nY), nScanlineSize );
}
} elseif((RLE_4 == nCompression) || (RLE_8 == nCompression))
{
rImageSize = rOStm.Tell();
ImplWriteRLE( rOStm, rAcc, RLE_4 == nCompression );
} elseif(!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,8,24 // (other cases are not written below) constauto ePixelFormat(convertToBPP(rAcc.GetBitCount())); const sal_uLong nAlignedWidth(AlignedWidth4Bytes(rAcc.Width() * sal_Int32(ePixelFormat))); bool bNative(false);
switch(rAcc.GetScanlineFormat())
{ case ScanlineFormat::N1BitMsbPal: case ScanlineFormat::N8BitPal: case ScanlineFormat::N24BitTcBgr:
{ if(rAcc.IsBottomUp() && (rAcc.GetScanlineSize() == nAlignedWidth))
{
bNative = true;
}
case vcl::PixelFormat::N24_BPP:
{ //valgrind, zero out the trailing unused alignment bytes
size_t nUnusedBytes = nAlignedWidth - nWidth * 3;
memset(aBuf.data() + nAlignedWidth - nUnusedBytes, 0, nUnusedBytes);
}
[[fallthrough]]; // #i59239# fallback to 24 bit format, if bitcount is non-default default:
{
BitmapColor aPixelColor;
for( tools::Long nY = nHeight - 1; nY >= 0; nY-- )
{
sal_uInt8* pTmp = aBuf.data();
for( tools::Long nX = 0; 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 );
aHeader.nSize = DIBINFOHEADERSIZE; // size dependent on CF_DIB type to use
aHeader.nWidth = rAcc.Width();
aHeader.nHeight = rAcc.Height();
aHeader.nPlanes = 1;
// #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,8,24 (other cases // are not written below) constauto ePixelFormat(convertToBPP(rAcc.GetBitCount()));
aHeader.nBitCount = sal_uInt16(ePixelFormat);
aHeader.nSizeImage = rAcc.Height() * AlignedWidth4Bytes(rAcc.Width() * aHeader.nBitCount);
if (bCompressed)
{ if (ePixelFormat == vcl::PixelFormat::N8_BPP)
nCompression = RLE_8;
}
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(100000, 100000), MapMode(MapUnit::Map100thMM), rBitmap.GetPrefMapMode())); constdouble fBmpWidthM(static_cast<double>(rBitmap.GetPrefSize().Width()) / aScale100000.Width()); constdouble fBmpHeightM(static_cast<double>(rBitmap.GetPrefSize().Height()) / aScale100000.Height());
if(bRetval)
{ // base bitmap was read, set as return value and try to read alpha extra-data const sal_uInt64 nStmPos(rIStm.Tell());
sal_uInt32 nMagic1(0);
sal_uInt32 nMagic2(0);
if(!bRetval)
{ // alpha extra data could not be read; reset, but use base bitmap as result
rIStm.ResetError();
rIStm.Seek(nStmPos);
bRetval = true;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.