Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / dibtools.cxx
blobdc3666978fb70ea8e294ed4155818d2ff7948048
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>
22 #include <cassert>
24 #include <vcl/salbtype.hxx>
25 #include <vcl/dibtools.hxx>
26 #include <tools/zcodec.hxx>
27 #include <tools/stream.hxx>
28 #include <tools/fract.hxx>
29 #include <vcl/bitmapex.hxx>
30 #include <vcl/bitmapaccess.hxx>
31 #include <vcl/outdev.hxx>
32 #include <memory>
35 #define DIBCOREHEADERSIZE ( 12UL )
36 #define DIBINFOHEADERSIZE ( sizeof(DIBInfoHeader) )
37 #define DIBV5HEADERSIZE ( sizeof(DIBV5Header) )
39 // - DIBInfoHeader and DIBV5Header
41 typedef sal_Int32 FXPT2DOT30;
43 namespace
46 struct CIEXYZ
48 FXPT2DOT30 aXyzX;
49 FXPT2DOT30 aXyzY;
50 FXPT2DOT30 aXyzZ;
52 CIEXYZ()
53 : aXyzX(0),
54 aXyzY(0),
55 aXyzZ(0)
59 struct CIEXYZTriple
61 CIEXYZ aXyzRed;
62 CIEXYZ aXyzGreen;
63 CIEXYZ aXyzBlue;
65 CIEXYZTriple()
66 : aXyzRed(),
67 aXyzGreen(),
68 aXyzBlue()
72 struct DIBInfoHeader
74 sal_uInt32 nSize;
75 sal_Int32 nWidth;
76 sal_Int32 nHeight;
77 sal_uInt16 nPlanes;
78 sal_uInt16 nBitCount;
79 sal_uInt32 nCompression;
80 sal_uInt32 nSizeImage;
81 sal_Int32 nXPelsPerMeter;
82 sal_Int32 nYPelsPerMeter;
83 sal_uInt32 nColsUsed;
84 sal_uInt32 nColsImportant;
86 DIBInfoHeader()
87 : nSize(0UL),
88 nWidth(0UL),
89 nHeight(0UL),
90 nPlanes(0),
91 nBitCount(0),
92 nCompression(0),
93 nSizeImage(0),
94 nXPelsPerMeter(0UL),
95 nYPelsPerMeter(0UL),
96 nColsUsed(0UL),
97 nColsImportant(0UL)
101 struct DIBV5Header : public DIBInfoHeader
103 sal_uInt32 nV5RedMask;
104 sal_uInt32 nV5GreenMask;
105 sal_uInt32 nV5BlueMask;
106 sal_uInt32 nV5AlphaMask;
107 sal_uInt32 nV5CSType;
108 CIEXYZTriple aV5Endpoints;
109 sal_uInt32 nV5GammaRed;
110 sal_uInt32 nV5GammaGreen;
111 sal_uInt32 nV5GammaBlue;
112 sal_uInt32 nV5Intent;
113 sal_uInt32 nV5ProfileData;
114 sal_uInt32 nV5ProfileSize;
115 sal_uInt32 nV5Reserved;
117 DIBV5Header()
118 : DIBInfoHeader(),
119 nV5RedMask(0UL),
120 nV5GreenMask(0UL),
121 nV5BlueMask(0UL),
122 nV5AlphaMask(0UL),
123 nV5CSType(0UL),
124 aV5Endpoints(),
125 nV5GammaRed(0UL),
126 nV5GammaGreen(0UL),
127 nV5GammaBlue(0UL),
128 nV5Intent(0UL),
129 nV5ProfileData(0UL),
130 nV5ProfileSize(0UL),
131 nV5Reserved(0UL)
135 inline sal_uInt16 discretizeBitcount( sal_uInt16 nInputCount )
137 return ( nInputCount <= 1 ) ? 1 :
138 ( nInputCount <= 4 ) ? 4 :
139 ( nInputCount <= 8 ) ? 8 : 24;
142 inline bool isBitfieldCompression( ScanlineFormat nScanlineFormat )
144 return (ScanlineFormat::N16BitTcLsbMask == nScanlineFormat) || (ScanlineFormat::N32BitTcMask == nScanlineFormat);
147 bool ImplReadDIBInfoHeader(SvStream& rIStm, DIBV5Header& rHeader, bool& bTopDown, bool bMSOFormat)
149 // BITMAPINFOHEADER or BITMAPCOREHEADER or BITMAPV5HEADER
150 sal_uInt64 const aStartPos(rIStm.Tell());
151 rIStm.ReadUInt32( rHeader.nSize );
153 // BITMAPCOREHEADER
154 if ( rHeader.nSize == DIBCOREHEADERSIZE )
156 sal_Int16 nTmp16;
158 rIStm.ReadInt16( nTmp16 ); rHeader.nWidth = nTmp16;
159 rIStm.ReadInt16( nTmp16 ); rHeader.nHeight = nTmp16;
160 rIStm.ReadUInt16( rHeader.nPlanes );
161 rIStm.ReadUInt16( rHeader.nBitCount );
163 else if ( bMSOFormat && rHeader.nSize == DIBINFOHEADERSIZE )
165 sal_Int16 nTmp16(0);
166 rIStm.ReadInt16(nTmp16);
167 rHeader.nWidth = nTmp16;
168 rIStm.ReadInt16(nTmp16);
169 rHeader.nHeight = nTmp16;
170 sal_uInt8 nTmp8(0);
171 rIStm.ReadUChar(nTmp8);
172 rHeader.nPlanes = nTmp8;
173 rIStm.ReadUChar(nTmp8);
174 rHeader.nBitCount = nTmp8;
175 rIStm.ReadInt16(nTmp16);
176 rHeader.nSizeImage = nTmp16;
177 rIStm.ReadInt16(nTmp16);
178 rHeader.nCompression = nTmp16;
179 if ( !rHeader.nSizeImage ) // uncompressed?
180 rHeader.nSizeImage = ((rHeader.nWidth * rHeader.nBitCount + 31) & ~31) / 8 * rHeader.nHeight;
181 rIStm.ReadInt32( rHeader.nXPelsPerMeter );
182 rIStm.ReadInt32( rHeader.nYPelsPerMeter );
183 rIStm.ReadUInt32( rHeader.nColsUsed );
184 rIStm.ReadUInt32( rHeader.nColsImportant );
186 else
188 // BITMAPCOREHEADER, BITMAPV5HEADER or unknown. Read as far as possible
189 std::size_t nUsed(sizeof(rHeader.nSize));
191 auto readUInt16 = [&nUsed, &rHeader, &rIStm](sal_uInt16 & v) {
192 if (nUsed < rHeader.nSize) {
193 rIStm.ReadUInt16(v);
194 nUsed += sizeof(v);
197 auto readInt32 = [&nUsed, &rHeader, &rIStm](sal_Int32 & v) {
198 if (nUsed < rHeader.nSize) {
199 rIStm.ReadInt32(v);
200 nUsed += sizeof(v);
203 auto readUInt32 = [&nUsed, &rHeader, &rIStm](sal_uInt32 & v) {
204 if (nUsed < rHeader.nSize) {
205 rIStm.ReadUInt32(v);
206 nUsed += sizeof(v);
210 // read DIBInfoHeader entries
211 readInt32( rHeader.nWidth );
212 readInt32( rHeader.nHeight );
213 readUInt16( rHeader.nPlanes );
214 readUInt16( rHeader.nBitCount );
215 readUInt32( rHeader.nCompression );
216 readUInt32( rHeader.nSizeImage );
217 readInt32( rHeader.nXPelsPerMeter );
218 readInt32( rHeader.nYPelsPerMeter );
219 readUInt32( rHeader.nColsUsed );
220 readUInt32( rHeader.nColsImportant );
222 // read DIBV5HEADER members
223 readUInt32( rHeader.nV5RedMask );
224 readUInt32( rHeader.nV5GreenMask );
225 readUInt32( rHeader.nV5BlueMask );
226 readUInt32( rHeader.nV5AlphaMask );
227 readUInt32( rHeader.nV5CSType );
229 // read contained CIEXYZTriple's
230 readInt32( rHeader.aV5Endpoints.aXyzRed.aXyzX );
231 readInt32( rHeader.aV5Endpoints.aXyzRed.aXyzY );
232 readInt32( rHeader.aV5Endpoints.aXyzRed.aXyzZ );
233 readInt32( rHeader.aV5Endpoints.aXyzGreen.aXyzX );
234 readInt32( rHeader.aV5Endpoints.aXyzGreen.aXyzY );
235 readInt32( rHeader.aV5Endpoints.aXyzGreen.aXyzZ );
236 readInt32( rHeader.aV5Endpoints.aXyzBlue.aXyzX );
237 readInt32( rHeader.aV5Endpoints.aXyzBlue.aXyzY );
238 readInt32( rHeader.aV5Endpoints.aXyzBlue.aXyzZ );
240 readUInt32( rHeader.nV5GammaRed );
241 readUInt32( rHeader.nV5GammaGreen );
242 readUInt32( rHeader.nV5GammaBlue );
243 readUInt32( rHeader.nV5Intent );
244 readUInt32( rHeader.nV5ProfileData );
245 readUInt32( rHeader.nV5ProfileSize );
246 readUInt32( rHeader.nV5Reserved );
248 // seek to EndPos
249 rIStm.Seek(aStartPos + rHeader.nSize);
252 if ( rHeader.nHeight < 0 )
254 bTopDown = true;
255 rHeader.nHeight *= -1;
257 else
259 bTopDown = false;
262 if ( rHeader.nWidth < 0 )
264 rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
267 // #144105# protect a little against damaged files
268 assert(rHeader.nHeight >= 0);
269 if (rHeader.nHeight != 0 && rHeader.nWidth >= 0
270 && (rHeader.nSizeImage / 16 / static_cast<sal_uInt32>(rHeader.nHeight)
271 > static_cast<sal_uInt32>(rHeader.nWidth)))
273 rHeader.nSizeImage = 0;
277 if (rHeader.nPlanes != 1)
278 return false;
280 if (rHeader.nBitCount != 0 && rHeader.nBitCount != 1 &&
281 rHeader.nBitCount != 4 && rHeader.nBitCount != 8 &&
282 rHeader.nBitCount != 16 && rHeader.nBitCount != 24 &&
283 rHeader.nBitCount != 32)
285 return false;
288 return rIStm.good();
291 bool ImplReadDIBPalette(SvStream& rIStm, BitmapPalette& rPal, bool bQuad)
293 const sal_uInt16 nColors = rPal.GetEntryCount();
294 const sal_uLong nPalSize = nColors * ( bQuad ? 4UL : 3UL );
295 BitmapColor aPalColor;
297 std::unique_ptr<sal_uInt8[]> pEntries(new sal_uInt8[ nPalSize ]);
298 if (rIStm.ReadBytes(pEntries.get(), nPalSize) != nPalSize)
300 return false;
303 sal_uInt8* pTmpEntry = pEntries.get();
304 for( sal_uInt16 i = 0; i < nColors; i++ )
306 aPalColor.SetBlue( *pTmpEntry++ );
307 aPalColor.SetGreen( *pTmpEntry++ );
308 aPalColor.SetRed( *pTmpEntry++ );
310 if( bQuad )
311 pTmpEntry++;
313 rPal[i] = aPalColor;
316 return( rIStm.GetError() == 0UL );
319 namespace
321 sal_uInt8 SanitizePaletteIndex(sal_uInt8 nIndex, BitmapPalette& rPalette, bool bForceToMonoWhileReading)
323 const sal_uInt16 nPaletteEntryCount = rPalette.GetEntryCount();
324 if (nPaletteEntryCount && nIndex >= nPaletteEntryCount)
326 auto nSanitizedIndex = nIndex % nPaletteEntryCount;
327 SAL_WARN_IF(nIndex != nSanitizedIndex, "vcl", "invalid colormap index: "
328 << static_cast<unsigned int>(nIndex) << ", colormap len is: "
329 << nPaletteEntryCount);
330 nIndex = nSanitizedIndex;
333 if (nPaletteEntryCount && bForceToMonoWhileReading)
335 return static_cast<sal_uInt8>(rPalette[nIndex].GetLuminance() >= 255);
338 return nIndex;
341 BitmapColor SanitizeColor(const BitmapColor &rColor, bool bForceToMonoWhileReading)
343 if (!bForceToMonoWhileReading)
344 return rColor;
345 return BitmapColor(static_cast<sal_uInt8>(rColor.GetLuminance() >= 255));
350 bool ImplDecodeRLE(sal_uInt8* pBuffer, DIBV5Header& rHeader, BitmapWriteAccess& rAcc, BitmapPalette& rPalette, bool bForceToMonoWhileReading, bool bRLE4)
352 Scanline pRLE = pBuffer;
353 Scanline pEndRLE = pBuffer + rHeader.nSizeImage;
354 long nY = rHeader.nHeight - 1;
355 const sal_uLong nWidth = rAcc.Width();
356 sal_uLong nCountByte;
357 sal_uLong nRunByte;
358 sal_uLong nX = 0UL;
359 sal_uInt8 cTmp;
360 bool bEndDecoding = false;
364 if (pRLE == pEndRLE)
365 return false;
366 if( ( nCountByte = *pRLE++ ) == 0 )
368 if (pRLE == pEndRLE)
369 return false;
370 nRunByte = *pRLE++;
372 if( nRunByte > 2 )
374 if( bRLE4 )
376 nCountByte = nRunByte >> 1;
378 for( sal_uLong i = 0UL; i < nCountByte; i++ )
380 if (pRLE == pEndRLE)
381 return false;
383 cTmp = *pRLE++;
385 if( nX < nWidth )
386 rAcc.SetPixelIndex(nY, nX++, SanitizePaletteIndex(cTmp >> 4, rPalette, bForceToMonoWhileReading));
388 if( nX < nWidth )
389 rAcc.SetPixelIndex(nY, nX++, SanitizePaletteIndex(cTmp & 0x0f, rPalette, bForceToMonoWhileReading));
392 if( nRunByte & 1 )
394 if (pRLE == pEndRLE)
395 return false;
397 if( nX < nWidth )
398 rAcc.SetPixelIndex(nY, nX++, SanitizePaletteIndex(*pRLE >> 4, rPalette, bForceToMonoWhileReading));
400 pRLE++;
403 if( ( ( nRunByte + 1 ) >> 1 ) & 1 )
405 if (pRLE == pEndRLE)
406 return false;
408 pRLE++;
411 else
413 for( sal_uLong i = 0UL; i < nRunByte; i++ )
415 if (pRLE == pEndRLE)
416 return false;
418 if( nX < nWidth )
419 rAcc.SetPixelIndex(nY, nX++, SanitizePaletteIndex(*pRLE, rPalette, bForceToMonoWhileReading));
421 pRLE++;
424 if( nRunByte & 1 )
426 if (pRLE == pEndRLE)
427 return false;
429 pRLE++;
433 else if( !nRunByte )
435 nY--;
436 nX = 0UL;
438 else if( nRunByte == 1 )
439 bEndDecoding = true;
440 else
442 if (pRLE == pEndRLE)
443 return false;
445 nX += *pRLE++;
447 if (pRLE == pEndRLE)
448 return false;
450 nY -= *pRLE++;
453 else
455 if (pRLE == pEndRLE)
456 return false;
457 cTmp = *pRLE++;
459 if( bRLE4 )
461 nRunByte = nCountByte >> 1;
463 for( sal_uLong i = 0UL; i < nRunByte; i++ )
465 if( nX < nWidth )
466 rAcc.SetPixelIndex(nY, nX++, SanitizePaletteIndex(cTmp >> 4, rPalette, bForceToMonoWhileReading));
468 if( nX < nWidth )
469 rAcc.SetPixelIndex(nY, nX++, SanitizePaletteIndex(cTmp & 0x0f, rPalette, bForceToMonoWhileReading));
472 if( ( nCountByte & 1 ) && ( nX < nWidth ) )
473 rAcc.SetPixelIndex(nY, nX++, SanitizePaletteIndex(cTmp >> 4, rPalette, bForceToMonoWhileReading));
475 else
477 for( sal_uLong i = 0UL; ( i < nCountByte ) && ( nX < nWidth ); i++ )
478 rAcc.SetPixelIndex(nY, nX++, SanitizePaletteIndex(cTmp, rPalette, bForceToMonoWhileReading));
482 while (!bEndDecoding && (nY >= 0));
484 return true;
487 bool ImplReadDIBBits(SvStream& rIStm, DIBV5Header& rHeader, BitmapWriteAccess& rAcc, BitmapPalette& rPalette, BitmapWriteAccess* pAccAlpha,
488 bool bTopDown, bool& rAlphaUsed, const sal_uInt64 nAlignedWidth,
489 const bool bForceToMonoWhileReading)
491 sal_uInt32 nRMask(( rHeader.nBitCount == 16 ) ? 0x00007c00UL : 0x00ff0000UL);
492 sal_uInt32 nGMask(( rHeader.nBitCount == 16 ) ? 0x000003e0UL : 0x0000ff00UL);
493 sal_uInt32 nBMask(( rHeader.nBitCount == 16 ) ? 0x0000001fUL : 0x000000ffUL);
494 bool bNative(false);
495 bool bTCMask(!pAccAlpha && ((16 == rHeader.nBitCount) || (32 == rHeader.nBitCount)));
496 bool bRLE((RLE_8 == rHeader.nCompression && 8 == rHeader.nBitCount) || (RLE_4 == rHeader.nCompression && 4 == rHeader.nBitCount));
498 // Is native format?
499 switch(rAcc.GetScanlineFormat())
501 case ScanlineFormat::N1BitMsbPal:
502 case ScanlineFormat::N24BitTcBgr:
504 // we can't trust arbitrary-sourced index based formats to have correct indexes, so we exclude the pal formats
505 // from raw read and force checking their colormap indexes
506 bNative = ( ( rAcc.IsBottomUp() != bTopDown ) && !bRLE && !bTCMask && ( rAcc.GetScanlineSize() == nAlignedWidth ) );
507 break;
510 default:
512 break;
516 // Read data
517 if (bNative)
519 if (nAlignedWidth
520 > std::numeric_limits<std::size_t>::max() / rHeader.nHeight)
522 return false;
524 std::size_t n = nAlignedWidth * rHeader.nHeight;
525 if (rIStm.ReadBytes(rAcc.GetBuffer(), n) != n)
527 return false;
530 else
532 // Read color mask
533 if(bTCMask && BITFIELDS == rHeader.nCompression)
535 rIStm.SeekRel( -12 );
536 rIStm.ReadUInt32( nRMask );
537 rIStm.ReadUInt32( nGMask );
538 rIStm.ReadUInt32( nBMask );
541 if (bRLE)
543 if(!rHeader.nSizeImage)
545 rHeader.nSizeImage = rIStm.remainingSize();
548 if (rHeader.nSizeImage > rIStm.remainingSize())
549 return false;
550 std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[rHeader.nSizeImage]);
551 if (rIStm.ReadBytes(pBuffer.get(), rHeader.nSizeImage)
552 != rHeader.nSizeImage)
554 return false;
556 if (!ImplDecodeRLE(pBuffer.get(), rHeader, rAcc, rPalette, bForceToMonoWhileReading, RLE_4 == rHeader.nCompression))
557 return false;
559 else
561 const long nWidth(rHeader.nWidth);
562 const long nHeight(rHeader.nHeight);
563 std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[nAlignedWidth]);
565 const long nI(bTopDown ? 1 : -1);
566 long nY(bTopDown ? 0 : nHeight - 1);
567 long nCount(nHeight);
569 switch(rHeader.nBitCount)
571 case 1:
573 for( ; nCount--; nY += nI )
575 sal_uInt8 * pTmp = pBuf.get();
576 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
577 != nAlignedWidth)
579 return false;
581 sal_uInt8 cTmp = *pTmp++;
583 for( long nX = 0, nShift = 8; nX < nWidth; nX++ )
585 if( !nShift )
587 nShift = 8;
588 cTmp = *pTmp++;
591 auto nIndex = (cTmp >> --nShift) & 1;
592 rAcc.SetPixelIndex(nY, nX, SanitizePaletteIndex(nIndex, rPalette, bForceToMonoWhileReading));
596 break;
598 case 4:
600 for( ; nCount--; nY += nI )
602 sal_uInt8 * pTmp = pBuf.get();
603 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
604 != nAlignedWidth)
606 return false;
608 sal_uInt8 cTmp = *pTmp++;
610 for( long nX = 0, nShift = 2; nX < nWidth; nX++ )
612 if( !nShift )
614 nShift = 2UL;
615 cTmp = *pTmp++;
618 auto nIndex = (cTmp >> ( --nShift << 2UL ) ) & 0x0f;
619 rAcc.SetPixelIndex(nY, nX, SanitizePaletteIndex(nIndex, rPalette, bForceToMonoWhileReading));
623 break;
625 case 8:
627 for( ; nCount--; nY += nI )
629 sal_uInt8 * pTmp = pBuf.get();
630 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
631 != nAlignedWidth)
633 return false;
636 for( long nX = 0; nX < nWidth; nX++ )
638 auto nIndex = *pTmp++;
639 rAcc.SetPixelIndex(nY, nX, SanitizePaletteIndex(nIndex, rPalette, bForceToMonoWhileReading));
643 break;
645 case 16:
647 ColorMaskElement aRedMask(nRMask);
648 if (!aRedMask.CalcMaskShift())
649 return false;
650 ColorMaskElement aGreenMask(nGMask);
651 if (!aGreenMask.CalcMaskShift())
652 return false;
653 ColorMaskElement aBlueMask(nBMask);
654 if (!aBlueMask.CalcMaskShift())
655 return false;
657 ColorMask aMask(aRedMask, aGreenMask, aBlueMask);
658 BitmapColor aColor;
660 for( ; nCount--; nY += nI )
662 sal_uInt16 * pTmp16 = reinterpret_cast<sal_uInt16*>(pBuf.get());
663 if (rIStm.ReadBytes(pTmp16, nAlignedWidth)
664 != nAlignedWidth)
666 return false;
669 for( long nX = 0; nX < nWidth; nX++ )
671 aMask.GetColorFor16BitLSB( aColor, reinterpret_cast<sal_uInt8*>(pTmp16++) );
672 rAcc.SetPixel(nY, nX, SanitizeColor(aColor, bForceToMonoWhileReading));
676 break;
678 case 24:
680 BitmapColor aPixelColor;
682 for( ; nCount--; nY += nI )
684 sal_uInt8* pTmp = pBuf.get();
685 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
686 != nAlignedWidth)
688 return false;
691 for( long nX = 0; nX < nWidth; nX++ )
693 aPixelColor.SetBlue( *pTmp++ );
694 aPixelColor.SetGreen( *pTmp++ );
695 aPixelColor.SetRed( *pTmp++ );
696 rAcc.SetPixel(nY, nX, SanitizeColor(aPixelColor, bForceToMonoWhileReading));
700 break;
702 case 32:
704 ColorMaskElement aRedMask(nRMask);
705 if (!aRedMask.CalcMaskShift())
706 return false;
707 ColorMaskElement aGreenMask(nGMask);
708 if (!aGreenMask.CalcMaskShift())
709 return false;
710 ColorMaskElement aBlueMask(nBMask);
711 if (!aBlueMask.CalcMaskShift())
712 return false;
713 ColorMask aMask(aRedMask, aGreenMask, aBlueMask);
715 BitmapColor aColor;
716 sal_uInt32* pTmp32;
718 if(pAccAlpha)
720 sal_uInt8 aAlpha;
722 for( ; nCount--; nY += nI )
724 pTmp32 = reinterpret_cast<sal_uInt32*>(pBuf.get());
725 if (rIStm.ReadBytes(pTmp32, nAlignedWidth)
726 != nAlignedWidth)
728 return false;
731 for( long nX = 0; nX < nWidth; nX++ )
733 aMask.GetColorAndAlphaFor32Bit( aColor, aAlpha, reinterpret_cast<sal_uInt8*>(pTmp32++) );
734 rAcc.SetPixel(nY, nX, SanitizeColor(aColor, bForceToMonoWhileReading));
735 pAccAlpha->SetPixelIndex(nY, nX, sal_uInt8(0xff) - aAlpha);
736 rAlphaUsed |= bool(0xff != aAlpha);
740 else
742 for( ; nCount--; nY += nI )
744 pTmp32 = reinterpret_cast<sal_uInt32*>(pBuf.get());
745 if (rIStm.ReadBytes(pTmp32, nAlignedWidth)
746 != nAlignedWidth)
748 return false;
751 for( long nX = 0; nX < nWidth; nX++ )
753 aMask.GetColorFor32Bit( aColor, reinterpret_cast<sal_uInt8*>(pTmp32++) );
754 rAcc.SetPixel(nY, nX, SanitizeColor(aColor, bForceToMonoWhileReading));
763 return( rIStm.GetError() == 0UL );
766 bool ImplReadDIBBody(SvStream& rIStm, Bitmap& rBmp, AlphaMask* pBmpAlpha, sal_uLong nOffset, bool bIsMask, bool bMSOFormat)
768 DIBV5Header aHeader;
769 const sal_uLong nStmPos = rIStm.Tell();
770 bool bTopDown(false);
772 if (!ImplReadDIBInfoHeader(rIStm, aHeader, bTopDown, bMSOFormat) && aHeader.nWidth && aHeader.nHeight && aHeader.nBitCount)
773 return false;
775 //BI_BITCOUNT_0 jpeg/png is unsupported
776 if (aHeader.nBitCount == 0)
777 return false;
779 // In case ImplReadDIB() didn't call ImplReadDIBFileHeader() before
780 // this method, nOffset is 0, that's OK.
781 if (nOffset && aHeader.nSize > nOffset)
783 // Header size claims to extend into the image data.
784 // Looks like an error.
785 return false;
788 sal_uInt16 nColors(0);
789 SvStream* pIStm;
790 std::unique_ptr<SvMemoryStream> pMemStm;
791 std::vector<sal_uInt8> aData;
793 if (aHeader.nBitCount <= 8)
795 if(aHeader.nColsUsed)
797 nColors = (sal_uInt16)aHeader.nColsUsed;
799 else
801 nColors = ( 1 << aHeader.nBitCount );
805 if (ZCOMPRESS == aHeader.nCompression)
807 sal_uInt32 nCodedSize(0);
808 sal_uInt32 nUncodedSize(0);
810 // read coding information
811 rIStm.ReadUInt32( nCodedSize ).ReadUInt32( nUncodedSize ).ReadUInt32( aHeader.nCompression );
812 if (nCodedSize > rIStm.remainingSize())
813 nCodedSize = sal_uInt32(rIStm.remainingSize());
814 size_t nSizeInc(4 * rIStm.remainingSize());
815 if (nUncodedSize < nSizeInc)
816 nSizeInc = nUncodedSize;
818 if (nSizeInc > 0)
820 // decode buffer
821 const sal_uLong nCodedPos = rIStm.Tell();
822 ZCodec aCodec;
823 aCodec.BeginCompression();
824 aData.resize(nSizeInc);
825 size_t nDataPos(0);
826 while (nUncodedSize > nDataPos)
828 assert(aData.size() > nDataPos);
829 const size_t nToRead(std::min<size_t>(nUncodedSize - nDataPos, aData.size() - nDataPos));
830 assert(nToRead > 0);
831 assert(!aData.empty());
832 const long nRead = aCodec.Read(rIStm, aData.data() + nDataPos, sal_uInt32(nToRead));
833 if (nRead > 0)
835 nDataPos += static_cast<unsigned long>(nRead);
836 // we haven't read everything yet: resize buffer and continue
837 if (nDataPos < nUncodedSize)
838 aData.resize(aData.size() + nSizeInc);
840 else
842 break;
845 // truncate the data buffer to actually read size
846 aData.resize(nDataPos);
847 // set the real uncoded size
848 nUncodedSize = sal_uInt32(aData.size());
849 aCodec.EndCompression();
851 // Seek behind the encoded block. There might have been bytes left or the codec might have read more than necessary.
852 rIStm.Seek(nCodedSize + nCodedPos);
855 if (aData.empty())
857 // add something so we can take address of the first element
858 aData.resize(1);
859 nUncodedSize = 0;
862 // set decoded bytes to memory stream,
863 // from which we will read the bitmap data
864 pMemStm.reset(new SvMemoryStream);
865 pIStm = pMemStm.get();
866 assert(!aData.empty());
867 pMemStm->SetBuffer(aData.data(), nUncodedSize, nUncodedSize);
868 nOffset = 0;
870 else
872 pIStm = &rIStm;
875 const sal_Int64 nBitsPerLine (static_cast<sal_Int64>(aHeader.nWidth) * static_cast<sal_Int64>(aHeader.nBitCount));
876 if (nBitsPerLine > SAL_MAX_UINT32)
877 return false;
878 const sal_uInt64 nAlignedWidth(AlignedWidth4Bytes(static_cast<sal_uLong>(nBitsPerLine)));
880 switch (aHeader.nCompression)
882 case RLE_8:
883 if (aHeader.nBitCount != 8)
884 return false;
885 break;
886 case RLE_4:
887 if (aHeader.nBitCount != 4)
888 return false;
889 break;
890 case BITFIELDS:
891 break;
892 case ZCOMPRESS:
893 case COMPRESS_NONE:
895 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
896 sal_uInt64 nMaxWidth = pIStm->remainingSize();
897 if (aHeader.nHeight != 0)
898 nMaxWidth /= aHeader.nHeight;
899 if (nMaxWidth < nAlignedWidth)
900 return false;
901 break;
903 default:
904 return false;
907 const Size aSizePixel(aHeader.nWidth, aHeader.nHeight);
908 AlphaMask aNewBmpAlpha;
909 AlphaMask::ScopedWriteAccess pAccAlpha;
910 bool bAlphaPossible(pBmpAlpha && aHeader.nBitCount == 32);
912 if (bAlphaPossible)
914 const bool bRedSet(0 != aHeader.nV5RedMask);
915 const bool bGreenSet(0 != aHeader.nV5GreenMask);
916 const bool bBlueSet(0 != aHeader.nV5BlueMask);
918 // some clipboard entries have alpha mask on zero to say that there is
919 // no alpha; do only use this when the other masks are set. The MS docu
920 // says that masks are only to be set when bV5Compression is set to
921 // BI_BITFIELDS, but there seem to exist a wild variety of usages...
922 if((bRedSet || bGreenSet || bBlueSet) && (0 == aHeader.nV5AlphaMask))
924 bAlphaPossible = false;
928 if (bAlphaPossible)
930 aNewBmpAlpha = AlphaMask(aSizePixel);
931 pAccAlpha = AlphaMask::ScopedWriteAccess(aNewBmpAlpha);
934 // read palette
935 BitmapPalette aPalette;
936 if (nColors)
938 aPalette.SetEntryCount(nColors);
939 ImplReadDIBPalette(*pIStm, aPalette, aHeader.nSize != DIBCOREHEADERSIZE);
942 if (pIStm->GetError())
943 return false;
945 sal_uInt16 nBitCount(discretizeBitcount(aHeader.nBitCount));
946 const BitmapPalette* pPal = &aPalette;
947 //ofz#948 match the surrounding logic of case TransparentType::Bitmap of
948 //ReadDIBBitmapEx but do it while reading for performance
949 const bool bIsAlpha = (nBitCount == 8 && !!aPalette && aPalette.IsGreyPalette());
950 const bool bForceToMonoWhileReading = (bIsMask && !bIsAlpha && nBitCount != 1);
951 if (bForceToMonoWhileReading)
953 pPal = nullptr;
954 nBitCount = 1;
955 SAL_WARN( "vcl", "forcing mask to monochrome");
958 Bitmap aNewBmp(aSizePixel, nBitCount, pPal);
959 Bitmap::ScopedWriteAccess pAcc(aNewBmp);
960 if (!pAcc)
961 return false;
962 if (pAcc->Width() != aHeader.nWidth || pAcc->Height() != aHeader.nHeight)
964 return false;
967 if(nOffset)
969 pIStm->SeekRel(nOffset - (pIStm->Tell() - nStmPos));
972 // read bits
973 bool bAlphaUsed(false);
974 bool bRet = ImplReadDIBBits(*pIStm, aHeader, *pAcc, aPalette, pAccAlpha.get(), bTopDown, bAlphaUsed, nAlignedWidth, bForceToMonoWhileReading);
976 if (bRet && aHeader.nXPelsPerMeter && aHeader.nYPelsPerMeter)
978 MapMode aMapMode(
979 MapUnit::MapMM,
980 Point(),
981 Fraction(1000, aHeader.nXPelsPerMeter),
982 Fraction(1000, aHeader.nYPelsPerMeter));
984 aNewBmp.SetPrefMapMode(aMapMode);
985 aNewBmp.SetPrefSize(Size(aHeader.nWidth, aHeader.nHeight));
988 pAcc.reset();
990 if (bAlphaPossible)
992 pAccAlpha.reset();
994 if(!bAlphaUsed)
996 bAlphaPossible = false;
1000 if (bRet)
1002 rBmp = aNewBmp;
1004 if(bAlphaPossible)
1006 *pBmpAlpha = aNewBmpAlpha;
1010 return bRet;
1013 bool ImplReadDIBFileHeader( SvStream& rIStm, sal_uLong& rOffset )
1015 bool bRet = false;
1017 const sal_uInt64 nSavedStreamPos( rIStm.Tell() );
1018 const sal_uInt64 nStreamLength( rIStm.Seek( STREAM_SEEK_TO_END ) );
1019 rIStm.Seek( nSavedStreamPos );
1021 sal_uInt16 nTmp16 = 0;
1022 rIStm.ReadUInt16( nTmp16 );
1024 if ( ( 0x4D42 == nTmp16 ) || ( 0x4142 == nTmp16 ) )
1026 sal_uInt32 nTmp32(0);
1027 if ( 0x4142 == nTmp16 )
1029 rIStm.SeekRel( 12 );
1030 rIStm.ReadUInt16( nTmp16 );
1031 rIStm.SeekRel( 8 );
1032 rIStm.ReadUInt32( nTmp32 );
1033 rOffset = nTmp32 - 28UL;
1034 bRet = ( 0x4D42 == nTmp16 );
1036 else // 0x4D42 == nTmp16, 'MB' from BITMAPFILEHEADER
1038 rIStm.SeekRel( 8 ); // we are on bfSize member of BITMAPFILEHEADER, forward to bfOffBits
1039 rIStm.ReadUInt32( nTmp32 ); // read bfOffBits
1040 rOffset = nTmp32 - 14UL; // adapt offset by sizeof(BITMAPFILEHEADER)
1041 bRet = ( rIStm.GetError() == 0UL );
1044 if ( rOffset >= nStreamLength )
1046 // Offset claims that image starts past the end of the
1047 // stream. Unlikely.
1048 rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
1049 bRet = false;
1052 else
1053 rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
1055 return bRet;
1058 bool ImplWriteDIBPalette( SvStream& rOStm, BitmapReadAccess& rAcc )
1060 const sal_uInt16 nColors = rAcc.GetPaletteEntryCount();
1061 const sal_uLong nPalSize = nColors * 4UL;
1062 std::unique_ptr<sal_uInt8[]> pEntries(new sal_uInt8[ nPalSize ]);
1063 sal_uInt8* pTmpEntry = pEntries.get();
1064 BitmapColor aPalColor;
1066 for( sal_uInt16 i = 0; i < nColors; i++ )
1068 const BitmapColor& rPalColor = rAcc.GetPaletteColor( i );
1070 *pTmpEntry++ = rPalColor.GetBlue();
1071 *pTmpEntry++ = rPalColor.GetGreen();
1072 *pTmpEntry++ = rPalColor.GetRed();
1073 *pTmpEntry++ = 0;
1076 rOStm.WriteBytes( pEntries.get(), nPalSize );
1078 return( rOStm.GetError() == 0UL );
1081 bool ImplWriteRLE( SvStream& rOStm, BitmapReadAccess& rAcc, bool bRLE4 )
1083 const sal_uLong nWidth = rAcc.Width();
1084 const sal_uLong nHeight = rAcc.Height();
1085 sal_uLong nX;
1086 sal_uLong nSaveIndex;
1087 sal_uLong nCount;
1088 sal_uLong nBufCount;
1089 std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ ( nWidth << 1 ) + 2 ]);
1090 sal_uInt8 cPix;
1091 sal_uInt8 cLast;
1092 bool bFound;
1094 for ( long nY = nHeight - 1; nY >= 0; nY-- )
1096 sal_uInt8* pTmp = pBuf.get();
1097 nX = nBufCount = 0UL;
1099 while( nX < nWidth )
1101 nCount = 1;
1102 cPix = rAcc.GetPixelIndex( nY, nX++ );
1104 while( ( nX < nWidth ) && ( nCount < 255 )
1105 && ( cPix == rAcc.GetPixelIndex( nY, nX ) ) )
1107 nX++;
1108 nCount++;
1111 if ( nCount > 1 )
1113 *pTmp++ = (sal_uInt8) nCount;
1114 *pTmp++ = ( bRLE4 ? ( ( cPix << 4 ) | cPix ) : cPix );
1115 nBufCount += 2;
1117 else
1119 cLast = cPix;
1120 nSaveIndex = nX - 1UL;
1121 bFound = false;
1123 while( ( nX < nWidth ) && ( nCount < 256 )
1124 && ( cPix = rAcc.GetPixelIndex( nY, nX ) ) != cLast )
1126 nX++; nCount++;
1127 cLast = cPix;
1128 bFound = true;
1131 if ( bFound )
1132 nX--;
1134 if ( nCount > 3 )
1136 *pTmp++ = 0;
1137 *pTmp++ = (sal_uInt8) --nCount;
1139 if( bRLE4 )
1141 for ( sal_uLong i = 0; i < nCount; i++, pTmp++ )
1143 *pTmp = rAcc.GetPixelIndex( nY, nSaveIndex++ ) << 4;
1145 if ( ++i < nCount )
1146 *pTmp |= rAcc.GetPixelIndex( nY, nSaveIndex++ );
1149 nCount = ( nCount + 1 ) >> 1;
1151 else
1153 for( sal_uLong i = 0UL; i < nCount; i++ )
1154 *pTmp++ = rAcc.GetPixelIndex( nY, nSaveIndex++ );
1157 if ( nCount & 1 )
1159 *pTmp++ = 0;
1160 nBufCount += ( nCount + 3 );
1162 else
1163 nBufCount += ( nCount + 2 );
1165 else
1167 *pTmp++ = 1;
1168 *pTmp++ = rAcc.GetPixelIndex( nY, nSaveIndex ) << (bRLE4 ? 4 : 0);
1170 if ( nCount == 3 )
1172 *pTmp++ = 1;
1173 *pTmp++ = rAcc.GetPixelIndex( nY, ++nSaveIndex ) << ( bRLE4 ? 4 : 0 );
1174 nBufCount += 4;
1176 else
1177 nBufCount += 2;
1182 pBuf[ nBufCount++ ] = 0;
1183 pBuf[ nBufCount++ ] = 0;
1185 rOStm.WriteBytes( pBuf.get(), nBufCount );
1188 rOStm.WriteUChar( 0 );
1189 rOStm.WriteUChar( 1 );
1191 return( rOStm.GetError() == 0UL );
1194 bool ImplWriteDIBBits(SvStream& rOStm, BitmapReadAccess& rAcc, BitmapReadAccess* pAccAlpha, sal_uLong nCompression, sal_uInt32& rImageSize)
1196 if(!pAccAlpha && BITFIELDS == nCompression)
1198 const ColorMask& rMask = rAcc.GetColorMask();
1199 SVBT32 aVal32;
1201 UInt32ToSVBT32( rMask.GetRedMask(), aVal32 );
1202 rOStm.WriteBytes( aVal32, 4UL );
1204 UInt32ToSVBT32( rMask.GetGreenMask(), aVal32 );
1205 rOStm.WriteBytes( aVal32, 4UL );
1207 UInt32ToSVBT32( rMask.GetBlueMask(), aVal32 );
1208 rOStm.WriteBytes( aVal32, 4UL );
1210 rImageSize = rOStm.Tell();
1212 if( rAcc.IsBottomUp() )
1213 rOStm.WriteBytes(rAcc.GetBuffer(), rAcc.Height() * rAcc.GetScanlineSize());
1214 else
1216 for( long nY = rAcc.Height() - 1, nScanlineSize = rAcc.GetScanlineSize(); nY >= 0; nY-- )
1217 rOStm.WriteBytes( rAcc.GetScanline(nY), nScanlineSize );
1220 else if(!pAccAlpha && ((RLE_4 == nCompression) || (RLE_8 == nCompression)))
1222 rImageSize = rOStm.Tell();
1223 ImplWriteRLE( rOStm, rAcc, RLE_4 == nCompression );
1225 else if(!nCompression)
1227 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are not
1228 // handled properly below (would have to set color masks, and
1229 // nCompression=BITFIELDS - but color mask is not set for
1230 // formats != *_TC_*). Note that this very problem might cause
1231 // trouble at other places - the introduction of 32 bit RGBA
1232 // bitmaps is relatively recent.
1233 // #i59239# discretize bitcount for aligned width to 1,4,8,24
1234 // (other cases are not written below)
1235 const sal_uInt16 nBitCount(pAccAlpha ? 32 : discretizeBitcount(rAcc.GetBitCount()));
1236 const sal_uLong nAlignedWidth(AlignedWidth4Bytes(rAcc.Width() * nBitCount));
1237 bool bNative(false);
1239 switch(rAcc.GetScanlineFormat())
1241 case ScanlineFormat::N1BitMsbPal:
1242 case ScanlineFormat::N4BitMsnPal:
1243 case ScanlineFormat::N8BitPal:
1244 case ScanlineFormat::N24BitTcBgr:
1246 if(!pAccAlpha && rAcc.IsBottomUp() && (rAcc.GetScanlineSize() == nAlignedWidth))
1248 bNative = true;
1251 break;
1254 default:
1256 break;
1260 rImageSize = rOStm.Tell();
1262 if(bNative)
1264 rOStm.WriteBytes(rAcc.GetBuffer(), nAlignedWidth * rAcc.Height());
1266 else
1268 const long nWidth(rAcc.Width());
1269 const long nHeight(rAcc.Height());
1270 std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ nAlignedWidth ]);
1271 switch( nBitCount )
1273 case 1:
1275 //valgrind, zero out the trailing unused alignment bytes
1276 size_t nUnusedBytes = nAlignedWidth - ((nWidth+7) / 8);
1277 memset(pBuf.get() + nAlignedWidth - nUnusedBytes, 0, nUnusedBytes);
1279 for( long nY = nHeight - 1; nY >= 0; nY-- )
1281 sal_uInt8* pTmp = pBuf.get();
1282 sal_uInt8 cTmp = 0;
1284 for( long nX = 0, nShift = 8; nX < nWidth; nX++ )
1286 if( !nShift )
1288 nShift = 8;
1289 *pTmp++ = cTmp;
1290 cTmp = 0;
1293 cTmp |= rAcc.GetPixelIndex( nY, nX ) << --nShift;
1296 *pTmp = cTmp;
1297 rOStm.WriteBytes( pBuf.get(), nAlignedWidth );
1300 break;
1302 case 4:
1304 //valgrind, zero out the trailing unused alignment bytes
1305 size_t nUnusedBytes = nAlignedWidth - ((nWidth+1) / 2);
1306 memset(pBuf.get() + nAlignedWidth - nUnusedBytes, 0, nUnusedBytes);
1308 for( long nY = nHeight - 1; nY >= 0; nY-- )
1310 sal_uInt8* pTmp = pBuf.get();
1311 sal_uInt8 cTmp = 0;
1313 for( long nX = 0, nShift = 2; nX < nWidth; nX++ )
1315 if( !nShift )
1317 nShift = 2;
1318 *pTmp++ = cTmp;
1319 cTmp = 0;
1322 cTmp |= rAcc.GetPixelIndex( nY, nX ) << ( --nShift << 2 );
1324 *pTmp = cTmp;
1325 rOStm.WriteBytes( pBuf.get(), nAlignedWidth );
1328 break;
1330 case 8:
1332 for( long nY = nHeight - 1; nY >= 0; nY-- )
1334 sal_uInt8* pTmp = pBuf.get();
1336 for( long nX = 0; nX < nWidth; nX++ )
1337 *pTmp++ = rAcc.GetPixelIndex( nY, nX );
1339 rOStm.WriteBytes( pBuf.get(), nAlignedWidth );
1342 break;
1344 case 24:
1346 //valgrind, zero out the trailing unused alignment bytes
1347 size_t nUnusedBytes = nAlignedWidth - nWidth * 3;
1348 memset(pBuf.get() + nAlignedWidth - nUnusedBytes, 0, nUnusedBytes);
1350 SAL_FALLTHROUGH;
1351 // #i59239# fallback to 24 bit format, if bitcount is non-default
1352 default:
1354 BitmapColor aPixelColor;
1355 const bool bWriteAlpha(32 == nBitCount && pAccAlpha);
1357 for( long nY = nHeight - 1; nY >= 0; nY-- )
1359 sal_uInt8* pTmp = pBuf.get();
1361 for( long nX = 0; nX < nWidth; nX++ )
1363 // when alpha is used, this may be non-24bit main bitmap, so use GetColor
1364 // instead of GetPixel to ensure RGB value
1365 aPixelColor = rAcc.GetColor( nY, nX );
1367 *pTmp++ = aPixelColor.GetBlue();
1368 *pTmp++ = aPixelColor.GetGreen();
1369 *pTmp++ = aPixelColor.GetRed();
1371 if(bWriteAlpha)
1373 *pTmp++ = (sal_uInt8)0xff - pAccAlpha->GetPixelIndex( nY, nX );
1377 rOStm.WriteBytes( pBuf.get(), nAlignedWidth );
1380 break;
1385 rImageSize = rOStm.Tell() - rImageSize;
1387 return (!rOStm.GetError());
1390 bool ImplWriteDIBBody(const Bitmap& rBitmap, SvStream& rOStm, BitmapReadAccess& rAcc, BitmapReadAccess* pAccAlpha, bool bCompressed)
1392 const MapMode aMapPixel(MapUnit::MapPixel);
1393 DIBV5Header aHeader;
1394 sal_uLong nImageSizePos(0);
1395 sal_uLong nEndPos(0);
1396 sal_uInt32 nCompression(COMPRESS_NONE);
1397 bool bRet(false);
1399 aHeader.nSize = pAccAlpha ? DIBV5HEADERSIZE : DIBINFOHEADERSIZE; // size dependent on CF_DIB type to use
1400 aHeader.nWidth = rAcc.Width();
1401 aHeader.nHeight = rAcc.Height();
1402 aHeader.nPlanes = 1;
1404 if(!pAccAlpha && isBitfieldCompression(rAcc.GetScanlineFormat()))
1406 aHeader.nBitCount = (ScanlineFormat::N16BitTcLsbMask == rAcc.GetScanlineFormat()) ? 16 : 32;
1407 aHeader.nSizeImage = rAcc.Height() * rAcc.GetScanlineSize();
1408 nCompression = BITFIELDS;
1410 else
1412 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are
1413 // not handled properly below (would have to set color
1414 // masks, and nCompression=BITFIELDS - but color mask is
1415 // not set for formats != *_TC_*). Note that this very
1416 // problem might cause trouble at other places - the
1417 // introduction of 32 bit RGBA bitmaps is relatively
1418 // recent.
1419 // #i59239# discretize bitcount to 1,4,8,24 (other cases
1420 // are not written below)
1421 const sal_uInt16 nBitCount(pAccAlpha ? 32 : discretizeBitcount(rAcc.GetBitCount()));
1422 aHeader.nBitCount = nBitCount;
1423 aHeader.nSizeImage = rAcc.Height() * AlignedWidth4Bytes(rAcc.Width() * aHeader.nBitCount);
1425 if(bCompressed)
1427 if(4 == nBitCount)
1429 nCompression = RLE_4;
1431 else if(8 == nBitCount)
1433 nCompression = RLE_8;
1438 if((rOStm.GetCompressMode() & SvStreamCompressFlags::ZBITMAP) && (rOStm.GetVersion() >= SOFFICE_FILEFORMAT_40))
1440 aHeader.nCompression = ZCOMPRESS;
1442 else
1444 aHeader.nCompression = nCompression;
1447 if(rBitmap.GetPrefSize().Width() && rBitmap.GetPrefSize().Height() && (rBitmap.GetPrefMapMode() != aMapPixel))
1449 // #i48108# Try to recover xpels/ypels as previously stored on
1450 // disk. The problem with just converting maPrefSize to 100th
1451 // mm and then relating that to the bitmap pixel size is that
1452 // MapMode is integer-based, and suffers from roundoffs,
1453 // especially if maPrefSize is small. Trying to circumvent
1454 // that by performing part of the math in floating point.
1455 const Size aScale100000(OutputDevice::LogicToLogic(Size(100000, 100000), MapUnit::Map100thMM, rBitmap.GetPrefMapMode()));
1456 const double fBmpWidthM((double)rBitmap.GetPrefSize().Width() / aScale100000.Width());
1457 const double fBmpHeightM((double)rBitmap.GetPrefSize().Height() / aScale100000.Height());
1459 if(!basegfx::fTools::equalZero(fBmpWidthM) && !basegfx::fTools::equalZero(fBmpHeightM))
1461 aHeader.nXPelsPerMeter = basegfx::fround(rAcc.Width() / fabs(fBmpWidthM));
1462 aHeader.nYPelsPerMeter = basegfx::fround(rAcc.Height() / fabs(fBmpHeightM));
1466 aHeader.nColsUsed = ((!pAccAlpha && aHeader.nBitCount <= 8) ? rAcc.GetPaletteEntryCount() : 0);
1467 aHeader.nColsImportant = 0;
1469 rOStm.WriteUInt32( aHeader.nSize );
1470 rOStm.WriteInt32( aHeader.nWidth );
1471 rOStm.WriteInt32( aHeader.nHeight );
1472 rOStm.WriteUInt16( aHeader.nPlanes );
1473 rOStm.WriteUInt16( aHeader.nBitCount );
1474 rOStm.WriteUInt32( aHeader.nCompression );
1476 nImageSizePos = rOStm.Tell();
1477 rOStm.SeekRel( sizeof( aHeader.nSizeImage ) );
1479 rOStm.WriteInt32( aHeader.nXPelsPerMeter );
1480 rOStm.WriteInt32( aHeader.nYPelsPerMeter );
1481 rOStm.WriteUInt32( aHeader.nColsUsed );
1482 rOStm.WriteUInt32( aHeader.nColsImportant );
1484 if(pAccAlpha) // only write DIBV5 when asked to do so
1486 aHeader.nV5CSType = 0x57696E20; // LCS_WINDOWS_COLOR_SPACE
1487 aHeader.nV5Intent = 0x00000004; // LCS_GM_IMAGES
1489 rOStm.WriteUInt32( aHeader.nV5RedMask );
1490 rOStm.WriteUInt32( aHeader.nV5GreenMask );
1491 rOStm.WriteUInt32( aHeader.nV5BlueMask );
1492 rOStm.WriteUInt32( aHeader.nV5AlphaMask );
1493 rOStm.WriteUInt32( aHeader.nV5CSType );
1495 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzRed.aXyzX );
1496 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzRed.aXyzY );
1497 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzRed.aXyzZ );
1498 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzGreen.aXyzX );
1499 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzGreen.aXyzY );
1500 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzGreen.aXyzZ );
1501 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzBlue.aXyzX );
1502 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzBlue.aXyzY );
1503 rOStm.WriteInt32( aHeader.aV5Endpoints.aXyzBlue.aXyzZ );
1505 rOStm.WriteUInt32( aHeader.nV5GammaRed );
1506 rOStm.WriteUInt32( aHeader.nV5GammaGreen );
1507 rOStm.WriteUInt32( aHeader.nV5GammaBlue );
1508 rOStm.WriteUInt32( aHeader.nV5Intent );
1509 rOStm.WriteUInt32( aHeader.nV5ProfileData );
1510 rOStm.WriteUInt32( aHeader.nV5ProfileSize );
1511 rOStm.WriteUInt32( aHeader.nV5Reserved );
1514 if(ZCOMPRESS == aHeader.nCompression)
1516 ZCodec aCodec;
1517 SvMemoryStream aMemStm(aHeader.nSizeImage + 4096, 65535);
1518 sal_uLong nCodedPos(rOStm.Tell());
1519 sal_uLong nLastPos(0);
1520 sal_uInt32 nCodedSize(0);
1521 sal_uInt32 nUncodedSize(0);
1523 // write uncoded data palette
1524 if(aHeader.nColsUsed)
1526 ImplWriteDIBPalette(aMemStm, rAcc);
1529 // write uncoded bits
1530 bRet = ImplWriteDIBBits(aMemStm, rAcc, pAccAlpha, nCompression, aHeader.nSizeImage);
1532 // get uncoded size
1533 nUncodedSize = aMemStm.Tell();
1535 // seek over compress info
1536 rOStm.SeekRel(12);
1538 // write compressed data
1539 aCodec.BeginCompression(3);
1540 aCodec.Write(rOStm, static_cast<sal_uInt8 const *>(aMemStm.GetData()), nUncodedSize);
1541 aCodec.EndCompression();
1543 // update compress info ( coded size, uncoded size, uncoded compression )
1544 nLastPos = rOStm.Tell();
1545 nCodedSize = nLastPos - nCodedPos - 12;
1546 rOStm.Seek(nCodedPos);
1547 rOStm.WriteUInt32( nCodedSize ).WriteUInt32( nUncodedSize ).WriteUInt32( nCompression );
1548 rOStm.Seek(nLastPos);
1550 if(bRet)
1552 bRet = (ERRCODE_NONE == rOStm.GetError());
1555 else
1557 if(aHeader.nColsUsed)
1559 ImplWriteDIBPalette(rOStm, rAcc);
1562 bRet = ImplWriteDIBBits(rOStm, rAcc, pAccAlpha, aHeader.nCompression, aHeader.nSizeImage);
1565 nEndPos = rOStm.Tell();
1566 rOStm.Seek(nImageSizePos);
1567 rOStm.WriteUInt32( aHeader.nSizeImage );
1568 rOStm.Seek(nEndPos);
1570 return bRet;
1573 bool ImplWriteDIBFileHeader(SvStream& rOStm, BitmapReadAccess& rAcc, bool bUseDIBV5)
1575 const sal_uInt32 nPalCount((rAcc.HasPalette() ? rAcc.GetPaletteEntryCount() : isBitfieldCompression(rAcc.GetScanlineFormat()) ? 3UL : 0UL));
1576 const sal_uInt32 nOffset(14 + (bUseDIBV5 ? DIBV5HEADERSIZE : DIBINFOHEADERSIZE) + nPalCount * 4UL);
1578 rOStm.WriteUInt16( 0x4D42 ); // 'MB' from BITMAPFILEHEADER
1579 rOStm.WriteUInt32( nOffset + (rAcc.Height() * rAcc.GetScanlineSize()) );
1580 rOStm.WriteUInt16( 0 );
1581 rOStm.WriteUInt16( 0 );
1582 rOStm.WriteUInt32( nOffset );
1584 return( rOStm.GetError() == 0UL );
1587 bool ImplReadDIB(
1588 Bitmap& rTarget,
1589 AlphaMask* pTargetAlpha,
1590 SvStream& rIStm,
1591 bool bFileHeader,
1592 bool bIsMask=false,
1593 bool bMSOFormat=false)
1595 const SvStreamEndian nOldFormat(rIStm.GetEndian());
1596 const sal_uLong nOldPos(rIStm.Tell());
1597 sal_uLong nOffset(0UL);
1598 bool bRet(false);
1600 rIStm.SetEndian(SvStreamEndian::LITTLE);
1602 if(bFileHeader)
1604 if(ImplReadDIBFileHeader(rIStm, nOffset))
1606 bRet = ImplReadDIBBody(rIStm, rTarget, nOffset >= DIBV5HEADERSIZE ? pTargetAlpha : nullptr, nOffset, bIsMask, bMSOFormat);
1609 else
1611 bRet = ImplReadDIBBody(rIStm, rTarget, nullptr, nOffset, bIsMask, bMSOFormat);
1614 if(!bRet)
1616 if(!rIStm.GetError())
1618 rIStm.SetError(SVSTREAM_GENERALERROR);
1621 rIStm.Seek(nOldPos);
1624 rIStm.SetEndian(nOldFormat);
1626 return bRet;
1629 bool ImplWriteDIB(
1630 const Bitmap& rSource,
1631 const Bitmap* pSourceAlpha,
1632 SvStream& rOStm,
1633 bool bCompressed,
1634 bool bFileHeader)
1636 const Size aSizePix(rSource.GetSizePixel());
1637 bool bRet(false);
1639 if(aSizePix.Width() && aSizePix.Height())
1641 Bitmap::ScopedReadAccess pAcc(const_cast< Bitmap& >(rSource));
1642 Bitmap::ScopedReadAccess pAccAlpha;
1643 const SvStreamEndian nOldFormat(rOStm.GetEndian());
1644 const sal_uLong nOldPos(rOStm.Tell());
1646 if(pSourceAlpha)
1648 const Size aSizePixAlpha(pSourceAlpha->GetSizePixel());
1650 if(aSizePixAlpha == aSizePix)
1652 pAccAlpha = Bitmap::ScopedReadAccess(const_cast< Bitmap& >(*pSourceAlpha));
1654 else
1656 OSL_ENSURE(false, "WriteDIB got an alpha channel, but it's pixel size differs from the base bitmap (!)");
1660 rOStm.SetEndian(SvStreamEndian::LITTLE);
1662 if (pAcc)
1664 if(bFileHeader)
1666 if(ImplWriteDIBFileHeader(rOStm, *pAcc, nullptr != pSourceAlpha))
1668 bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, pAccAlpha.get(), bCompressed);
1671 else
1673 bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, pAccAlpha.get(), bCompressed);
1676 pAcc.reset();
1679 pAccAlpha.reset();
1681 if(!bRet)
1683 rOStm.SetError(SVSTREAM_GENERALERROR);
1684 rOStm.Seek(nOldPos);
1687 rOStm.SetEndian(nOldFormat);
1690 return bRet;
1693 } // unnamed namespace
1695 bool ReadDIB(
1696 Bitmap& rTarget,
1697 SvStream& rIStm,
1698 bool bFileHeader,
1699 bool bMSOFormat)
1701 return ImplReadDIB(rTarget, nullptr, rIStm, bFileHeader, false, bMSOFormat);
1704 bool ReadDIBBitmapEx(
1705 BitmapEx& rTarget,
1706 SvStream& rIStm)
1708 Bitmap aBmp;
1709 bool bRetval(ImplReadDIB(aBmp, nullptr, rIStm, true) && !rIStm.GetError());
1711 if(bRetval)
1713 // base bitmap was read, set as return value and try to read alpha extra-data
1714 const sal_uLong nStmPos(rIStm.Tell());
1715 sal_uInt32 nMagic1(0);
1716 sal_uInt32 nMagic2(0);
1718 rTarget = BitmapEx(aBmp);
1719 rIStm.ReadUInt32( nMagic1 ).ReadUInt32( nMagic2 );
1720 bRetval = (0x25091962 == nMagic1) && (0xACB20201 == nMagic2) && !rIStm.GetError();
1722 if(bRetval)
1724 sal_uInt8 tmp = 0;
1725 rIStm.ReadUChar( tmp );
1726 TransparentType transparent = (TransparentType)tmp;
1727 bRetval = !rIStm.GetError();
1729 if(bRetval)
1731 switch (transparent)
1733 case TransparentType::Bitmap:
1735 Bitmap aMask;
1737 bRetval = ImplReadDIB(aMask, nullptr, rIStm, true, true);
1739 if(bRetval)
1741 if(!!aMask)
1743 // do we have an alpha mask?
1744 if((8 == aMask.GetBitCount()) && aMask.HasGreyPalette())
1746 AlphaMask aAlpha;
1748 // create alpha mask quickly (without greyscale conversion)
1749 aAlpha.ImplSetBitmap(aMask);
1750 rTarget = BitmapEx(aBmp, aAlpha);
1752 else
1754 rTarget = BitmapEx(aBmp, aMask);
1758 break;
1760 case TransparentType::Color:
1762 Color aTransparentColor;
1764 ReadColor( rIStm, aTransparentColor );
1765 bRetval = !rIStm.GetError();
1767 if(bRetval)
1769 rTarget = BitmapEx(aBmp, aTransparentColor);
1771 break;
1773 default: break;
1778 if(!bRetval)
1780 // alpha extra data could not be read; reset, but use base bitmap as result
1781 rIStm.ResetError();
1782 rIStm.Seek(nStmPos);
1783 bRetval = true;
1787 return bRetval;
1790 bool ReadDIBV5(
1791 Bitmap& rTarget,
1792 AlphaMask& rTargetAlpha,
1793 SvStream& rIStm)
1795 return ImplReadDIB(rTarget, &rTargetAlpha, rIStm, true);
1798 bool WriteDIB(
1799 const Bitmap& rSource,
1800 SvStream& rOStm,
1801 bool bCompressed,
1802 bool bFileHeader)
1804 return ImplWriteDIB(rSource, nullptr, rOStm, bCompressed, bFileHeader);
1807 bool WriteDIBBitmapEx(
1808 const BitmapEx& rSource,
1809 SvStream& rOStm)
1811 if(ImplWriteDIB(rSource.GetBitmap(), nullptr, rOStm, true, true))
1813 rOStm.WriteUInt32( 0x25091962 );
1814 rOStm.WriteUInt32( 0xACB20201 );
1815 rOStm.WriteUChar( (sal_uChar)rSource.eTransparent );
1817 if(TransparentType::Bitmap == rSource.eTransparent)
1819 return ImplWriteDIB(rSource.aMask, nullptr, rOStm, true, true);
1821 else if(TransparentType::Color == rSource.eTransparent)
1823 WriteColor( rOStm, rSource.aTransparentColor );
1824 return true;
1828 return false;
1831 sal_uInt32 getDIBV5HeaderSize()
1833 return DIBV5HEADERSIZE;
1836 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */