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 <vcl/dibtools.hxx>
26 #include <comphelper/fileformat.h>
27 #include <tools/zcodec.hxx>
28 #include <tools/stream.hxx>
29 #include <tools/fract.hxx>
30 #include <tools/helpers.hxx>
31 #include <tools/GenericTypeSerializer.hxx>
32 #include <unotools/configmgr.hxx>
33 #include <vcl/bitmapex.hxx>
34 #include <vcl/bitmapaccess.hxx>
35 #include <vcl/outdev.hxx>
36 #include <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 sal_uInt16
discretizeBitcount( sal_uInt16 nInputCount
)
141 return ( nInputCount
<= 1 ) ? 1 :
142 ( nInputCount
<= 4 ) ? 4 :
143 ( nInputCount
<= 8 ) ? 8 : 24;
146 bool isBitfieldCompression( ScanlineFormat nScanlineFormat
)
148 return ScanlineFormat::N32BitTcMask
== nScanlineFormat
;
151 bool ImplReadDIBInfoHeader(SvStream
& rIStm
, DIBV5Header
& rHeader
, bool& bTopDown
, bool bMSOFormat
)
153 // BITMAPINFOHEADER or BITMAPCOREHEADER or BITMAPV5HEADER
154 sal_uInt64
const aStartPos(rIStm
.Tell());
155 rIStm
.ReadUInt32( rHeader
.nSize
);
158 if ( rHeader
.nSize
== DIBCOREHEADERSIZE
)
162 rIStm
.ReadInt16( nTmp16
); rHeader
.nWidth
= nTmp16
;
163 rIStm
.ReadInt16( nTmp16
); rHeader
.nHeight
= nTmp16
;
164 rIStm
.ReadUInt16( rHeader
.nPlanes
);
165 rIStm
.ReadUInt16( rHeader
.nBitCount
);
167 else if ( bMSOFormat
&& rHeader
.nSize
== DIBINFOHEADERSIZE
)
170 rIStm
.ReadInt16(nTmp16
);
171 rHeader
.nWidth
= nTmp16
;
172 rIStm
.ReadInt16(nTmp16
);
173 rHeader
.nHeight
= nTmp16
;
175 rIStm
.ReadUChar(nTmp8
);
176 rHeader
.nPlanes
= nTmp8
;
177 rIStm
.ReadUChar(nTmp8
);
178 rHeader
.nBitCount
= nTmp8
;
179 rIStm
.ReadInt16(nTmp16
);
180 rHeader
.nSizeImage
= nTmp16
;
181 rIStm
.ReadInt16(nTmp16
);
182 rHeader
.nCompression
= nTmp16
;
183 if ( !rHeader
.nSizeImage
) // uncompressed?
184 rHeader
.nSizeImage
= ((rHeader
.nWidth
* rHeader
.nBitCount
+ 31) & ~31) / 8 * rHeader
.nHeight
;
185 rIStm
.ReadInt32( rHeader
.nXPelsPerMeter
);
186 rIStm
.ReadInt32( rHeader
.nYPelsPerMeter
);
187 rIStm
.ReadUInt32( rHeader
.nColsUsed
);
188 rIStm
.ReadUInt32( rHeader
.nColsImportant
);
192 // BITMAPCOREHEADER, BITMAPV5HEADER or unknown. Read as far as possible
193 std::size_t nUsed(sizeof(rHeader
.nSize
));
195 auto readUInt16
= [&nUsed
, &rHeader
, &rIStm
](sal_uInt16
& v
) {
196 if (nUsed
< rHeader
.nSize
) {
201 auto readInt32
= [&nUsed
, &rHeader
, &rIStm
](sal_Int32
& v
) {
202 if (nUsed
< rHeader
.nSize
) {
207 auto readUInt32
= [&nUsed
, &rHeader
, &rIStm
](sal_uInt32
& v
) {
208 if (nUsed
< rHeader
.nSize
) {
214 // read DIBInfoHeader entries
215 readInt32( rHeader
.nWidth
);
216 readInt32( rHeader
.nHeight
);
217 readUInt16( rHeader
.nPlanes
);
218 readUInt16( rHeader
.nBitCount
);
219 readUInt32( rHeader
.nCompression
);
220 readUInt32( rHeader
.nSizeImage
);
221 readInt32( rHeader
.nXPelsPerMeter
);
222 readInt32( rHeader
.nYPelsPerMeter
);
223 readUInt32( rHeader
.nColsUsed
);
224 readUInt32( rHeader
.nColsImportant
);
226 // read DIBV5HEADER members
227 readUInt32( rHeader
.nV5RedMask
);
228 readUInt32( rHeader
.nV5GreenMask
);
229 readUInt32( rHeader
.nV5BlueMask
);
230 readUInt32( rHeader
.nV5AlphaMask
);
231 readUInt32( rHeader
.nV5CSType
);
233 // read contained CIEXYZTriple's
234 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzX
);
235 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzY
);
236 readInt32( rHeader
.aV5Endpoints
.aXyzRed
.aXyzZ
);
237 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzX
);
238 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzY
);
239 readInt32( rHeader
.aV5Endpoints
.aXyzGreen
.aXyzZ
);
240 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzX
);
241 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzY
);
242 readInt32( rHeader
.aV5Endpoints
.aXyzBlue
.aXyzZ
);
244 readUInt32( rHeader
.nV5GammaRed
);
245 readUInt32( rHeader
.nV5GammaGreen
);
246 readUInt32( rHeader
.nV5GammaBlue
);
247 readUInt32( rHeader
.nV5Intent
);
248 readUInt32( rHeader
.nV5ProfileData
);
249 readUInt32( rHeader
.nV5ProfileSize
);
250 readUInt32( rHeader
.nV5Reserved
);
253 if (!checkSeek(rIStm
, aStartPos
+ rHeader
.nSize
))
257 if (rHeader
.nHeight
== SAL_MIN_INT32
)
260 if ( rHeader
.nHeight
< 0 )
263 rHeader
.nHeight
*= -1;
270 if ( rHeader
.nWidth
< 0 || rHeader
.nXPelsPerMeter
< 0 || rHeader
.nYPelsPerMeter
< 0 )
272 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
275 // #144105# protect a little against damaged files
276 assert(rHeader
.nHeight
>= 0);
277 if (rHeader
.nHeight
!= 0 && rHeader
.nWidth
>= 0
278 && (rHeader
.nSizeImage
/ 16 / static_cast<sal_uInt32
>(rHeader
.nHeight
)
279 > static_cast<sal_uInt32
>(rHeader
.nWidth
)))
281 rHeader
.nSizeImage
= 0;
285 if (rHeader
.nPlanes
!= 1)
288 if (rHeader
.nBitCount
!= 0 && rHeader
.nBitCount
!= 1 &&
289 rHeader
.nBitCount
!= 4 && rHeader
.nBitCount
!= 8 &&
290 rHeader
.nBitCount
!= 16 && rHeader
.nBitCount
!= 24 &&
291 rHeader
.nBitCount
!= 32)
299 bool ImplReadDIBPalette(SvStream
& rIStm
, BitmapPalette
& rPal
, bool bQuad
)
301 const sal_uInt16 nColors
= rPal
.GetEntryCount();
302 const sal_uLong nPalSize
= nColors
* ( bQuad
? 4UL : 3UL );
303 BitmapColor aPalColor
;
305 std::unique_ptr
<sal_uInt8
[]> pEntries(new sal_uInt8
[ nPalSize
]);
306 if (rIStm
.ReadBytes(pEntries
.get(), nPalSize
) != nPalSize
)
311 sal_uInt8
* pTmpEntry
= pEntries
.get();
312 for( sal_uInt16 i
= 0; i
< nColors
; i
++ )
314 aPalColor
.SetBlue( *pTmpEntry
++ );
315 aPalColor
.SetGreen( *pTmpEntry
++ );
316 aPalColor
.SetRed( *pTmpEntry
++ );
324 return rIStm
.GetError() == ERRCODE_NONE
;
327 BitmapColor
SanitizePaletteIndex(sal_uInt8 nIndex
, BitmapPalette
& rPalette
, bool bForceToMonoWhileReading
)
329 const sal_uInt16 nPaletteEntryCount
= rPalette
.GetEntryCount();
330 if (nPaletteEntryCount
&& nIndex
>= nPaletteEntryCount
)
332 auto nSanitizedIndex
= nIndex
% nPaletteEntryCount
;
333 SAL_WARN_IF(nIndex
!= nSanitizedIndex
, "vcl", "invalid colormap index: "
334 << static_cast<unsigned int>(nIndex
) << ", colormap len is: "
335 << nPaletteEntryCount
);
336 nIndex
= nSanitizedIndex
;
339 if (nPaletteEntryCount
&& bForceToMonoWhileReading
)
341 return BitmapColor(static_cast<sal_uInt8
>(rPalette
[nIndex
].GetLuminance() >= 255));
344 return BitmapColor(nIndex
);
347 BitmapColor
SanitizeColor(const BitmapColor
&rColor
, bool bForceToMonoWhileReading
)
349 if (!bForceToMonoWhileReading
)
351 return BitmapColor(static_cast<sal_uInt8
>(rColor
.GetLuminance() >= 255));
354 bool ImplDecodeRLE(sal_uInt8
* pBuffer
, DIBV5Header
const & rHeader
, BitmapWriteAccess
& rAcc
, BitmapPalette
& rPalette
, bool bForceToMonoWhileReading
, bool bRLE4
)
356 Scanline pRLE
= pBuffer
;
357 Scanline pEndRLE
= pBuffer
+ rHeader
.nSizeImage
;
358 long nY
= rHeader
.nHeight
- 1;
359 const sal_uLong nWidth
= rAcc
.Width();
360 sal_uLong nCountByte
;
364 bool bEndDecoding
= false;
370 if( ( nCountByte
= *pRLE
++ ) == 0 )
378 Scanline pScanline
= rAcc
.GetScanline(nY
);
381 nCountByte
= nRunByte
>> 1;
383 for( sal_uLong i
= 0; i
< nCountByte
; i
++ )
391 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
, bForceToMonoWhileReading
));
394 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
& 0x0f, rPalette
, bForceToMonoWhileReading
));
403 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(*pRLE
>> 4, rPalette
, bForceToMonoWhileReading
));
408 if( ( ( nRunByte
+ 1 ) >> 1 ) & 1 )
418 for( sal_uLong i
= 0; i
< nRunByte
; i
++ )
424 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(*pRLE
, rPalette
, bForceToMonoWhileReading
));
443 else if( nRunByte
== 1 )
464 Scanline pScanline
= rAcc
.GetScanline(nY
);
467 nRunByte
= nCountByte
>> 1;
469 for (sal_uLong i
= 0; i
< nRunByte
&& nX
< nWidth
; ++i
)
471 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
, bForceToMonoWhileReading
));
473 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
& 0x0f, rPalette
, bForceToMonoWhileReading
));
476 if( ( nCountByte
& 1 ) && ( nX
< nWidth
) )
477 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
>> 4, rPalette
, bForceToMonoWhileReading
));
481 for (sal_uLong i
= 0; i
< nCountByte
&& nX
< nWidth
; ++i
)
482 rAcc
.SetPixelOnData(pScanline
, nX
++, SanitizePaletteIndex(cTmp
, rPalette
, bForceToMonoWhileReading
));
486 while (!bEndDecoding
&& (nY
>= 0));
491 bool ImplReadDIBBits(SvStream
& rIStm
, DIBV5Header
& rHeader
, BitmapWriteAccess
& rAcc
, BitmapPalette
& rPalette
, BitmapWriteAccess
* pAccAlpha
,
492 bool bTopDown
, bool& rAlphaUsed
, const sal_uInt64 nAlignedWidth
,
493 const bool bForceToMonoWhileReading
)
495 sal_uInt32
nRMask(( rHeader
.nBitCount
== 16 ) ? 0x00007c00UL
: 0x00ff0000UL
);
496 sal_uInt32
nGMask(( rHeader
.nBitCount
== 16 ) ? 0x000003e0UL
: 0x0000ff00UL
);
497 sal_uInt32
nBMask(( rHeader
.nBitCount
== 16 ) ? 0x0000001fUL
: 0x000000ffUL
);
499 bool bTCMask(!pAccAlpha
&& ((16 == rHeader
.nBitCount
) || (32 == rHeader
.nBitCount
)));
500 bool bRLE((RLE_8
== rHeader
.nCompression
&& 8 == rHeader
.nBitCount
) || (RLE_4
== rHeader
.nCompression
&& 4 == rHeader
.nBitCount
));
503 switch(rAcc
.GetScanlineFormat())
505 case ScanlineFormat::N1BitMsbPal
:
506 case ScanlineFormat::N24BitTcBgr
:
508 // we can't trust arbitrary-sourced index based formats to have correct indexes, so we exclude the pal formats
509 // from raw read and force checking their colormap indexes
510 bNative
= ( ( rAcc
.IsBottomUp() != bTopDown
) && !bRLE
&& !bTCMask
&& ( rAcc
.GetScanlineSize() == nAlignedWidth
) );
524 > std::numeric_limits
<std::size_t>::max() / rHeader
.nHeight
)
528 std::size_t n
= nAlignedWidth
* rHeader
.nHeight
;
529 if (rIStm
.ReadBytes(rAcc
.GetBuffer(), n
) != n
)
537 if(bTCMask
&& BITFIELDS
== rHeader
.nCompression
)
539 rIStm
.SeekRel( -12 );
540 rIStm
.ReadUInt32( nRMask
);
541 rIStm
.ReadUInt32( nGMask
);
542 rIStm
.ReadUInt32( nBMask
);
545 const long nWidth(rHeader
.nWidth
);
546 const long nHeight(rHeader
.nHeight
);
548 if (utl::ConfigManager::IsFuzzing() && (o3tl::checked_multiply(nWidth
, nHeight
, nResult
) || nResult
> 4000000))
553 if(!rHeader
.nSizeImage
)
555 rHeader
.nSizeImage
= rIStm
.remainingSize();
558 if (rHeader
.nSizeImage
> rIStm
.remainingSize())
560 std::vector
<sal_uInt8
> aBuffer(rHeader
.nSizeImage
);
561 if (rIStm
.ReadBytes(aBuffer
.data(), rHeader
.nSizeImage
) != rHeader
.nSizeImage
)
563 if (!ImplDecodeRLE(aBuffer
.data(), rHeader
, rAcc
, rPalette
, bForceToMonoWhileReading
, RLE_4
== rHeader
.nCompression
))
568 if (nAlignedWidth
> rIStm
.remainingSize())
570 // ofz#11188 avoid timeout
571 // all following paths will enter a case statement, and nCount
572 // is always at least 1, so we can check here before allocation
573 // if at least one row can be read
576 std::vector
<sal_uInt8
> aBuf(nAlignedWidth
);
578 const long nI(bTopDown
? 1 : -1);
579 long nY(bTopDown
? 0 : nHeight
- 1);
580 long nCount(nHeight
);
582 switch(rHeader
.nBitCount
)
586 for( ; nCount
--; nY
+= nI
)
588 sal_uInt8
* pTmp
= aBuf
.data();
589 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
594 sal_uInt8 cTmp
= *pTmp
++;
595 Scanline pScanline
= rAcc
.GetScanline(nY
);
596 for( long nX
= 0, nShift
= 8; nX
< nWidth
; nX
++ )
604 auto nIndex
= (cTmp
>> --nShift
) & 1;
605 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
, bForceToMonoWhileReading
));
613 for( ; nCount
--; nY
+= nI
)
615 sal_uInt8
* pTmp
= aBuf
.data();
616 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
621 sal_uInt8 cTmp
= *pTmp
++;
622 Scanline pScanline
= rAcc
.GetScanline(nY
);
623 for( long nX
= 0, nShift
= 2; nX
< nWidth
; nX
++ )
631 auto nIndex
= (cTmp
>> ( --nShift
<< 2 ) ) & 0x0f;
632 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
, bForceToMonoWhileReading
));
640 for( ; nCount
--; nY
+= nI
)
642 sal_uInt8
* pTmp
= aBuf
.data();
643 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
649 Scanline pScanline
= rAcc
.GetScanline(nY
);
650 for( long nX
= 0; nX
< nWidth
; nX
++ )
652 auto nIndex
= *pTmp
++;
653 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizePaletteIndex(nIndex
, rPalette
, bForceToMonoWhileReading
));
661 ColorMaskElement
aRedMask(nRMask
);
662 if (!aRedMask
.CalcMaskShift())
664 ColorMaskElement
aGreenMask(nGMask
);
665 if (!aGreenMask
.CalcMaskShift())
667 ColorMaskElement
aBlueMask(nBMask
);
668 if (!aBlueMask
.CalcMaskShift())
671 ColorMask
aMask(aRedMask
, aGreenMask
, aBlueMask
);
674 for( ; nCount
--; nY
+= nI
)
676 sal_uInt16
* pTmp16
= reinterpret_cast<sal_uInt16
*>(aBuf
.data());
677 if (rIStm
.ReadBytes(pTmp16
, nAlignedWidth
)
683 Scanline pScanline
= rAcc
.GetScanline(nY
);
684 for( long nX
= 0; nX
< nWidth
; nX
++ )
686 aMask
.GetColorFor16BitLSB( aColor
, reinterpret_cast<sal_uInt8
*>(pTmp16
++) );
687 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizeColor(aColor
, bForceToMonoWhileReading
));
695 BitmapColor aPixelColor
;
697 for( ; nCount
--; nY
+= nI
)
699 sal_uInt8
* pTmp
= aBuf
.data();
700 if (rIStm
.ReadBytes(pTmp
, nAlignedWidth
)
706 Scanline pScanline
= rAcc
.GetScanline(nY
);
707 for( long nX
= 0; nX
< nWidth
; nX
++ )
709 aPixelColor
.SetBlue( *pTmp
++ );
710 aPixelColor
.SetGreen( *pTmp
++ );
711 aPixelColor
.SetRed( *pTmp
++ );
712 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizeColor(aPixelColor
, bForceToMonoWhileReading
));
720 ColorMaskElement
aRedMask(nRMask
);
721 if (!aRedMask
.CalcMaskShift())
723 ColorMaskElement
aGreenMask(nGMask
);
724 if (!aGreenMask
.CalcMaskShift())
726 ColorMaskElement
aBlueMask(nBMask
);
727 if (!aBlueMask
.CalcMaskShift())
729 ColorMask
aMask(aRedMask
, aGreenMask
, aBlueMask
);
738 for( ; nCount
--; nY
+= nI
)
740 pTmp32
= reinterpret_cast<sal_uInt32
*>(aBuf
.data());
741 if (rIStm
.ReadBytes(pTmp32
, nAlignedWidth
)
747 Scanline pScanline
= rAcc
.GetScanline(nY
);
748 Scanline pAlphaScanline
= pAccAlpha
->GetScanline(nY
);
749 for( long nX
= 0; nX
< nWidth
; nX
++ )
751 aMask
.GetColorAndAlphaFor32Bit( aColor
, aAlpha
, reinterpret_cast<sal_uInt8
*>(pTmp32
++) );
752 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizeColor(aColor
, bForceToMonoWhileReading
));
753 pAccAlpha
->SetPixelOnData(pAlphaScanline
, nX
, BitmapColor(sal_uInt8(0xff) - aAlpha
));
754 rAlphaUsed
|= 0xff != aAlpha
;
760 for( ; nCount
--; nY
+= nI
)
762 pTmp32
= reinterpret_cast<sal_uInt32
*>(aBuf
.data());
763 if (rIStm
.ReadBytes(pTmp32
, nAlignedWidth
)
769 Scanline pScanline
= rAcc
.GetScanline(nY
);
770 for( long nX
= 0; nX
< nWidth
; nX
++ )
772 aMask
.GetColorFor32Bit( aColor
, reinterpret_cast<sal_uInt8
*>(pTmp32
++) );
773 rAcc
.SetPixelOnData(pScanline
, nX
, SanitizeColor(aColor
, bForceToMonoWhileReading
));
782 return rIStm
.GetError() == ERRCODE_NONE
;
785 bool ImplReadDIBBody(SvStream
& rIStm
, Bitmap
& rBmp
, AlphaMask
* pBmpAlpha
, sal_uLong nOffset
, bool bIsMask
, bool bMSOFormat
)
788 const sal_uLong nStmPos
= rIStm
.Tell();
789 bool bTopDown(false);
791 if (!ImplReadDIBInfoHeader(rIStm
, aHeader
, bTopDown
, bMSOFormat
))
794 //BI_BITCOUNT_0 jpeg/png is unsupported
795 if (aHeader
.nBitCount
== 0)
798 if (aHeader
.nWidth
<= 0 || aHeader
.nHeight
<= 0)
801 // In case ImplReadDIB() didn't call ImplReadDIBFileHeader() before
802 // this method, nOffset is 0, that's OK.
803 if (nOffset
&& aHeader
.nSize
> nOffset
)
805 // Header size claims to extend into the image data.
806 // Looks like an error.
810 sal_uInt16
nColors(0);
812 std::unique_ptr
<SvMemoryStream
> pMemStm
;
813 std::vector
<sal_uInt8
> aData
;
815 if (aHeader
.nBitCount
<= 8)
817 if(aHeader
.nColsUsed
)
819 nColors
= static_cast<sal_uInt16
>(aHeader
.nColsUsed
);
823 nColors
= ( 1 << aHeader
.nBitCount
);
827 if (ZCOMPRESS
== aHeader
.nCompression
)
829 sal_uInt32
nCodedSize(0);
830 sal_uInt32
nUncodedSize(0);
832 // read coding information
833 rIStm
.ReadUInt32( nCodedSize
).ReadUInt32( nUncodedSize
).ReadUInt32( aHeader
.nCompression
);
834 if (nCodedSize
> rIStm
.remainingSize())
835 nCodedSize
= sal_uInt32(rIStm
.remainingSize());
837 pMemStm
.reset(new SvMemoryStream
);
838 // There may be bytes left over or the codec might read more than
839 // necessary. So to preserve the correctness of the source stream copy
841 pMemStm
->WriteStream(rIStm
, nCodedSize
);
844 size_t nSizeInc(4 * pMemStm
->remainingSize());
845 if (nUncodedSize
< nSizeInc
)
846 nSizeInc
= nUncodedSize
;
852 aCodec
.BeginCompression();
853 aData
.resize(nSizeInc
);
855 while (nUncodedSize
> nDataPos
)
857 assert(aData
.size() > nDataPos
);
858 const size_t nToRead(std::min
<size_t>(nUncodedSize
- nDataPos
, aData
.size() - nDataPos
));
860 assert(!aData
.empty());
861 const long nRead
= aCodec
.Read(*pMemStm
, aData
.data() + nDataPos
, sal_uInt32(nToRead
));
864 nDataPos
+= static_cast<unsigned long>(nRead
);
865 // we haven't read everything yet: resize buffer and continue
866 if (nDataPos
< nUncodedSize
)
867 aData
.resize(aData
.size() + nSizeInc
);
874 // truncate the data buffer to actually read size
875 aData
.resize(nDataPos
);
876 // set the real uncoded size
877 nUncodedSize
= sal_uInt32(aData
.size());
878 aCodec
.EndCompression();
883 // add something so we can take address of the first element
888 // set decoded bytes to memory stream,
889 // from which we will read the bitmap data
890 pMemStm
.reset(new SvMemoryStream
);
891 pIStm
= pMemStm
.get();
892 assert(!aData
.empty());
893 pMemStm
->SetBuffer(aData
.data(), nUncodedSize
, nUncodedSize
);
902 BitmapPalette aPalette
;
905 aPalette
.SetEntryCount(nColors
);
906 ImplReadDIBPalette(*pIStm
, aPalette
, aHeader
.nSize
!= DIBCOREHEADERSIZE
);
909 if (pIStm
->GetError())
914 pIStm
->SeekRel(nOffset
- (pIStm
->Tell() - nStmPos
));
917 const sal_Int64
nBitsPerLine (static_cast<sal_Int64
>(aHeader
.nWidth
) * static_cast<sal_Int64
>(aHeader
.nBitCount
));
918 if (nBitsPerLine
> SAL_MAX_UINT32
)
920 const sal_uInt64
nAlignedWidth(AlignedWidth4Bytes(static_cast<sal_uLong
>(nBitsPerLine
)));
922 switch (aHeader
.nCompression
)
926 if (aHeader
.nBitCount
!= 8)
928 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
929 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
930 nMaxWidth
*= 256; //assume generous compression ratio
931 nMaxWidth
/= aHeader
.nHeight
;
932 if (nMaxWidth
< static_cast<sal_uInt64
>(aHeader
.nWidth
))
938 if (aHeader
.nBitCount
!= 4)
940 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
941 nMaxWidth
*= 512; //assume generous compression ratio
942 nMaxWidth
/= aHeader
.nHeight
;
943 if (nMaxWidth
< static_cast<sal_uInt64
>(aHeader
.nWidth
))
948 // tdf#122958 invalid compression value used
949 if (aHeader
.nCompression
& 0x000F)
951 // lets assume that there was an error in the generating application
952 // and allow through as COMPRESS_NONE if the bottom byte is 0
953 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader
.nCompression
<< ", rejecting bmp");
957 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader
.nCompression
<< ", assuming meant to be COMPRESS_NONE");
963 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
964 sal_uInt64 nMaxWidth
= pIStm
->remainingSize();
965 nMaxWidth
/= aHeader
.nHeight
;
966 if (nMaxWidth
< nAlignedWidth
)
972 const Size
aSizePixel(aHeader
.nWidth
, aHeader
.nHeight
);
973 AlphaMask aNewBmpAlpha
;
974 AlphaScopedWriteAccess pAccAlpha
;
975 bool bAlphaPossible(pBmpAlpha
&& aHeader
.nBitCount
== 32);
979 const bool bRedSet(0 != aHeader
.nV5RedMask
);
980 const bool bGreenSet(0 != aHeader
.nV5GreenMask
);
981 const bool bBlueSet(0 != aHeader
.nV5BlueMask
);
983 // some clipboard entries have alpha mask on zero to say that there is
984 // no alpha; do only use this when the other masks are set. The MS docu
985 // says that masks are only to be set when bV5Compression is set to
986 // BI_BITFIELDS, but there seem to exist a wild variety of usages...
987 if((bRedSet
|| bGreenSet
|| bBlueSet
) && (0 == aHeader
.nV5AlphaMask
))
989 bAlphaPossible
= false;
995 aNewBmpAlpha
= AlphaMask(aSizePixel
);
996 pAccAlpha
= AlphaScopedWriteAccess(aNewBmpAlpha
);
999 sal_uInt16
nBitCount(discretizeBitcount(aHeader
.nBitCount
));
1000 const BitmapPalette
* pPal
= &aPalette
;
1001 //ofz#948 match the surrounding logic of case TransparentType::Bitmap of
1002 //ReadDIBBitmapEx but do it while reading for performance
1003 const bool bIsAlpha
= (nBitCount
== 8 && !!aPalette
&& aPalette
.IsGreyPalette());
1004 const bool bForceToMonoWhileReading
= (bIsMask
&& !bIsAlpha
&& nBitCount
!= 1);
1005 if (bForceToMonoWhileReading
)
1009 SAL_WARN( "vcl", "forcing mask to monochrome");
1012 Bitmap
aNewBmp(aSizePixel
, nBitCount
, pPal
);
1013 BitmapScopedWriteAccess
pAcc(aNewBmp
);
1016 if (pAcc
->Width() != aHeader
.nWidth
|| pAcc
->Height() != aHeader
.nHeight
)
1022 bool bAlphaUsed(false);
1023 bool bRet
= ImplReadDIBBits(*pIStm
, aHeader
, *pAcc
, aPalette
, pAccAlpha
.get(), bTopDown
, bAlphaUsed
, nAlignedWidth
, bForceToMonoWhileReading
);
1025 if (bRet
&& aHeader
.nXPelsPerMeter
&& aHeader
.nYPelsPerMeter
)
1030 Fraction(1000, aHeader
.nXPelsPerMeter
),
1031 Fraction(1000, aHeader
.nYPelsPerMeter
));
1033 aNewBmp
.SetPrefMapMode(aMapMode
);
1034 aNewBmp
.SetPrefSize(Size(aHeader
.nWidth
, aHeader
.nHeight
));
1045 bAlphaPossible
= false;
1055 *pBmpAlpha
= aNewBmpAlpha
;
1062 bool ImplReadDIBFileHeader( SvStream
& rIStm
, sal_uLong
& rOffset
)
1066 const sal_uInt64 nStreamLength
= rIStm
.TellEnd();
1068 sal_uInt16 nTmp16
= 0;
1069 rIStm
.ReadUInt16( nTmp16
);
1071 if ( ( 0x4D42 == nTmp16
) || ( 0x4142 == nTmp16
) )
1073 sal_uInt32
nTmp32(0);
1074 if ( 0x4142 == nTmp16
)
1076 rIStm
.SeekRel( 12 );
1077 rIStm
.ReadUInt16( nTmp16
);
1079 rIStm
.ReadUInt32( nTmp32
);
1080 rOffset
= nTmp32
- 28;
1081 bRet
= ( 0x4D42 == nTmp16
);
1083 else // 0x4D42 == nTmp16, 'MB' from BITMAPFILEHEADER
1085 rIStm
.SeekRel( 8 ); // we are on bfSize member of BITMAPFILEHEADER, forward to bfOffBits
1086 rIStm
.ReadUInt32( nTmp32
); // read bfOffBits
1087 rOffset
= nTmp32
- 14; // adapt offset by sizeof(BITMAPFILEHEADER)
1088 bRet
= rIStm
.GetError() == ERRCODE_NONE
;
1091 if ( rOffset
>= nStreamLength
)
1093 // Offset claims that image starts past the end of the
1094 // stream. Unlikely.
1095 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
1100 rIStm
.SetError( SVSTREAM_FILEFORMAT_ERROR
);
1105 bool ImplWriteDIBPalette( SvStream
& rOStm
, BitmapReadAccess
const & rAcc
)
1107 const sal_uInt16 nColors
= rAcc
.GetPaletteEntryCount();
1108 const sal_uLong nPalSize
= nColors
* 4UL;
1109 std::unique_ptr
<sal_uInt8
[]> pEntries(new sal_uInt8
[ nPalSize
]);
1110 sal_uInt8
* pTmpEntry
= pEntries
.get();
1112 for( sal_uInt16 i
= 0; i
< nColors
; i
++ )
1114 const BitmapColor
& rPalColor
= rAcc
.GetPaletteColor( i
);
1116 *pTmpEntry
++ = rPalColor
.GetBlue();
1117 *pTmpEntry
++ = rPalColor
.GetGreen();
1118 *pTmpEntry
++ = rPalColor
.GetRed();
1122 rOStm
.WriteBytes( pEntries
.get(), nPalSize
);
1124 return rOStm
.GetError() == ERRCODE_NONE
;
1127 bool ImplWriteRLE( SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, bool bRLE4
)
1129 const sal_uLong nWidth
= rAcc
.Width();
1130 const sal_uLong nHeight
= rAcc
.Height();
1132 sal_uLong nSaveIndex
;
1134 sal_uLong nBufCount
;
1135 std::vector
<sal_uInt8
> aBuf(( nWidth
<< 1 ) + 2);
1140 for ( long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1142 sal_uInt8
* pTmp
= aBuf
.data();
1144 Scanline pScanline
= rAcc
.GetScanline( nY
);
1146 while( nX
< nWidth
)
1149 cPix
= rAcc
.GetIndexFromData( pScanline
, nX
++ );
1151 while( ( nX
< nWidth
) && ( nCount
< 255 )
1152 && ( cPix
== rAcc
.GetIndexFromData( pScanline
, nX
) ) )
1160 *pTmp
++ = static_cast<sal_uInt8
>(nCount
);
1161 *pTmp
++ = ( bRLE4
? ( ( cPix
<< 4 ) | cPix
) : cPix
);
1167 nSaveIndex
= nX
- 1;
1170 while( ( nX
< nWidth
) && ( nCount
< 256 )
1171 && ( cPix
= rAcc
.GetIndexFromData( pScanline
, nX
) ) != cLast
)
1184 *pTmp
++ = static_cast<sal_uInt8
>(--nCount
);
1188 for ( sal_uLong i
= 0; i
< nCount
; i
++, pTmp
++ )
1190 *pTmp
= rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ ) << 4;
1193 *pTmp
|= rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ );
1196 nCount
= ( nCount
+ 1 ) >> 1;
1200 for( sal_uLong i
= 0; i
< nCount
; i
++ )
1201 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nSaveIndex
++ );
1207 nBufCount
+= ( nCount
+ 3 );
1210 nBufCount
+= ( nCount
+ 2 );
1215 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nSaveIndex
) << (bRLE4
? 4 : 0);
1220 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, ++nSaveIndex
) << ( bRLE4
? 4 : 0 );
1229 aBuf
[ nBufCount
++ ] = 0;
1230 aBuf
[ nBufCount
++ ] = 0;
1232 rOStm
.WriteBytes(aBuf
.data(), nBufCount
);
1235 rOStm
.WriteUChar( 0 );
1236 rOStm
.WriteUChar( 1 );
1238 return rOStm
.GetError() == ERRCODE_NONE
;
1241 bool ImplWriteDIBBits(SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, BitmapReadAccess
const * pAccAlpha
, sal_uLong nCompression
, sal_uInt32
& rImageSize
)
1243 if(!pAccAlpha
&& BITFIELDS
== nCompression
)
1245 const ColorMask
& rMask
= rAcc
.GetColorMask();
1248 UInt32ToSVBT32( rMask
.GetRedMask(), aVal32
);
1249 rOStm
.WriteBytes( aVal32
, 4UL );
1251 UInt32ToSVBT32( rMask
.GetGreenMask(), aVal32
);
1252 rOStm
.WriteBytes( aVal32
, 4UL );
1254 UInt32ToSVBT32( rMask
.GetBlueMask(), aVal32
);
1255 rOStm
.WriteBytes( aVal32
, 4UL );
1257 rImageSize
= rOStm
.Tell();
1259 if( rAcc
.IsBottomUp() )
1260 rOStm
.WriteBytes(rAcc
.GetBuffer(), rAcc
.Height() * rAcc
.GetScanlineSize());
1263 for( long nY
= rAcc
.Height() - 1, nScanlineSize
= rAcc
.GetScanlineSize(); nY
>= 0; nY
-- )
1264 rOStm
.WriteBytes( rAcc
.GetScanline(nY
), nScanlineSize
);
1267 else if(!pAccAlpha
&& ((RLE_4
== nCompression
) || (RLE_8
== nCompression
)))
1269 rImageSize
= rOStm
.Tell();
1270 ImplWriteRLE( rOStm
, rAcc
, RLE_4
== nCompression
);
1272 else if(!nCompression
)
1274 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are not
1275 // handled properly below (would have to set color masks, and
1276 // nCompression=BITFIELDS - but color mask is not set for
1277 // formats != *_TC_*). Note that this very problem might cause
1278 // trouble at other places - the introduction of 32 bit RGBA
1279 // bitmaps is relatively recent.
1280 // #i59239# discretize bitcount for aligned width to 1,4,8,24
1281 // (other cases are not written below)
1282 const sal_uInt16
nBitCount(pAccAlpha
? 32 : discretizeBitcount(rAcc
.GetBitCount()));
1283 const sal_uLong
nAlignedWidth(AlignedWidth4Bytes(rAcc
.Width() * nBitCount
));
1284 bool bNative(false);
1286 switch(rAcc
.GetScanlineFormat())
1288 case ScanlineFormat::N1BitMsbPal
:
1289 case ScanlineFormat::N4BitMsnPal
:
1290 case ScanlineFormat::N8BitPal
:
1291 case ScanlineFormat::N24BitTcBgr
:
1293 if(!pAccAlpha
&& rAcc
.IsBottomUp() && (rAcc
.GetScanlineSize() == nAlignedWidth
))
1307 rImageSize
= rOStm
.Tell();
1311 rOStm
.WriteBytes(rAcc
.GetBuffer(), nAlignedWidth
* rAcc
.Height());
1315 const long nWidth(rAcc
.Width());
1316 const long nHeight(rAcc
.Height());
1317 std::vector
<sal_uInt8
> aBuf(nAlignedWidth
);
1322 //valgrind, zero out the trailing unused alignment bytes
1323 size_t nUnusedBytes
= nAlignedWidth
- ((nWidth
+7) / 8);
1324 memset(aBuf
.data() + nAlignedWidth
- nUnusedBytes
, 0, nUnusedBytes
);
1326 for( long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1328 sal_uInt8
* pTmp
= aBuf
.data();
1330 Scanline pScanline
= rAcc
.GetScanline( nY
);
1332 for( long nX
= 0, nShift
= 8; nX
< nWidth
; nX
++ )
1341 cTmp
|= rAcc
.GetIndexFromData( pScanline
, nX
) << --nShift
;
1345 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1352 //valgrind, zero out the trailing unused alignment bytes
1353 size_t nUnusedBytes
= nAlignedWidth
- ((nWidth
+1) / 2);
1354 memset(aBuf
.data() + nAlignedWidth
- nUnusedBytes
, 0, nUnusedBytes
);
1356 for( long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1358 sal_uInt8
* pTmp
= aBuf
.data();
1360 Scanline pScanline
= rAcc
.GetScanline( nY
);
1362 for( long nX
= 0, nShift
= 2; nX
< nWidth
; nX
++ )
1371 cTmp
|= rAcc
.GetIndexFromData( pScanline
, nX
) << ( --nShift
<< 2 );
1374 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1381 for( long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1383 sal_uInt8
* pTmp
= aBuf
.data();
1384 Scanline pScanline
= rAcc
.GetScanline( nY
);
1386 for( long nX
= 0; nX
< nWidth
; nX
++ )
1387 *pTmp
++ = rAcc
.GetIndexFromData( pScanline
, nX
);
1389 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1396 //valgrind, zero out the trailing unused alignment bytes
1397 size_t nUnusedBytes
= nAlignedWidth
- nWidth
* 3;
1398 memset(aBuf
.data() + nAlignedWidth
- nUnusedBytes
, 0, nUnusedBytes
);
1401 // #i59239# fallback to 24 bit format, if bitcount is non-default
1404 BitmapColor aPixelColor
;
1405 const bool bWriteAlpha(32 == nBitCount
&& pAccAlpha
);
1407 for( long nY
= nHeight
- 1; nY
>= 0; nY
-- )
1409 sal_uInt8
* pTmp
= aBuf
.data();
1410 Scanline pScanlineAlpha
= bWriteAlpha
? pAccAlpha
->GetScanline( nY
) : nullptr;
1412 for( long nX
= 0; nX
< nWidth
; nX
++ )
1414 // when alpha is used, this may be non-24bit main bitmap, so use GetColor
1415 // instead of GetPixel to ensure RGB value
1416 aPixelColor
= rAcc
.GetColor( nY
, nX
);
1418 *pTmp
++ = aPixelColor
.GetBlue();
1419 *pTmp
++ = aPixelColor
.GetGreen();
1420 *pTmp
++ = aPixelColor
.GetRed();
1424 *pTmp
++ = sal_uInt8(0xff) - pAccAlpha
->GetIndexFromData( pScanlineAlpha
, nX
);
1428 rOStm
.WriteBytes(aBuf
.data(), nAlignedWidth
);
1436 rImageSize
= rOStm
.Tell() - rImageSize
;
1438 return (!rOStm
.GetError());
1441 bool ImplWriteDIBBody(const Bitmap
& rBitmap
, SvStream
& rOStm
, BitmapReadAccess
const & rAcc
, BitmapReadAccess
const * pAccAlpha
, bool bCompressed
)
1443 const MapMode
aMapPixel(MapUnit::MapPixel
);
1444 DIBV5Header aHeader
;
1445 sal_uLong
nImageSizePos(0);
1446 sal_uLong
nEndPos(0);
1447 sal_uInt32
nCompression(COMPRESS_NONE
);
1450 aHeader
.nSize
= pAccAlpha
? DIBV5HEADERSIZE
: DIBINFOHEADERSIZE
; // size dependent on CF_DIB type to use
1451 aHeader
.nWidth
= rAcc
.Width();
1452 aHeader
.nHeight
= rAcc
.Height();
1453 aHeader
.nPlanes
= 1;
1455 if(!pAccAlpha
&& isBitfieldCompression(rAcc
.GetScanlineFormat()))
1457 aHeader
.nBitCount
= 32;
1458 aHeader
.nSizeImage
= rAcc
.Height() * rAcc
.GetScanlineSize();
1459 nCompression
= BITFIELDS
;
1463 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are
1464 // not handled properly below (would have to set color
1465 // masks, and nCompression=BITFIELDS - but color mask is
1466 // not set for formats != *_TC_*). Note that this very
1467 // problem might cause trouble at other places - the
1468 // introduction of 32 bit RGBA bitmaps is relatively
1470 // #i59239# discretize bitcount to 1,4,8,24 (other cases
1471 // are not written below)
1472 const sal_uInt16
nBitCount(pAccAlpha
? 32 : discretizeBitcount(rAcc
.GetBitCount()));
1473 aHeader
.nBitCount
= nBitCount
;
1474 aHeader
.nSizeImage
= rAcc
.Height() * AlignedWidth4Bytes(rAcc
.Width() * aHeader
.nBitCount
);
1480 nCompression
= RLE_4
;
1482 else if(8 == nBitCount
)
1484 nCompression
= RLE_8
;
1489 if((rOStm
.GetCompressMode() & SvStreamCompressFlags::ZBITMAP
) && (rOStm
.GetVersion() >= SOFFICE_FILEFORMAT_40
))
1491 aHeader
.nCompression
= ZCOMPRESS
;
1495 aHeader
.nCompression
= nCompression
;
1498 if(rBitmap
.GetPrefSize().Width() && rBitmap
.GetPrefSize().Height() && (rBitmap
.GetPrefMapMode() != aMapPixel
))
1500 // #i48108# Try to recover xpels/ypels as previously stored on
1501 // disk. The problem with just converting maPrefSize to 100th
1502 // mm and then relating that to the bitmap pixel size is that
1503 // MapMode is integer-based, and suffers from roundoffs,
1504 // especially if maPrefSize is small. Trying to circumvent
1505 // that by performing part of the math in floating point.
1506 const Size
aScale100000(OutputDevice::LogicToLogic(Size(100000, 100000), MapMode(MapUnit::Map100thMM
), rBitmap
.GetPrefMapMode()));
1507 const double fBmpWidthM(static_cast<double>(rBitmap
.GetPrefSize().Width()) / aScale100000
.Width());
1508 const double fBmpHeightM(static_cast<double>(rBitmap
.GetPrefSize().Height()) / aScale100000
.Height());
1510 if(!basegfx::fTools::equalZero(fBmpWidthM
) && !basegfx::fTools::equalZero(fBmpHeightM
))
1512 aHeader
.nXPelsPerMeter
= basegfx::fround(rAcc
.Width() / fabs(fBmpWidthM
));
1513 aHeader
.nYPelsPerMeter
= basegfx::fround(rAcc
.Height() / fabs(fBmpHeightM
));
1517 aHeader
.nColsUsed
= ((!pAccAlpha
&& aHeader
.nBitCount
<= 8) ? rAcc
.GetPaletteEntryCount() : 0);
1518 aHeader
.nColsImportant
= 0;
1520 rOStm
.WriteUInt32( aHeader
.nSize
);
1521 rOStm
.WriteInt32( aHeader
.nWidth
);
1522 rOStm
.WriteInt32( aHeader
.nHeight
);
1523 rOStm
.WriteUInt16( aHeader
.nPlanes
);
1524 rOStm
.WriteUInt16( aHeader
.nBitCount
);
1525 rOStm
.WriteUInt32( aHeader
.nCompression
);
1527 nImageSizePos
= rOStm
.Tell();
1528 rOStm
.SeekRel( sizeof( aHeader
.nSizeImage
) );
1530 rOStm
.WriteInt32( aHeader
.nXPelsPerMeter
);
1531 rOStm
.WriteInt32( aHeader
.nYPelsPerMeter
);
1532 rOStm
.WriteUInt32( aHeader
.nColsUsed
);
1533 rOStm
.WriteUInt32( aHeader
.nColsImportant
);
1535 if(pAccAlpha
) // only write DIBV5 when asked to do so
1537 aHeader
.nV5CSType
= 0x57696E20; // LCS_WINDOWS_COLOR_SPACE
1538 aHeader
.nV5Intent
= 0x00000004; // LCS_GM_IMAGES
1540 rOStm
.WriteUInt32( aHeader
.nV5RedMask
);
1541 rOStm
.WriteUInt32( aHeader
.nV5GreenMask
);
1542 rOStm
.WriteUInt32( aHeader
.nV5BlueMask
);
1543 rOStm
.WriteUInt32( aHeader
.nV5AlphaMask
);
1544 rOStm
.WriteUInt32( aHeader
.nV5CSType
);
1546 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzRed
.aXyzX
);
1547 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzRed
.aXyzY
);
1548 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzRed
.aXyzZ
);
1549 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzGreen
.aXyzX
);
1550 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzGreen
.aXyzY
);
1551 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzGreen
.aXyzZ
);
1552 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzBlue
.aXyzX
);
1553 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzBlue
.aXyzY
);
1554 rOStm
.WriteInt32( aHeader
.aV5Endpoints
.aXyzBlue
.aXyzZ
);
1556 rOStm
.WriteUInt32( aHeader
.nV5GammaRed
);
1557 rOStm
.WriteUInt32( aHeader
.nV5GammaGreen
);
1558 rOStm
.WriteUInt32( aHeader
.nV5GammaBlue
);
1559 rOStm
.WriteUInt32( aHeader
.nV5Intent
);
1560 rOStm
.WriteUInt32( aHeader
.nV5ProfileData
);
1561 rOStm
.WriteUInt32( aHeader
.nV5ProfileSize
);
1562 rOStm
.WriteUInt32( aHeader
.nV5Reserved
);
1565 if(ZCOMPRESS
== aHeader
.nCompression
)
1568 SvMemoryStream
aMemStm(aHeader
.nSizeImage
+ 4096, 65535);
1569 sal_uLong
nCodedPos(rOStm
.Tell());
1570 sal_uLong
nLastPos(0);
1571 sal_uInt32
nCodedSize(0);
1572 sal_uInt32
nUncodedSize(0);
1574 // write uncoded data palette
1575 if(aHeader
.nColsUsed
)
1577 ImplWriteDIBPalette(aMemStm
, rAcc
);
1580 // write uncoded bits
1581 bRet
= ImplWriteDIBBits(aMemStm
, rAcc
, pAccAlpha
, nCompression
, aHeader
.nSizeImage
);
1584 nUncodedSize
= aMemStm
.Tell();
1586 // seek over compress info
1589 // write compressed data
1590 aCodec
.BeginCompression(3);
1591 aCodec
.Write(rOStm
, static_cast<sal_uInt8
const *>(aMemStm
.GetData()), nUncodedSize
);
1592 aCodec
.EndCompression();
1594 // update compress info ( coded size, uncoded size, uncoded compression )
1595 nLastPos
= rOStm
.Tell();
1596 nCodedSize
= nLastPos
- nCodedPos
- 12;
1597 rOStm
.Seek(nCodedPos
);
1598 rOStm
.WriteUInt32( nCodedSize
).WriteUInt32( nUncodedSize
).WriteUInt32( nCompression
);
1599 rOStm
.Seek(nLastPos
);
1603 bRet
= (ERRCODE_NONE
== rOStm
.GetError());
1608 if(aHeader
.nColsUsed
)
1610 ImplWriteDIBPalette(rOStm
, rAcc
);
1613 bRet
= ImplWriteDIBBits(rOStm
, rAcc
, pAccAlpha
, aHeader
.nCompression
, aHeader
.nSizeImage
);
1616 nEndPos
= rOStm
.Tell();
1617 rOStm
.Seek(nImageSizePos
);
1618 rOStm
.WriteUInt32( aHeader
.nSizeImage
);
1619 rOStm
.Seek(nEndPos
);
1624 bool ImplWriteDIBFileHeader(SvStream
& rOStm
, BitmapReadAccess
const & rAcc
)
1626 const sal_uInt32
nPalCount((rAcc
.HasPalette() ? rAcc
.GetPaletteEntryCount() : isBitfieldCompression(rAcc
.GetScanlineFormat()) ? 3UL : 0UL));
1627 const sal_uInt32
nOffset(14 + DIBINFOHEADERSIZE
+ nPalCount
* 4UL);
1629 rOStm
.WriteUInt16( 0x4D42 ); // 'MB' from BITMAPFILEHEADER
1630 rOStm
.WriteUInt32( nOffset
+ (rAcc
.Height() * rAcc
.GetScanlineSize()) );
1631 rOStm
.WriteUInt16( 0 );
1632 rOStm
.WriteUInt16( 0 );
1633 rOStm
.WriteUInt32( nOffset
);
1635 return rOStm
.GetError() == ERRCODE_NONE
;
1640 AlphaMask
* pTargetAlpha
,
1644 bool bMSOFormat
=false)
1646 const SvStreamEndian
nOldFormat(rIStm
.GetEndian());
1647 const sal_uLong
nOldPos(rIStm
.Tell());
1648 sal_uLong
nOffset(0);
1651 rIStm
.SetEndian(SvStreamEndian::LITTLE
);
1655 if(ImplReadDIBFileHeader(rIStm
, nOffset
))
1657 bRet
= ImplReadDIBBody(rIStm
, rTarget
, nOffset
>= DIBV5HEADERSIZE
? pTargetAlpha
: nullptr, nOffset
, bIsMask
, bMSOFormat
);
1662 bRet
= ImplReadDIBBody(rIStm
, rTarget
, nullptr, nOffset
, bIsMask
, bMSOFormat
);
1667 if(!rIStm
.GetError())
1669 rIStm
.SetError(SVSTREAM_GENERALERROR
);
1672 rIStm
.Seek(nOldPos
);
1675 rIStm
.SetEndian(nOldFormat
);
1681 const Bitmap
& rSource
,
1686 const Size
aSizePix(rSource
.GetSizePixel());
1689 if(aSizePix
.Width() && aSizePix
.Height())
1691 Bitmap::ScopedReadAccess
pAcc(const_cast< Bitmap
& >(rSource
));
1692 Bitmap::ScopedReadAccess pAccAlpha
;
1693 const SvStreamEndian
nOldFormat(rOStm
.GetEndian());
1694 const sal_uLong
nOldPos(rOStm
.Tell());
1696 rOStm
.SetEndian(SvStreamEndian::LITTLE
);
1702 if(ImplWriteDIBFileHeader(rOStm
, *pAcc
))
1704 bRet
= ImplWriteDIBBody(rSource
, rOStm
, *pAcc
, pAccAlpha
.get(), bCompressed
);
1709 bRet
= ImplWriteDIBBody(rSource
, rOStm
, *pAcc
, pAccAlpha
.get(), bCompressed
);
1719 rOStm
.SetError(SVSTREAM_GENERALERROR
);
1720 rOStm
.Seek(nOldPos
);
1723 rOStm
.SetEndian(nOldFormat
);
1729 } // unnamed namespace
1737 return ImplReadDIB(rTarget
, nullptr, rIStm
, bFileHeader
, false, bMSOFormat
);
1740 bool ReadDIBBitmapEx(
1747 bool bRetval(ImplReadDIB(aBmp
, nullptr, rIStm
, bFileHeader
, /*bMask*/false, bMSOFormat
) && !rIStm
.GetError());
1751 // base bitmap was read, set as return value and try to read alpha extra-data
1752 const sal_uLong
nStmPos(rIStm
.Tell());
1753 sal_uInt32
nMagic1(0);
1754 sal_uInt32
nMagic2(0);
1756 rTarget
= BitmapEx(aBmp
);
1757 rIStm
.ReadUInt32( nMagic1
).ReadUInt32( nMagic2
);
1758 bRetval
= (0x25091962 == nMagic1
) && (0xACB20201 == nMagic2
) && !rIStm
.GetError();
1763 rIStm
.ReadUChar( tmp
);
1764 TransparentType transparent
= static_cast<TransparentType
>(tmp
);
1765 bRetval
= !rIStm
.GetError();
1769 switch (transparent
)
1771 case TransparentType::Bitmap
:
1775 bRetval
= ImplReadDIB(aMask
, nullptr, rIStm
, true, true);
1781 // do we have an alpha mask?
1782 if((8 == aMask
.GetBitCount()) && aMask
.HasGreyPalette())
1786 // create alpha mask quickly (without greyscale conversion)
1787 aAlpha
.ImplSetBitmap(aMask
);
1788 rTarget
= BitmapEx(aBmp
, aAlpha
);
1792 rTarget
= BitmapEx(aBmp
, aMask
);
1798 case TransparentType::Color
:
1800 Color aTransparentColor
;
1802 tools::GenericTypeSerializer
aSerializer(rIStm
);
1803 aSerializer
.readColor(aTransparentColor
);
1805 bRetval
= !rIStm
.GetError();
1809 rTarget
= BitmapEx(aBmp
, aTransparentColor
);
1820 // alpha extra data could not be read; reset, but use base bitmap as result
1822 rIStm
.Seek(nStmPos
);
1832 AlphaMask
& rTargetAlpha
,
1835 return ImplReadDIB(rTarget
, &rTargetAlpha
, rIStm
, true);
1840 const unsigned char* pBuf
,
1841 const ScanlineFormat nFormat
,
1845 BitmapScopedWriteAccess
pWriteAccess(rTarget
.maBitmap
.AcquireWriteAccess(), rTarget
.maBitmap
);
1846 for (int nRow
= 0; nRow
< nHeight
; ++nRow
)
1848 pWriteAccess
->CopyScanline(nRow
, pBuf
+ (nStride
* nRow
), nFormat
, nStride
);
1855 const Bitmap
& rSource
,
1860 return ImplWriteDIB(rSource
, rOStm
, bCompressed
, bFileHeader
);
1864 const BitmapEx
& rSource
,
1868 return ImplWriteDIB(rSource
.GetBitmapRef(), rOStm
, bCompressed
, /*bFileHeader*/true);
1871 bool WriteDIBBitmapEx(
1872 const BitmapEx
& rSource
,
1875 if(ImplWriteDIB(rSource
.GetBitmap(), rOStm
, true, true))
1877 rOStm
.WriteUInt32( 0x25091962 );
1878 rOStm
.WriteUInt32( 0xACB20201 );
1879 rOStm
.WriteUChar( static_cast<sal_uChar
>(rSource
.meTransparent
) );
1881 if(TransparentType::Bitmap
== rSource
.meTransparent
)
1883 return ImplWriteDIB(rSource
.maMask
, rOStm
, true, true);
1885 else if(TransparentType::Color
== rSource
.meTransparent
)
1887 tools::GenericTypeSerializer
aSerializer(rOStm
);
1888 aSerializer
.writeColor(rSource
.maTransparentColor
);
1896 sal_uInt32
getDIBV5HeaderSize()
1898 return DIBV5HEADERSIZE
;
1901 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */