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