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 <comphelper/configuration.hxx>
34 #include <vcl/bitmapex.hxx>
35 #include <vcl/outdev.hxx>
36 #include <vcl/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
;
80 sal_uInt32 nCompression
;
81 sal_uInt32 nSizeImage
;
82 sal_Int32 nXPelsPerMeter
;
83 sal_Int32 nYPelsPerMeter
;
85 sal_uInt32 nColsImportant
;
102 struct DIBV5Header
: public DIBInfoHeader
104 sal_uInt32 nV5RedMask
;
105 sal_uInt32 nV5GreenMask
;
106 sal_uInt32 nV5BlueMask
;
107 sal_uInt32 nV5AlphaMask
;
108 sal_uInt32 nV5CSType
;
109 CIEXYZTriple aV5Endpoints
;
110 sal_uInt32 nV5GammaRed
;
111 sal_uInt32 nV5GammaGreen
;
112 sal_uInt32 nV5GammaBlue
;
113 sal_uInt32 nV5Intent
;
114 sal_uInt32 nV5ProfileData
;
115 sal_uInt32 nV5ProfileSize
;
116 sal_uInt32 nV5Reserved
;
134 vcl::PixelFormat
convertToBPP(sal_uInt16 nCount
)
136 return (nCount
<= 8) ? vcl::PixelFormat::N8_BPP
:
137 vcl::PixelFormat::N24_BPP
;
140 bool isBitfieldCompression( ScanlineFormat nScanlineFormat
)
142 return ScanlineFormat::N32BitTcMask
== nScanlineFormat
;
145 bool ImplReadDIBInfoHeader(SvStream
& rIStm
, DIBV5Header
& rHeader
, bool& bTopDown
, bool bMSOFormat
)
147 if (rIStm
.remainingSize() <= 4)
149 // BITMAPINFOHEADER or BITMAPCOREHEADER or BITMAPV5HEADER
150 sal_uInt64
const aStartPos(rIStm
.Tell());
151 rIStm
.ReadUInt32( rHeader
.nSize
);
154 if ( rHeader
.nSize
== DIBCOREHEADERSIZE
)
158 rIStm
.ReadInt16( nTmp16
); rHeader
.nWidth
= nTmp16
;
159 rIStm
.ReadInt16( nTmp16
); rHeader
.nHeight
= nTmp16
;
160 rIStm
.ReadUInt16( rHeader
.nPlanes
);
161 rIStm
.ReadUInt16( rHeader
.nBitCount
);
163 else if ( bMSOFormat
&& rHeader
.nSize
== DIBINFOHEADERSIZE
)
166 rIStm
.ReadInt16(nTmp16
);
167 rHeader
.nWidth
= nTmp16
;
168 rIStm
.ReadInt16(nTmp16
);
169 rHeader
.nHeight
= nTmp16
;
171 rIStm
.ReadUChar(nTmp8
);
172 rHeader
.nPlanes
= nTmp8
;
173 rIStm
.ReadUChar(nTmp8
);
174 rHeader
.nBitCount
= nTmp8
;
175 rIStm
.ReadInt16(nTmp16
);
176 rHeader
.nSizeImage
= nTmp16
;
177 rIStm
.ReadInt16(nTmp16
);
178 rHeader
.nCompression
= nTmp16
;
179 if ( !rHeader
.nSizeImage
) // uncompressed?
180 rHeader
.nSizeImage
= ((rHeader
.nWidth
* rHeader
.nBitCount
+ 31) & ~31) / 8 * rHeader
.nHeight
;
181 rIStm
.ReadInt32( rHeader
.nXPelsPerMeter
);
182 rIStm
.ReadInt32( rHeader
.nYPelsPerMeter
);
183 rIStm
.ReadUInt32( rHeader
.nColsUsed
);
184 rIStm
.ReadUInt32( rHeader
.nColsImportant
);
188 // BITMAPCOREHEADER, BITMAPV5HEADER or unknown. Read as far as possible
189 std::size_t nUsed(sizeof(rHeader
.nSize
));
191 auto readUInt16
= [&nUsed
, &rHeader
, &rIStm
](sal_uInt16
& v
) {
192 if (nUsed
< rHeader
.nSize
) {
197 auto readInt32
= [&nUsed
, &rHeader
, &rIStm
](sal_Int32
& v
) {
198 if (nUsed
< rHeader
.nSize
) {
203 auto readUInt32
= [&nUsed
, &rHeader
, &rIStm
](sal_uInt32
& v
) {
204 if (nUsed
< rHeader
.nSize
) {
210 // read DIBInfoHeader entries
211 readInt32( rHeader
.nWidth
);
212 readInt32( rHeader
.nHeight
);
213 readUInt16( rHeader
.nPlanes
);
214 readUInt16( rHeader
.nBitCount
);
215 readUInt32( rHeader
.nCompression
);
216 readUInt32( rHeader
.nSizeImage
);
217 readInt32( rHeader
.nXPelsPerMeter
);
218 readInt32( rHeader
.nYPelsPerMeter
);
219 readUInt32( rHeader
.nColsUsed
);
220 readUInt32( rHeader
.nColsImportant
);
222 // read DIBV5HEADER members
223 readUInt32( rHeader
.nV5RedMask
);
224 readUInt32( rHeader
.nV5GreenMask
);
225 readUInt32( rHeader
.nV5BlueMask
);
226 readUInt32( rHeader
.nV5AlphaMask
);
227 readUInt32( rHeader
.nV5CSType
);
229 // read contained CIEXYZTriple's
230 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzX
);
231 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzY
);
232 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzZ
);
233 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzX
);
234 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzY
);
235 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzZ
);
236 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzX
);
237 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzY
);
238 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzZ
);
240 readUInt32( rHeader
.nV5GammaRed
);
241 readUInt32( rHeader
.nV5GammaGreen
);
242 readUInt32( rHeader
.nV5GammaBlue
);
243 readUInt32( rHeader
.nV5Intent
);
244 readUInt32( rHeader
.nV5ProfileData
);
245 readUInt32( rHeader
.nV5ProfileSize
);
246 readUInt32( rHeader
.nV5Reserved
);
248 // Read color mask. An additional 12 bytes of color bitfields follow the info header (WinBMPv3-NT)
249 sal_uInt32 nColorMask
= 0;
250 if (BITFIELDS
== rHeader
.nCompression
&& DIBINFOHEADERSIZE
== rHeader
.nSize
)
252 rIStm
.ReadUInt32( rHeader
.nV5RedMask
);
253 rIStm
.ReadUInt32( rHeader
.nV5GreenMask
);
254 rIStm
.ReadUInt32( rHeader
.nV5BlueMask
);
259 if (!checkSeek(rIStm
, aStartPos
+ rHeader
.nSize
+ nColorMask
))
263 if (!rIStm
.good() || rHeader
.nHeight
== SAL_MIN_INT32
)
266 if ( rHeader
.nHeight
< 0 )
269 rHeader
.nHeight
*= -1;
276 if ( rHeader
.nWidth
< 0 || rHeader
.nXPelsPerMeter
< 0 || rHeader
.nYPelsPerMeter
< 0 )
278 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
281 // #144105# protect a little against damaged files
282 assert(rHeader
.nHeight
>= 0);
283 if (rHeader
.nHeight
!= 0 && rHeader
.nWidth
>= 0
284 && (rHeader
.nSizeImage
/ 16 / static_cast<sal_uInt32
>(rHeader
.nHeight
)
285 > o3tl::make_unsigned(rHeader
.nWidth
)))
287 rHeader
.nSizeImage
= 0;
291 if (rHeader
.nPlanes
!= 1)
294 if (rHeader
.nBitCount
!= 0 && rHeader
.nBitCount
!= 1 &&
295 rHeader
.nBitCount
!= 4 && rHeader
.nBitCount
!= 8 &&
296 rHeader
.nBitCount
!= 16 && rHeader
.nBitCount
!= 24 &&
297 rHeader
.nBitCount
!= 32)
305 bool ImplReadDIBPalette(SvStream
& rIStm
, BitmapPalette
& rPal
, bool bQuad
)
307 const sal_uInt16 nColors
= rPal
.GetEntryCount();
308 const sal_uLong nPalSize
= nColors
* ( bQuad
? 4UL : 3UL );
309 BitmapColor aPalColor
;
311 std::unique_ptr
<sal_uInt8
[]> pEntries(new sal_uInt8
[ nPalSize
]);
312 if (rIStm
.ReadBytes(pEntries
.get(), nPalSize
) != nPalSize
)
317 sal_uInt8
* pTmpEntry
= pEntries
.get();
318 for( sal_uInt16 i
= 0; i
< nColors
; i
++ )
320 aPalColor
.SetBlue( *pTmpEntry
++ );
321 aPalColor
.SetGreen( *pTmpEntry
++ );
322 aPalColor
.SetRed( *pTmpEntry
++ );
330 return rIStm
.GetError() == ERRCODE_NONE
;
333 BitmapColor
SanitizePaletteIndex(sal_uInt8 nIndex
, BitmapPalette
& rPalette
)
335 const sal_uInt16 nPaletteEntryCount
= rPalette
.GetEntryCount();
336 if (nPaletteEntryCount
&& nIndex
>= nPaletteEntryCount
)
338 auto nSanitizedIndex
= nIndex
% nPaletteEntryCount
;
339 SAL_WARN_IF(nIndex
!= nSanitizedIndex
, "vcl", "invalid colormap index: "
340 << static_cast<unsigned int>(nIndex
) << ", colormap len is: "
341 << nPaletteEntryCount
);
342 nIndex
= nSanitizedIndex
;
344 return BitmapColor(nIndex
);
347 bool ImplDecodeRLE(sal_uInt8
* pBuffer
, DIBV5Header
const & rHeader
, BitmapWriteAccess
& rAcc
, BitmapPalette
& rPalette
, bool bRLE4
)
349 Scanline pRLE
= pBuffer
;
350 Scanline pEndRLE
= pBuffer
+ rHeader
.nSizeImage
;
351 tools::Long nY
= rHeader
.nHeight
- 1;
352 const sal_uLong nWidth
= rAcc
.Width();
353 sal_uLong nCountByte
;
357 bool bEndDecoding
= false;
363 if( ( nCountByte
= *pRLE
++ ) == 0 )
371 Scanline pScanline
= rAcc
.GetScanline(nY
);
374 nCountByte
= nRunByte
>> 1;
376 for( sal_uLong i
= 0; i
< nCountByte
; i
++ )
384 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
));
387 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
& 0x0f, rPalette
));
396 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(*pRLE
>> 4, rPalette
));
401 if( ( ( nRunByte
+ 1 ) >> 1 ) & 1 )
411 for( sal_uLong i
= 0; i
< nRunByte
; i
++ )
417 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(*pRLE
, rPalette
));
436 else if( nRunByte
== 1 )
457 Scanline pScanline
= rAcc
.GetScanline(nY
);
460 nRunByte
= nCountByte
>> 1;
462 for (sal_uLong i
= 0; i
< nRunByte
&& nX
< nWidth
; ++i
)
464 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
));
466 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
& 0x0f, rPalette
));
469 if( ( nCountByte
& 1 ) && ( nX
< nWidth
) )
470 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
));
474 for (sal_uLong i
= 0; i
< nCountByte
&& nX
< nWidth
; ++i
)
475 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
, rPalette
));
479 while (!bEndDecoding
&& (nY
>= 0));
484 bool ImplReadDIBBits(SvStream
& rIStm
, DIBV5Header
& rHeader
, BitmapWriteAccess
& rAcc
, BitmapPalette
& rPalette
, BitmapWriteAccess
* pAccAlpha
,
485 bool bTopDown
, bool& rAlphaUsed
, const sal_uInt64 nAlignedWidth
)
487 sal_uInt32
nRMask(( rHeader
.nBitCount
== 16 ) ? 0x00007c00UL
: 0x00ff0000UL
);
488 sal_uInt32
nGMask(( rHeader
.nBitCount
== 16 ) ? 0x000003e0UL
: 0x0000ff00UL
);
489 sal_uInt32
nBMask(( rHeader
.nBitCount
== 16 ) ? 0x0000001fUL
: 0x000000ffUL
);
491 bool bTCMask(!pAccAlpha
&& ((16 == rHeader
.nBitCount
) || (32 == rHeader
.nBitCount
)));
492 bool bRLE((RLE_8
== rHeader
.nCompression
&& 8 == rHeader
.nBitCount
) || (RLE_4
== rHeader
.nCompression
&& 4 == rHeader
.nBitCount
));
495 switch(rAcc
.GetScanlineFormat())
497 case ScanlineFormat::N1BitMsbPal
:
498 case ScanlineFormat::N24BitTcBgr
:
500 // we can't trust arbitrary-sourced index based formats to have correct indexes, so we exclude the pal formats
501 // from raw read and force checking their colormap indexes
502 bNative
= bTopDown
&& !bRLE
&& !bTCMask
&& ( rAcc
.GetScanlineSize() == nAlignedWidth
);
516 > std::numeric_limits
<std::size_t>::max() / rHeader
.nHeight
)
520 std::size_t n
= nAlignedWidth
* rHeader
.nHeight
;
521 if (rIStm
.ReadBytes(rAcc
.GetBuffer(), n
) != n
)
528 if (rHeader
.nV5RedMask
> 0)
529 nRMask
= rHeader
.nV5RedMask
;
530 if (rHeader
.nV5GreenMask
> 0)
531 nGMask
= rHeader
.nV5GreenMask
;
532 if (rHeader
.nV5BlueMask
> 0)
533 nBMask
= rHeader
.nV5BlueMask
;
535 const tools::Long
nWidth(rHeader
.nWidth
);
536 const tools::Long
nHeight(rHeader
.nHeight
);
537 tools::Long nResult
= 0;
538 if (comphelper::IsFuzzing() && (o3tl::checked_multiply(nWidth
, nHeight
, nResult
) || nResult
> 4000000))
543 if(!rHeader
.nSizeImage
)
545 rHeader
.nSizeImage
= rIStm
.remainingSize();
548 if (rHeader
.nSizeImage
> rIStm
.remainingSize())
550 std::vector
<sal_uInt8
> aBuffer(rHeader
.nSizeImage
);
551 if (rIStm
.ReadBytes(aBuffer
.data(), rHeader
.nSizeImage
) != rHeader
.nSizeImage
)
553 if (!ImplDecodeRLE(aBuffer
.data(), rHeader
, rAcc
, rPalette
, RLE_4
== rHeader
.nCompression
))
558 if (nAlignedWidth
> rIStm
.remainingSize())
560 // ofz#11188 avoid timeout
561 // all following paths will enter a case statement, and nCount
562 // is always at least 1, so we can check here before allocation
563 // if at least one row can be read
566 std::vector
<sal_uInt8
> aBuf(nAlignedWidth
);
568 const tools::Long
nI(bTopDown
? 1 : -1);
569 tools::Long
nY(bTopDown
? 0 : nHeight
- 1);
570 tools::Long
nCount(nHeight
);
572 switch(rHeader
.nBitCount
)
576 for( ; nCount
--; nY
+= nI
)
578 sal_uInt8
* pTmp
= aBuf
.data();
579 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
584 sal_uInt8 cTmp
= *pTmp
++;
585 Scanline pScanline
= rAcc
.GetScanline(nY
);
586 for( tools::Long nX
= 0, nShift
= 8; nX
< nWidth
; nX
++ )
594 auto nIndex
= (cTmp
>> --nShift
) & 1;
595 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
));
603 for( ; nCount
--; nY
+= nI
)
605 sal_uInt8
* pTmp
= aBuf
.data();
606 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
611 sal_uInt8 cTmp
= *pTmp
++;
612 Scanline pScanline
= rAcc
.GetScanline(nY
);
613 for( tools::Long nX
= 0, nShift
= 2; nX
< nWidth
; nX
++ )
621 auto nIndex
= (cTmp
>> ( --nShift
<< 2 ) ) & 0x0f;
622 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
));
630 for( ; nCount
--; nY
+= nI
)
632 sal_uInt8
* pTmp
= aBuf
.data();
633 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
639 Scanline pScanline
= rAcc
.GetScanline(nY
);
640 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
642 auto nIndex
= *pTmp
++;
643 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
));
651 ColorMaskElement
aRedMask(nRMask
);
652 if (!aRedMask
.CalcMaskShift())
654 ColorMaskElement
aGreenMask(nGMask
);
655 if (!aGreenMask
.CalcMaskShift())
657 ColorMaskElement
aBlueMask(nBMask
);
658 if (!aBlueMask
.CalcMaskShift())
661 ColorMask
aMask(aRedMask
, aGreenMask
, aBlueMask
);
664 for( ; nCount
--; nY
+= nI
)
666 sal_uInt16
* pTmp16
= reinterpret_cast<sal_uInt16
*>(aBuf
.data());
667 if (rIStm
.ReadBytes(pTmp16
, nAlignedWidth
)
673 Scanline pScanline
= rAcc
.GetScanline(nY
);
674 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
676 aMask
.GetColorFor16BitLSB( aColor
, reinterpret_cast<sal_uInt8
*>(pTmp16
++) );
677 rAcc
.SetPixelOnData(pScanline
, nX
, aColor
);
685 BitmapColor aPixelColor
;
687 for( ; nCount
--; nY
+= nI
)
689 sal_uInt8
* pTmp
= aBuf
.data();
690 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
696 Scanline pScanline
= rAcc
.GetScanline(nY
);
697 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
699 aPixelColor
.SetBlue( *pTmp
++ );
700 aPixelColor
.SetGreen( *pTmp
++ );
701 aPixelColor
.SetRed( *pTmp
++ );
702 rAcc
.SetPixelOnData(pScanline
, nX
, aPixelColor
);
710 ColorMaskElement
aRedMask(nRMask
);
711 if (!aRedMask
.CalcMaskShift())
713 ColorMaskElement
aGreenMask(nGMask
);
714 if (!aGreenMask
.CalcMaskShift())
716 ColorMaskElement
aBlueMask(nBMask
);
717 if (!aBlueMask
.CalcMaskShift())
719 ColorMask
aMask(aRedMask
, aGreenMask
, aBlueMask
);
728 for( ; nCount
--; nY
+= nI
)
730 pTmp32
= reinterpret_cast<sal_uInt32
*>(aBuf
.data());
731 if (rIStm
.ReadBytes(pTmp32
, nAlignedWidth
)
737 Scanline pScanline
= rAcc
.GetScanline(nY
);
738 Scanline pAlphaScanline
= pAccAlpha
->GetScanline(nY
);
739 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
741 aMask
.GetColorAndAlphaFor32Bit( aColor
, aAlpha
, reinterpret_cast<sal_uInt8
*>(pTmp32
++) );
742 rAcc
.SetPixelOnData(pScanline
, nX
, aColor
);
743 pAccAlpha
->SetPixelOnData(pAlphaScanline
, nX
, BitmapColor(sal_uInt8(0xff) - aAlpha
));
744 rAlphaUsed
|= 0xff != aAlpha
;
750 for( ; nCount
--; nY
+= nI
)
752 pTmp32
= reinterpret_cast<sal_uInt32
*>(aBuf
.data());
753 if (rIStm
.ReadBytes(pTmp32
, nAlignedWidth
)
759 Scanline pScanline
= rAcc
.GetScanline(nY
);
760 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
762 aMask
.GetColorFor32Bit( aColor
, reinterpret_cast<sal_uInt8
*>(pTmp32
++) );
763 rAcc
.SetPixelOnData(pScanline
, nX
, aColor
);
772 return rIStm
.GetError() == ERRCODE_NONE
;
775 bool ImplReadDIBBody(SvStream
& rIStm
, Bitmap
& rBmp
, AlphaMask
* pBmpAlpha
, sal_uInt64 nOffset
, bool bMSOFormat
)
778 const sal_uInt64 nStmPos
= rIStm
.Tell();
779 bool bTopDown(false);
781 if (!ImplReadDIBInfoHeader(rIStm
, aHeader
, bTopDown
, bMSOFormat
))
784 //BI_BITCOUNT_0 jpeg/png is unsupported
785 if (aHeader
.nBitCount
== 0)
788 if (aHeader
.nWidth
<= 0 || aHeader
.nHeight
<= 0)
791 // In case ImplReadDIB() didn't call ImplReadDIBFileHeader() before
792 // this method, nOffset is 0, that's OK.
793 if (nOffset
&& aHeader
.nSize
> nOffset
)
795 // Header size claims to extend into the image data.
796 // Looks like an error.
800 sal_uInt16
nColors(0);
802 std::unique_ptr
<SvMemoryStream
> pMemStm
;
803 std::vector
<sal_uInt8
> aData
;
805 if (aHeader
.nBitCount
<= 8)
807 if(aHeader
.nColsUsed
)
809 nColors
= static_cast<sal_uInt16
>(aHeader
.nColsUsed
);
813 nColors
= ( 1 << aHeader
.nBitCount
);
817 if (ZCOMPRESS
== aHeader
.nCompression
)
819 sal_uInt32
nCodedSize(0);
820 sal_uInt32
nUncodedSize(0);
822 // read coding information
823 rIStm
.ReadUInt32( nCodedSize
).ReadUInt32( nUncodedSize
).ReadUInt32( aHeader
.nCompression
);
824 if (nCodedSize
> rIStm
.remainingSize())
825 nCodedSize
= sal_uInt32(rIStm
.remainingSize());
827 pMemStm
.reset(new SvMemoryStream
);
828 // There may be bytes left over or the codec might read more than
829 // necessary. So to preserve the correctness of the source stream copy
831 pMemStm
->WriteStream(rIStm
, nCodedSize
);
834 size_t nSizeInc(4 * pMemStm
->remainingSize());
835 if (nUncodedSize
< nSizeInc
)
836 nSizeInc
= nUncodedSize
;
842 aCodec
.BeginCompression();
843 aData
.resize(nSizeInc
);
845 while (nUncodedSize
> nDataPos
)
847 assert(aData
.size() > nDataPos
);
848 const size_t nToRead(std::min
<size_t>(nUncodedSize
- nDataPos
, aData
.size() - nDataPos
));
850 assert(!aData
.empty());
851 const tools::Long nRead
= aCodec
.Read(*pMemStm
, aData
.data() + nDataPos
, sal_uInt32(nToRead
));
854 nDataPos
+= static_cast<tools::ULong
>(nRead
);
855 // we haven't read everything yet: resize buffer and continue
856 if (nDataPos
< nUncodedSize
)
857 aData
.resize(aData
.size() + nSizeInc
);
864 // truncate the data buffer to actually read size
865 aData
.resize(nDataPos
);
866 // set the real uncoded size
867 nUncodedSize
= sal_uInt32(aData
.size());
868 aCodec
.EndCompression();
873 // add something so we can take address of the first element
878 // set decoded bytes to memory stream,
879 // from which we will read the bitmap data
880 pMemStm
.reset(new SvMemoryStream
);
881 pIStm
= pMemStm
.get();
882 assert(!aData
.empty());
883 pMemStm
->SetBuffer(aData
.data(), nUncodedSize
, nUncodedSize
);
892 BitmapPalette aPalette
;
895 aPalette
.SetEntryCount(nColors
);
896 ImplReadDIBPalette(*pIStm
, aPalette
, aHeader
.nSize
!= DIBCOREHEADERSIZE
);
899 if (pIStm
->GetError())
904 // It is problematic to seek backwards. We are at the
905 // end of BITMAPINFOHEADER or 12 bytes further in case
906 // of WinBMPv3-NT format. It is possible to seek forward
907 // though because a gap may be there.
908 sal_Int64 nSeekRel
= nOffset
- (pIStm
->Tell() - nStmPos
);
910 pIStm
->SeekRel(nSeekRel
);
913 const sal_Int64
nBitsPerLine (static_cast<sal_Int64
>(aHeader
.nWidth
) * static_cast<sal_Int64
>(aHeader
.nBitCount
));
914 if (nBitsPerLine
> SAL_MAX_UINT32
)
916 const sal_uInt64
nAlignedWidth(AlignedWidth4Bytes(static_cast<sal_uLong
>(nBitsPerLine
)));
918 switch (aHeader
.nCompression
)
922 if (aHeader
.nBitCount
!= 8)
924 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
925 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
926 nMaxWidth
*= 256; //assume generous compression ratio
927 nMaxWidth
/= aHeader
.nHeight
;
928 if (nMaxWidth
< o3tl::make_unsigned(aHeader
.nWidth
))
934 if (aHeader
.nBitCount
!= 4)
936 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
937 nMaxWidth
*= 512; //assume generous compression ratio
938 nMaxWidth
/= aHeader
.nHeight
;
939 if (nMaxWidth
< o3tl::make_unsigned(aHeader
.nWidth
))
944 // tdf#122958 invalid compression value used
945 if (aHeader
.nCompression
& 0x000F)
947 // let's assume that there was an error in the generating application
948 // and allow through as COMPRESS_NONE if the bottom byte is 0
949 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader
.nCompression
<< ", rejecting bmp");
953 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader
.nCompression
<< ", assuming meant to be COMPRESS_NONE");
959 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
960 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
961 nMaxWidth
/= aHeader
.nHeight
;
962 if (nMaxWidth
< nAlignedWidth
)
968 const Size
aSizePixel(aHeader
.nWidth
, aHeader
.nHeight
);
969 AlphaMask aNewBmpAlpha
;
970 BitmapScopedWriteAccess pAccAlpha
;
971 bool bAlphaPossible(pBmpAlpha
&& aHeader
.nBitCount
== 32);
975 const bool bRedSet(0 != aHeader
.nV5RedMask
);
976 const bool bGreenSet(0 != aHeader
.nV5GreenMask
);
977 const bool bBlueSet(0 != aHeader
.nV5BlueMask
);
979 // some clipboard entries have alpha mask on zero to say that there is
980 // no alpha; do only use this when the other masks are set. The MS docu
981 // says that masks are only to be set when bV5Compression is set to
982 // BI_BITFIELDS, but there seem to exist a wild variety of usages...
983 if((bRedSet
|| bGreenSet
|| bBlueSet
) && (0 == aHeader
.nV5AlphaMask
))
985 bAlphaPossible
= false;
991 aNewBmpAlpha
= AlphaMask(aSizePixel
);
992 pAccAlpha
= aNewBmpAlpha
;
995 vcl::PixelFormat
ePixelFormat(convertToBPP(aHeader
.nBitCount
));
996 const BitmapPalette
* pPal
= &aPalette
;
997 //ofz#948 match the surrounding logic of case TransparentType::Bitmap of
998 //ReadDIBBitmapEx but do it while reading for performance
1000 Bitmap
aNewBmp(aSizePixel
, ePixelFormat
, pPal
);
1001 BitmapScopedWriteAccess
pAcc(aNewBmp
);
1004 if (pAcc
->Width() != aHeader
.nWidth
|| pAcc
->Height() != aHeader
.nHeight
)
1010 bool bAlphaUsed(false);
1011 bool bRet
= ImplReadDIBBits(*pIStm
, aHeader
, *pAcc
, aPalette
, pAccAlpha
.get(), bTopDown
, bAlphaUsed
, nAlignedWidth
);
1013 if (bRet
&& aHeader
.nXPelsPerMeter
&& aHeader
.nYPelsPerMeter
)
1018 Fraction(1000, aHeader
.nXPelsPerMeter
),
1019 Fraction(1000, aHeader
.nYPelsPerMeter
));
1021 aNewBmp
.SetPrefMapMode(aMapMode
);
1022 aNewBmp
.SetPrefSize(Size(aHeader
.nWidth
, aHeader
.nHeight
));
1033 bAlphaPossible
= false;
1039 rBmp
= std::move(aNewBmp
);
1043 *pBmpAlpha
= std::move(aNewBmpAlpha
);
1050 bool ImplReadDIBFileHeader( SvStream
& rIStm
, sal_uLong
& rOffset
)
1054 const sal_uInt64 nStreamLength
= rIStm
.TellEnd();
1056 sal_uInt16 nTmp16
= 0;
1057 rIStm
.ReadUInt16( nTmp16
);
1059 if ( ( 0x4D42 == nTmp16
) || ( 0x4142 == nTmp16
) )
1061 sal_uInt32
nTmp32(0);
1062 if ( 0x4142 == nTmp16
)
1064 rIStm
.SeekRel( 12 );
1065 rIStm
.ReadUInt16( nTmp16
);
1067 rIStm
.ReadUInt32( nTmp32
);
1068 rOffset
= nTmp32
- 28;
1069 bRet
= ( 0x4D42 == nTmp16
);
1071 else // 0x4D42 == nTmp16, 'MB' from BITMAPFILEHEADER
1073 rIStm
.SeekRel( 8 ); // we are on bfSize member of BITMAPFILEHEADER, forward to bfOffBits
1074 rIStm
.ReadUInt32( nTmp32
); // read bfOffBits
1075 rOffset
= nTmp32
- 14; // adapt offset by sizeof(BITMAPFILEHEADER)
1076 bRet
= rIStm
.GetError() == ERRCODE_NONE
;
1079 if ( rOffset
>= nStreamLength
)
1081 // Offset claims that image starts past the end of the
1082 // stream. Unlikely.
1083 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
1088 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
1093 bool ImplWriteDIBPalette( SvStream
& rOStm
, BitmapReadAccess
const & rAcc
)
1095 const sal_uInt16 nColors
= rAcc
.GetPaletteEntryCount();
1096 const sal_uLong nPalSize
= nColors
* 4UL;
1097 std::unique_ptr
<sal_uInt8
[]> pEntries(new sal_uInt8
[ nPalSize
]);
1098 sal_uInt8
* pTmpEntry
= pEntries
.get();
1100 for( sal_uInt16 i
= 0; i
< nColors
; i
++ )
1102 const BitmapColor
& rPalColor
= rAcc
.GetPaletteColor( i
);
1104 *pTmpEntry
++ = rPalColor
.GetBlue();
1105 *pTmpEntry
++ = rPalColor
.GetGreen();
1106 *pTmpEntry
++ = rPalColor
.GetRed();
1110 rOStm
.WriteBytes( pEntries
.get(), nPalSize
);
1112 return rOStm
.GetError() == ERRCODE_NONE
;
1115 bool ImplWriteRLE( SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, bool bRLE4
)
1117 const sal_uLong nWidth
= rAcc
.Width();
1118 const sal_uLong nHeight
= rAcc
.Height();
1120 sal_uLong nSaveIndex
;
1122 sal_uLong nBufCount
;
1123 std::vector
<sal_uInt8
> aBuf(( nWidth
<< 1 ) + 2);
1128 for ( tools::Long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1130 sal_uInt8
* pTmp
= aBuf
.data();
1132 Scanline pScanline
= rAcc
.GetScanline( nY
);
1134 while( nX
< nWidth
)
1137 cPix
= rAcc
.GetIndexFromData( pScanline
, nX
++ );
1139 while( ( nX
< nWidth
) && ( nCount
< 255 )
1140 && ( cPix
== rAcc
.GetIndexFromData( pScanline
, nX
) ) )
1148 *pTmp
++ = static_cast<sal_uInt8
>(nCount
);
1149 *pTmp
++ = ( bRLE4
? ( ( cPix
<< 4 ) | cPix
) : cPix
);
1155 nSaveIndex
= nX
- 1;
1158 while( ( nX
< nWidth
) && ( nCount
< 256 ) )
1160 cPix
= rAcc
.GetIndexFromData( pScanline
, nX
);
1174 *pTmp
++ = static_cast<sal_uInt8
>(--nCount
);
1178 for ( sal_uLong i
= 0; i
< nCount
; i
++, pTmp
++ )
1180 *pTmp
= rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ ) << 4;
1183 *pTmp
|= rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ );
1186 nCount
= ( nCount
+ 1 ) >> 1;
1190 for( sal_uLong i
= 0; i
< nCount
; i
++ )
1191 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ );
1197 nBufCount
+= ( nCount
+ 3 );
1200 nBufCount
+= ( nCount
+ 2 );
1205 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nSaveIndex
) << (bRLE4
? 4 : 0);
1210 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, ++nSaveIndex
) << ( bRLE4
? 4 : 0 );
1219 aBuf
[ nBufCount
++ ] = 0;
1220 aBuf
[ nBufCount
++ ] = 0;
1222 rOStm
.WriteBytes(aBuf
.data(), nBufCount
);
1225 rOStm
.WriteUChar( 0 );
1226 rOStm
.WriteUChar( 1 );
1228 return rOStm
.GetError() == ERRCODE_NONE
;
1231 bool ImplWriteDIBBits(SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, sal_uLong nCompression
, sal_uInt32
& rImageSize
)
1233 if(BITFIELDS
== nCompression
)
1235 const ColorMask
& rMask
= rAcc
.GetColorMask();
1238 UInt32ToSVBT32( rMask
.GetRedMask(), aVal32
);
1239 rOStm
.WriteBytes( aVal32
, 4UL );
1241 UInt32ToSVBT32( rMask
.GetGreenMask(), aVal32
);
1242 rOStm
.WriteBytes( aVal32
, 4UL );
1244 UInt32ToSVBT32( rMask
.GetBlueMask(), aVal32
);
1245 rOStm
.WriteBytes( aVal32
, 4UL );
1247 rImageSize
= rOStm
.Tell();
1249 for( tools::Long nY
= rAcc
.Height() - 1, nScanlineSize
= rAcc
.GetScanlineSize(); nY
>= 0; nY
-- )
1250 rOStm
.WriteBytes( rAcc
.GetScanline(nY
), nScanlineSize
);
1252 else if((RLE_4
== nCompression
) || (RLE_8
== nCompression
))
1254 rImageSize
= rOStm
.Tell();
1255 ImplWriteRLE( rOStm
, rAcc
, RLE_4
== nCompression
);
1257 else if(!nCompression
)
1259 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are not
1260 // handled properly below (would have to set color masks, and
1261 // nCompression=BITFIELDS - but color mask is not set for
1262 // formats != *_TC_*). Note that this very problem might cause
1263 // trouble at other places - the introduction of 32 bit RGBA
1264 // bitmaps is relatively recent.
1265 // #i59239# discretize bitcount for aligned width to 1,8,24
1266 // (other cases are not written below)
1267 const auto ePixelFormat(convertToBPP(rAcc
.GetBitCount()));
1268 const sal_uLong
nAlignedWidth(AlignedWidth4Bytes(rAcc
.Width() * sal_Int32(ePixelFormat
)));
1270 rImageSize
= rOStm
.Tell();
1272 const tools::Long
nWidth(rAcc
.Width());
1273 const tools::Long
nHeight(rAcc
.Height());
1274 std::vector
<sal_uInt8
> aBuf(nAlignedWidth
);
1275 switch(ePixelFormat
)
1277 case vcl::PixelFormat::N8_BPP
:
1279 for( tools::Long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1281 sal_uInt8
* pTmp
= aBuf
.data();
1282 Scanline pScanline
= rAcc
.GetScanline( nY
);
1284 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1285 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nX
);
1287 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1292 case vcl::PixelFormat::N24_BPP
:
1294 //valgrind, zero out the trailing unused alignment bytes
1295 size_t nUnusedBytes
= nAlignedWidth
- nWidth
* 3;
1296 memset(aBuf
.data() + nAlignedWidth
- nUnusedBytes
, 0, nUnusedBytes
);
1299 // #i59239# fallback to 24 bit format, if bitcount is non-default
1302 BitmapColor aPixelColor
;
1304 for( tools::Long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1306 sal_uInt8
* pTmp
= aBuf
.data();
1308 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1310 // when alpha is used, this may be non-24bit main bitmap, so use GetColor
1311 // instead of GetPixel to ensure RGB value
1312 aPixelColor
= rAcc
.GetColor( nY
, nX
);
1314 *pTmp
++ = aPixelColor
.GetBlue();
1315 *pTmp
++ = aPixelColor
.GetGreen();
1316 *pTmp
++ = aPixelColor
.GetRed();
1319 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1326 rImageSize
= rOStm
.Tell() - rImageSize
;
1328 return (!rOStm
.GetError());
1331 bool ImplWriteDIBBody(const Bitmap
& rBitmap
, SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, bool bCompressed
)
1333 const MapMode
aMapPixel(MapUnit::MapPixel
);
1334 DIBV5Header aHeader
;
1335 sal_uInt64
nImageSizePos(0);
1336 sal_uInt64
nEndPos(0);
1337 sal_uInt32
nCompression(COMPRESS_NONE
);
1340 aHeader
.nSize
= DIBINFOHEADERSIZE
; // size dependent on CF_DIB type to use
1341 aHeader
.nWidth
= rAcc
.Width();
1342 aHeader
.nHeight
= rAcc
.Height();
1343 aHeader
.nPlanes
= 1;
1345 if(isBitfieldCompression(rAcc
.GetScanlineFormat()))
1347 aHeader
.nBitCount
= 32;
1348 aHeader
.nSizeImage
= rAcc
.Height() * rAcc
.GetScanlineSize();
1349 nCompression
= BITFIELDS
;
1353 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are
1354 // not handled properly below (would have to set color
1355 // masks, and nCompression=BITFIELDS - but color mask is
1356 // not set for formats != *_TC_*). Note that this very
1357 // problem might cause trouble at other places - the
1358 // introduction of 32 bit RGBA bitmaps is relatively
1360 // #i59239# discretize bitcount to 1,8,24 (other cases
1361 // are not written below)
1362 const auto ePixelFormat(convertToBPP(rAcc
.GetBitCount()));
1363 aHeader
.nBitCount
= sal_uInt16(ePixelFormat
);
1364 aHeader
.nSizeImage
= rAcc
.Height() * AlignedWidth4Bytes(rAcc
.Width() * aHeader
.nBitCount
);
1368 if (ePixelFormat
== vcl::PixelFormat::N8_BPP
)
1369 nCompression
= RLE_8
;
1373 if((rOStm
.GetCompressMode() & SvStreamCompressFlags::ZBITMAP
) && (rOStm
.GetVersion() >= SOFFICE_FILEFORMAT_40
))
1375 aHeader
.nCompression
= ZCOMPRESS
;
1379 aHeader
.nCompression
= nCompression
;
1382 if(rBitmap
.GetPrefSize().Width() && rBitmap
.GetPrefSize().Height() && (rBitmap
.GetPrefMapMode() != aMapPixel
))
1384 // #i48108# Try to recover xpels/ypels as previously stored on
1385 // disk. The problem with just converting maPrefSize to 100th
1386 // mm and then relating that to the bitmap pixel size is that
1387 // MapMode is integer-based, and suffers from roundoffs,
1388 // especially if maPrefSize is small. Trying to circumvent
1389 // that by performing part of the math in floating point.
1390 const Size
aScale100000(OutputDevice::LogicToLogic(Size(100000, 100000), MapMode(MapUnit::Map100thMM
), rBitmap
.GetPrefMapMode()));
1391 const double fBmpWidthM(static_cast<double>(rBitmap
.GetPrefSize().Width()) / aScale100000
.Width());
1392 const double fBmpHeightM(static_cast<double>(rBitmap
.GetPrefSize().Height()) / aScale100000
.Height());
1394 if(!basegfx::fTools::equalZero(fBmpWidthM
) && !basegfx::fTools::equalZero(fBmpHeightM
))
1396 aHeader
.nXPelsPerMeter
= basegfx::fround(rAcc
.Width() / fabs(fBmpWidthM
));
1397 aHeader
.nYPelsPerMeter
= basegfx::fround(rAcc
.Height() / fabs(fBmpHeightM
));
1401 aHeader
.nColsUsed
= ((aHeader
.nBitCount
<= 8) ? rAcc
.GetPaletteEntryCount() : 0);
1402 aHeader
.nColsImportant
= 0;
1404 rOStm
.WriteUInt32( aHeader
.nSize
);
1405 rOStm
.WriteInt32( aHeader
.nWidth
);
1406 rOStm
.WriteInt32( aHeader
.nHeight
);
1407 rOStm
.WriteUInt16( aHeader
.nPlanes
);
1408 rOStm
.WriteUInt16( aHeader
.nBitCount
);
1409 rOStm
.WriteUInt32( aHeader
.nCompression
);
1411 nImageSizePos
= rOStm
.Tell();
1412 rOStm
.SeekRel( sizeof( aHeader
.nSizeImage
) );
1414 rOStm
.WriteInt32( aHeader
.nXPelsPerMeter
);
1415 rOStm
.WriteInt32( aHeader
.nYPelsPerMeter
);
1416 rOStm
.WriteUInt32( aHeader
.nColsUsed
);
1417 rOStm
.WriteUInt32( aHeader
.nColsImportant
);
1419 if(ZCOMPRESS
== aHeader
.nCompression
)
1422 SvMemoryStream
aMemStm(aHeader
.nSizeImage
+ 4096, 65535);
1423 sal_uInt64
nCodedPos(rOStm
.Tell());
1424 sal_uInt64
nLastPos(0);
1425 sal_uInt32
nCodedSize(0);
1426 sal_uInt32
nUncodedSize(0);
1428 // write uncoded data palette
1429 if(aHeader
.nColsUsed
)
1431 ImplWriteDIBPalette(aMemStm
, rAcc
);
1434 // write uncoded bits
1435 bRet
= ImplWriteDIBBits(aMemStm
, rAcc
, nCompression
, aHeader
.nSizeImage
);
1438 nUncodedSize
= aMemStm
.Tell();
1440 // seek over compress info
1443 // write compressed data
1444 aCodec
.BeginCompression(3);
1445 aCodec
.Write(rOStm
, static_cast<sal_uInt8
const *>(aMemStm
.GetData()), nUncodedSize
);
1446 aCodec
.EndCompression();
1448 // update compress info ( coded size, uncoded size, uncoded compression )
1449 nLastPos
= rOStm
.Tell();
1450 nCodedSize
= nLastPos
- nCodedPos
- 12;
1451 rOStm
.Seek(nCodedPos
);
1452 rOStm
.WriteUInt32( nCodedSize
).WriteUInt32( nUncodedSize
).WriteUInt32( nCompression
);
1453 rOStm
.Seek(nLastPos
);
1457 bRet
= (ERRCODE_NONE
== rOStm
.GetError());
1462 if(aHeader
.nColsUsed
)
1464 ImplWriteDIBPalette(rOStm
, rAcc
);
1467 bRet
= ImplWriteDIBBits(rOStm
, rAcc
, aHeader
.nCompression
, aHeader
.nSizeImage
);
1470 nEndPos
= rOStm
.Tell();
1471 rOStm
.Seek(nImageSizePos
);
1472 rOStm
.WriteUInt32( aHeader
.nSizeImage
);
1473 rOStm
.Seek(nEndPos
);
1478 bool ImplWriteDIBFileHeader(SvStream
& rOStm
, BitmapReadAccess
const & rAcc
)
1480 const sal_uInt32
nPalCount((rAcc
.HasPalette() ? rAcc
.GetPaletteEntryCount() : isBitfieldCompression(rAcc
.GetScanlineFormat()) ? 3UL : 0UL));
1481 const sal_uInt32
nOffset(14 + DIBINFOHEADERSIZE
+ nPalCount
* 4UL);
1483 rOStm
.WriteUInt16( 0x4D42 ); // 'MB' from BITMAPFILEHEADER
1484 rOStm
.WriteUInt32( nOffset
+ (rAcc
.Height() * rAcc
.GetScanlineSize()) );
1485 rOStm
.WriteUInt16( 0 );
1486 rOStm
.WriteUInt16( 0 );
1487 rOStm
.WriteUInt32( nOffset
);
1489 return rOStm
.GetError() == ERRCODE_NONE
;
1494 AlphaMask
* pTargetAlpha
,
1497 bool bMSOFormat
=false)
1499 const SvStreamEndian
nOldFormat(rIStm
.GetEndian());
1500 const auto nOldPos(rIStm
.Tell());
1501 sal_uLong
nOffset(0);
1504 rIStm
.SetEndian(SvStreamEndian::LITTLE
);
1508 if(ImplReadDIBFileHeader(rIStm
, nOffset
))
1510 bRet
= ImplReadDIBBody(rIStm
, rTarget
, nOffset
>= DIBV5HEADERSIZE
? pTargetAlpha
: nullptr, nOffset
, bMSOFormat
);
1515 bRet
= ImplReadDIBBody(rIStm
, rTarget
, nullptr, nOffset
, bMSOFormat
);
1520 if(!rIStm
.GetError()) // Set error and stop processing whole stream due to security reason
1522 rIStm
.SetError(SVSTREAM_GENERALERROR
);
1525 rIStm
.Seek(nOldPos
);
1528 rIStm
.SetEndian(nOldFormat
);
1534 const Bitmap
& rSource
,
1539 const Size
aSizePix(rSource
.GetSizePixel());
1542 if(!aSizePix
.Width() || !aSizePix
.Height())
1545 BitmapScopedReadAccess
pAcc(rSource
);
1546 const SvStreamEndian
nOldFormat(rOStm
.GetEndian());
1547 const sal_uInt64
nOldPos(rOStm
.Tell());
1549 rOStm
.SetEndian(SvStreamEndian::LITTLE
);
1555 if(ImplWriteDIBFileHeader(rOStm
, *pAcc
))
1557 bRet
= ImplWriteDIBBody(rSource
, rOStm
, *pAcc
, bCompressed
);
1562 bRet
= ImplWriteDIBBody(rSource
, rOStm
, *pAcc
, bCompressed
);
1570 rOStm
.SetError(SVSTREAM_GENERALERROR
);
1571 rOStm
.Seek(nOldPos
);
1574 rOStm
.SetEndian(nOldFormat
);
1579 } // unnamed namespace
1587 return ImplReadDIB(rTarget
, nullptr, rIStm
, bFileHeader
, bMSOFormat
);
1590 bool ReadDIBBitmapEx(
1597 bool bRetval(ImplReadDIB(aBmp
, nullptr, rIStm
, bFileHeader
, bMSOFormat
) && !rIStm
.GetError());
1601 // base bitmap was read, set as return value and try to read alpha extra-data
1602 const sal_uInt64
nStmPos(rIStm
.Tell());
1603 sal_uInt32
nMagic1(0);
1604 sal_uInt32
nMagic2(0);
1606 rTarget
= BitmapEx(aBmp
);
1607 if (rIStm
.remainingSize() >= 4)
1608 rIStm
.ReadUInt32( nMagic1
).ReadUInt32( nMagic2
);
1609 bRetval
= (0x25091962 == nMagic1
) && (0xACB20201 == nMagic2
) && !rIStm
.GetError();
1614 rIStm
.ReadUChar( tmp
);
1615 bRetval
= !rIStm
.GetError();
1621 case 2: // TransparentType::Bitmap
1625 bRetval
= ImplReadDIB(aMask
, nullptr, rIStm
, true);
1627 if(bRetval
&& !aMask
.IsEmpty())
1628 rTarget
= BitmapEx(aBmp
, aMask
);
1632 case 1: // backwards compat for old option TransparentType::Color
1634 Color aTransparentColor
;
1636 tools::GenericTypeSerializer
aSerializer(rIStm
);
1637 aSerializer
.readColor(aTransparentColor
);
1639 bRetval
= rIStm
.good();
1643 rTarget
= BitmapEx(aBmp
, aTransparentColor
);
1654 // alpha extra data could not be read; reset, but use base bitmap as result
1656 rIStm
.Seek(nStmPos
);
1666 AlphaMask
& rTargetAlpha
,
1669 bool rv
= ImplReadDIB(rTarget
, &rTargetAlpha
, rIStm
, true);
1670 // convert transparency->alpha
1672 rTargetAlpha
.Invert();
1678 const unsigned char* pBuf
,
1679 const ScanlineFormat nFormat
,
1683 BitmapScopedWriteAccess
pWriteAccess(rTarget
.maBitmap
);
1684 for (int nRow
= 0; nRow
< nHeight
; ++nRow
)
1686 pWriteAccess
->CopyScanline(nRow
, pBuf
+ (nStride
* nRow
), nFormat
, nStride
);
1693 const Bitmap
& rSource
,
1698 return ImplWriteDIB(rSource
, rOStm
, bCompressed
, bFileHeader
);
1702 const BitmapEx
& rSource
,
1706 return ImplWriteDIB(rSource
.GetBitmap(), rOStm
, bCompressed
, /*bFileHeader*/true);
1709 bool WriteDIBBitmapEx(
1710 const BitmapEx
& rSource
,
1713 if(ImplWriteDIB(rSource
.GetBitmap(), rOStm
, true, true))
1715 rOStm
.WriteUInt32( 0x25091962 );
1716 rOStm
.WriteUInt32( 0xACB20201 );
1717 rOStm
.WriteUChar( rSource
.IsAlpha() ? 2 : 0 ); // Used to be TransparentType enum
1719 if(rSource
.IsAlpha())
1721 // invert the alpha because the other routines actually want transparency
1722 AlphaMask tmpAlpha
= rSource
.maAlphaMask
;
1724 return ImplWriteDIB(tmpAlpha
.GetBitmap(), rOStm
, true, true);
1731 sal_uInt32
getDIBV5HeaderSize()
1733 return DIBV5HEADERSIZE
;
1736 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */