Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / gdi / dibtools.cxx
blobce1df4a68a651221720dd5cb8e198d4e94951505
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 <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>
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 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 );
157 // BITMAPCOREHEADER
158 if ( rHeader.nSize == DIBCOREHEADERSIZE )
160 sal_Int16 nTmp16;
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 )
169 sal_Int16 nTmp16(0);
170 rIStm.ReadInt16(nTmp16);
171 rHeader.nWidth = nTmp16;
172 rIStm.ReadInt16(nTmp16);
173 rHeader.nHeight = nTmp16;
174 sal_uInt8 nTmp8(0);
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 );
190 else
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) {
197 rIStm.ReadUInt16(v);
198 nUsed += sizeof(v);
201 auto readInt32 = [&nUsed, &rHeader, &rIStm](sal_Int32 & v) {
202 if (nUsed < rHeader.nSize) {
203 rIStm.ReadInt32(v);
204 nUsed += sizeof(v);
207 auto readUInt32 = [&nUsed, &rHeader, &rIStm](sal_uInt32 & v) {
208 if (nUsed < rHeader.nSize) {
209 rIStm.ReadUInt32(v);
210 nUsed += sizeof(v);
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 );
252 // seek to EndPos
253 if (!checkSeek(rIStm, aStartPos + rHeader.nSize))
254 return false;
257 if (rHeader.nHeight == SAL_MIN_INT32)
258 return false;
260 if ( rHeader.nHeight < 0 )
262 bTopDown = true;
263 rHeader.nHeight *= -1;
265 else
267 bTopDown = false;
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)
286 return false;
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)
293 return false;
296 return rIStm.good();
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)
308 return false;
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++ );
318 if( bQuad )
319 pTmpEntry++;
321 rPal[i] = aPalColor;
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)
350 return rColor;
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;
361 sal_uLong nRunByte;
362 sal_uLong nX = 0;
363 sal_uInt8 cTmp;
364 bool bEndDecoding = false;
368 if (pRLE == pEndRLE)
369 return false;
370 if( ( nCountByte = *pRLE++ ) == 0 )
372 if (pRLE == pEndRLE)
373 return false;
374 nRunByte = *pRLE++;
376 if( nRunByte > 2 )
378 Scanline pScanline = rAcc.GetScanline(nY);
379 if( bRLE4 )
381 nCountByte = nRunByte >> 1;
383 for( sal_uLong i = 0; i < nCountByte; i++ )
385 if (pRLE == pEndRLE)
386 return false;
388 cTmp = *pRLE++;
390 if( nX < nWidth )
391 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp >> 4, rPalette, bForceToMonoWhileReading));
393 if( nX < nWidth )
394 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp & 0x0f, rPalette, bForceToMonoWhileReading));
397 if( nRunByte & 1 )
399 if (pRLE == pEndRLE)
400 return false;
402 if( nX < nWidth )
403 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(*pRLE >> 4, rPalette, bForceToMonoWhileReading));
405 pRLE++;
408 if( ( ( nRunByte + 1 ) >> 1 ) & 1 )
410 if (pRLE == pEndRLE)
411 return false;
413 pRLE++;
416 else
418 for( sal_uLong i = 0; i < nRunByte; i++ )
420 if (pRLE == pEndRLE)
421 return false;
423 if( nX < nWidth )
424 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(*pRLE, rPalette, bForceToMonoWhileReading));
426 pRLE++;
429 if( nRunByte & 1 )
431 if (pRLE == pEndRLE)
432 return false;
434 pRLE++;
438 else if( !nRunByte )
440 nY--;
441 nX = 0;
443 else if( nRunByte == 1 )
444 bEndDecoding = true;
445 else
447 if (pRLE == pEndRLE)
448 return false;
450 nX += *pRLE++;
452 if (pRLE == pEndRLE)
453 return false;
455 nY -= *pRLE++;
458 else
460 if (pRLE == pEndRLE)
461 return false;
462 cTmp = *pRLE++;
464 Scanline pScanline = rAcc.GetScanline(nY);
465 if( bRLE4 )
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));
472 if( nX < nWidth )
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));
479 else
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));
488 return true;
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);
498 bool bNative(false);
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));
502 // Is native format?
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 ) );
511 break;
514 default:
516 break;
520 // Read data
521 if (bNative)
523 if (nAlignedWidth
524 > std::numeric_limits<std::size_t>::max() / rHeader.nHeight)
526 return false;
528 std::size_t n = nAlignedWidth * rHeader.nHeight;
529 if (rIStm.ReadBytes(rAcc.GetBuffer(), n) != n)
531 return false;
534 else
536 // Read color mask
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);
547 long nResult = 0;
548 if (utl::ConfigManager::IsFuzzing() && (o3tl::checked_multiply(nWidth, nHeight, nResult) || nResult > 4000000))
549 return false;
551 if (bRLE)
553 if(!rHeader.nSizeImage)
555 rHeader.nSizeImage = rIStm.remainingSize();
558 if (rHeader.nSizeImage > rIStm.remainingSize())
559 return false;
560 std::vector<sal_uInt8> aBuffer(rHeader.nSizeImage);
561 if (rIStm.ReadBytes(aBuffer.data(), rHeader.nSizeImage) != rHeader.nSizeImage)
562 return false;
563 if (!ImplDecodeRLE(aBuffer.data(), rHeader, rAcc, rPalette, bForceToMonoWhileReading, RLE_4 == rHeader.nCompression))
564 return false;
566 else
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
574 return false;
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)
584 case 1:
586 for( ; nCount--; nY += nI )
588 sal_uInt8 * pTmp = aBuf.data();
589 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
590 != nAlignedWidth)
592 return false;
594 sal_uInt8 cTmp = *pTmp++;
595 Scanline pScanline = rAcc.GetScanline(nY);
596 for( long nX = 0, nShift = 8; nX < nWidth; nX++ )
598 if( !nShift )
600 nShift = 8;
601 cTmp = *pTmp++;
604 auto nIndex = (cTmp >> --nShift) & 1;
605 rAcc.SetPixelOnData(pScanline, nX, SanitizePaletteIndex(nIndex, rPalette, bForceToMonoWhileReading));
609 break;
611 case 4:
613 for( ; nCount--; nY += nI )
615 sal_uInt8 * pTmp = aBuf.data();
616 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
617 != nAlignedWidth)
619 return false;
621 sal_uInt8 cTmp = *pTmp++;
622 Scanline pScanline = rAcc.GetScanline(nY);
623 for( long nX = 0, nShift = 2; nX < nWidth; nX++ )
625 if( !nShift )
627 nShift = 2;
628 cTmp = *pTmp++;
631 auto nIndex = (cTmp >> ( --nShift << 2 ) ) & 0x0f;
632 rAcc.SetPixelOnData(pScanline, nX, SanitizePaletteIndex(nIndex, rPalette, bForceToMonoWhileReading));
636 break;
638 case 8:
640 for( ; nCount--; nY += nI )
642 sal_uInt8 * pTmp = aBuf.data();
643 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
644 != nAlignedWidth)
646 return false;
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));
657 break;
659 case 16:
661 ColorMaskElement aRedMask(nRMask);
662 if (!aRedMask.CalcMaskShift())
663 return false;
664 ColorMaskElement aGreenMask(nGMask);
665 if (!aGreenMask.CalcMaskShift())
666 return false;
667 ColorMaskElement aBlueMask(nBMask);
668 if (!aBlueMask.CalcMaskShift())
669 return false;
671 ColorMask aMask(aRedMask, aGreenMask, aBlueMask);
672 BitmapColor aColor;
674 for( ; nCount--; nY += nI )
676 sal_uInt16 * pTmp16 = reinterpret_cast<sal_uInt16*>(aBuf.data());
677 if (rIStm.ReadBytes(pTmp16, nAlignedWidth)
678 != nAlignedWidth)
680 return false;
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));
691 break;
693 case 24:
695 BitmapColor aPixelColor;
697 for( ; nCount--; nY += nI )
699 sal_uInt8* pTmp = aBuf.data();
700 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
701 != nAlignedWidth)
703 return false;
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));
716 break;
718 case 32:
720 ColorMaskElement aRedMask(nRMask);
721 if (!aRedMask.CalcMaskShift())
722 return false;
723 ColorMaskElement aGreenMask(nGMask);
724 if (!aGreenMask.CalcMaskShift())
725 return false;
726 ColorMaskElement aBlueMask(nBMask);
727 if (!aBlueMask.CalcMaskShift())
728 return false;
729 ColorMask aMask(aRedMask, aGreenMask, aBlueMask);
731 BitmapColor aColor;
732 sal_uInt32* pTmp32;
734 if(pAccAlpha)
736 sal_uInt8 aAlpha;
738 for( ; nCount--; nY += nI )
740 pTmp32 = reinterpret_cast<sal_uInt32*>(aBuf.data());
741 if (rIStm.ReadBytes(pTmp32, nAlignedWidth)
742 != nAlignedWidth)
744 return false;
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;
758 else
760 for( ; nCount--; nY += nI )
762 pTmp32 = reinterpret_cast<sal_uInt32*>(aBuf.data());
763 if (rIStm.ReadBytes(pTmp32, nAlignedWidth)
764 != nAlignedWidth)
766 return false;
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)
787 DIBV5Header aHeader;
788 const sal_uLong nStmPos = rIStm.Tell();
789 bool bTopDown(false);
791 if (!ImplReadDIBInfoHeader(rIStm, aHeader, bTopDown, bMSOFormat))
792 return false;
794 //BI_BITCOUNT_0 jpeg/png is unsupported
795 if (aHeader.nBitCount == 0)
796 return false;
798 if (aHeader.nWidth <= 0 || aHeader.nHeight <= 0)
799 return false;
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.
807 return false;
810 sal_uInt16 nColors(0);
811 SvStream* pIStm;
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);
821 else
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
840 // the encoded block
841 pMemStm->WriteStream(rIStm, nCodedSize);
842 pMemStm->Seek(0);
844 size_t nSizeInc(4 * pMemStm->remainingSize());
845 if (nUncodedSize < nSizeInc)
846 nSizeInc = nUncodedSize;
848 if (nSizeInc > 0)
850 // decode buffer
851 ZCodec aCodec;
852 aCodec.BeginCompression();
853 aData.resize(nSizeInc);
854 size_t nDataPos(0);
855 while (nUncodedSize > nDataPos)
857 assert(aData.size() > nDataPos);
858 const size_t nToRead(std::min<size_t>(nUncodedSize - nDataPos, aData.size() - nDataPos));
859 assert(nToRead > 0);
860 assert(!aData.empty());
861 const long nRead = aCodec.Read(*pMemStm, aData.data() + nDataPos, sal_uInt32(nToRead));
862 if (nRead > 0)
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);
869 else
871 break;
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();
881 if (aData.empty())
883 // add something so we can take address of the first element
884 aData.resize(1);
885 nUncodedSize = 0;
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);
894 nOffset = 0;
896 else
898 pIStm = &rIStm;
901 // read palette
902 BitmapPalette aPalette;
903 if (nColors)
905 aPalette.SetEntryCount(nColors);
906 ImplReadDIBPalette(*pIStm, aPalette, aHeader.nSize != DIBCOREHEADERSIZE);
909 if (pIStm->GetError())
910 return false;
912 if (nOffset)
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)
919 return false;
920 const sal_uInt64 nAlignedWidth(AlignedWidth4Bytes(static_cast<sal_uLong>(nBitsPerLine)));
922 switch (aHeader.nCompression)
924 case RLE_8:
926 if (aHeader.nBitCount != 8)
927 return false;
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))
933 return false;
934 break;
936 case RLE_4:
938 if (aHeader.nBitCount != 4)
939 return false;
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))
944 return false;
945 break;
947 default:
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");
954 return false;
956 else
957 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader.nCompression << ", assuming meant to be COMPRESS_NONE");
958 [[fallthrough]];
959 case BITFIELDS:
960 case ZCOMPRESS:
961 case 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)
967 return false;
968 break;
972 const Size aSizePixel(aHeader.nWidth, aHeader.nHeight);
973 AlphaMask aNewBmpAlpha;
974 AlphaScopedWriteAccess pAccAlpha;
975 bool bAlphaPossible(pBmpAlpha && aHeader.nBitCount == 32);
977 if (bAlphaPossible)
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;
993 if (bAlphaPossible)
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)
1007 pPal = nullptr;
1008 nBitCount = 1;
1009 SAL_WARN( "vcl", "forcing mask to monochrome");
1012 Bitmap aNewBmp(aSizePixel, nBitCount, pPal);
1013 BitmapScopedWriteAccess pAcc(aNewBmp);
1014 if (!pAcc)
1015 return false;
1016 if (pAcc->Width() != aHeader.nWidth || pAcc->Height() != aHeader.nHeight)
1018 return false;
1021 // read bits
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)
1027 MapMode aMapMode(
1028 MapUnit::MapMM,
1029 Point(),
1030 Fraction(1000, aHeader.nXPelsPerMeter),
1031 Fraction(1000, aHeader.nYPelsPerMeter));
1033 aNewBmp.SetPrefMapMode(aMapMode);
1034 aNewBmp.SetPrefSize(Size(aHeader.nWidth, aHeader.nHeight));
1037 pAcc.reset();
1039 if (bAlphaPossible)
1041 pAccAlpha.reset();
1043 if(!bAlphaUsed)
1045 bAlphaPossible = false;
1049 if (bRet)
1051 rBmp = aNewBmp;
1053 if(bAlphaPossible)
1055 *pBmpAlpha = aNewBmpAlpha;
1059 return bRet;
1062 bool ImplReadDIBFileHeader( SvStream& rIStm, sal_uLong& rOffset )
1064 bool bRet = false;
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 );
1078 rIStm.SeekRel( 8 );
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 );
1096 bRet = false;
1099 else
1100 rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
1102 return bRet;
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();
1119 *pTmpEntry++ = 0;
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();
1131 sal_uLong nX;
1132 sal_uLong nSaveIndex;
1133 sal_uLong nCount;
1134 sal_uLong nBufCount;
1135 std::vector<sal_uInt8> aBuf(( nWidth << 1 ) + 2);
1136 sal_uInt8 cPix;
1137 sal_uInt8 cLast;
1138 bool bFound;
1140 for ( long nY = nHeight - 1; nY >= 0; nY-- )
1142 sal_uInt8* pTmp = aBuf.data();
1143 nX = nBufCount = 0;
1144 Scanline pScanline = rAcc.GetScanline( nY );
1146 while( nX < nWidth )
1148 nCount = 1;
1149 cPix = rAcc.GetIndexFromData( pScanline, nX++ );
1151 while( ( nX < nWidth ) && ( nCount < 255 )
1152 && ( cPix == rAcc.GetIndexFromData( pScanline, nX ) ) )
1154 nX++;
1155 nCount++;
1158 if ( nCount > 1 )
1160 *pTmp++ = static_cast<sal_uInt8>(nCount);
1161 *pTmp++ = ( bRLE4 ? ( ( cPix << 4 ) | cPix ) : cPix );
1162 nBufCount += 2;
1164 else
1166 cLast = cPix;
1167 nSaveIndex = nX - 1;
1168 bFound = false;
1170 while( ( nX < nWidth ) && ( nCount < 256 )
1171 && ( cPix = rAcc.GetIndexFromData( pScanline, nX ) ) != cLast )
1173 nX++; nCount++;
1174 cLast = cPix;
1175 bFound = true;
1178 if ( bFound )
1179 nX--;
1181 if ( nCount > 3 )
1183 *pTmp++ = 0;
1184 *pTmp++ = static_cast<sal_uInt8>(--nCount);
1186 if( bRLE4 )
1188 for ( sal_uLong i = 0; i < nCount; i++, pTmp++ )
1190 *pTmp = rAcc.GetIndexFromData( pScanline, nSaveIndex++ ) << 4;
1192 if ( ++i < nCount )
1193 *pTmp |= rAcc.GetIndexFromData( pScanline, nSaveIndex++ );
1196 nCount = ( nCount + 1 ) >> 1;
1198 else
1200 for( sal_uLong i = 0; i < nCount; i++ )
1201 *pTmp++ = rAcc.GetIndexFromData( pScanline, nSaveIndex++ );
1204 if ( nCount & 1 )
1206 *pTmp++ = 0;
1207 nBufCount += ( nCount + 3 );
1209 else
1210 nBufCount += ( nCount + 2 );
1212 else
1214 *pTmp++ = 1;
1215 *pTmp++ = rAcc.GetIndexFromData( pScanline, nSaveIndex ) << (bRLE4 ? 4 : 0);
1217 if ( nCount == 3 )
1219 *pTmp++ = 1;
1220 *pTmp++ = rAcc.GetIndexFromData( pScanline, ++nSaveIndex ) << ( bRLE4 ? 4 : 0 );
1221 nBufCount += 4;
1223 else
1224 nBufCount += 2;
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();
1246 SVBT32 aVal32;
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());
1261 else
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))
1295 bNative = true;
1298 break;
1301 default:
1303 break;
1307 rImageSize = rOStm.Tell();
1309 if(bNative)
1311 rOStm.WriteBytes(rAcc.GetBuffer(), nAlignedWidth * rAcc.Height());
1313 else
1315 const long nWidth(rAcc.Width());
1316 const long nHeight(rAcc.Height());
1317 std::vector<sal_uInt8> aBuf(nAlignedWidth);
1318 switch( nBitCount )
1320 case 1:
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();
1329 sal_uInt8 cTmp = 0;
1330 Scanline pScanline = rAcc.GetScanline( nY );
1332 for( long nX = 0, nShift = 8; nX < nWidth; nX++ )
1334 if( !nShift )
1336 nShift = 8;
1337 *pTmp++ = cTmp;
1338 cTmp = 0;
1341 cTmp |= rAcc.GetIndexFromData( pScanline, nX ) << --nShift;
1344 *pTmp = cTmp;
1345 rOStm.WriteBytes(aBuf.data(), nAlignedWidth);
1348 break;
1350 case 4:
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();
1359 sal_uInt8 cTmp = 0;
1360 Scanline pScanline = rAcc.GetScanline( nY );
1362 for( long nX = 0, nShift = 2; nX < nWidth; nX++ )
1364 if( !nShift )
1366 nShift = 2;
1367 *pTmp++ = cTmp;
1368 cTmp = 0;
1371 cTmp |= rAcc.GetIndexFromData( pScanline, nX ) << ( --nShift << 2 );
1373 *pTmp = cTmp;
1374 rOStm.WriteBytes(aBuf.data(), nAlignedWidth);
1377 break;
1379 case 8:
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);
1392 break;
1394 case 24:
1396 //valgrind, zero out the trailing unused alignment bytes
1397 size_t nUnusedBytes = nAlignedWidth - nWidth * 3;
1398 memset(aBuf.data() + nAlignedWidth - nUnusedBytes, 0, nUnusedBytes);
1400 [[fallthrough]];
1401 // #i59239# fallback to 24 bit format, if bitcount is non-default
1402 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();
1422 if(bWriteAlpha)
1424 *pTmp++ = sal_uInt8(0xff) - pAccAlpha->GetIndexFromData( pScanlineAlpha, nX );
1428 rOStm.WriteBytes(aBuf.data(), nAlignedWidth);
1431 break;
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);
1448 bool bRet(false);
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;
1461 else
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
1469 // recent.
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);
1476 if(bCompressed)
1478 if(4 == 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;
1493 else
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)
1567 ZCodec aCodec;
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);
1583 // get uncoded size
1584 nUncodedSize = aMemStm.Tell();
1586 // seek over compress info
1587 rOStm.SeekRel(12);
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);
1601 if(bRet)
1603 bRet = (ERRCODE_NONE == rOStm.GetError());
1606 else
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);
1621 return bRet;
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;
1638 bool ImplReadDIB(
1639 Bitmap& rTarget,
1640 AlphaMask* pTargetAlpha,
1641 SvStream& rIStm,
1642 bool bFileHeader,
1643 bool bIsMask=false,
1644 bool bMSOFormat=false)
1646 const SvStreamEndian nOldFormat(rIStm.GetEndian());
1647 const sal_uLong nOldPos(rIStm.Tell());
1648 sal_uLong nOffset(0);
1649 bool bRet(false);
1651 rIStm.SetEndian(SvStreamEndian::LITTLE);
1653 if(bFileHeader)
1655 if(ImplReadDIBFileHeader(rIStm, nOffset))
1657 bRet = ImplReadDIBBody(rIStm, rTarget, nOffset >= DIBV5HEADERSIZE ? pTargetAlpha : nullptr, nOffset, bIsMask, bMSOFormat);
1660 else
1662 bRet = ImplReadDIBBody(rIStm, rTarget, nullptr, nOffset, bIsMask, bMSOFormat);
1665 if(!bRet)
1667 if(!rIStm.GetError())
1669 rIStm.SetError(SVSTREAM_GENERALERROR);
1672 rIStm.Seek(nOldPos);
1675 rIStm.SetEndian(nOldFormat);
1677 return bRet;
1680 bool ImplWriteDIB(
1681 const Bitmap& rSource,
1682 SvStream& rOStm,
1683 bool bCompressed,
1684 bool bFileHeader)
1686 const Size aSizePix(rSource.GetSizePixel());
1687 bool bRet(false);
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);
1698 if (pAcc)
1700 if(bFileHeader)
1702 if(ImplWriteDIBFileHeader(rOStm, *pAcc))
1704 bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, pAccAlpha.get(), bCompressed);
1707 else
1709 bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, pAccAlpha.get(), bCompressed);
1712 pAcc.reset();
1715 pAccAlpha.reset();
1717 if(!bRet)
1719 rOStm.SetError(SVSTREAM_GENERALERROR);
1720 rOStm.Seek(nOldPos);
1723 rOStm.SetEndian(nOldFormat);
1726 return bRet;
1729 } // unnamed namespace
1731 bool ReadDIB(
1732 Bitmap& rTarget,
1733 SvStream& rIStm,
1734 bool bFileHeader,
1735 bool bMSOFormat)
1737 return ImplReadDIB(rTarget, nullptr, rIStm, bFileHeader, false, bMSOFormat);
1740 bool ReadDIBBitmapEx(
1741 BitmapEx& rTarget,
1742 SvStream& rIStm,
1743 bool bFileHeader,
1744 bool bMSOFormat)
1746 Bitmap aBmp;
1747 bool bRetval(ImplReadDIB(aBmp, nullptr, rIStm, bFileHeader, /*bMask*/false, bMSOFormat) && !rIStm.GetError());
1749 if(bRetval)
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();
1760 if(bRetval)
1762 sal_uInt8 tmp = 0;
1763 rIStm.ReadUChar( tmp );
1764 TransparentType transparent = static_cast<TransparentType>(tmp);
1765 bRetval = !rIStm.GetError();
1767 if(bRetval)
1769 switch (transparent)
1771 case TransparentType::Bitmap:
1773 Bitmap aMask;
1775 bRetval = ImplReadDIB(aMask, nullptr, rIStm, true, true);
1777 if(bRetval)
1779 if(!!aMask)
1781 // do we have an alpha mask?
1782 if((8 == aMask.GetBitCount()) && aMask.HasGreyPalette())
1784 AlphaMask aAlpha;
1786 // create alpha mask quickly (without greyscale conversion)
1787 aAlpha.ImplSetBitmap(aMask);
1788 rTarget = BitmapEx(aBmp, aAlpha);
1790 else
1792 rTarget = BitmapEx(aBmp, aMask);
1796 break;
1798 case TransparentType::Color:
1800 Color aTransparentColor;
1802 tools::GenericTypeSerializer aSerializer(rIStm);
1803 aSerializer.readColor(aTransparentColor);
1805 bRetval = !rIStm.GetError();
1807 if(bRetval)
1809 rTarget = BitmapEx(aBmp, aTransparentColor);
1811 break;
1813 default: break;
1818 if(!bRetval)
1820 // alpha extra data could not be read; reset, but use base bitmap as result
1821 rIStm.ResetError();
1822 rIStm.Seek(nStmPos);
1823 bRetval = true;
1827 return bRetval;
1830 bool ReadDIBV5(
1831 Bitmap& rTarget,
1832 AlphaMask& rTargetAlpha,
1833 SvStream& rIStm)
1835 return ImplReadDIB(rTarget, &rTargetAlpha, rIStm, true);
1838 bool ReadRawDIB(
1839 BitmapEx& rTarget,
1840 const unsigned char* pBuf,
1841 const ScanlineFormat nFormat,
1842 const int nHeight,
1843 const int nStride)
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);
1851 return true;
1854 bool WriteDIB(
1855 const Bitmap& rSource,
1856 SvStream& rOStm,
1857 bool bCompressed,
1858 bool bFileHeader)
1860 return ImplWriteDIB(rSource, rOStm, bCompressed, bFileHeader);
1863 bool WriteDIB(
1864 const BitmapEx& rSource,
1865 SvStream& rOStm,
1866 bool bCompressed)
1868 return ImplWriteDIB(rSource.GetBitmapRef(), rOStm, bCompressed, /*bFileHeader*/true);
1871 bool WriteDIBBitmapEx(
1872 const BitmapEx& rSource,
1873 SvStream& rOStm)
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);
1889 return true;
1893 return false;
1896 sal_uInt32 getDIBV5HeaderSize()
1898 return DIBV5HEADERSIZE;
1901 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */