1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <vcl/dibtools.hxx>
27 #include <comphelper/fileformat.h>
28 #include <tools/zcodec.hxx>
29 #include <tools/stream.hxx>
30 #include <tools/fract.hxx>
31 #include <tools/helpers.hxx>
32 #include <tools/GenericTypeSerializer.hxx>
33 #include <unotools/configmgr.hxx>
34 #include <vcl/bitmapex.hxx>
35 #include <vcl/outdev.hxx>
36 #include <bitmap/BitmapWriteAccess.hxx>
39 #define DIBCOREHEADERSIZE ( 12UL )
40 #define DIBINFOHEADERSIZE ( sizeof(DIBInfoHeader) )
41 #define DIBV5HEADERSIZE ( sizeof(DIBV5Header) )
43 // - DIBInfoHeader and DIBV5Header
45 typedef sal_Int32 FXPT2DOT30
;
83 sal_uInt32 nCompression
;
84 sal_uInt32 nSizeImage
;
85 sal_Int32 nXPelsPerMeter
;
86 sal_Int32 nYPelsPerMeter
;
88 sal_uInt32 nColsImportant
;
105 struct DIBV5Header
: public DIBInfoHeader
107 sal_uInt32 nV5RedMask
;
108 sal_uInt32 nV5GreenMask
;
109 sal_uInt32 nV5BlueMask
;
110 sal_uInt32 nV5AlphaMask
;
111 sal_uInt32 nV5CSType
;
112 CIEXYZTriple aV5Endpoints
;
113 sal_uInt32 nV5GammaRed
;
114 sal_uInt32 nV5GammaGreen
;
115 sal_uInt32 nV5GammaBlue
;
116 sal_uInt32 nV5Intent
;
117 sal_uInt32 nV5ProfileData
;
118 sal_uInt32 nV5ProfileSize
;
119 sal_uInt32 nV5Reserved
;
139 vcl::PixelFormat
convertToBPP(sal_uInt16 nCount
)
141 return (nCount
<= 1) ? vcl::PixelFormat::N1_BPP
:
142 (nCount
<= 8) ? vcl::PixelFormat::N8_BPP
:
143 vcl::PixelFormat::N24_BPP
;
146 bool isBitfieldCompression( ScanlineFormat nScanlineFormat
)
148 return ScanlineFormat::N32BitTcMask
== nScanlineFormat
;
151 bool ImplReadDIBInfoHeader(SvStream
& rIStm
, DIBV5Header
& rHeader
, bool& bTopDown
, bool bMSOFormat
)
153 if (rIStm
.remainingSize() <= 4)
155 // BITMAPINFOHEADER or BITMAPCOREHEADER or BITMAPV5HEADER
156 sal_uInt64
const aStartPos(rIStm
.Tell());
157 rIStm
.ReadUInt32( rHeader
.nSize
);
160 if ( rHeader
.nSize
== DIBCOREHEADERSIZE
)
164 rIStm
.ReadInt16( nTmp16
); rHeader
.nWidth
= nTmp16
;
165 rIStm
.ReadInt16( nTmp16
); rHeader
.nHeight
= nTmp16
;
166 rIStm
.ReadUInt16( rHeader
.nPlanes
);
167 rIStm
.ReadUInt16( rHeader
.nBitCount
);
169 else if ( bMSOFormat
&& rHeader
.nSize
== DIBINFOHEADERSIZE
)
172 rIStm
.ReadInt16(nTmp16
);
173 rHeader
.nWidth
= nTmp16
;
174 rIStm
.ReadInt16(nTmp16
);
175 rHeader
.nHeight
= nTmp16
;
177 rIStm
.ReadUChar(nTmp8
);
178 rHeader
.nPlanes
= nTmp8
;
179 rIStm
.ReadUChar(nTmp8
);
180 rHeader
.nBitCount
= nTmp8
;
181 rIStm
.ReadInt16(nTmp16
);
182 rHeader
.nSizeImage
= nTmp16
;
183 rIStm
.ReadInt16(nTmp16
);
184 rHeader
.nCompression
= nTmp16
;
185 if ( !rHeader
.nSizeImage
) // uncompressed?
186 rHeader
.nSizeImage
= ((rHeader
.nWidth
* rHeader
.nBitCount
+ 31) & ~31) / 8 * rHeader
.nHeight
;
187 rIStm
.ReadInt32( rHeader
.nXPelsPerMeter
);
188 rIStm
.ReadInt32( rHeader
.nYPelsPerMeter
);
189 rIStm
.ReadUInt32( rHeader
.nColsUsed
);
190 rIStm
.ReadUInt32( rHeader
.nColsImportant
);
194 // BITMAPCOREHEADER, BITMAPV5HEADER or unknown. Read as far as possible
195 std::size_t nUsed(sizeof(rHeader
.nSize
));
197 auto readUInt16
= [&nUsed
, &rHeader
, &rIStm
](sal_uInt16
& v
) {
198 if (nUsed
< rHeader
.nSize
) {
203 auto readInt32
= [&nUsed
, &rHeader
, &rIStm
](sal_Int32
& v
) {
204 if (nUsed
< rHeader
.nSize
) {
209 auto readUInt32
= [&nUsed
, &rHeader
, &rIStm
](sal_uInt32
& v
) {
210 if (nUsed
< rHeader
.nSize
) {
216 // read DIBInfoHeader entries
217 readInt32( rHeader
.nWidth
);
218 readInt32( rHeader
.nHeight
);
219 readUInt16( rHeader
.nPlanes
);
220 readUInt16( rHeader
.nBitCount
);
221 readUInt32( rHeader
.nCompression
);
222 readUInt32( rHeader
.nSizeImage
);
223 readInt32( rHeader
.nXPelsPerMeter
);
224 readInt32( rHeader
.nYPelsPerMeter
);
225 readUInt32( rHeader
.nColsUsed
);
226 readUInt32( rHeader
.nColsImportant
);
228 // read DIBV5HEADER members
229 readUInt32( rHeader
.nV5RedMask
);
230 readUInt32( rHeader
.nV5GreenMask
);
231 readUInt32( rHeader
.nV5BlueMask
);
232 readUInt32( rHeader
.nV5AlphaMask
);
233 readUInt32( rHeader
.nV5CSType
);
235 // read contained CIEXYZTriple's
236 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzX
);
237 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzY
);
238 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzZ
);
239 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzX
);
240 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzY
);
241 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzZ
);
242 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzX
);
243 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzY
);
244 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzZ
);
246 readUInt32( rHeader
.nV5GammaRed
);
247 readUInt32( rHeader
.nV5GammaGreen
);
248 readUInt32( rHeader
.nV5GammaBlue
);
249 readUInt32( rHeader
.nV5Intent
);
250 readUInt32( rHeader
.nV5ProfileData
);
251 readUInt32( rHeader
.nV5ProfileSize
);
252 readUInt32( rHeader
.nV5Reserved
);
255 if (!checkSeek(rIStm
, aStartPos
+ rHeader
.nSize
))
259 if (rHeader
.nHeight
== SAL_MIN_INT32
)
262 if ( rHeader
.nHeight
< 0 )
265 rHeader
.nHeight
*= -1;
272 if ( rHeader
.nWidth
< 0 || rHeader
.nXPelsPerMeter
< 0 || rHeader
.nYPelsPerMeter
< 0 )
274 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
277 // #144105# protect a little against damaged files
278 assert(rHeader
.nHeight
>= 0);
279 if (rHeader
.nHeight
!= 0 && rHeader
.nWidth
>= 0
280 && (rHeader
.nSizeImage
/ 16 / static_cast<sal_uInt32
>(rHeader
.nHeight
)
281 > o3tl::make_unsigned(rHeader
.nWidth
)))
283 rHeader
.nSizeImage
= 0;
287 if (rHeader
.nPlanes
!= 1)
290 if (rHeader
.nBitCount
!= 0 && rHeader
.nBitCount
!= 1 &&
291 rHeader
.nBitCount
!= 4 && rHeader
.nBitCount
!= 8 &&
292 rHeader
.nBitCount
!= 16 && rHeader
.nBitCount
!= 24 &&
293 rHeader
.nBitCount
!= 32)
301 bool ImplReadDIBPalette(SvStream
& rIStm
, BitmapPalette
& rPal
, bool bQuad
)
303 const sal_uInt16 nColors
= rPal
.GetEntryCount();
304 const sal_uLong nPalSize
= nColors
* ( bQuad
? 4UL : 3UL );
305 BitmapColor aPalColor
;
307 std::unique_ptr
<sal_uInt8
[]> pEntries(new sal_uInt8
[ nPalSize
]);
308 if (rIStm
.ReadBytes(pEntries
.get(), nPalSize
) != nPalSize
)
313 sal_uInt8
* pTmpEntry
= pEntries
.get();
314 for( sal_uInt16 i
= 0; i
< nColors
; i
++ )
316 aPalColor
.SetBlue( *pTmpEntry
++ );
317 aPalColor
.SetGreen( *pTmpEntry
++ );
318 aPalColor
.SetRed( *pTmpEntry
++ );
326 return rIStm
.GetError() == ERRCODE_NONE
;
329 BitmapColor
SanitizePaletteIndex(sal_uInt8 nIndex
, BitmapPalette
& rPalette
, bool bForceToMonoWhileReading
)
331 const sal_uInt16 nPaletteEntryCount
= rPalette
.GetEntryCount();
332 if (nPaletteEntryCount
&& nIndex
>= nPaletteEntryCount
)
334 auto nSanitizedIndex
= nIndex
% nPaletteEntryCount
;
335 SAL_WARN_IF(nIndex
!= nSanitizedIndex
, "vcl", "invalid colormap index: "
336 << static_cast<unsigned int>(nIndex
) << ", colormap len is: "
337 << nPaletteEntryCount
);
338 nIndex
= nSanitizedIndex
;
341 if (nPaletteEntryCount
&& bForceToMonoWhileReading
)
343 return BitmapColor(static_cast<sal_uInt8
>(rPalette
[nIndex
].GetLuminance() >= 255));
346 return BitmapColor(nIndex
);
349 BitmapColor
SanitizeColor(const BitmapColor
&rColor
, bool bForceToMonoWhileReading
)
351 if (!bForceToMonoWhileReading
)
353 return BitmapColor(static_cast<sal_uInt8
>(rColor
.GetLuminance() >= 255));
356 bool ImplDecodeRLE(sal_uInt8
* pBuffer
, DIBV5Header
const & rHeader
, BitmapWriteAccess
& rAcc
, BitmapPalette
& rPalette
, bool bForceToMonoWhileReading
, bool bRLE4
)
358 Scanline pRLE
= pBuffer
;
359 Scanline pEndRLE
= pBuffer
+ rHeader
.nSizeImage
;
360 tools::Long nY
= rHeader
.nHeight
- 1;
361 const sal_uLong nWidth
= rAcc
.Width();
362 sal_uLong nCountByte
;
366 bool bEndDecoding
= false;
372 if( ( nCountByte
= *pRLE
++ ) == 0 )
380 Scanline pScanline
= rAcc
.GetScanline(nY
);
383 nCountByte
= nRunByte
>> 1;
385 for( sal_uLong i
= 0; i
< nCountByte
; i
++ )
393 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
, bForceToMonoWhileReading
));
396 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
& 0x0f, rPalette
, bForceToMonoWhileReading
));
405 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(*pRLE
>> 4, rPalette
, bForceToMonoWhileReading
));
410 if( ( ( nRunByte
+ 1 ) >> 1 ) & 1 )
420 for( sal_uLong i
= 0; i
< nRunByte
; i
++ )
426 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(*pRLE
, rPalette
, bForceToMonoWhileReading
));
445 else if( nRunByte
== 1 )
466 Scanline pScanline
= rAcc
.GetScanline(nY
);
469 nRunByte
= nCountByte
>> 1;
471 for (sal_uLong i
= 0; i
< nRunByte
&& nX
< nWidth
; ++i
)
473 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
, bForceToMonoWhileReading
));
475 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
& 0x0f, rPalette
, bForceToMonoWhileReading
));
478 if( ( nCountByte
& 1 ) && ( nX
< nWidth
) )
479 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
, bForceToMonoWhileReading
));
483 for (sal_uLong i
= 0; i
< nCountByte
&& nX
< nWidth
; ++i
)
484 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
, rPalette
, bForceToMonoWhileReading
));
488 while (!bEndDecoding
&& (nY
>= 0));
493 bool ImplReadDIBBits(SvStream
& rIStm
, DIBV5Header
& rHeader
, BitmapWriteAccess
& rAcc
, BitmapPalette
& rPalette
, BitmapWriteAccess
* pAccAlpha
,
494 bool bTopDown
, bool& rAlphaUsed
, const sal_uInt64 nAlignedWidth
,
495 const bool bForceToMonoWhileReading
)
497 sal_uInt32
nRMask(( rHeader
.nBitCount
== 16 ) ? 0x00007c00UL
: 0x00ff0000UL
);
498 sal_uInt32
nGMask(( rHeader
.nBitCount
== 16 ) ? 0x000003e0UL
: 0x0000ff00UL
);
499 sal_uInt32
nBMask(( rHeader
.nBitCount
== 16 ) ? 0x0000001fUL
: 0x000000ffUL
);
501 bool bTCMask(!pAccAlpha
&& ((16 == rHeader
.nBitCount
) || (32 == rHeader
.nBitCount
)));
502 bool bRLE((RLE_8
== rHeader
.nCompression
&& 8 == rHeader
.nBitCount
) || (RLE_4
== rHeader
.nCompression
&& 4 == rHeader
.nBitCount
));
505 switch(rAcc
.GetScanlineFormat())
507 case ScanlineFormat::N1BitMsbPal
:
508 case ScanlineFormat::N24BitTcBgr
:
510 // we can't trust arbitrary-sourced index based formats to have correct indexes, so we exclude the pal formats
511 // from raw read and force checking their colormap indexes
512 bNative
= ( ( rAcc
.IsBottomUp() != bTopDown
) && !bRLE
&& !bTCMask
&& ( rAcc
.GetScanlineSize() == nAlignedWidth
) );
526 > std::numeric_limits
<std::size_t>::max() / rHeader
.nHeight
)
530 std::size_t n
= nAlignedWidth
* rHeader
.nHeight
;
531 if (rIStm
.ReadBytes(rAcc
.GetBuffer(), n
) != n
)
539 if(bTCMask
&& BITFIELDS
== rHeader
.nCompression
)
541 rIStm
.SeekRel( -12 );
542 rIStm
.ReadUInt32( nRMask
);
543 rIStm
.ReadUInt32( nGMask
);
544 rIStm
.ReadUInt32( nBMask
);
547 const tools::Long
nWidth(rHeader
.nWidth
);
548 const tools::Long
nHeight(rHeader
.nHeight
);
549 tools::Long nResult
= 0;
550 if (utl::ConfigManager::IsFuzzing() && (o3tl::checked_multiply(nWidth
, nHeight
, nResult
) || nResult
> 4000000))
555 if(!rHeader
.nSizeImage
)
557 rHeader
.nSizeImage
= rIStm
.remainingSize();
560 if (rHeader
.nSizeImage
> rIStm
.remainingSize())
562 std::vector
<sal_uInt8
> aBuffer(rHeader
.nSizeImage
);
563 if (rIStm
.ReadBytes(aBuffer
.data(), rHeader
.nSizeImage
) != rHeader
.nSizeImage
)
565 if (!ImplDecodeRLE(aBuffer
.data(), rHeader
, rAcc
, rPalette
, bForceToMonoWhileReading
, RLE_4
== rHeader
.nCompression
))
570 if (nAlignedWidth
> rIStm
.remainingSize())
572 // ofz#11188 avoid timeout
573 // all following paths will enter a case statement, and nCount
574 // is always at least 1, so we can check here before allocation
575 // if at least one row can be read
578 std::vector
<sal_uInt8
> aBuf(nAlignedWidth
);
580 const tools::Long
nI(bTopDown
? 1 : -1);
581 tools::Long
nY(bTopDown
? 0 : nHeight
- 1);
582 tools::Long
nCount(nHeight
);
584 switch(rHeader
.nBitCount
)
588 for( ; nCount
--; nY
+= nI
)
590 sal_uInt8
* pTmp
= aBuf
.data();
591 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
596 sal_uInt8 cTmp
= *pTmp
++;
597 Scanline pScanline
= rAcc
.GetScanline(nY
);
598 for( tools::Long nX
= 0, nShift
= 8; nX
< nWidth
; nX
++ )
606 auto nIndex
= (cTmp
>> --nShift
) & 1;
607 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
, bForceToMonoWhileReading
));
615 for( ; nCount
--; nY
+= nI
)
617 sal_uInt8
* pTmp
= aBuf
.data();
618 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
623 sal_uInt8 cTmp
= *pTmp
++;
624 Scanline pScanline
= rAcc
.GetScanline(nY
);
625 for( tools::Long nX
= 0, nShift
= 2; nX
< nWidth
; nX
++ )
633 auto nIndex
= (cTmp
>> ( --nShift
<< 2 ) ) & 0x0f;
634 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
, bForceToMonoWhileReading
));
642 for( ; nCount
--; nY
+= nI
)
644 sal_uInt8
* pTmp
= aBuf
.data();
645 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
651 Scanline pScanline
= rAcc
.GetScanline(nY
);
652 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
654 auto nIndex
= *pTmp
++;
655 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
, bForceToMonoWhileReading
));
663 ColorMaskElement
aRedMask(nRMask
);
664 if (!aRedMask
.CalcMaskShift())
666 ColorMaskElement
aGreenMask(nGMask
);
667 if (!aGreenMask
.CalcMaskShift())
669 ColorMaskElement
aBlueMask(nBMask
);
670 if (!aBlueMask
.CalcMaskShift())
673 ColorMask
aMask(aRedMask
, aGreenMask
, aBlueMask
);
676 for( ; nCount
--; nY
+= nI
)
678 sal_uInt16
* pTmp16
= reinterpret_cast<sal_uInt16
*>(aBuf
.data());
679 if (rIStm
.ReadBytes(pTmp16
, nAlignedWidth
)
685 Scanline pScanline
= rAcc
.GetScanline(nY
);
686 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
688 aMask
.GetColorFor16BitLSB( aColor
, reinterpret_cast<sal_uInt8
*>(pTmp16
++) );
689 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizeColor(aColor
, bForceToMonoWhileReading
));
697 BitmapColor aPixelColor
;
699 for( ; nCount
--; nY
+= nI
)
701 sal_uInt8
* pTmp
= aBuf
.data();
702 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
708 Scanline pScanline
= rAcc
.GetScanline(nY
);
709 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
711 aPixelColor
.SetBlue( *pTmp
++ );
712 aPixelColor
.SetGreen( *pTmp
++ );
713 aPixelColor
.SetRed( *pTmp
++ );
714 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizeColor(aPixelColor
, bForceToMonoWhileReading
));
722 ColorMaskElement
aRedMask(nRMask
);
723 if (!aRedMask
.CalcMaskShift())
725 ColorMaskElement
aGreenMask(nGMask
);
726 if (!aGreenMask
.CalcMaskShift())
728 ColorMaskElement
aBlueMask(nBMask
);
729 if (!aBlueMask
.CalcMaskShift())
731 ColorMask
aMask(aRedMask
, aGreenMask
, aBlueMask
);
740 for( ; nCount
--; nY
+= nI
)
742 pTmp32
= reinterpret_cast<sal_uInt32
*>(aBuf
.data());
743 if (rIStm
.ReadBytes(pTmp32
, nAlignedWidth
)
749 Scanline pScanline
= rAcc
.GetScanline(nY
);
750 Scanline pAlphaScanline
= pAccAlpha
->GetScanline(nY
);
751 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
753 aMask
.GetColorAndAlphaFor32Bit( aColor
, aAlpha
, reinterpret_cast<sal_uInt8
*>(pTmp32
++) );
754 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizeColor(aColor
, bForceToMonoWhileReading
));
755 pAccAlpha
->SetPixelOnData(pAlphaScanline
, nX
, BitmapColor(sal_uInt8(0xff) - aAlpha
));
756 rAlphaUsed
|= 0xff != aAlpha
;
762 for( ; nCount
--; nY
+= nI
)
764 pTmp32
= reinterpret_cast<sal_uInt32
*>(aBuf
.data());
765 if (rIStm
.ReadBytes(pTmp32
, nAlignedWidth
)
771 Scanline pScanline
= rAcc
.GetScanline(nY
);
772 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
774 aMask
.GetColorFor32Bit( aColor
, reinterpret_cast<sal_uInt8
*>(pTmp32
++) );
775 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizeColor(aColor
, bForceToMonoWhileReading
));
784 return rIStm
.GetError() == ERRCODE_NONE
;
787 bool ImplReadDIBBody(SvStream
& rIStm
, Bitmap
& rBmp
, AlphaMask
* pBmpAlpha
, sal_uLong nOffset
, bool bIsMask
, bool bMSOFormat
)
790 const sal_uLong nStmPos
= rIStm
.Tell();
791 bool bTopDown(false);
793 if (!ImplReadDIBInfoHeader(rIStm
, aHeader
, bTopDown
, bMSOFormat
))
796 //BI_BITCOUNT_0 jpeg/png is unsupported
797 if (aHeader
.nBitCount
== 0)
800 if (aHeader
.nWidth
<= 0 || aHeader
.nHeight
<= 0)
803 // In case ImplReadDIB() didn't call ImplReadDIBFileHeader() before
804 // this method, nOffset is 0, that's OK.
805 if (nOffset
&& aHeader
.nSize
> nOffset
)
807 // Header size claims to extend into the image data.
808 // Looks like an error.
812 sal_uInt16
nColors(0);
814 std::unique_ptr
<SvMemoryStream
> pMemStm
;
815 std::vector
<sal_uInt8
> aData
;
817 if (aHeader
.nBitCount
<= 8)
819 if(aHeader
.nColsUsed
)
821 nColors
= static_cast<sal_uInt16
>(aHeader
.nColsUsed
);
825 nColors
= ( 1 << aHeader
.nBitCount
);
829 if (ZCOMPRESS
== aHeader
.nCompression
)
831 sal_uInt32
nCodedSize(0);
832 sal_uInt32
nUncodedSize(0);
834 // read coding information
835 rIStm
.ReadUInt32( nCodedSize
).ReadUInt32( nUncodedSize
).ReadUInt32( aHeader
.nCompression
);
836 if (nCodedSize
> rIStm
.remainingSize())
837 nCodedSize
= sal_uInt32(rIStm
.remainingSize());
839 pMemStm
.reset(new SvMemoryStream
);
840 // There may be bytes left over or the codec might read more than
841 // necessary. So to preserve the correctness of the source stream copy
843 pMemStm
->WriteStream(rIStm
, nCodedSize
);
846 size_t nSizeInc(4 * pMemStm
->remainingSize());
847 if (nUncodedSize
< nSizeInc
)
848 nSizeInc
= nUncodedSize
;
854 aCodec
.BeginCompression();
855 aData
.resize(nSizeInc
);
857 while (nUncodedSize
> nDataPos
)
859 assert(aData
.size() > nDataPos
);
860 const size_t nToRead(std::min
<size_t>(nUncodedSize
- nDataPos
, aData
.size() - nDataPos
));
862 assert(!aData
.empty());
863 const tools::Long nRead
= aCodec
.Read(*pMemStm
, aData
.data() + nDataPos
, sal_uInt32(nToRead
));
866 nDataPos
+= static_cast<tools::ULong
>(nRead
);
867 // we haven't read everything yet: resize buffer and continue
868 if (nDataPos
< nUncodedSize
)
869 aData
.resize(aData
.size() + nSizeInc
);
876 // truncate the data buffer to actually read size
877 aData
.resize(nDataPos
);
878 // set the real uncoded size
879 nUncodedSize
= sal_uInt32(aData
.size());
880 aCodec
.EndCompression();
885 // add something so we can take address of the first element
890 // set decoded bytes to memory stream,
891 // from which we will read the bitmap data
892 pMemStm
.reset(new SvMemoryStream
);
893 pIStm
= pMemStm
.get();
894 assert(!aData
.empty());
895 pMemStm
->SetBuffer(aData
.data(), nUncodedSize
, nUncodedSize
);
904 BitmapPalette aPalette
;
907 aPalette
.SetEntryCount(nColors
);
908 ImplReadDIBPalette(*pIStm
, aPalette
, aHeader
.nSize
!= DIBCOREHEADERSIZE
);
911 if (pIStm
->GetError())
916 pIStm
->SeekRel(nOffset
- (pIStm
->Tell() - nStmPos
));
919 const sal_Int64
nBitsPerLine (static_cast<sal_Int64
>(aHeader
.nWidth
) * static_cast<sal_Int64
>(aHeader
.nBitCount
));
920 if (nBitsPerLine
> SAL_MAX_UINT32
)
922 const sal_uInt64
nAlignedWidth(AlignedWidth4Bytes(static_cast<sal_uLong
>(nBitsPerLine
)));
924 switch (aHeader
.nCompression
)
928 if (aHeader
.nBitCount
!= 8)
930 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
931 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
932 nMaxWidth
*= 256; //assume generous compression ratio
933 nMaxWidth
/= aHeader
.nHeight
;
934 if (nMaxWidth
< o3tl::make_unsigned(aHeader
.nWidth
))
940 if (aHeader
.nBitCount
!= 4)
942 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
943 nMaxWidth
*= 512; //assume generous compression ratio
944 nMaxWidth
/= aHeader
.nHeight
;
945 if (nMaxWidth
< o3tl::make_unsigned(aHeader
.nWidth
))
950 // tdf#122958 invalid compression value used
951 if (aHeader
.nCompression
& 0x000F)
953 // lets assume that there was an error in the generating application
954 // and allow through as COMPRESS_NONE if the bottom byte is 0
955 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader
.nCompression
<< ", rejecting bmp");
959 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader
.nCompression
<< ", assuming meant to be COMPRESS_NONE");
965 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
966 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
967 nMaxWidth
/= aHeader
.nHeight
;
968 if (nMaxWidth
< nAlignedWidth
)
974 const Size
aSizePixel(aHeader
.nWidth
, aHeader
.nHeight
);
975 AlphaMask aNewBmpAlpha
;
976 AlphaScopedWriteAccess pAccAlpha
;
977 bool bAlphaPossible(pBmpAlpha
&& aHeader
.nBitCount
== 32);
981 const bool bRedSet(0 != aHeader
.nV5RedMask
);
982 const bool bGreenSet(0 != aHeader
.nV5GreenMask
);
983 const bool bBlueSet(0 != aHeader
.nV5BlueMask
);
985 // some clipboard entries have alpha mask on zero to say that there is
986 // no alpha; do only use this when the other masks are set. The MS docu
987 // says that masks are only to be set when bV5Compression is set to
988 // BI_BITFIELDS, but there seem to exist a wild variety of usages...
989 if((bRedSet
|| bGreenSet
|| bBlueSet
) && (0 == aHeader
.nV5AlphaMask
))
991 bAlphaPossible
= false;
997 aNewBmpAlpha
= AlphaMask(aSizePixel
);
998 pAccAlpha
= AlphaScopedWriteAccess(aNewBmpAlpha
);
1001 vcl::PixelFormat
ePixelFormat(convertToBPP(aHeader
.nBitCount
));
1002 const BitmapPalette
* pPal
= &aPalette
;
1003 //ofz#948 match the surrounding logic of case TransparentType::Bitmap of
1004 //ReadDIBBitmapEx but do it while reading for performance
1005 const bool bIsAlpha
= (ePixelFormat
== vcl::PixelFormat::N8_BPP
&&
1006 !!aPalette
&& aPalette
.IsGreyPalette8Bit());
1007 const bool bForceToMonoWhileReading
= (bIsMask
&& !bIsAlpha
&& ePixelFormat
!= vcl::PixelFormat::N1_BPP
);
1008 if (bForceToMonoWhileReading
)
1011 ePixelFormat
= vcl::PixelFormat::N1_BPP
;
1012 SAL_WARN( "vcl", "forcing mask to monochrome");
1015 Bitmap
aNewBmp(aSizePixel
, ePixelFormat
, pPal
);
1016 BitmapScopedWriteAccess
pAcc(aNewBmp
);
1019 if (pAcc
->Width() != aHeader
.nWidth
|| pAcc
->Height() != aHeader
.nHeight
)
1025 bool bAlphaUsed(false);
1026 bool bRet
= ImplReadDIBBits(*pIStm
, aHeader
, *pAcc
, aPalette
, pAccAlpha
.get(), bTopDown
, bAlphaUsed
, nAlignedWidth
, bForceToMonoWhileReading
);
1028 if (bRet
&& aHeader
.nXPelsPerMeter
&& aHeader
.nYPelsPerMeter
)
1033 Fraction(1000, aHeader
.nXPelsPerMeter
),
1034 Fraction(1000, aHeader
.nYPelsPerMeter
));
1036 aNewBmp
.SetPrefMapMode(aMapMode
);
1037 aNewBmp
.SetPrefSize(Size(aHeader
.nWidth
, aHeader
.nHeight
));
1048 bAlphaPossible
= false;
1058 *pBmpAlpha
= aNewBmpAlpha
;
1065 bool ImplReadDIBFileHeader( SvStream
& rIStm
, sal_uLong
& rOffset
)
1069 const sal_uInt64 nStreamLength
= rIStm
.TellEnd();
1071 sal_uInt16 nTmp16
= 0;
1072 rIStm
.ReadUInt16( nTmp16
);
1074 if ( ( 0x4D42 == nTmp16
) || ( 0x4142 == nTmp16
) )
1076 sal_uInt32
nTmp32(0);
1077 if ( 0x4142 == nTmp16
)
1079 rIStm
.SeekRel( 12 );
1080 rIStm
.ReadUInt16( nTmp16
);
1082 rIStm
.ReadUInt32( nTmp32
);
1083 rOffset
= nTmp32
- 28;
1084 bRet
= ( 0x4D42 == nTmp16
);
1086 else // 0x4D42 == nTmp16, 'MB' from BITMAPFILEHEADER
1088 rIStm
.SeekRel( 8 ); // we are on bfSize member of BITMAPFILEHEADER, forward to bfOffBits
1089 rIStm
.ReadUInt32( nTmp32
); // read bfOffBits
1090 rOffset
= nTmp32
- 14; // adapt offset by sizeof(BITMAPFILEHEADER)
1091 bRet
= rIStm
.GetError() == ERRCODE_NONE
;
1094 if ( rOffset
>= nStreamLength
)
1096 // Offset claims that image starts past the end of the
1097 // stream. Unlikely.
1098 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
1103 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
1108 bool ImplWriteDIBPalette( SvStream
& rOStm
, BitmapReadAccess
const & rAcc
)
1110 const sal_uInt16 nColors
= rAcc
.GetPaletteEntryCount();
1111 const sal_uLong nPalSize
= nColors
* 4UL;
1112 std::unique_ptr
<sal_uInt8
[]> pEntries(new sal_uInt8
[ nPalSize
]);
1113 sal_uInt8
* pTmpEntry
= pEntries
.get();
1115 for( sal_uInt16 i
= 0; i
< nColors
; i
++ )
1117 const BitmapColor
& rPalColor
= rAcc
.GetPaletteColor( i
);
1119 *pTmpEntry
++ = rPalColor
.GetBlue();
1120 *pTmpEntry
++ = rPalColor
.GetGreen();
1121 *pTmpEntry
++ = rPalColor
.GetRed();
1125 rOStm
.WriteBytes( pEntries
.get(), nPalSize
);
1127 return rOStm
.GetError() == ERRCODE_NONE
;
1130 bool ImplWriteRLE( SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, bool bRLE4
)
1132 const sal_uLong nWidth
= rAcc
.Width();
1133 const sal_uLong nHeight
= rAcc
.Height();
1135 sal_uLong nSaveIndex
;
1137 sal_uLong nBufCount
;
1138 std::vector
<sal_uInt8
> aBuf(( nWidth
<< 1 ) + 2);
1143 for ( tools::Long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1145 sal_uInt8
* pTmp
= aBuf
.data();
1147 Scanline pScanline
= rAcc
.GetScanline( nY
);
1149 while( nX
< nWidth
)
1152 cPix
= rAcc
.GetIndexFromData( pScanline
, nX
++ );
1154 while( ( nX
< nWidth
) && ( nCount
< 255 )
1155 && ( cPix
== rAcc
.GetIndexFromData( pScanline
, nX
) ) )
1163 *pTmp
++ = static_cast<sal_uInt8
>(nCount
);
1164 *pTmp
++ = ( bRLE4
? ( ( cPix
<< 4 ) | cPix
) : cPix
);
1170 nSaveIndex
= nX
- 1;
1173 while( ( nX
< nWidth
) && ( nCount
< 256 ) )
1175 cPix
= rAcc
.GetIndexFromData( pScanline
, nX
);
1189 *pTmp
++ = static_cast<sal_uInt8
>(--nCount
);
1193 for ( sal_uLong i
= 0; i
< nCount
; i
++, pTmp
++ )
1195 *pTmp
= rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ ) << 4;
1198 *pTmp
|= rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ );
1201 nCount
= ( nCount
+ 1 ) >> 1;
1205 for( sal_uLong i
= 0; i
< nCount
; i
++ )
1206 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ );
1212 nBufCount
+= ( nCount
+ 3 );
1215 nBufCount
+= ( nCount
+ 2 );
1220 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nSaveIndex
) << (bRLE4
? 4 : 0);
1225 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, ++nSaveIndex
) << ( bRLE4
? 4 : 0 );
1234 aBuf
[ nBufCount
++ ] = 0;
1235 aBuf
[ nBufCount
++ ] = 0;
1237 rOStm
.WriteBytes(aBuf
.data(), nBufCount
);
1240 rOStm
.WriteUChar( 0 );
1241 rOStm
.WriteUChar( 1 );
1243 return rOStm
.GetError() == ERRCODE_NONE
;
1246 bool ImplWriteDIBBits(SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, BitmapReadAccess
const * pAccAlpha
, sal_uLong nCompression
, sal_uInt32
& rImageSize
)
1248 if(!pAccAlpha
&& BITFIELDS
== nCompression
)
1250 const ColorMask
& rMask
= rAcc
.GetColorMask();
1253 UInt32ToSVBT32( rMask
.GetRedMask(), aVal32
);
1254 rOStm
.WriteBytes( aVal32
, 4UL );
1256 UInt32ToSVBT32( rMask
.GetGreenMask(), aVal32
);
1257 rOStm
.WriteBytes( aVal32
, 4UL );
1259 UInt32ToSVBT32( rMask
.GetBlueMask(), aVal32
);
1260 rOStm
.WriteBytes( aVal32
, 4UL );
1262 rImageSize
= rOStm
.Tell();
1264 if( rAcc
.IsBottomUp() )
1265 rOStm
.WriteBytes(rAcc
.GetBuffer(), rAcc
.Height() * rAcc
.GetScanlineSize());
1268 for( tools::Long nY
= rAcc
.Height() - 1, nScanlineSize
= rAcc
.GetScanlineSize(); nY
>= 0; nY
-- )
1269 rOStm
.WriteBytes( rAcc
.GetScanline(nY
), nScanlineSize
);
1272 else if(!pAccAlpha
&& ((RLE_4
== nCompression
) || (RLE_8
== nCompression
)))
1274 rImageSize
= rOStm
.Tell();
1275 ImplWriteRLE( rOStm
, rAcc
, RLE_4
== nCompression
);
1277 else if(!nCompression
)
1279 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are not
1280 // handled properly below (would have to set color masks, and
1281 // nCompression=BITFIELDS - but color mask is not set for
1282 // formats != *_TC_*). Note that this very problem might cause
1283 // trouble at other places - the introduction of 32 bit RGBA
1284 // bitmaps is relatively recent.
1285 // #i59239# discretize bitcount for aligned width to 1,8,24
1286 // (other cases are not written below)
1287 const auto ePixelFormat(pAccAlpha
? vcl::PixelFormat::N32_BPP
: convertToBPP(rAcc
.GetBitCount()));
1288 const sal_uLong
nAlignedWidth(AlignedWidth4Bytes(rAcc
.Width() * sal_Int32(ePixelFormat
)));
1289 bool bNative(false);
1291 switch(rAcc
.GetScanlineFormat())
1293 case ScanlineFormat::N1BitMsbPal
:
1294 case ScanlineFormat::N8BitPal
:
1295 case ScanlineFormat::N24BitTcBgr
:
1297 if(!pAccAlpha
&& rAcc
.IsBottomUp() && (rAcc
.GetScanlineSize() == nAlignedWidth
))
1311 rImageSize
= rOStm
.Tell();
1315 rOStm
.WriteBytes(rAcc
.GetBuffer(), nAlignedWidth
* rAcc
.Height());
1319 const tools::Long
nWidth(rAcc
.Width());
1320 const tools::Long
nHeight(rAcc
.Height());
1321 std::vector
<sal_uInt8
> aBuf(nAlignedWidth
);
1322 switch(ePixelFormat
)
1324 case vcl::PixelFormat::N1_BPP
:
1326 //valgrind, zero out the trailing unused alignment bytes
1327 size_t nUnusedBytes
= nAlignedWidth
- ((nWidth
+7) / 8);
1328 memset(aBuf
.data() + nAlignedWidth
- nUnusedBytes
, 0, nUnusedBytes
);
1330 for( tools::Long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1332 sal_uInt8
* pTmp
= aBuf
.data();
1334 Scanline pScanline
= rAcc
.GetScanline( nY
);
1336 for( tools::Long nX
= 0, nShift
= 8; nX
< nWidth
; nX
++ )
1345 cTmp
|= rAcc
.GetIndexFromData( pScanline
, nX
) << --nShift
;
1349 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1354 case vcl::PixelFormat::N8_BPP
:
1356 for( tools::Long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1358 sal_uInt8
* pTmp
= aBuf
.data();
1359 Scanline pScanline
= rAcc
.GetScanline( nY
);
1361 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1362 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nX
);
1364 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1369 case vcl::PixelFormat::N24_BPP
:
1371 //valgrind, zero out the trailing unused alignment bytes
1372 size_t nUnusedBytes
= nAlignedWidth
- nWidth
* 3;
1373 memset(aBuf
.data() + nAlignedWidth
- nUnusedBytes
, 0, nUnusedBytes
);
1376 // #i59239# fallback to 24 bit format, if bitcount is non-default
1379 BitmapColor aPixelColor
;
1380 const bool bWriteAlpha(ePixelFormat
== vcl::PixelFormat::N32_BPP
&& pAccAlpha
);
1382 for( tools::Long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1384 sal_uInt8
* pTmp
= aBuf
.data();
1385 Scanline pScanlineAlpha
= bWriteAlpha
? pAccAlpha
->GetScanline( nY
) : nullptr;
1387 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1389 // when alpha is used, this may be non-24bit main bitmap, so use GetColor
1390 // instead of GetPixel to ensure RGB value
1391 aPixelColor
= rAcc
.GetColor( nY
, nX
);
1393 *pTmp
++ = aPixelColor
.GetBlue();
1394 *pTmp
++ = aPixelColor
.GetGreen();
1395 *pTmp
++ = aPixelColor
.GetRed();
1399 *pTmp
++ = sal_uInt8(0xff) - pAccAlpha
->GetIndexFromData( pScanlineAlpha
, nX
);
1403 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1411 rImageSize
= rOStm
.Tell() - rImageSize
;
1413 return (!rOStm
.GetError());
1416 bool ImplWriteDIBBody(const Bitmap
& rBitmap
, SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, BitmapReadAccess
const * pAccAlpha
, bool bCompressed
)
1418 const MapMode
aMapPixel(MapUnit::MapPixel
);
1419 DIBV5Header aHeader
;
1420 sal_uLong
nImageSizePos(0);
1421 sal_uLong
nEndPos(0);
1422 sal_uInt32
nCompression(COMPRESS_NONE
);
1425 aHeader
.nSize
= pAccAlpha
? DIBV5HEADERSIZE
: DIBINFOHEADERSIZE
; // size dependent on CF_DIB type to use
1426 aHeader
.nWidth
= rAcc
.Width();
1427 aHeader
.nHeight
= rAcc
.Height();
1428 aHeader
.nPlanes
= 1;
1430 if(!pAccAlpha
&& isBitfieldCompression(rAcc
.GetScanlineFormat()))
1432 aHeader
.nBitCount
= 32;
1433 aHeader
.nSizeImage
= rAcc
.Height() * rAcc
.GetScanlineSize();
1434 nCompression
= BITFIELDS
;
1438 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are
1439 // not handled properly below (would have to set color
1440 // masks, and nCompression=BITFIELDS - but color mask is
1441 // not set for formats != *_TC_*). Note that this very
1442 // problem might cause trouble at other places - the
1443 // introduction of 32 bit RGBA bitmaps is relatively
1445 // #i59239# discretize bitcount to 1,8,24 (other cases
1446 // are not written below)
1447 const auto ePixelFormat(pAccAlpha
? vcl::PixelFormat::N32_BPP
: convertToBPP(rAcc
.GetBitCount()));
1448 aHeader
.nBitCount
= sal_uInt16(ePixelFormat
);
1449 aHeader
.nSizeImage
= rAcc
.Height() * AlignedWidth4Bytes(rAcc
.Width() * aHeader
.nBitCount
);
1453 if (ePixelFormat
== vcl::PixelFormat::N8_BPP
)
1454 nCompression
= RLE_8
;
1458 if((rOStm
.GetCompressMode() & SvStreamCompressFlags::ZBITMAP
) && (rOStm
.GetVersion() >= SOFFICE_FILEFORMAT_40
))
1460 aHeader
.nCompression
= ZCOMPRESS
;
1464 aHeader
.nCompression
= nCompression
;
1467 if(rBitmap
.GetPrefSize().Width() && rBitmap
.GetPrefSize().Height() && (rBitmap
.GetPrefMapMode() != aMapPixel
))
1469 // #i48108# Try to recover xpels/ypels as previously stored on
1470 // disk. The problem with just converting maPrefSize to 100th
1471 // mm and then relating that to the bitmap pixel size is that
1472 // MapMode is integer-based, and suffers from roundoffs,
1473 // especially if maPrefSize is small. Trying to circumvent
1474 // that by performing part of the math in floating point.
1475 const Size
aScale100000(OutputDevice::LogicToLogic(Size(100000, 100000), MapMode(MapUnit::Map100thMM
), rBitmap
.GetPrefMapMode()));
1476 const double fBmpWidthM(static_cast<double>(rBitmap
.GetPrefSize().Width()) / aScale100000
.Width());
1477 const double fBmpHeightM(static_cast<double>(rBitmap
.GetPrefSize().Height()) / aScale100000
.Height());
1479 if(!basegfx::fTools::equalZero(fBmpWidthM
) && !basegfx::fTools::equalZero(fBmpHeightM
))
1481 aHeader
.nXPelsPerMeter
= basegfx::fround(rAcc
.Width() / fabs(fBmpWidthM
));
1482 aHeader
.nYPelsPerMeter
= basegfx::fround(rAcc
.Height() / fabs(fBmpHeightM
));
1486 aHeader
.nColsUsed
= ((!pAccAlpha
&& aHeader
.nBitCount
<= 8) ? rAcc
.GetPaletteEntryCount() : 0);
1487 aHeader
.nColsImportant
= 0;
1489 rOStm
.WriteUInt32( aHeader
.nSize
);
1490 rOStm
.WriteInt32( aHeader
.nWidth
);
1491 rOStm
.WriteInt32( aHeader
.nHeight
);
1492 rOStm
.WriteUInt16( aHeader
.nPlanes
);
1493 rOStm
.WriteUInt16( aHeader
.nBitCount
);
1494 rOStm
.WriteUInt32( aHeader
.nCompression
);
1496 nImageSizePos
= rOStm
.Tell();
1497 rOStm
.SeekRel( sizeof( aHeader
.nSizeImage
) );
1499 rOStm
.WriteInt32( aHeader
.nXPelsPerMeter
);
1500 rOStm
.WriteInt32( aHeader
.nYPelsPerMeter
);
1501 rOStm
.WriteUInt32( aHeader
.nColsUsed
);
1502 rOStm
.WriteUInt32( aHeader
.nColsImportant
);
1504 if(pAccAlpha
) // only write DIBV5 when asked to do so
1506 aHeader
.nV5CSType
= 0x57696E20; // LCS_WINDOWS_COLOR_SPACE
1507 aHeader
.nV5Intent
= 0x00000004; // LCS_GM_IMAGES
1509 rOStm
.WriteUInt32( aHeader
.nV5RedMask
);
1510 rOStm
.WriteUInt32( aHeader
.nV5GreenMask
);
1511 rOStm
.WriteUInt32( aHeader
.nV5BlueMask
);
1512 rOStm
.WriteUInt32( aHeader
.nV5AlphaMask
);
1513 rOStm
.WriteUInt32( aHeader
.nV5CSType
);
1515 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzRed
.aXyzX
);
1516 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzRed
.aXyzY
);
1517 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzRed
.aXyzZ
);
1518 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzGreen
.aXyzX
);
1519 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzGreen
.aXyzY
);
1520 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzGreen
.aXyzZ
);
1521 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzBlue
.aXyzX
);
1522 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzBlue
.aXyzY
);
1523 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzBlue
.aXyzZ
);
1525 rOStm
.WriteUInt32( aHeader
.nV5GammaRed
);
1526 rOStm
.WriteUInt32( aHeader
.nV5GammaGreen
);
1527 rOStm
.WriteUInt32( aHeader
.nV5GammaBlue
);
1528 rOStm
.WriteUInt32( aHeader
.nV5Intent
);
1529 rOStm
.WriteUInt32( aHeader
.nV5ProfileData
);
1530 rOStm
.WriteUInt32( aHeader
.nV5ProfileSize
);
1531 rOStm
.WriteUInt32( aHeader
.nV5Reserved
);
1534 if(ZCOMPRESS
== aHeader
.nCompression
)
1537 SvMemoryStream
aMemStm(aHeader
.nSizeImage
+ 4096, 65535);
1538 sal_uLong
nCodedPos(rOStm
.Tell());
1539 sal_uLong
nLastPos(0);
1540 sal_uInt32
nCodedSize(0);
1541 sal_uInt32
nUncodedSize(0);
1543 // write uncoded data palette
1544 if(aHeader
.nColsUsed
)
1546 ImplWriteDIBPalette(aMemStm
, rAcc
);
1549 // write uncoded bits
1550 bRet
= ImplWriteDIBBits(aMemStm
, rAcc
, pAccAlpha
, nCompression
, aHeader
.nSizeImage
);
1553 nUncodedSize
= aMemStm
.Tell();
1555 // seek over compress info
1558 // write compressed data
1559 aCodec
.BeginCompression(3);
1560 aCodec
.Write(rOStm
, static_cast<sal_uInt8
const *>(aMemStm
.GetData()), nUncodedSize
);
1561 aCodec
.EndCompression();
1563 // update compress info ( coded size, uncoded size, uncoded compression )
1564 nLastPos
= rOStm
.Tell();
1565 nCodedSize
= nLastPos
- nCodedPos
- 12;
1566 rOStm
.Seek(nCodedPos
);
1567 rOStm
.WriteUInt32( nCodedSize
).WriteUInt32( nUncodedSize
).WriteUInt32( nCompression
);
1568 rOStm
.Seek(nLastPos
);
1572 bRet
= (ERRCODE_NONE
== rOStm
.GetError());
1577 if(aHeader
.nColsUsed
)
1579 ImplWriteDIBPalette(rOStm
, rAcc
);
1582 bRet
= ImplWriteDIBBits(rOStm
, rAcc
, pAccAlpha
, aHeader
.nCompression
, aHeader
.nSizeImage
);
1585 nEndPos
= rOStm
.Tell();
1586 rOStm
.Seek(nImageSizePos
);
1587 rOStm
.WriteUInt32( aHeader
.nSizeImage
);
1588 rOStm
.Seek(nEndPos
);
1593 bool ImplWriteDIBFileHeader(SvStream
& rOStm
, BitmapReadAccess
const & rAcc
)
1595 const sal_uInt32
nPalCount((rAcc
.HasPalette() ? rAcc
.GetPaletteEntryCount() : isBitfieldCompression(rAcc
.GetScanlineFormat()) ? 3UL : 0UL));
1596 const sal_uInt32
nOffset(14 + DIBINFOHEADERSIZE
+ nPalCount
* 4UL);
1598 rOStm
.WriteUInt16( 0x4D42 ); // 'MB' from BITMAPFILEHEADER
1599 rOStm
.WriteUInt32( nOffset
+ (rAcc
.Height() * rAcc
.GetScanlineSize()) );
1600 rOStm
.WriteUInt16( 0 );
1601 rOStm
.WriteUInt16( 0 );
1602 rOStm
.WriteUInt32( nOffset
);
1604 return rOStm
.GetError() == ERRCODE_NONE
;
1609 AlphaMask
* pTargetAlpha
,
1613 bool bMSOFormat
=false)
1615 const SvStreamEndian
nOldFormat(rIStm
.GetEndian());
1616 const auto nOldPos(rIStm
.Tell());
1617 sal_uLong
nOffset(0);
1620 rIStm
.SetEndian(SvStreamEndian::LITTLE
);
1624 if(ImplReadDIBFileHeader(rIStm
, nOffset
))
1626 bRet
= ImplReadDIBBody(rIStm
, rTarget
, nOffset
>= DIBV5HEADERSIZE
? pTargetAlpha
: nullptr, nOffset
, bIsMask
, bMSOFormat
);
1631 bRet
= ImplReadDIBBody(rIStm
, rTarget
, nullptr, nOffset
, bIsMask
, bMSOFormat
);
1636 if(!rIStm
.GetError()) // Set error and stop processing whole stream due to security reason
1638 rIStm
.SetError(SVSTREAM_GENERALERROR
);
1641 rIStm
.Seek(nOldPos
);
1644 rIStm
.SetEndian(nOldFormat
);
1650 const Bitmap
& rSource
,
1655 const Size
aSizePix(rSource
.GetSizePixel());
1658 if(aSizePix
.Width() && aSizePix
.Height())
1660 Bitmap::ScopedReadAccess
pAcc(const_cast< Bitmap
& >(rSource
));
1661 Bitmap::ScopedReadAccess pAccAlpha
;
1662 const SvStreamEndian
nOldFormat(rOStm
.GetEndian());
1663 const sal_uLong
nOldPos(rOStm
.Tell());
1665 rOStm
.SetEndian(SvStreamEndian::LITTLE
);
1671 if(ImplWriteDIBFileHeader(rOStm
, *pAcc
))
1673 bRet
= ImplWriteDIBBody(rSource
, rOStm
, *pAcc
, pAccAlpha
.get(), bCompressed
);
1678 bRet
= ImplWriteDIBBody(rSource
, rOStm
, *pAcc
, pAccAlpha
.get(), bCompressed
);
1688 rOStm
.SetError(SVSTREAM_GENERALERROR
);
1689 rOStm
.Seek(nOldPos
);
1692 rOStm
.SetEndian(nOldFormat
);
1698 } // unnamed namespace
1706 return ImplReadDIB(rTarget
, nullptr, rIStm
, bFileHeader
, false, bMSOFormat
);
1709 bool ReadDIBBitmapEx(
1716 bool bRetval(ImplReadDIB(aBmp
, nullptr, rIStm
, bFileHeader
, /*bMask*/false, bMSOFormat
) && !rIStm
.GetError());
1720 // base bitmap was read, set as return value and try to read alpha extra-data
1721 const sal_uLong
nStmPos(rIStm
.Tell());
1722 sal_uInt32
nMagic1(0);
1723 sal_uInt32
nMagic2(0);
1725 rTarget
= BitmapEx(aBmp
);
1726 if (rIStm
.remainingSize() >= 4)
1727 rIStm
.ReadUInt32( nMagic1
).ReadUInt32( nMagic2
);
1728 bRetval
= (0x25091962 == nMagic1
) && (0xACB20201 == nMagic2
) && !rIStm
.GetError();
1733 rIStm
.ReadUChar( tmp
);
1734 bRetval
= !rIStm
.GetError();
1740 case 2: // TransparentType::Bitmap
1744 bRetval
= ImplReadDIB(aMask
, nullptr, rIStm
, true, true);
1748 if(!aMask
.IsEmpty())
1750 // do we have an alpha mask?
1751 if (aMask
.getPixelFormat() == vcl::PixelFormat::N8_BPP
&& aMask
.HasGreyPalette8Bit())
1755 // create alpha mask quickly (without greyscale conversion)
1756 aAlpha
.ImplSetBitmap(aMask
);
1757 rTarget
= BitmapEx(aBmp
, aAlpha
);
1761 rTarget
= BitmapEx(aBmp
, aMask
);
1767 case 1: // backwards compat for old option TransparentType::Color
1769 Color aTransparentColor
;
1771 tools::GenericTypeSerializer
aSerializer(rIStm
);
1772 aSerializer
.readColor(aTransparentColor
);
1774 bRetval
= !rIStm
.GetError();
1778 rTarget
= BitmapEx(aBmp
, aTransparentColor
);
1789 // alpha extra data could not be read; reset, but use base bitmap as result
1791 rIStm
.Seek(nStmPos
);
1801 AlphaMask
& rTargetAlpha
,
1804 return ImplReadDIB(rTarget
, &rTargetAlpha
, rIStm
, true);
1809 const unsigned char* pBuf
,
1810 const ScanlineFormat nFormat
,
1814 BitmapScopedWriteAccess
pWriteAccess(rTarget
.maBitmap
.AcquireWriteAccess(), rTarget
.maBitmap
);
1815 for (int nRow
= 0; nRow
< nHeight
; ++nRow
)
1817 pWriteAccess
->CopyScanline(nRow
, pBuf
+ (nStride
* nRow
), nFormat
, nStride
);
1824 const Bitmap
& rSource
,
1829 return ImplWriteDIB(rSource
, rOStm
, bCompressed
, bFileHeader
);
1833 const BitmapEx
& rSource
,
1837 return ImplWriteDIB(rSource
.GetBitmap(), rOStm
, bCompressed
, /*bFileHeader*/true);
1840 bool WriteDIBBitmapEx(
1841 const BitmapEx
& rSource
,
1844 if(ImplWriteDIB(rSource
.GetBitmap(), rOStm
, true, true))
1846 rOStm
.WriteUInt32( 0x25091962 );
1847 rOStm
.WriteUInt32( 0xACB20201 );
1848 rOStm
.WriteUChar( rSource
.IsAlpha() ? 2 : 0 ); // Used to be TransparentType enum
1850 if(rSource
.IsAlpha())
1852 return ImplWriteDIB(rSource
.maAlphaMask
, rOStm
, true, true);
1859 sal_uInt32
getDIBV5HeaderSize()
1861 return DIBV5HEADERSIZE
;
1864 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */