LibreOffice Module vcl (master) 1
dibtools.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include <cassert>
24
25#include <o3tl/safeint.hxx>
26#include <vcl/dibtools.hxx>
28#include <tools/zcodec.hxx>
29#include <tools/stream.hxx>
30#include <tools/fract.hxx>
31#include <tools/helpers.hxx>
34#include <vcl/bitmapex.hxx>
35#include <vcl/outdev.hxx>
37#include <memory>
38
39#define DIBCOREHEADERSIZE ( 12UL )
40#define DIBINFOHEADERSIZE ( sizeof(DIBInfoHeader) )
41#define DIBV5HEADERSIZE ( sizeof(DIBV5Header) )
42
43// - DIBInfoHeader and DIBV5Header
44
45typedef sal_Int32 FXPT2DOT30;
46
47namespace
48{
49
50struct CIEXYZ
51{
52 FXPT2DOT30 aXyzX;
53 FXPT2DOT30 aXyzY;
54 FXPT2DOT30 aXyzZ;
55
56 CIEXYZ()
57 : aXyzX(0),
58 aXyzY(0),
59 aXyzZ(0)
60 {}
61};
62
63struct CIEXYZTriple
64{
65 CIEXYZ aXyzRed;
66 CIEXYZ aXyzGreen;
67 CIEXYZ aXyzBlue;
68
69 CIEXYZTriple()
70 {}
71};
72
73struct DIBInfoHeader
74{
75 sal_uInt32 nSize;
76 sal_Int32 nWidth;
77 sal_Int32 nHeight;
78 sal_uInt16 nPlanes;
79 sal_uInt16 nBitCount;
80 sal_uInt32 nCompression;
81 sal_uInt32 nSizeImage;
82 sal_Int32 nXPelsPerMeter;
83 sal_Int32 nYPelsPerMeter;
84 sal_uInt32 nColsUsed;
85 sal_uInt32 nColsImportant;
86
87 DIBInfoHeader()
88 : nSize(0),
89 nWidth(0),
90 nHeight(0),
91 nPlanes(0),
92 nBitCount(0),
93 nCompression(0),
94 nSizeImage(0),
95 nXPelsPerMeter(0),
96 nYPelsPerMeter(0),
97 nColsUsed(0),
98 nColsImportant(0)
99 {}
100};
101
102struct DIBV5Header : public DIBInfoHeader
103{
104 sal_uInt32 nV5RedMask;
105 sal_uInt32 nV5GreenMask;
106 sal_uInt32 nV5BlueMask;
107 sal_uInt32 nV5AlphaMask;
108 sal_uInt32 nV5CSType;
109 CIEXYZTriple aV5Endpoints;
110 sal_uInt32 nV5GammaRed;
111 sal_uInt32 nV5GammaGreen;
112 sal_uInt32 nV5GammaBlue;
113 sal_uInt32 nV5Intent;
114 sal_uInt32 nV5ProfileData;
115 sal_uInt32 nV5ProfileSize;
116 sal_uInt32 nV5Reserved;
117
118 DIBV5Header()
119 : nV5RedMask(0),
120 nV5GreenMask(0),
121 nV5BlueMask(0),
122 nV5AlphaMask(0),
123 nV5CSType(0),
124 nV5GammaRed(0),
125 nV5GammaGreen(0),
126 nV5GammaBlue(0),
127 nV5Intent(0),
128 nV5ProfileData(0),
129 nV5ProfileSize(0),
130 nV5Reserved(0)
131 {}
132};
133
134vcl::PixelFormat convertToBPP(sal_uInt16 nCount)
135{
136 return (nCount <= 8) ? vcl::PixelFormat::N8_BPP :
138}
139
140bool isBitfieldCompression( ScanlineFormat nScanlineFormat )
141{
142 return ScanlineFormat::N32BitTcMask == nScanlineFormat;
143}
144
145bool ImplReadDIBInfoHeader(SvStream& rIStm, DIBV5Header& rHeader, bool& bTopDown, bool bMSOFormat)
146{
147 if (rIStm.remainingSize() <= 4)
148 return false;
149 // BITMAPINFOHEADER or BITMAPCOREHEADER or BITMAPV5HEADER
150 sal_uInt64 const aStartPos(rIStm.Tell());
151 rIStm.ReadUInt32( rHeader.nSize );
152
153 // BITMAPCOREHEADER
154 if ( rHeader.nSize == DIBCOREHEADERSIZE )
155 {
156 sal_Int16 nTmp16;
157
158 rIStm.ReadInt16( nTmp16 ); rHeader.nWidth = nTmp16;
159 rIStm.ReadInt16( nTmp16 ); rHeader.nHeight = nTmp16;
160 rIStm.ReadUInt16( rHeader.nPlanes );
161 rIStm.ReadUInt16( rHeader.nBitCount );
162 }
163 else if ( bMSOFormat && rHeader.nSize == DIBINFOHEADERSIZE )
164 {
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 );
185 }
186 else
187 {
188 // BITMAPCOREHEADER, BITMAPV5HEADER or unknown. Read as far as possible
189 std::size_t nUsed(sizeof(rHeader.nSize));
190
191 auto readUInt16 = [&nUsed, &rHeader, &rIStm](sal_uInt16 & v) {
192 if (nUsed < rHeader.nSize) {
193 rIStm.ReadUInt16(v);
194 nUsed += sizeof(v);
195 }
196 };
197 auto readInt32 = [&nUsed, &rHeader, &rIStm](sal_Int32 & v) {
198 if (nUsed < rHeader.nSize) {
199 rIStm.ReadInt32(v);
200 nUsed += sizeof(v);
201 }
202 };
203 auto readUInt32 = [&nUsed, &rHeader, &rIStm](sal_uInt32 & v) {
204 if (nUsed < rHeader.nSize) {
205 rIStm.ReadUInt32(v);
206 nUsed += sizeof(v);
207 }
208 };
209
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 );
221
222 // read DIBV5HEADER members
223 readUInt32( rHeader.nV5RedMask );
224 readUInt32( rHeader.nV5GreenMask );
225 readUInt32( rHeader.nV5BlueMask );
226 readUInt32( rHeader.nV5AlphaMask );
227 readUInt32( rHeader.nV5CSType );
228
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 );
239
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 );
247
248 // Read color mask. An additional 12 bytes of color bitfields follow the info header (WinBMPv3-NT)
249 sal_uInt32 nColorMask = 0;
250 if (BITFIELDS == rHeader.nCompression && DIBINFOHEADERSIZE == rHeader.nSize)
251 {
252 rIStm.ReadUInt32( rHeader.nV5RedMask );
253 rIStm.ReadUInt32( rHeader.nV5GreenMask );
254 rIStm.ReadUInt32( rHeader.nV5BlueMask );
255 nColorMask = 12;
256 }
257
258 // seek to EndPos
259 if (!checkSeek(rIStm, aStartPos + rHeader.nSize + nColorMask))
260 return false;
261 }
262
263 if (!rIStm.good() || rHeader.nHeight == SAL_MIN_INT32)
264 return false;
265
266 if ( rHeader.nHeight < 0 )
267 {
268 bTopDown = true;
269 rHeader.nHeight *= -1;
270 }
271 else
272 {
273 bTopDown = false;
274 }
275
276 if ( rHeader.nWidth < 0 || rHeader.nXPelsPerMeter < 0 || rHeader.nYPelsPerMeter < 0 )
277 {
278 rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
279 }
280
281 // #144105# protect a little against damaged files
282 assert(rHeader.nHeight >= 0);
283 if (rHeader.nHeight != 0 && rHeader.nWidth >= 0
284 && (rHeader.nSizeImage / 16 / static_cast<sal_uInt32>(rHeader.nHeight)
285 > o3tl::make_unsigned(rHeader.nWidth)))
286 {
287 rHeader.nSizeImage = 0;
288 }
289
290
291 if (rHeader.nPlanes != 1)
292 return false;
293
294 if (rHeader.nBitCount != 0 && rHeader.nBitCount != 1 &&
295 rHeader.nBitCount != 4 && rHeader.nBitCount != 8 &&
296 rHeader.nBitCount != 16 && rHeader.nBitCount != 24 &&
297 rHeader.nBitCount != 32)
298 {
299 return false;
300 }
301
302 return rIStm.good();
303}
304
305bool ImplReadDIBPalette(SvStream& rIStm, BitmapPalette& rPal, bool bQuad)
306{
307 const sal_uInt16 nColors = rPal.GetEntryCount();
308 const sal_uLong nPalSize = nColors * ( bQuad ? 4UL : 3UL );
309 BitmapColor aPalColor;
310
311 std::unique_ptr<sal_uInt8[]> pEntries(new sal_uInt8[ nPalSize ]);
312 if (rIStm.ReadBytes(pEntries.get(), nPalSize) != nPalSize)
313 {
314 return false;
315 }
316
317 sal_uInt8* pTmpEntry = pEntries.get();
318 for( sal_uInt16 i = 0; i < nColors; i++ )
319 {
320 aPalColor.SetBlue( *pTmpEntry++ );
321 aPalColor.SetGreen( *pTmpEntry++ );
322 aPalColor.SetRed( *pTmpEntry++ );
323
324 if( bQuad )
325 pTmpEntry++;
326
327 rPal[i] = aPalColor;
328 }
329
330 return rIStm.GetError() == ERRCODE_NONE;
331}
332
333BitmapColor SanitizePaletteIndex(sal_uInt8 nIndex, BitmapPalette& rPalette)
334{
335 const sal_uInt16 nPaletteEntryCount = rPalette.GetEntryCount();
336 if (nPaletteEntryCount && nIndex >= nPaletteEntryCount)
337 {
338 auto nSanitizedIndex = nIndex % nPaletteEntryCount;
339 SAL_WARN_IF(nIndex != nSanitizedIndex, "vcl", "invalid colormap index: "
340 << static_cast<unsigned int>(nIndex) << ", colormap len is: "
341 << nPaletteEntryCount);
342 nIndex = nSanitizedIndex;
343 }
344 return BitmapColor(nIndex);
345}
346
347bool ImplDecodeRLE(sal_uInt8* pBuffer, DIBV5Header const & rHeader, BitmapWriteAccess& rAcc, BitmapPalette& rPalette, bool bRLE4)
348{
349 Scanline pRLE = pBuffer;
350 Scanline pEndRLE = pBuffer + rHeader.nSizeImage;
351 tools::Long nY = rHeader.nHeight - 1;
352 const sal_uLong nWidth = rAcc.Width();
353 sal_uLong nCountByte;
354 sal_uLong nRunByte;
355 sal_uLong nX = 0;
356 sal_uInt8 cTmp;
357 bool bEndDecoding = false;
358
359 do
360 {
361 if (pRLE == pEndRLE)
362 return false;
363 if( ( nCountByte = *pRLE++ ) == 0 )
364 {
365 if (pRLE == pEndRLE)
366 return false;
367 nRunByte = *pRLE++;
368
369 if( nRunByte > 2 )
370 {
371 Scanline pScanline = rAcc.GetScanline(nY);
372 if( bRLE4 )
373 {
374 nCountByte = nRunByte >> 1;
375
376 for( sal_uLong i = 0; i < nCountByte; i++ )
377 {
378 if (pRLE == pEndRLE)
379 return false;
380
381 cTmp = *pRLE++;
382
383 if( nX < nWidth )
384 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp >> 4, rPalette));
385
386 if( nX < nWidth )
387 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp & 0x0f, rPalette));
388 }
389
390 if( nRunByte & 1 )
391 {
392 if (pRLE == pEndRLE)
393 return false;
394
395 if( nX < nWidth )
396 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(*pRLE >> 4, rPalette));
397
398 pRLE++;
399 }
400
401 if( ( ( nRunByte + 1 ) >> 1 ) & 1 )
402 {
403 if (pRLE == pEndRLE)
404 return false;
405
406 pRLE++;
407 }
408 }
409 else
410 {
411 for( sal_uLong i = 0; i < nRunByte; i++ )
412 {
413 if (pRLE == pEndRLE)
414 return false;
415
416 if( nX < nWidth )
417 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(*pRLE, rPalette));
418
419 pRLE++;
420 }
421
422 if( nRunByte & 1 )
423 {
424 if (pRLE == pEndRLE)
425 return false;
426
427 pRLE++;
428 }
429 }
430 }
431 else if( !nRunByte )
432 {
433 nY--;
434 nX = 0;
435 }
436 else if( nRunByte == 1 )
437 bEndDecoding = true;
438 else
439 {
440 if (pRLE == pEndRLE)
441 return false;
442
443 nX += *pRLE++;
444
445 if (pRLE == pEndRLE)
446 return false;
447
448 nY -= *pRLE++;
449 }
450 }
451 else
452 {
453 if (pRLE == pEndRLE)
454 return false;
455 cTmp = *pRLE++;
456
457 Scanline pScanline = rAcc.GetScanline(nY);
458 if( bRLE4 )
460 nRunByte = nCountByte >> 1;
461
462 for (sal_uLong i = 0; i < nRunByte && nX < nWidth; ++i)
463 {
464 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp >> 4, rPalette));
465 if( nX < nWidth )
466 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp & 0x0f, rPalette));
467 }
468
469 if( ( nCountByte & 1 ) && ( nX < nWidth ) )
470 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp >> 4, rPalette));
471 }
472 else
473 {
474 for (sal_uLong i = 0; i < nCountByte && nX < nWidth; ++i)
475 rAcc.SetPixelOnData(pScanline, nX++, SanitizePaletteIndex(cTmp, rPalette));
476 }
477 }
478 }
479 while (!bEndDecoding && (nY >= 0));
480
481 return true;
482}
483
484bool ImplReadDIBBits(SvStream& rIStm, DIBV5Header& rHeader, BitmapWriteAccess& rAcc, BitmapPalette& rPalette, BitmapWriteAccess* pAccAlpha,
485 bool bTopDown, bool& rAlphaUsed, const sal_uInt64 nAlignedWidth)
486{
487 sal_uInt32 nRMask(( rHeader.nBitCount == 16 ) ? 0x00007c00UL : 0x00ff0000UL);
488 sal_uInt32 nGMask(( rHeader.nBitCount == 16 ) ? 0x000003e0UL : 0x0000ff00UL);
489 sal_uInt32 nBMask(( rHeader.nBitCount == 16 ) ? 0x0000001fUL : 0x000000ffUL);
490 bool bNative(false);
491 bool bTCMask(!pAccAlpha && ((16 == rHeader.nBitCount) || (32 == rHeader.nBitCount)));
492 bool bRLE((RLE_8 == rHeader.nCompression && 8 == rHeader.nBitCount) || (RLE_4 == rHeader.nCompression && 4 == rHeader.nBitCount));
493
494 // Is native format?
495 switch(rAcc.GetScanlineFormat())
496 {
499 {
500 // we can't trust arbitrary-sourced index based formats to have correct indexes, so we exclude the pal formats
501 // from raw read and force checking their colormap indexes
502 bNative = ( ( rAcc.IsBottomUp() != bTopDown ) && !bRLE && !bTCMask && ( rAcc.GetScanlineSize() == nAlignedWidth ) );
503 break;
504 }
505
506 default:
507 {
508 break;
509 }
510 }
511
512 // Read data
513 if (bNative)
514 {
515 if (nAlignedWidth
516 > std::numeric_limits<std::size_t>::max() / rHeader.nHeight)
517 {
518 return false;
519 }
520 std::size_t n = nAlignedWidth * rHeader.nHeight;
521 if (rIStm.ReadBytes(rAcc.GetBuffer(), n) != n)
522 {
523 return false;
524 }
525 }
526 else
527 {
528 if (rHeader.nV5RedMask > 0)
529 nRMask = rHeader.nV5RedMask;
530 if (rHeader.nV5GreenMask > 0)
531 nGMask = rHeader.nV5GreenMask;
532 if (rHeader.nV5BlueMask > 0)
533 nBMask = rHeader.nV5BlueMask;
534
535 const tools::Long nWidth(rHeader.nWidth);
536 const tools::Long nHeight(rHeader.nHeight);
537 tools::Long nResult = 0;
538 if (utl::ConfigManager::IsFuzzing() && (o3tl::checked_multiply(nWidth, nHeight, nResult) || nResult > 4000000))
539 return false;
540
541 if (bRLE)
542 {
543 if(!rHeader.nSizeImage)
544 {
545 rHeader.nSizeImage = rIStm.remainingSize();
546 }
547
548 if (rHeader.nSizeImage > rIStm.remainingSize())
549 return false;
550 std::vector<sal_uInt8> aBuffer(rHeader.nSizeImage);
551 if (rIStm.ReadBytes(aBuffer.data(), rHeader.nSizeImage) != rHeader.nSizeImage)
552 return false;
553 if (!ImplDecodeRLE(aBuffer.data(), rHeader, rAcc, rPalette, RLE_4 == rHeader.nCompression))
554 return false;
555 }
556 else
557 {
558 if (nAlignedWidth > rIStm.remainingSize())
559 {
560 // ofz#11188 avoid timeout
561 // all following paths will enter a case statement, and nCount
562 // is always at least 1, so we can check here before allocation
563 // if at least one row can be read
564 return false;
565 }
566 std::vector<sal_uInt8> aBuf(nAlignedWidth);
567
568 const tools::Long nI(bTopDown ? 1 : -1);
569 tools::Long nY(bTopDown ? 0 : nHeight - 1);
570 tools::Long nCount(nHeight);
571
572 switch(rHeader.nBitCount)
573 {
574 case 1:
575 {
576 for( ; nCount--; nY += nI )
577 {
578 sal_uInt8 * pTmp = aBuf.data();
579 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
580 != nAlignedWidth)
581 {
582 return false;
583 }
584 sal_uInt8 cTmp = *pTmp++;
585 Scanline pScanline = rAcc.GetScanline(nY);
586 for( tools::Long nX = 0, nShift = 8; nX < nWidth; nX++ )
587 {
588 if( !nShift )
589 {
590 nShift = 8;
591 cTmp = *pTmp++;
592 }
593
594 auto nIndex = (cTmp >> --nShift) & 1;
595 rAcc.SetPixelOnData(pScanline, nX, SanitizePaletteIndex(nIndex, rPalette));
596 }
597 }
598 }
599 break;
600
601 case 4:
602 {
603 for( ; nCount--; nY += nI )
604 {
605 sal_uInt8 * pTmp = aBuf.data();
606 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
607 != nAlignedWidth)
608 {
609 return false;
610 }
611 sal_uInt8 cTmp = *pTmp++;
612 Scanline pScanline = rAcc.GetScanline(nY);
613 for( tools::Long nX = 0, nShift = 2; nX < nWidth; nX++ )
614 {
615 if( !nShift )
616 {
617 nShift = 2;
618 cTmp = *pTmp++;
619 }
620
621 auto nIndex = (cTmp >> ( --nShift << 2 ) ) & 0x0f;
622 rAcc.SetPixelOnData(pScanline, nX, SanitizePaletteIndex(nIndex, rPalette));
623 }
624 }
625 }
626 break;
627
628 case 8:
629 {
630 for( ; nCount--; nY += nI )
631 {
632 sal_uInt8 * pTmp = aBuf.data();
633 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
634 != nAlignedWidth)
635 {
636 return false;
637 }
638
639 Scanline pScanline = rAcc.GetScanline(nY);
640 for( tools::Long nX = 0; nX < nWidth; nX++ )
641 {
642 auto nIndex = *pTmp++;
643 rAcc.SetPixelOnData(pScanline, nX, SanitizePaletteIndex(nIndex, rPalette));
644 }
645 }
646 }
647 break;
648
649 case 16:
650 {
651 ColorMaskElement aRedMask(nRMask);
652 if (!aRedMask.CalcMaskShift())
653 return false;
654 ColorMaskElement aGreenMask(nGMask);
655 if (!aGreenMask.CalcMaskShift())
656 return false;
657 ColorMaskElement aBlueMask(nBMask);
658 if (!aBlueMask.CalcMaskShift())
659 return false;
660
661 ColorMask aMask(aRedMask, aGreenMask, aBlueMask);
662 BitmapColor aColor;
663
664 for( ; nCount--; nY += nI )
665 {
666 sal_uInt16 * pTmp16 = reinterpret_cast<sal_uInt16*>(aBuf.data());
667 if (rIStm.ReadBytes(pTmp16, nAlignedWidth)
668 != nAlignedWidth)
669 {
670 return false;
671 }
672
673 Scanline pScanline = rAcc.GetScanline(nY);
674 for( tools::Long nX = 0; nX < nWidth; nX++ )
675 {
676 aMask.GetColorFor16BitLSB( aColor, reinterpret_cast<sal_uInt8*>(pTmp16++) );
677 rAcc.SetPixelOnData(pScanline, nX, aColor);
678 }
679 }
680 }
681 break;
682
683 case 24:
684 {
685 BitmapColor aPixelColor;
686
687 for( ; nCount--; nY += nI )
688 {
689 sal_uInt8* pTmp = aBuf.data();
690 if (rIStm.ReadBytes(pTmp, nAlignedWidth)
691 != nAlignedWidth)
692 {
693 return false;
694 }
695
696 Scanline pScanline = rAcc.GetScanline(nY);
697 for( tools::Long nX = 0; nX < nWidth; nX++ )
698 {
699 aPixelColor.SetBlue( *pTmp++ );
700 aPixelColor.SetGreen( *pTmp++ );
701 aPixelColor.SetRed( *pTmp++ );
702 rAcc.SetPixelOnData(pScanline, nX, aPixelColor);
703 }
704 }
705 }
706 break;
707
708 case 32:
709 {
710 ColorMaskElement aRedMask(nRMask);
711 if (!aRedMask.CalcMaskShift())
712 return false;
713 ColorMaskElement aGreenMask(nGMask);
714 if (!aGreenMask.CalcMaskShift())
715 return false;
716 ColorMaskElement aBlueMask(nBMask);
717 if (!aBlueMask.CalcMaskShift())
718 return false;
719 ColorMask aMask(aRedMask, aGreenMask, aBlueMask);
720
721 BitmapColor aColor;
722 sal_uInt32* pTmp32;
723
724 if(pAccAlpha)
725 {
726 sal_uInt8 aAlpha;
727
728 for( ; nCount--; nY += nI )
729 {
730 pTmp32 = reinterpret_cast<sal_uInt32*>(aBuf.data());
731 if (rIStm.ReadBytes(pTmp32, nAlignedWidth)
732 != nAlignedWidth)
733 {
734 return false;
735 }
736
737 Scanline pScanline = rAcc.GetScanline(nY);
738 Scanline pAlphaScanline = pAccAlpha->GetScanline(nY);
739 for( tools::Long nX = 0; nX < nWidth; nX++ )
740 {
741 aMask.GetColorAndAlphaFor32Bit( aColor, aAlpha, reinterpret_cast<sal_uInt8*>(pTmp32++) );
742 rAcc.SetPixelOnData(pScanline, nX, aColor);
743 pAccAlpha->SetPixelOnData(pAlphaScanline, nX, BitmapColor(sal_uInt8(0xff) - aAlpha));
744 rAlphaUsed |= 0xff != aAlpha;
745 }
746 }
747 }
748 else
749 {
750 for( ; nCount--; nY += nI )
751 {
752 pTmp32 = reinterpret_cast<sal_uInt32*>(aBuf.data());
753 if (rIStm.ReadBytes(pTmp32, nAlignedWidth)
754 != nAlignedWidth)
755 {
756 return false;
757 }
758
759 Scanline pScanline = rAcc.GetScanline(nY);
760 for( tools::Long nX = 0; nX < nWidth; nX++ )
761 {
762 aMask.GetColorFor32Bit( aColor, reinterpret_cast<sal_uInt8*>(pTmp32++) );
763 rAcc.SetPixelOnData(pScanline, nX, aColor);
764 }
765 }
766 }
767 }
768 }
769 }
770 }
771
772 return rIStm.GetError() == ERRCODE_NONE;
773}
774
775bool ImplReadDIBBody(SvStream& rIStm, Bitmap& rBmp, AlphaMask* pBmpAlpha, sal_uInt64 nOffset, bool bMSOFormat)
776{
777 DIBV5Header aHeader;
778 const sal_uInt64 nStmPos = rIStm.Tell();
779 bool bTopDown(false);
780
781 if (!ImplReadDIBInfoHeader(rIStm, aHeader, bTopDown, bMSOFormat))
782 return false;
783
784 //BI_BITCOUNT_0 jpeg/png is unsupported
785 if (aHeader.nBitCount == 0)
786 return false;
787
788 if (aHeader.nWidth <= 0 || aHeader.nHeight <= 0)
789 return false;
790
791 // In case ImplReadDIB() didn't call ImplReadDIBFileHeader() before
792 // this method, nOffset is 0, that's OK.
793 if (nOffset && aHeader.nSize > nOffset)
794 {
795 // Header size claims to extend into the image data.
796 // Looks like an error.
797 return false;
798 }
799
800 sal_uInt16 nColors(0);
801 SvStream* pIStm;
802 std::unique_ptr<SvMemoryStream> pMemStm;
803 std::vector<sal_uInt8> aData;
804
805 if (aHeader.nBitCount <= 8)
806 {
807 if(aHeader.nColsUsed)
808 {
809 nColors = static_cast<sal_uInt16>(aHeader.nColsUsed);
810 }
811 else
812 {
813 nColors = ( 1 << aHeader.nBitCount );
814 }
815 }
816
817 if (ZCOMPRESS == aHeader.nCompression)
818 {
819 sal_uInt32 nCodedSize(0);
820 sal_uInt32 nUncodedSize(0);
821
822 // read coding information
823 rIStm.ReadUInt32( nCodedSize ).ReadUInt32( nUncodedSize ).ReadUInt32( aHeader.nCompression );
824 if (nCodedSize > rIStm.remainingSize())
825 nCodedSize = sal_uInt32(rIStm.remainingSize());
826
827 pMemStm.reset(new SvMemoryStream);
828 // There may be bytes left over or the codec might read more than
829 // necessary. So to preserve the correctness of the source stream copy
830 // the encoded block
831 pMemStm->WriteStream(rIStm, nCodedSize);
832 pMemStm->Seek(0);
833
834 size_t nSizeInc(4 * pMemStm->remainingSize());
835 if (nUncodedSize < nSizeInc)
836 nSizeInc = nUncodedSize;
837
838 if (nSizeInc > 0)
839 {
840 // decode buffer
841 ZCodec aCodec;
842 aCodec.BeginCompression();
843 aData.resize(nSizeInc);
844 size_t nDataPos(0);
845 while (nUncodedSize > nDataPos)
846 {
847 assert(aData.size() > nDataPos);
848 const size_t nToRead(std::min<size_t>(nUncodedSize - nDataPos, aData.size() - nDataPos));
849 assert(nToRead > 0);
850 assert(!aData.empty());
851 const tools::Long nRead = aCodec.Read(*pMemStm, aData.data() + nDataPos, sal_uInt32(nToRead));
852 if (nRead > 0)
853 {
854 nDataPos += static_cast<tools::ULong>(nRead);
855 // we haven't read everything yet: resize buffer and continue
856 if (nDataPos < nUncodedSize)
857 aData.resize(aData.size() + nSizeInc);
858 }
859 else
860 {
861 break;
862 }
863 }
864 // truncate the data buffer to actually read size
865 aData.resize(nDataPos);
866 // set the real uncoded size
867 nUncodedSize = sal_uInt32(aData.size());
868 aCodec.EndCompression();
869 }
870
871 if (aData.empty())
872 {
873 // add something so we can take address of the first element
874 aData.resize(1);
875 nUncodedSize = 0;
876 }
877
878 // set decoded bytes to memory stream,
879 // from which we will read the bitmap data
880 pMemStm.reset(new SvMemoryStream);
881 pIStm = pMemStm.get();
882 assert(!aData.empty());
883 pMemStm->SetBuffer(aData.data(), nUncodedSize, nUncodedSize);
884 nOffset = 0;
885 }
886 else
887 {
888 pIStm = &rIStm;
889 }
890
891 // read palette
892 BitmapPalette aPalette;
893 if (nColors)
894 {
895 aPalette.SetEntryCount(nColors);
896 ImplReadDIBPalette(*pIStm, aPalette, aHeader.nSize != DIBCOREHEADERSIZE);
897 }
898
899 if (pIStm->GetError())
900 return false;
901
902 if (nOffset)
903 {
904 // It is problematic to seek backwards. We are at the
905 // end of BITMAPINFOHEADER or 12 bytes further in case
906 // of WinBMPv3-NT format. It is possible to seek forward
907 // though because a gap may be there.
908 sal_Int32 nSeekRel = nOffset - (pIStm->Tell() - nStmPos);
909 if (nSeekRel > 0)
910 pIStm->SeekRel(nSeekRel);
911 }
912
913 const sal_Int64 nBitsPerLine (static_cast<sal_Int64>(aHeader.nWidth) * static_cast<sal_Int64>(aHeader.nBitCount));
914 if (nBitsPerLine > SAL_MAX_UINT32)
915 return false;
916 const sal_uInt64 nAlignedWidth(AlignedWidth4Bytes(static_cast<sal_uLong>(nBitsPerLine)));
917
918 switch (aHeader.nCompression)
919 {
920 case RLE_8:
921 {
922 if (aHeader.nBitCount != 8)
923 return false;
924 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
925 sal_uInt64 nMaxWidth = pIStm->remainingSize();
926 nMaxWidth *= 256; //assume generous compression ratio
927 nMaxWidth /= aHeader.nHeight;
928 if (nMaxWidth < o3tl::make_unsigned(aHeader.nWidth))
929 return false;
930 break;
931 }
932 case RLE_4:
933 {
934 if (aHeader.nBitCount != 4)
935 return false;
936 sal_uInt64 nMaxWidth = pIStm->remainingSize();
937 nMaxWidth *= 512; //assume generous compression ratio
938 nMaxWidth /= aHeader.nHeight;
939 if (nMaxWidth < o3tl::make_unsigned(aHeader.nWidth))
940 return false;
941 break;
942 }
943 default:
944 // tdf#122958 invalid compression value used
945 if (aHeader.nCompression & 0x000F)
946 {
947 // lets assume that there was an error in the generating application
948 // and allow through as COMPRESS_NONE if the bottom byte is 0
949 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader.nCompression << ", rejecting bmp");
950 return false;
951 }
952 else
953 SAL_WARN( "vcl", "bad bmp compression scheme: " << aHeader.nCompression << ", assuming meant to be COMPRESS_NONE");
954 [[fallthrough]];
955 case BITFIELDS:
956 case ZCOMPRESS:
957 case COMPRESS_NONE:
958 {
959 // (partially) check the image dimensions to avoid potential large bitmap allocation if the input is damaged
960 sal_uInt64 nMaxWidth = pIStm->remainingSize();
961 nMaxWidth /= aHeader.nHeight;
962 if (nMaxWidth < nAlignedWidth)
963 return false;
964 break;
965 }
966 }
967
968 const Size aSizePixel(aHeader.nWidth, aHeader.nHeight);
969 AlphaMask aNewBmpAlpha;
970 AlphaScopedWriteAccess pAccAlpha;
971 bool bAlphaPossible(pBmpAlpha && aHeader.nBitCount == 32);
972
973 if (bAlphaPossible)
974 {
975 const bool bRedSet(0 != aHeader.nV5RedMask);
976 const bool bGreenSet(0 != aHeader.nV5GreenMask);
977 const bool bBlueSet(0 != aHeader.nV5BlueMask);
978
979 // some clipboard entries have alpha mask on zero to say that there is
980 // no alpha; do only use this when the other masks are set. The MS docu
981 // says that masks are only to be set when bV5Compression is set to
982 // BI_BITFIELDS, but there seem to exist a wild variety of usages...
983 if((bRedSet || bGreenSet || bBlueSet) && (0 == aHeader.nV5AlphaMask))
984 {
985 bAlphaPossible = false;
986 }
987 }
988
989 if (bAlphaPossible)
990 {
991 aNewBmpAlpha = AlphaMask(aSizePixel);
992 pAccAlpha = AlphaScopedWriteAccess(aNewBmpAlpha);
993 }
994
995 vcl::PixelFormat ePixelFormat(convertToBPP(aHeader.nBitCount));
996 const BitmapPalette* pPal = &aPalette;
997 //ofz#948 match the surrounding logic of case TransparentType::Bitmap of
998 //ReadDIBBitmapEx but do it while reading for performance
999
1000 Bitmap aNewBmp(aSizePixel, ePixelFormat, pPal);
1001 BitmapScopedWriteAccess pAcc(aNewBmp);
1002 if (!pAcc)
1003 return false;
1004 if (pAcc->Width() != aHeader.nWidth || pAcc->Height() != aHeader.nHeight)
1005 {
1006 return false;
1007 }
1008
1009 // read bits
1010 bool bAlphaUsed(false);
1011 bool bRet = ImplReadDIBBits(*pIStm, aHeader, *pAcc, aPalette, pAccAlpha.get(), bTopDown, bAlphaUsed, nAlignedWidth);
1012
1013 if (bRet && aHeader.nXPelsPerMeter && aHeader.nYPelsPerMeter)
1014 {
1015 MapMode aMapMode(
1016 MapUnit::MapMM,
1017 Point(),
1018 Fraction(1000, aHeader.nXPelsPerMeter),
1019 Fraction(1000, aHeader.nYPelsPerMeter));
1020
1021 aNewBmp.SetPrefMapMode(aMapMode);
1022 aNewBmp.SetPrefSize(Size(aHeader.nWidth, aHeader.nHeight));
1023 }
1024
1025 pAcc.reset();
1026
1027 if (bAlphaPossible)
1028 {
1029 pAccAlpha.reset();
1030
1031 if(!bAlphaUsed)
1032 {
1033 bAlphaPossible = false;
1034 }
1035 }
1036
1037 if (bRet)
1038 {
1039 rBmp = aNewBmp;
1040
1041 if(bAlphaPossible)
1042 {
1043 *pBmpAlpha = aNewBmpAlpha;
1044 }
1045 }
1046
1047 return bRet;
1048}
1049
1050bool ImplReadDIBFileHeader( SvStream& rIStm, sal_uLong& rOffset )
1051{
1052 bool bRet = false;
1053
1054 const sal_uInt64 nStreamLength = rIStm.TellEnd();
1055
1056 sal_uInt16 nTmp16 = 0;
1057 rIStm.ReadUInt16( nTmp16 );
1058
1059 if ( ( 0x4D42 == nTmp16 ) || ( 0x4142 == nTmp16 ) )
1060 {
1061 sal_uInt32 nTmp32(0);
1062 if ( 0x4142 == nTmp16 )
1063 {
1064 rIStm.SeekRel( 12 );
1065 rIStm.ReadUInt16( nTmp16 );
1066 rIStm.SeekRel( 8 );
1067 rIStm.ReadUInt32( nTmp32 );
1068 rOffset = nTmp32 - 28;
1069 bRet = ( 0x4D42 == nTmp16 );
1070 }
1071 else // 0x4D42 == nTmp16, 'MB' from BITMAPFILEHEADER
1072 {
1073 rIStm.SeekRel( 8 ); // we are on bfSize member of BITMAPFILEHEADER, forward to bfOffBits
1074 rIStm.ReadUInt32( nTmp32 ); // read bfOffBits
1075 rOffset = nTmp32 - 14; // adapt offset by sizeof(BITMAPFILEHEADER)
1076 bRet = rIStm.GetError() == ERRCODE_NONE;
1077 }
1078
1079 if ( rOffset >= nStreamLength )
1080 {
1081 // Offset claims that image starts past the end of the
1082 // stream. Unlikely.
1083 rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
1084 bRet = false;
1085 }
1086 }
1087 else
1088 rIStm.SetError( SVSTREAM_FILEFORMAT_ERROR );
1089
1090 return bRet;
1091}
1092
1093bool ImplWriteDIBPalette( SvStream& rOStm, BitmapReadAccess const & rAcc )
1094{
1095 const sal_uInt16 nColors = rAcc.GetPaletteEntryCount();
1096 const sal_uLong nPalSize = nColors * 4UL;
1097 std::unique_ptr<sal_uInt8[]> pEntries(new sal_uInt8[ nPalSize ]);
1098 sal_uInt8* pTmpEntry = pEntries.get();
1099
1100 for( sal_uInt16 i = 0; i < nColors; i++ )
1101 {
1102 const BitmapColor& rPalColor = rAcc.GetPaletteColor( i );
1103
1104 *pTmpEntry++ = rPalColor.GetBlue();
1105 *pTmpEntry++ = rPalColor.GetGreen();
1106 *pTmpEntry++ = rPalColor.GetRed();
1107 *pTmpEntry++ = 0;
1108 }
1109
1110 rOStm.WriteBytes( pEntries.get(), nPalSize );
1111
1112 return rOStm.GetError() == ERRCODE_NONE;
1113}
1114
1115bool ImplWriteRLE( SvStream& rOStm, BitmapReadAccess const & rAcc, bool bRLE4 )
1116{
1117 const sal_uLong nWidth = rAcc.Width();
1118 const sal_uLong nHeight = rAcc.Height();
1119 sal_uLong nX;
1120 sal_uLong nSaveIndex;
1122 sal_uLong nBufCount;
1123 std::vector<sal_uInt8> aBuf(( nWidth << 1 ) + 2);
1124 sal_uInt8 cPix;
1125 sal_uInt8 cLast;
1126 bool bFound;
1127
1128 for ( tools::Long nY = nHeight - 1; nY >= 0; nY-- )
1129 {
1130 sal_uInt8* pTmp = aBuf.data();
1131 nX = nBufCount = 0;
1132 Scanline pScanline = rAcc.GetScanline( nY );
1133
1134 while( nX < nWidth )
1135 {
1136 nCount = 1;
1137 cPix = rAcc.GetIndexFromData( pScanline, nX++ );
1138
1139 while( ( nX < nWidth ) && ( nCount < 255 )
1140 && ( cPix == rAcc.GetIndexFromData( pScanline, nX ) ) )
1141 {
1142 nX++;
1143 nCount++;
1144 }
1145
1146 if ( nCount > 1 )
1147 {
1148 *pTmp++ = static_cast<sal_uInt8>(nCount);
1149 *pTmp++ = ( bRLE4 ? ( ( cPix << 4 ) | cPix ) : cPix );
1150 nBufCount += 2;
1151 }
1152 else
1153 {
1154 cLast = cPix;
1155 nSaveIndex = nX - 1;
1156 bFound = false;
1157
1158 while( ( nX < nWidth ) && ( nCount < 256 ) )
1159 {
1160 cPix = rAcc.GetIndexFromData( pScanline, nX );
1161 if (cPix == cLast)
1162 break;
1163 nX++; nCount++;
1164 cLast = cPix;
1165 bFound = true;
1166 }
1167
1168 if ( bFound )
1169 nX--;
1170
1171 if ( nCount > 3 )
1172 {
1173 *pTmp++ = 0;
1174 *pTmp++ = static_cast<sal_uInt8>(--nCount);
1175
1176 if( bRLE4 )
1177 {
1178 for ( sal_uLong i = 0; i < nCount; i++, pTmp++ )
1179 {
1180 *pTmp = rAcc.GetIndexFromData( pScanline, nSaveIndex++ ) << 4;
1181
1182 if ( ++i < nCount )
1183 *pTmp |= rAcc.GetIndexFromData( pScanline, nSaveIndex++ );
1184 }
1185
1186 nCount = ( nCount + 1 ) >> 1;
1187 }
1188 else
1189 {
1190 for( sal_uLong i = 0; i < nCount; i++ )
1191 *pTmp++ = rAcc.GetIndexFromData( pScanline, nSaveIndex++ );
1192 }
1193
1194 if ( nCount & 1 )
1195 {
1196 *pTmp++ = 0;
1197 nBufCount += ( nCount + 3 );
1198 }
1199 else
1200 nBufCount += ( nCount + 2 );
1201 }
1202 else
1203 {
1204 *pTmp++ = 1;
1205 *pTmp++ = rAcc.GetIndexFromData( pScanline, nSaveIndex ) << (bRLE4 ? 4 : 0);
1206
1207 if ( nCount == 3 )
1208 {
1209 *pTmp++ = 1;
1210 *pTmp++ = rAcc.GetIndexFromData( pScanline, ++nSaveIndex ) << ( bRLE4 ? 4 : 0 );
1211 nBufCount += 4;
1212 }
1213 else
1214 nBufCount += 2;
1215 }
1216 }
1217 }
1218
1219 aBuf[ nBufCount++ ] = 0;
1220 aBuf[ nBufCount++ ] = 0;
1221
1222 rOStm.WriteBytes(aBuf.data(), nBufCount);
1223 }
1224
1225 rOStm.WriteUChar( 0 );
1226 rOStm.WriteUChar( 1 );
1227
1228 return rOStm.GetError() == ERRCODE_NONE;
1229}
1230
1231bool ImplWriteDIBBits(SvStream& rOStm, BitmapReadAccess const & rAcc, sal_uLong nCompression, sal_uInt32& rImageSize)
1232{
1233 if(BITFIELDS == nCompression)
1234 {
1235 const ColorMask& rMask = rAcc.GetColorMask();
1236 SVBT32 aVal32;
1237
1238 UInt32ToSVBT32( rMask.GetRedMask(), aVal32 );
1239 rOStm.WriteBytes( aVal32, 4UL );
1240
1241 UInt32ToSVBT32( rMask.GetGreenMask(), aVal32 );
1242 rOStm.WriteBytes( aVal32, 4UL );
1243
1244 UInt32ToSVBT32( rMask.GetBlueMask(), aVal32 );
1245 rOStm.WriteBytes( aVal32, 4UL );
1246
1247 rImageSize = rOStm.Tell();
1248
1249 if( rAcc.IsBottomUp() )
1250 rOStm.WriteBytes(rAcc.GetBuffer(), rAcc.Height() * rAcc.GetScanlineSize());
1251 else
1252 {
1253 for( tools::Long nY = rAcc.Height() - 1, nScanlineSize = rAcc.GetScanlineSize(); nY >= 0; nY-- )
1254 rOStm.WriteBytes( rAcc.GetScanline(nY), nScanlineSize );
1255 }
1256 }
1257 else if((RLE_4 == nCompression) || (RLE_8 == nCompression))
1258 {
1259 rImageSize = rOStm.Tell();
1260 ImplWriteRLE( rOStm, rAcc, RLE_4 == nCompression );
1261 }
1262 else if(!nCompression)
1263 {
1264 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are not
1265 // handled properly below (would have to set color masks, and
1266 // nCompression=BITFIELDS - but color mask is not set for
1267 // formats != *_TC_*). Note that this very problem might cause
1268 // trouble at other places - the introduction of 32 bit RGBA
1269 // bitmaps is relatively recent.
1270 // #i59239# discretize bitcount for aligned width to 1,8,24
1271 // (other cases are not written below)
1272 const auto ePixelFormat(convertToBPP(rAcc.GetBitCount()));
1273 const sal_uLong nAlignedWidth(AlignedWidth4Bytes(rAcc.Width() * sal_Int32(ePixelFormat)));
1274 bool bNative(false);
1275
1276 switch(rAcc.GetScanlineFormat())
1277 {
1281 {
1282 if(rAcc.IsBottomUp() && (rAcc.GetScanlineSize() == nAlignedWidth))
1283 {
1284 bNative = true;
1285 }
1286
1287 break;
1288 }
1289
1290 default:
1291 {
1292 break;
1293 }
1294 }
1295
1296 rImageSize = rOStm.Tell();
1297
1298 if(bNative)
1299 {
1300 rOStm.WriteBytes(rAcc.GetBuffer(), nAlignedWidth * rAcc.Height());
1301 }
1302 else
1303 {
1304 const tools::Long nWidth(rAcc.Width());
1305 const tools::Long nHeight(rAcc.Height());
1306 std::vector<sal_uInt8> aBuf(nAlignedWidth);
1307 switch(ePixelFormat)
1308 {
1310 {
1311 for( tools::Long nY = nHeight - 1; nY >= 0; nY-- )
1312 {
1313 sal_uInt8* pTmp = aBuf.data();
1314 Scanline pScanline = rAcc.GetScanline( nY );
1315
1316 for( tools::Long nX = 0; nX < nWidth; nX++ )
1317 *pTmp++ = rAcc.GetIndexFromData( pScanline, nX );
1318
1319 rOStm.WriteBytes(aBuf.data(), nAlignedWidth);
1320 }
1321 }
1322 break;
1323
1325 {
1326 //valgrind, zero out the trailing unused alignment bytes
1327 size_t nUnusedBytes = nAlignedWidth - nWidth * 3;
1328 memset(aBuf.data() + nAlignedWidth - nUnusedBytes, 0, nUnusedBytes);
1329 }
1330 [[fallthrough]];
1331 // #i59239# fallback to 24 bit format, if bitcount is non-default
1332 default:
1333 {
1334 BitmapColor aPixelColor;
1335
1336 for( tools::Long nY = nHeight - 1; nY >= 0; nY-- )
1337 {
1338 sal_uInt8* pTmp = aBuf.data();
1339
1340 for( tools::Long nX = 0; nX < nWidth; nX++ )
1341 {
1342 // when alpha is used, this may be non-24bit main bitmap, so use GetColor
1343 // instead of GetPixel to ensure RGB value
1344 aPixelColor = rAcc.GetColor( nY, nX );
1345
1346 *pTmp++ = aPixelColor.GetBlue();
1347 *pTmp++ = aPixelColor.GetGreen();
1348 *pTmp++ = aPixelColor.GetRed();
1349 }
1350
1351 rOStm.WriteBytes(aBuf.data(), nAlignedWidth);
1352 }
1353 }
1354 break;
1355 }
1356 }
1357 }
1358
1359 rImageSize = rOStm.Tell() - rImageSize;
1360
1361 return (!rOStm.GetError());
1362}
1363
1364bool ImplWriteDIBBody(const Bitmap& rBitmap, SvStream& rOStm, BitmapReadAccess const & rAcc, bool bCompressed)
1365{
1366 const MapMode aMapPixel(MapUnit::MapPixel);
1367 DIBV5Header aHeader;
1368 sal_uInt64 nImageSizePos(0);
1369 sal_uInt64 nEndPos(0);
1370 sal_uInt32 nCompression(COMPRESS_NONE);
1371 bool bRet(false);
1372
1373 aHeader.nSize = DIBINFOHEADERSIZE; // size dependent on CF_DIB type to use
1374 aHeader.nWidth = rAcc.Width();
1375 aHeader.nHeight = rAcc.Height();
1376 aHeader.nPlanes = 1;
1377
1378 if(isBitfieldCompression(rAcc.GetScanlineFormat()))
1379 {
1380 aHeader.nBitCount = 32;
1381 aHeader.nSizeImage = rAcc.Height() * rAcc.GetScanlineSize();
1382 nCompression = BITFIELDS;
1383 }
1384 else
1385 {
1386 // #i5xxx# Limit bitcount to 24bit, the 32 bit cases are
1387 // not handled properly below (would have to set color
1388 // masks, and nCompression=BITFIELDS - but color mask is
1389 // not set for formats != *_TC_*). Note that this very
1390 // problem might cause trouble at other places - the
1391 // introduction of 32 bit RGBA bitmaps is relatively
1392 // recent.
1393 // #i59239# discretize bitcount to 1,8,24 (other cases
1394 // are not written below)
1395 const auto ePixelFormat(convertToBPP(rAcc.GetBitCount()));
1396 aHeader.nBitCount = sal_uInt16(ePixelFormat);
1397 aHeader.nSizeImage = rAcc.Height() * AlignedWidth4Bytes(rAcc.Width() * aHeader.nBitCount);
1398
1399 if (bCompressed)
1400 {
1401 if (ePixelFormat == vcl::PixelFormat::N8_BPP)
1402 nCompression = RLE_8;
1403 }
1404 }
1405
1406 if((rOStm.GetCompressMode() & SvStreamCompressFlags::ZBITMAP) && (rOStm.GetVersion() >= SOFFICE_FILEFORMAT_40))
1407 {
1408 aHeader.nCompression = ZCOMPRESS;
1409 }
1410 else
1411 {
1412 aHeader.nCompression = nCompression;
1413 }
1414
1415 if(rBitmap.GetPrefSize().Width() && rBitmap.GetPrefSize().Height() && (rBitmap.GetPrefMapMode() != aMapPixel))
1416 {
1417 // #i48108# Try to recover xpels/ypels as previously stored on
1418 // disk. The problem with just converting maPrefSize to 100th
1419 // mm and then relating that to the bitmap pixel size is that
1420 // MapMode is integer-based, and suffers from roundoffs,
1421 // especially if maPrefSize is small. Trying to circumvent
1422 // that by performing part of the math in floating point.
1423 const Size aScale100000(OutputDevice::LogicToLogic(Size(100000, 100000), MapMode(MapUnit::Map100thMM), rBitmap.GetPrefMapMode()));
1424 const double fBmpWidthM(static_cast<double>(rBitmap.GetPrefSize().Width()) / aScale100000.Width());
1425 const double fBmpHeightM(static_cast<double>(rBitmap.GetPrefSize().Height()) / aScale100000.Height());
1426
1427 if(!basegfx::fTools::equalZero(fBmpWidthM) && !basegfx::fTools::equalZero(fBmpHeightM))
1428 {
1429 aHeader.nXPelsPerMeter = basegfx::fround(rAcc.Width() / fabs(fBmpWidthM));
1430 aHeader.nYPelsPerMeter = basegfx::fround(rAcc.Height() / fabs(fBmpHeightM));
1431 }
1432 }
1433
1434 aHeader.nColsUsed = ((aHeader.nBitCount <= 8) ? rAcc.GetPaletteEntryCount() : 0);
1435 aHeader.nColsImportant = 0;
1436
1437 rOStm.WriteUInt32( aHeader.nSize );
1438 rOStm.WriteInt32( aHeader.nWidth );
1439 rOStm.WriteInt32( aHeader.nHeight );
1440 rOStm.WriteUInt16( aHeader.nPlanes );
1441 rOStm.WriteUInt16( aHeader.nBitCount );
1442 rOStm.WriteUInt32( aHeader.nCompression );
1443
1444 nImageSizePos = rOStm.Tell();
1445 rOStm.SeekRel( sizeof( aHeader.nSizeImage ) );
1446
1447 rOStm.WriteInt32( aHeader.nXPelsPerMeter );
1448 rOStm.WriteInt32( aHeader.nYPelsPerMeter );
1449 rOStm.WriteUInt32( aHeader.nColsUsed );
1450 rOStm.WriteUInt32( aHeader.nColsImportant );
1451
1452 if(ZCOMPRESS == aHeader.nCompression)
1453 {
1454 ZCodec aCodec;
1455 SvMemoryStream aMemStm(aHeader.nSizeImage + 4096, 65535);
1456 sal_uInt64 nCodedPos(rOStm.Tell());
1457 sal_uInt64 nLastPos(0);
1458 sal_uInt32 nCodedSize(0);
1459 sal_uInt32 nUncodedSize(0);
1460
1461 // write uncoded data palette
1462 if(aHeader.nColsUsed)
1463 {
1464 ImplWriteDIBPalette(aMemStm, rAcc);
1465 }
1466
1467 // write uncoded bits
1468 bRet = ImplWriteDIBBits(aMemStm, rAcc, nCompression, aHeader.nSizeImage);
1469
1470 // get uncoded size
1471 nUncodedSize = aMemStm.Tell();
1472
1473 // seek over compress info
1474 rOStm.SeekRel(12);
1475
1476 // write compressed data
1477 aCodec.BeginCompression(3);
1478 aCodec.Write(rOStm, static_cast<sal_uInt8 const *>(aMemStm.GetData()), nUncodedSize);
1479 aCodec.EndCompression();
1480
1481 // update compress info ( coded size, uncoded size, uncoded compression )
1482 nLastPos = rOStm.Tell();
1483 nCodedSize = nLastPos - nCodedPos - 12;
1484 rOStm.Seek(nCodedPos);
1485 rOStm.WriteUInt32( nCodedSize ).WriteUInt32( nUncodedSize ).WriteUInt32( nCompression );
1486 rOStm.Seek(nLastPos);
1487
1488 if(bRet)
1489 {
1490 bRet = (ERRCODE_NONE == rOStm.GetError());
1491 }
1492 }
1493 else
1494 {
1495 if(aHeader.nColsUsed)
1496 {
1497 ImplWriteDIBPalette(rOStm, rAcc);
1498 }
1499
1500 bRet = ImplWriteDIBBits(rOStm, rAcc, aHeader.nCompression, aHeader.nSizeImage);
1501 }
1502
1503 nEndPos = rOStm.Tell();
1504 rOStm.Seek(nImageSizePos);
1505 rOStm.WriteUInt32( aHeader.nSizeImage );
1506 rOStm.Seek(nEndPos);
1507
1508 return bRet;
1509}
1510
1511bool ImplWriteDIBFileHeader(SvStream& rOStm, BitmapReadAccess const & rAcc)
1512{
1513 const sal_uInt32 nPalCount((rAcc.HasPalette() ? rAcc.GetPaletteEntryCount() : isBitfieldCompression(rAcc.GetScanlineFormat()) ? 3UL : 0UL));
1514 const sal_uInt32 nOffset(14 + DIBINFOHEADERSIZE + nPalCount * 4UL);
1515
1516 rOStm.WriteUInt16( 0x4D42 ); // 'MB' from BITMAPFILEHEADER
1517 rOStm.WriteUInt32( nOffset + (rAcc.Height() * rAcc.GetScanlineSize()) );
1518 rOStm.WriteUInt16( 0 );
1519 rOStm.WriteUInt16( 0 );
1520 rOStm.WriteUInt32( nOffset );
1521
1522 return rOStm.GetError() == ERRCODE_NONE;
1523}
1524
1525bool ImplReadDIB(
1526 Bitmap& rTarget,
1527 AlphaMask* pTargetAlpha,
1528 SvStream& rIStm,
1529 bool bFileHeader,
1530 bool bMSOFormat=false)
1531{
1532 const SvStreamEndian nOldFormat(rIStm.GetEndian());
1533 const auto nOldPos(rIStm.Tell());
1534 sal_uLong nOffset(0);
1535 bool bRet(false);
1536
1537 rIStm.SetEndian(SvStreamEndian::LITTLE);
1538
1539 if(bFileHeader)
1540 {
1541 if(ImplReadDIBFileHeader(rIStm, nOffset))
1542 {
1543 bRet = ImplReadDIBBody(rIStm, rTarget, nOffset >= DIBV5HEADERSIZE ? pTargetAlpha : nullptr, nOffset, bMSOFormat);
1544 }
1545 }
1546 else
1547 {
1548 bRet = ImplReadDIBBody(rIStm, rTarget, nullptr, nOffset, bMSOFormat);
1549 }
1550
1551 if(!bRet)
1552 {
1553 if(!rIStm.GetError()) // Set error and stop processing whole stream due to security reason
1554 {
1555 rIStm.SetError(SVSTREAM_GENERALERROR);
1556 }
1557
1558 rIStm.Seek(nOldPos);
1559 }
1560
1561 rIStm.SetEndian(nOldFormat);
1562
1563 return bRet;
1564}
1565
1566bool ImplWriteDIB(
1567 const Bitmap& rSource,
1568 SvStream& rOStm,
1569 bool bCompressed,
1570 bool bFileHeader)
1571{
1572 const Size aSizePix(rSource.GetSizePixel());
1573 bool bRet(false);
1574
1575 if(!aSizePix.Width() || !aSizePix.Height())
1576 return false;
1577
1578 Bitmap::ScopedReadAccess pAcc(const_cast< Bitmap& >(rSource));
1579 const SvStreamEndian nOldFormat(rOStm.GetEndian());
1580 const sal_uInt64 nOldPos(rOStm.Tell());
1581
1582 rOStm.SetEndian(SvStreamEndian::LITTLE);
1583
1584 if (pAcc)
1585 {
1586 if(bFileHeader)
1587 {
1588 if(ImplWriteDIBFileHeader(rOStm, *pAcc))
1589 {
1590 bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, bCompressed);
1591 }
1592 }
1593 else
1594 {
1595 bRet = ImplWriteDIBBody(rSource, rOStm, *pAcc, bCompressed);
1596 }
1597
1598 pAcc.reset();
1599 }
1600
1601 if(!bRet)
1602 {
1603 rOStm.SetError(SVSTREAM_GENERALERROR);
1604 rOStm.Seek(nOldPos);
1605 }
1606
1607 rOStm.SetEndian(nOldFormat);
1608
1609 return bRet;
1610}
1611
1612} // unnamed namespace
1613
1615 Bitmap& rTarget,
1616 SvStream& rIStm,
1617 bool bFileHeader,
1618 bool bMSOFormat)
1619{
1620 return ImplReadDIB(rTarget, nullptr, rIStm, bFileHeader, bMSOFormat);
1621}
1622
1624 BitmapEx& rTarget,
1625 SvStream& rIStm,
1626 bool bFileHeader,
1627 bool bMSOFormat)
1628{
1629 Bitmap aBmp;
1630 bool bRetval(ImplReadDIB(aBmp, nullptr, rIStm, bFileHeader, bMSOFormat) && !rIStm.GetError());
1631
1632 if(bRetval)
1633 {
1634 // base bitmap was read, set as return value and try to read alpha extra-data
1635 const sal_uInt64 nStmPos(rIStm.Tell());
1636 sal_uInt32 nMagic1(0);
1637 sal_uInt32 nMagic2(0);
1638
1639 rTarget = BitmapEx(aBmp);
1640 if (rIStm.remainingSize() >= 4)
1641 rIStm.ReadUInt32( nMagic1 ).ReadUInt32( nMagic2 );
1642 bRetval = (0x25091962 == nMagic1) && (0xACB20201 == nMagic2) && !rIStm.GetError();
1643
1644 if(bRetval)
1645 {
1646 sal_uInt8 tmp = 0;
1647 rIStm.ReadUChar( tmp );
1648 bRetval = !rIStm.GetError();
1649
1650 if(bRetval)
1651 {
1652 switch (tmp)
1653 {
1654 case 2: // TransparentType::Bitmap
1655 {
1656 Bitmap aMask;
1657
1658 bRetval = ImplReadDIB(aMask, nullptr, rIStm, true);
1659
1660 if(bRetval && !aMask.IsEmpty())
1661 rTarget = BitmapEx(aBmp, aMask);
1662
1663 break;
1664 }
1665 case 1: // backwards compat for old option TransparentType::Color
1666 {
1667 Color aTransparentColor;
1668
1669 tools::GenericTypeSerializer aSerializer(rIStm);
1670 aSerializer.readColor(aTransparentColor);
1671
1672 bRetval = rIStm.good();
1673
1674 if(bRetval)
1675 {
1676 rTarget = BitmapEx(aBmp, aTransparentColor);
1677 }
1678 break;
1679 }
1680 default: break;
1681 }
1682 }
1683 }
1684
1685 if(!bRetval)
1686 {
1687 // alpha extra data could not be read; reset, but use base bitmap as result
1688 rIStm.ResetError();
1689 rIStm.Seek(nStmPos);
1690 bRetval = true;
1691 }
1692 }
1693
1694 return bRetval;
1695}
1696
1698 Bitmap& rTarget,
1699 AlphaMask& rTargetAlpha,
1700 SvStream& rIStm)
1701{
1702 bool rv = ImplReadDIB(rTarget, &rTargetAlpha, rIStm, true);
1703 // convert transparency->alpha
1704 if (rv)
1705 rTargetAlpha.Invert();
1706 return rv;
1707}
1708
1710 BitmapEx& rTarget,
1711 const unsigned char* pBuf,
1712 const ScanlineFormat nFormat,
1713 const int nHeight,
1714 const int nStride)
1715{
1716 BitmapScopedWriteAccess pWriteAccess(rTarget.maBitmap.AcquireWriteAccess(), rTarget.maBitmap);
1717 for (int nRow = 0; nRow < nHeight; ++nRow)
1718 {
1719 pWriteAccess->CopyScanline(nRow, pBuf + (nStride * nRow), nFormat, nStride);
1720 }
1721
1722 return true;
1723}
1724
1726 const Bitmap& rSource,
1727 SvStream& rOStm,
1728 bool bCompressed,
1729 bool bFileHeader)
1730{
1731 return ImplWriteDIB(rSource, rOStm, bCompressed, bFileHeader);
1732}
1733
1735 const BitmapEx& rSource,
1736 SvStream& rOStm,
1737 bool bCompressed)
1738{
1739 return ImplWriteDIB(rSource.GetBitmap(), rOStm, bCompressed, /*bFileHeader*/true);
1740}
1741
1743 const BitmapEx& rSource,
1744 SvStream& rOStm)
1745{
1746 if(ImplWriteDIB(rSource.GetBitmap(), rOStm, true, true))
1747 {
1748 rOStm.WriteUInt32( 0x25091962 );
1749 rOStm.WriteUInt32( 0xACB20201 );
1750 rOStm.WriteUChar( rSource.IsAlpha() ? 2 : 0 ); // Used to be TransparentType enum
1751
1752 if(rSource.IsAlpha())
1753 {
1754 // invert the alpha because the other routines actually want transparency
1755 AlphaMask tmpAlpha = rSource.maAlphaMask;
1756 tmpAlpha.Invert();
1757 return ImplWriteDIB(tmpAlpha, rOStm, true, true);
1758 }
1759 }
1760
1761 return false;
1762}
1763
1765{
1766 return DIBV5HEADERSIZE;
1767}
1768
1769/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
vcl::ScopedBitmapAccess< BitmapWriteAccess, AlphaMask, &AlphaMask::AcquireAlphaWriteAccess > AlphaScopedWriteAccess
sal_uInt8 * Scanline
Definition: Scanline.hxx:26
ScanlineFormat
Definition: Scanline.hxx:29
bool IsAlpha() const
Definition: BitmapEx.cxx:207
Bitmap GetBitmap(Color aTransparentReplaceColor) const
Definition: BitmapEx.cxx:217
AlphaMask maAlphaMask
Definition: bitmapex.hxx:468
tools::Long Height() const
tools::Long Width() const
bool IsBottomUp() const
bool HasPalette() const
ScanlineFormat GetScanlineFormat() const
sal_uInt16 GetPaletteEntryCount() const
sal_uInt16 GetBitCount() const
const ColorMask & GetColorMask() const
const BitmapColor & GetPaletteColor(sal_uInt16 nColor) const
sal_uInt32 GetScanlineSize() const
sal_uInt16 GetEntryCount() const
void SetEntryCount(sal_uInt16 nCount)
Scanline GetBuffer() const
BitmapColor GetColor(tools::Long nY, tools::Long nX) const
void SetPixelOnData(sal_uInt8 *pData, tools::Long nX, const BitmapColor &rBitmapColor)
sal_uInt8 GetIndexFromData(const sal_uInt8 *pData, tools::Long nX) const
Scanline GetScanline(tools::Long nY) const
const MapMode & GetPrefMapMode() const
Size GetSizePixel() const
bool IsEmpty() const
bool Invert()
Perform the Invert operation on every pixel.
Definition: bitmappaint.cxx:61
const Size & GetPrefSize() const
sal_uInt32 GetRedMask() const
Definition: ColorMask.hxx:120
sal_uInt32 GetGreenMask() const
Definition: ColorMask.hxx:125
sal_uInt32 GetBlueMask() const
Definition: ColorMask.hxx:130
sal_uInt8 GetBlue() const
void SetGreen(sal_uInt8 nGreen)
void SetRed(sal_uInt8 nRed)
sal_uInt8 GetRed() const
sal_uInt8 GetGreen() const
void SetBlue(sal_uInt8 nBlue)
SAL_WARN_UNUSED_RESULT Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1580
constexpr tools::Long Height() const
constexpr tools::Long Width() const
virtual void ResetError()
SvStream & WriteInt32(sal_Int32 nInt32)
sal_uInt64 Tell() const
void SetEndian(SvStreamEndian SvStreamEndian)
bool good() const
virtual sal_uInt64 TellEnd()
SvStream & ReadInt16(sal_Int16 &rInt16)
SvStreamCompressFlags GetCompressMode() const
std::size_t WriteBytes(const void *pData, std::size_t nSize)
SvStream & WriteUChar(unsigned char nChar)
SvStream & WriteUInt16(sal_uInt16 nUInt16)
sal_Int32 GetVersion() const
SvStream & WriteUInt32(sal_uInt32 nUInt32)
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
SvStreamEndian GetEndian() const
void SetError(ErrCode nErrorCode)
sal_uInt64 Seek(sal_uInt64 nPos)
SvStream & ReadInt32(sal_Int32 &rInt32)
std::size_t ReadBytes(void *pData, std::size_t nSize)
sal_uInt64 SeekRel(sal_Int64 nPos)
ErrCode GetError() const
SvStream & ReadUInt16(sal_uInt16 &rUInt16)
sal_uInt64 remainingSize()
SvStream & ReadUChar(unsigned char &rChar)
tools::Long Read(SvStream &rIStm, sal_uInt8 *pData, sal_uInt32 nSize)
tools::Long EndCompression()
void BeginCompression(int nCompressLevel=ZCODEC_DEFAULT_COMPRESSION, bool gzLib=false)
void Write(SvStream &rOStm, const sal_uInt8 *pData, sal_uInt32 nSize)
void readColor(Color &rColor)
static bool IsFuzzing()
This template handles BitmapAccess the RAII way.
int nCount
bool WriteDIBBitmapEx(const BitmapEx &rSource, SvStream &rOStm)
Definition: dibtools.cxx:1742
bool WriteDIB(const Bitmap &rSource, SvStream &rOStm, bool bCompressed, bool bFileHeader)
Definition: dibtools.cxx:1725
sal_Int32 FXPT2DOT30
Definition: dibtools.cxx:45
sal_uInt32 getDIBV5HeaderSize()
Definition: dibtools.cxx:1764
bool ReadDIBV5(Bitmap &rTarget, AlphaMask &rTargetAlpha, SvStream &rIStm)
Definition: dibtools.cxx:1697
#define DIBV5HEADERSIZE
Definition: dibtools.cxx:41
#define DIBINFOHEADERSIZE
Definition: dibtools.cxx:40
bool ReadDIBBitmapEx(BitmapEx &rTarget, SvStream &rIStm, bool bFileHeader, bool bMSOFormat)
Definition: dibtools.cxx:1623
#define DIBCOREHEADERSIZE
Definition: dibtools.cxx:39
bool ReadDIB(Bitmap &rTarget, SvStream &rIStm, bool bFileHeader, bool bMSOFormat)
Definition: dibtools.cxx:1614
bool ReadRawDIB(BitmapEx &rTarget, const unsigned char *pBuf, const ScanlineFormat nFormat, const int nHeight, const int nStride)
Definition: dibtools.cxx:1709
#define BITFIELDS
Definition: dibtools.hxx:37
#define COMPRESS_NONE
Definition: dibtools.hxx:34
#define RLE_8
Definition: dibtools.hxx:35
#define RLE_4
Definition: dibtools.hxx:36
#define ZCOMPRESS
Definition: dibtools.hxx:38
float v
#define ERRCODE_NONE
FilterGroup & rTarget
sal_uInt32 AlignedWidth4Bytes(sal_uInt32 nWidthBits)
sal_Int32 nIndex
short nBitCount
Definition: ipict.cxx:80
sal_Int64 n
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
aBuf
constexpr OUStringLiteral aData
bool equalZero(const T &rfVal)
B2IRange fround(const B2DRange &rRange)
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
unsigned long ULong
long Long
PixelFormat
Pixel format of the bitmap in bits per pixel.
Definition: BitmapTypes.hxx:20
sal_uInt8 SVBT32[4]
sal_uIntPtr sal_uLong
SvStreamEndian
TOOLS_DLLPUBLIC bool checkSeek(SvStream &rSt, sal_uInt64 nOffset)
unsigned char sal_uInt8
std::unique_ptr< char[]> aBuffer