bump product version to 7.2.5.1
[LibreOffice.git] / vcl / source / bitmap / dibtools.cxx
blob17e5c51c88caa13d2b3bc1c168f397da7612a59f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
23 #include <cassert>
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>
37 #include <memory>
39 #define DIBCOREHEADERSIZE ( 12UL )
40 #define DIBINFOHEADERSIZE ( sizeof(DIBInfoHeader) )
41 #define DIBV5HEADERSIZE ( sizeof(DIBV5Header) )
43 // - DIBInfoHeader and DIBV5Header
45 typedef sal_Int32 FXPT2DOT30;
47 namespace
50 struct CIEXYZ
52 FXPT2DOT30 aXyzX;
53 FXPT2DOT30 aXyzY;
54 FXPT2DOT30 aXyzZ;
56 CIEXYZ()
57 : aXyzX(0),
58 aXyzY(0),
59 aXyzZ(0)
63 struct CIEXYZTriple
65 CIEXYZ aXyzRed;
66 CIEXYZ aXyzGreen;
67 CIEXYZ aXyzBlue;
69 CIEXYZTriple()
70 : aXyzRed(),
71 aXyzGreen(),
72 aXyzBlue()
76 struct DIBInfoHeader
78 sal_uInt32 nSize;
79 sal_Int32 nWidth;
80 sal_Int32 nHeight;
81 sal_uInt16 nPlanes;
82 sal_uInt16 nBitCount;
83 sal_uInt32 nCompression;
84 sal_uInt32 nSizeImage;
85 sal_Int32 nXPelsPerMeter;
86 sal_Int32 nYPelsPerMeter;
87 sal_uInt32 nColsUsed;
88 sal_uInt32 nColsImportant;
90 DIBInfoHeader()
91 : nSize(0),
92 nWidth(0),
93 nHeight(0),
94 nPlanes(0),
95 nBitCount(0),
96 nCompression(0),
97 nSizeImage(0),
98 nXPelsPerMeter(0),
99 nYPelsPerMeter(0),
100 nColsUsed(0),
101 nColsImportant(0)
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;
121 DIBV5Header()
122 : DIBInfoHeader(),
123 nV5RedMask(0),
124 nV5GreenMask(0),
125 nV5BlueMask(0),
126 nV5AlphaMask(0),
127 nV5CSType(0),
128 aV5Endpoints(),
129 nV5GammaRed(0),
130 nV5GammaGreen(0),
131 nV5GammaBlue(0),
132 nV5Intent(0),
133 nV5ProfileData(0),
134 nV5ProfileSize(0),
135 nV5Reserved(0)
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)
154 return false;
155 // BITMAPINFOHEADER or BITMAPCOREHEADER or BITMAPV5HEADER
156 sal_uInt64 const aStartPos(rIStm.Tell());
157 rIStm.ReadUInt32( rHeader.nSize );
159 // BITMAPCOREHEADER
160 if ( rHeader.nSize == DIBCOREHEADERSIZE )
162 sal_Int16 nTmp16;
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 )
171 sal_Int16 nTmp16(0);
172 rIStm.ReadInt16(nTmp16);
173 rHeader.nWidth = nTmp16;
174 rIStm.ReadInt16(nTmp16);
175 rHeader.nHeight = nTmp16;
176 sal_uInt8 nTmp8(0);
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 );
192 else
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) {
199 rIStm.ReadUInt16(v);
200 nUsed += sizeof(v);
203 auto readInt32 = [&nUsed, &rHeader, &rIStm](sal_Int32 & v) {
204 if (nUsed < rHeader.nSize) {
205 rIStm.ReadInt32(v);
206 nUsed += sizeof(v);
209 auto readUInt32 = [&nUsed, &rHeader, &rIStm](sal_uInt32 & v) {
210 if (nUsed < rHeader.nSize) {
211 rIStm.ReadUInt32(v);
212 nUsed += sizeof(v);
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 );
254 // seek to EndPos
255 if (!checkSeek(rIStm, aStartPos + rHeader.nSize))
256 return false;
259 if (rHeader.nHeight == SAL_MIN_INT32)
260 return false;
262 if ( rHeader.nHeight < 0 )
264 bTopDown = true;
265 rHeader.nHeight *= -1;
267 else
269 bTopDown = false;
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)
288 return false;
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)
295 return false;
298 return rIStm.good();
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)
310 return false;
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++ );
320 if( bQuad )
321 pTmpEntry++;
323 rPal[i] = aPalColor;
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)
352 return rColor;
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;
363 sal_uLong nRunByte;
364 sal_uLong nX = 0;
365 sal_uInt8 cTmp;
366 bool bEndDecoding = false;
370 if (pRLE == pEndRLE)
371 return false;
372 if( ( nCountByte = *pRLE++ ) == 0 )
374 if (pRLE == pEndRLE)
375 return false;
376 nRunByte = *pRLE++;
378 if( nRunByte > 2 )
380 Scanline pScanline = rAcc.GetScanline(nY);
381 if( bRLE4 )
383 nCountByte = nRunByte >> 1;
385 for( sal_uLong i = 0; i < nCountByte; i++ )
387 if (pRLE == pEndRLE)
388 return false;
390 cTmp = *pRLE++;
392 if( nX < nWidth )
393 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp >> 4, rPalette, bForceToMonoWhileReading));
395 if( nX < nWidth )
396 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp & 0x0f, rPalette, bForceToMonoWhileReading));
399 if( nRunByte & 1 )
401 if (pRLE == pEndRLE)
402 return false;
404 if( nX < nWidth )
405 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(*pRLE >> 4, rPalette, bForceToMonoWhileReading));
407 pRLE++;
410 if( ( ( nRunByte + 1 ) >> 1 ) & 1 )
412 if (pRLE == pEndRLE)
413 return false;
415 pRLE++;
418 else
420 for( sal_uLong i = 0; i < nRunByte; i++ )
422 if (pRLE == pEndRLE)
423 return false;
425 if( nX < nWidth )
426 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(*pRLE, rPalette, bForceToMonoWhileReading));
428 pRLE++;
431 if( nRunByte & 1 )
433 if (pRLE == pEndRLE)
434 return false;
436 pRLE++;
440 else if( !nRunByte )
442 nY--;
443 nX = 0;
445 else if( nRunByte == 1 )
446 bEndDecoding = true;
447 else
449 if (pRLE == pEndRLE)
450 return false;
452 nX += *pRLE++;
454 if (pRLE == pEndRLE)
455 return false;
457 nY -= *pRLE++;
460 else
462 if (pRLE == pEndRLE)
463 return false;
464 cTmp = *pRLE++;
466 Scanline pScanline = rAcc.GetScanline(nY);
467 if( bRLE4 )
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));
474 if( nX < nWidth )
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));
481 else
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));
490 return true;
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);
500 bool bNative(false);
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));
504 // Is native format?
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 ) );
513 break;
516 default:
518 break;
522 // Read data
523 if (bNative)
525 if (nAlignedWidth
526 > std::numeric_limits<std::size_t>::max() / rHeader.nHeight)
528 return false;
530 std::size_t n = nAlignedWidth * rHeader.nHeight;
531 if (rIStm.ReadBytes(rAcc.GetBuffer(), n) != n)
533 return false;
536 else
538 // Read color mask
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))
551 return false;
553 if (bRLE)
555 if(!rHeader.nSizeImage)
557 rHeader.nSizeImage = rIStm.remainingSize();
560 if (rHeader.nSizeImage > rIStm.remainingSize())
561 return false;
562 std::vector<sal_uInt8> aBuffer(rHeader.nSizeImage);
563 if (rIStm.ReadBytes(aBuffer.data(), rHeader.nSizeImage) != rHeader.nSizeImage)
564 return false;
565 if (!ImplDecodeRLE(aBuffer.data(), rHeader, rAcc, rPalette, bForceToMonoWhileReading, RLE_4 == rHeader.nCompression))
566 return false;
568 else
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
576 return false;
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)
586 case 1:
588 for( ; nCount--; nY += nI )
590 sal_uInt8 * pTmp = aBuf.data();
591 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
592 != nAlignedWidth)
594 return false;
596 sal_uInt8 cTmp = *pTmp++;
597 Scanline pScanline = rAcc.GetScanline(nY);
598 for( tools::Long nX = 0, nShift = 8; nX < nWidth; nX++ )
600 if( !nShift )
602 nShift = 8;
603 cTmp = *pTmp++;
606 auto nIndex = (cTmp >> --nShift) & 1;
607 rAcc.SetPixelOnData(pScanline, nX, SanitizePaletteIndex(nIndex, rPalette, bForceToMonoWhileReading));
611 break;
613 case 4:
615 for( ; nCount--; nY += nI )
617 sal_uInt8 * pTmp = aBuf.data();
618 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
619 != nAlignedWidth)
621 return false;
623 sal_uInt8 cTmp = *pTmp++;
624 Scanline pScanline = rAcc.GetScanline(nY);
625 for( tools::Long nX = 0, nShift = 2; nX < nWidth; nX++ )
627 if( !nShift )
629 nShift = 2;
630 cTmp = *pTmp++;
633 auto nIndex = (cTmp >> ( --nShift << 2 ) ) & 0x0f;
634 rAcc.SetPixelOnData(pScanline, nX, SanitizePaletteIndex(nIndex, rPalette, bForceToMonoWhileReading));
638 break;
640 case 8:
642 for( ; nCount--; nY += nI )
644 sal_uInt8 * pTmp = aBuf.data();
645 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
646 != nAlignedWidth)
648 return false;
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));
659 break;
661 case 16:
663 ColorMaskElement aRedMask(nRMask);
664 if (!aRedMask.CalcMaskShift())
665 return false;
666 ColorMaskElement aGreenMask(nGMask);
667 if (!aGreenMask.CalcMaskShift())
668 return false;
669 ColorMaskElement aBlueMask(nBMask);
670 if (!aBlueMask.CalcMaskShift())
671 return false;
673 ColorMask aMask(aRedMask, aGreenMask, aBlueMask);
674 BitmapColor aColor;
676 for( ; nCount--; nY += nI )
678 sal_uInt16 * pTmp16 = reinterpret_cast<sal_uInt16*>(aBuf.data());
679 if (rIStm.ReadBytes(pTmp16, nAlignedWidth)
680 != nAlignedWidth)
682 return false;
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));
693 break;
695 case 24:
697 BitmapColor aPixelColor;
699 for( ; nCount--; nY += nI )
701 sal_uInt8* pTmp = aBuf.data();
702 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
703 != nAlignedWidth)
705 return false;
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));
718 break;
720 case 32:
722 ColorMaskElement aRedMask(nRMask);
723 if (!aRedMask.CalcMaskShift())
724 return false;
725 ColorMaskElement aGreenMask(nGMask);
726 if (!aGreenMask.CalcMaskShift())
727 return false;
728 ColorMaskElement aBlueMask(nBMask);
729 if (!aBlueMask.CalcMaskShift())
730 return false;
731 ColorMask aMask(aRedMask, aGreenMask, aBlueMask);
733 BitmapColor aColor;
734 sal_uInt32* pTmp32;
736 if(pAccAlpha)
738 sal_uInt8 aAlpha;
740 for( ; nCount--; nY += nI )
742 pTmp32 = reinterpret_cast<sal_uInt32*>(aBuf.data());
743 if (rIStm.ReadBytes(pTmp32, nAlignedWidth)
744 != nAlignedWidth)
746 return false;
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;
760 else
762 for( ; nCount--; nY += nI )
764 pTmp32 = reinterpret_cast<sal_uInt32*>(aBuf.data());
765 if (rIStm.ReadBytes(pTmp32, nAlignedWidth)
766 != nAlignedWidth)
768 return false;
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)
789 DIBV5Header aHeader;
790 const sal_uLong nStmPos = rIStm.Tell();
791 bool bTopDown(false);
793 if (!ImplReadDIBInfoHeader(rIStm, aHeader, bTopDown, bMSOFormat))
794 return false;
796 //BI_BITCOUNT_0 jpeg/png is unsupported
797 if (aHeader.nBitCount == 0)
798 return false;
800 if (aHeader.nWidth <= 0 || aHeader.nHeight <= 0)
801 return false;
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.
809 return false;
812 sal_uInt16 nColors(0);
813 SvStream* pIStm;
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);
823 else
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
842 // the encoded block
843 pMemStm->WriteStream(rIStm, nCodedSize);
844 pMemStm->Seek(0);
846 size_t nSizeInc(4 * pMemStm->remainingSize());
847 if (nUncodedSize < nSizeInc)
848 nSizeInc = nUncodedSize;
850 if (nSizeInc > 0)
852 // decode buffer
853 ZCodec aCodec;
854 aCodec.BeginCompression();
855 aData.resize(nSizeInc);
856 size_t nDataPos(0);
857 while (nUncodedSize > nDataPos)
859 assert(aData.size() > nDataPos);
860 const size_t nToRead(std::min<size_t>(nUncodedSize - nDataPos, aData.size() - nDataPos));
861 assert(nToRead > 0);
862 assert(!aData.empty());
863 const tools::Long nRead = aCodec.Read(*pMemStm, aData.data() + nDataPos, sal_uInt32(nToRead));
864 if (nRead > 0)
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);
871 else
873 break;
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();
883 if (aData.empty())
885 // add something so we can take address of the first element
886 aData.resize(1);
887 nUncodedSize = 0;
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);
896 nOffset = 0;
898 else
900 pIStm = &rIStm;
903 // read palette
904 BitmapPalette aPalette;
905 if (nColors)
907 aPalette.SetEntryCount(nColors);
908 ImplReadDIBPalette(*pIStm, aPalette, aHeader.nSize != DIBCOREHEADERSIZE);
911 if (pIStm->GetError())
912 return false;
914 if (nOffset)
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)
921 return false;
922 const sal_uInt64 nAlignedWidth(AlignedWidth4Bytes(static_cast<sal_uLong>(nBitsPerLine)));
924 switch (aHeader.nCompression)
926 case RLE_8:
928 if (aHeader.nBitCount != 8)
929 return false;
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))
935 return false;
936 break;
938 case RLE_4:
940 if (aHeader.nBitCount != 4)
941 return false;
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))
946 return false;
947 break;
949 default:
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");
956 return false;
958 else
959 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader.nCompression << ", assuming meant to be COMPRESS_NONE");
960 [[fallthrough]];
961 case BITFIELDS:
962 case ZCOMPRESS:
963 case 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)
969 return false;
970 break;
974 const Size aSizePixel(aHeader.nWidth, aHeader.nHeight);
975 AlphaMask aNewBmpAlpha;
976 AlphaScopedWriteAccess pAccAlpha;
977 bool bAlphaPossible(pBmpAlpha && aHeader.nBitCount == 32);
979 if (bAlphaPossible)
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;
995 if (bAlphaPossible)
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)
1010 pPal = nullptr;
1011 ePixelFormat = vcl::PixelFormat::N1_BPP;
1012 SAL_WARN( "vcl", "forcing mask to monochrome");
1015 Bitmap aNewBmp(aSizePixel, ePixelFormat, pPal);
1016 BitmapScopedWriteAccess pAcc(aNewBmp);
1017 if (!pAcc)
1018 return false;
1019 if (pAcc->Width() != aHeader.nWidth || pAcc->Height() != aHeader.nHeight)
1021 return false;
1024 // read bits
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)
1030 MapMode aMapMode(
1031 MapUnit::MapMM,
1032 Point(),
1033 Fraction(1000, aHeader.nXPelsPerMeter),
1034 Fraction(1000, aHeader.nYPelsPerMeter));
1036 aNewBmp.SetPrefMapMode(aMapMode);
1037 aNewBmp.SetPrefSize(Size(aHeader.nWidth, aHeader.nHeight));
1040 pAcc.reset();
1042 if (bAlphaPossible)
1044 pAccAlpha.reset();
1046 if(!bAlphaUsed)
1048 bAlphaPossible = false;
1052 if (bRet)
1054 rBmp = aNewBmp;
1056 if(bAlphaPossible)
1058 *pBmpAlpha = aNewBmpAlpha;
1062 return bRet;
1065 bool ImplReadDIBFileHeader( SvStream& rIStm, sal_uLong& rOffset )
1067 bool bRet = false;
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 );
1081 rIStm.SeekRel( 8 );
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 );
1099 bRet = false;
1102 else
1103 rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
1105 return bRet;
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();
1122 *pTmpEntry++ = 0;
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();
1134 sal_uLong nX;
1135 sal_uLong nSaveIndex;
1136 sal_uLong nCount;
1137 sal_uLong nBufCount;
1138 std::vector<sal_uInt8> aBuf(( nWidth << 1 ) + 2);
1139 sal_uInt8 cPix;
1140 sal_uInt8 cLast;
1141 bool bFound;
1143 for ( tools::Long nY = nHeight - 1; nY >= 0; nY-- )
1145 sal_uInt8* pTmp = aBuf.data();
1146 nX = nBufCount = 0;
1147 Scanline pScanline = rAcc.GetScanline( nY );
1149 while( nX < nWidth )
1151 nCount = 1;
1152 cPix = rAcc.GetIndexFromData( pScanline, nX++ );
1154 while( ( nX < nWidth ) && ( nCount < 255 )
1155 && ( cPix == rAcc.GetIndexFromData( pScanline, nX ) ) )
1157 nX++;
1158 nCount++;
1161 if ( nCount > 1 )
1163 *pTmp++ = static_cast<sal_uInt8>(nCount);
1164 *pTmp++ = ( bRLE4 ? ( ( cPix << 4 ) | cPix ) : cPix );
1165 nBufCount += 2;
1167 else
1169 cLast = cPix;
1170 nSaveIndex = nX - 1;
1171 bFound = false;
1173 while( ( nX < nWidth ) && ( nCount < 256 ) )
1175 cPix = rAcc.GetIndexFromData( pScanline, nX );
1176 if (cPix == cLast)
1177 break;
1178 nX++; nCount++;
1179 cLast = cPix;
1180 bFound = true;
1183 if ( bFound )
1184 nX--;
1186 if ( nCount > 3 )
1188 *pTmp++ = 0;
1189 *pTmp++ = static_cast<sal_uInt8>(--nCount);
1191 if( bRLE4 )
1193 for ( sal_uLong i = 0; i < nCount; i++, pTmp++ )
1195 *pTmp = rAcc.GetIndexFromData( pScanline, nSaveIndex++ ) << 4;
1197 if ( ++i < nCount )
1198 *pTmp |= rAcc.GetIndexFromData( pScanline, nSaveIndex++ );
1201 nCount = ( nCount + 1 ) >> 1;
1203 else
1205 for( sal_uLong i = 0; i < nCount; i++ )
1206 *pTmp++ = rAcc.GetIndexFromData( pScanline, nSaveIndex++ );
1209 if ( nCount & 1 )
1211 *pTmp++ = 0;
1212 nBufCount += ( nCount + 3 );
1214 else
1215 nBufCount += ( nCount + 2 );
1217 else
1219 *pTmp++ = 1;
1220 *pTmp++ = rAcc.GetIndexFromData( pScanline, nSaveIndex ) << (bRLE4 ? 4 : 0);
1222 if ( nCount == 3 )
1224 *pTmp++ = 1;
1225 *pTmp++ = rAcc.GetIndexFromData( pScanline, ++nSaveIndex ) << ( bRLE4 ? 4 : 0 );
1226 nBufCount += 4;
1228 else
1229 nBufCount += 2;
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();
1251 SVBT32 aVal32;
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());
1266 else
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))
1299 bNative = true;
1302 break;
1305 default:
1307 break;
1311 rImageSize = rOStm.Tell();
1313 if(bNative)
1315 rOStm.WriteBytes(rAcc.GetBuffer(), nAlignedWidth * rAcc.Height());
1317 else
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();
1333 sal_uInt8 cTmp = 0;
1334 Scanline pScanline = rAcc.GetScanline( nY );
1336 for( tools::Long nX = 0, nShift = 8; nX < nWidth; nX++ )
1338 if( !nShift )
1340 nShift = 8;
1341 *pTmp++ = cTmp;
1342 cTmp = 0;
1345 cTmp |= rAcc.GetIndexFromData( pScanline, nX ) << --nShift;
1348 *pTmp = cTmp;
1349 rOStm.WriteBytes(aBuf.data(), nAlignedWidth);
1352 break;
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);
1367 break;
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);
1375 [[fallthrough]];
1376 // #i59239# fallback to 24 bit format, if bitcount is non-default
1377 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();
1397 if(bWriteAlpha)
1399 *pTmp++ = sal_uInt8(0xff) - pAccAlpha->GetIndexFromData( pScanlineAlpha, nX );
1403 rOStm.WriteBytes(aBuf.data(), nAlignedWidth);
1406 break;
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);
1423 bool bRet(false);
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;
1436 else
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
1444 // recent.
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);
1451 if (bCompressed)
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;
1462 else
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)
1536 ZCodec aCodec;
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);
1552 // get uncoded size
1553 nUncodedSize = aMemStm.Tell();
1555 // seek over compress info
1556 rOStm.SeekRel(12);
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);
1570 if(bRet)
1572 bRet = (ERRCODE_NONE == rOStm.GetError());
1575 else
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);
1590 return bRet;
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;
1607 bool ImplReadDIB(
1608 Bitmap& rTarget,
1609 AlphaMask* pTargetAlpha,
1610 SvStream& rIStm,
1611 bool bFileHeader,
1612 bool bIsMask=false,
1613 bool bMSOFormat=false)
1615 const SvStreamEndian nOldFormat(rIStm.GetEndian());
1616 const auto nOldPos(rIStm.Tell());
1617 sal_uLong nOffset(0);
1618 bool bRet(false);
1620 rIStm.SetEndian(SvStreamEndian::LITTLE);
1622 if(bFileHeader)
1624 if(ImplReadDIBFileHeader(rIStm, nOffset))
1626 bRet = ImplReadDIBBody(rIStm, rTarget, nOffset >= DIBV5HEADERSIZE ? pTargetAlpha : nullptr, nOffset, bIsMask, bMSOFormat);
1629 else
1631 bRet = ImplReadDIBBody(rIStm, rTarget, nullptr, nOffset, bIsMask, bMSOFormat);
1634 if(!bRet)
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);
1646 return bRet;
1649 bool ImplWriteDIB(
1650 const Bitmap& rSource,
1651 SvStream& rOStm,
1652 bool bCompressed,
1653 bool bFileHeader)
1655 const Size aSizePix(rSource.GetSizePixel());
1656 bool bRet(false);
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);
1667 if (pAcc)
1669 if(bFileHeader)
1671 if(ImplWriteDIBFileHeader(rOStm, *pAcc))
1673 bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, pAccAlpha.get(), bCompressed);
1676 else
1678 bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, pAccAlpha.get(), bCompressed);
1681 pAcc.reset();
1684 pAccAlpha.reset();
1686 if(!bRet)
1688 rOStm.SetError(SVSTREAM_GENERALERROR);
1689 rOStm.Seek(nOldPos);
1692 rOStm.SetEndian(nOldFormat);
1695 return bRet;
1698 } // unnamed namespace
1700 bool ReadDIB(
1701 Bitmap& rTarget,
1702 SvStream& rIStm,
1703 bool bFileHeader,
1704 bool bMSOFormat)
1706 return ImplReadDIB(rTarget, nullptr, rIStm, bFileHeader, false, bMSOFormat);
1709 bool ReadDIBBitmapEx(
1710 BitmapEx& rTarget,
1711 SvStream& rIStm,
1712 bool bFileHeader,
1713 bool bMSOFormat)
1715 Bitmap aBmp;
1716 bool bRetval(ImplReadDIB(aBmp, nullptr, rIStm, bFileHeader, /*bMask*/false, bMSOFormat) && !rIStm.GetError());
1718 if(bRetval)
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();
1730 if(bRetval)
1732 sal_uInt8 tmp = 0;
1733 rIStm.ReadUChar( tmp );
1734 bRetval = !rIStm.GetError();
1736 if(bRetval)
1738 switch (tmp)
1740 case 2: // TransparentType::Bitmap
1742 Bitmap aMask;
1744 bRetval = ImplReadDIB(aMask, nullptr, rIStm, true, true);
1746 if(bRetval)
1748 if(!aMask.IsEmpty())
1750 // do we have an alpha mask?
1751 if (aMask.getPixelFormat() == vcl::PixelFormat::N8_BPP && aMask.HasGreyPalette8Bit())
1753 AlphaMask aAlpha;
1755 // create alpha mask quickly (without greyscale conversion)
1756 aAlpha.ImplSetBitmap(aMask);
1757 rTarget = BitmapEx(aBmp, aAlpha);
1759 else
1761 rTarget = BitmapEx(aBmp, aMask);
1765 break;
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();
1776 if(bRetval)
1778 rTarget = BitmapEx(aBmp, aTransparentColor);
1780 break;
1782 default: break;
1787 if(!bRetval)
1789 // alpha extra data could not be read; reset, but use base bitmap as result
1790 rIStm.ResetError();
1791 rIStm.Seek(nStmPos);
1792 bRetval = true;
1796 return bRetval;
1799 bool ReadDIBV5(
1800 Bitmap& rTarget,
1801 AlphaMask& rTargetAlpha,
1802 SvStream& rIStm)
1804 return ImplReadDIB(rTarget, &rTargetAlpha, rIStm, true);
1807 bool ReadRawDIB(
1808 BitmapEx& rTarget,
1809 const unsigned char* pBuf,
1810 const ScanlineFormat nFormat,
1811 const int nHeight,
1812 const int nStride)
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);
1820 return true;
1823 bool WriteDIB(
1824 const Bitmap& rSource,
1825 SvStream& rOStm,
1826 bool bCompressed,
1827 bool bFileHeader)
1829 return ImplWriteDIB(rSource, rOStm, bCompressed, bFileHeader);
1832 bool WriteDIB(
1833 const BitmapEx& rSource,
1834 SvStream& rOStm,
1835 bool bCompressed)
1837 return ImplWriteDIB(rSource.GetBitmap(), rOStm, bCompressed, /*bFileHeader*/true);
1840 bool WriteDIBBitmapEx(
1841 const BitmapEx& rSource,
1842 SvStream& rOStm)
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);
1856 return false;
1859 sal_uInt32 getDIBV5HeaderSize()
1861 return DIBV5HEADERSIZE;
1864 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */