1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <tools/helpers.hxx>
27 #include <vcl/bitmap.hxx>
28 #include <vcl/bitmapex.hxx>
29 #include <vcl/outdev.hxx>
32 #include <salinst.hxx>
35 #include <vcl/skia/SkiaHelper.hxx>
37 #include <vcl/bitmap/BitmapMonochromeFilter.hxx>
39 #include <bitmap/BitmapScaleSuperFilter.hxx>
40 #include <bitmap/BitmapScaleConvolutionFilter.hxx>
41 #include <bitmap/BitmapFastScaleFilter.hxx>
42 #include <bitmap/BitmapInterpolateScaleFilter.hxx>
43 #include <vcl/BitmapWriteAccess.hxx>
44 #include <bitmap/impoctree.hxx>
45 #include <bitmap/Octree.hxx>
55 #include <tools/stream.hxx>
56 #include <vcl/graphicfilter.hxx>
63 Bitmap::Bitmap(const Bitmap
& rBitmap
)
64 : mxSalBmp(rBitmap
.mxSalBmp
)
65 , maPrefMapMode(rBitmap
.maPrefMapMode
)
66 , maPrefSize(rBitmap
.maPrefSize
)
70 Bitmap::Bitmap(std::shared_ptr
<SalBitmap
> pSalBitmap
)
71 : mxSalBmp(std::move(pSalBitmap
))
72 , maPrefMapMode(MapMode(MapUnit::MapPixel
))
73 , maPrefSize(mxSalBmp
->GetSize())
77 Bitmap::Bitmap( const Size
& rSizePixel
, vcl::PixelFormat ePixelFormat
, const BitmapPalette
* pPal
)
79 if (!(rSizePixel
.Width() && rSizePixel
.Height()))
84 case vcl::PixelFormat::N8_BPP
:
86 static const BitmapPalette aPalN8_BPP
= [] {
87 BitmapPalette
aPal(1 << sal_uInt16(vcl::PixelFormat::N8_BPP
));
88 aPal
[ 0 ] = COL_BLACK
;
90 aPal
[ 2 ] = COL_GREEN
;
93 aPal
[ 5 ] = COL_MAGENTA
;
94 aPal
[ 6 ] = COL_BROWN
;
96 aPal
[ 8 ] = COL_LIGHTGRAY
;
97 aPal
[ 9 ] = COL_LIGHTBLUE
;
98 aPal
[ 10 ] = COL_LIGHTGREEN
;
99 aPal
[ 11 ] = COL_LIGHTCYAN
;
100 aPal
[ 12 ] = COL_LIGHTRED
;
101 aPal
[ 13 ] = COL_LIGHTMAGENTA
;
102 aPal
[ 14 ] = COL_YELLOW
;
103 aPal
[ 15 ] = COL_WHITE
;
105 // Create dither palette
106 sal_uInt16 nActCol
= 16;
108 for( sal_uInt16 nB
= 0; nB
< 256; nB
+= 51 )
109 for( sal_uInt16 nG
= 0; nG
< 256; nG
+= 51 )
110 for( sal_uInt16 nR
= 0; nR
< 256; nR
+= 51 )
111 aPal
[ nActCol
++ ] = BitmapColor( static_cast<sal_uInt8
>(nR
), static_cast<sal_uInt8
>(nG
), static_cast<sal_uInt8
>(nB
) );
113 // Set standard Office colors
114 aPal
[ nActCol
++ ] = BitmapColor( 0, 184, 255 );
123 static const BitmapPalette aPalEmpty
;
124 if (!pPal
|| !vcl::isPalettePixelFormat(ePixelFormat
))
130 mxSalBmp
= ImplGetSVData()->mpDefInst
->CreateSalBitmap();
131 mxSalBmp
->Create(rSizePixel
, ePixelFormat
, *pPal
);
138 void savePNG(const OUString
& sWhere
, const Bitmap
& rBmp
)
140 SvFileStream
aStream(sWhere
, StreamMode::WRITE
| StreamMode::TRUNC
);
141 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
142 rFilter
.compressAsPNG(BitmapEx(rBmp
), aStream
);
151 // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
152 static const OUString
sDumpPath(OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
153 // Stepping into the dtor of a bitmap you need, and setting the volatile variable to true in
154 // debugger, would dump the bitmap in question
155 static volatile bool save(false);
156 if (!sDumpPath
.isEmpty() && save
)
159 savePNG(sDumpPath
+ "BitmapDump.png", *this);
167 constexpr std::enable_if_t
<255 % (N
- 1) == 0, std::array
<BitmapColor
, N
>> getGreyscalePalette()
169 const int step
= 255 / (N
- 1);
170 std::array
<BitmapColor
, N
> a
;
171 for (size_t i
= 0; i
< N
; ++i
)
172 a
[i
] = BitmapColor(i
* step
, i
* step
, i
* step
);
177 const BitmapPalette
& Bitmap::GetGreyPalette( int nEntries
)
179 // Create greyscale palette with 2, 4, 16 or 256 entries
184 static const BitmapPalette aGreyPalette2
= getGreyscalePalette
<2>();
185 return aGreyPalette2
;
189 static const BitmapPalette aGreyPalette4
= getGreyscalePalette
<4>();
190 return aGreyPalette4
;
194 static const BitmapPalette aGreyPalette16
= getGreyscalePalette
<16>();
195 return aGreyPalette16
;
199 static const BitmapPalette aGreyPalette256
= getGreyscalePalette
<256>();
200 return aGreyPalette256
;
203 OSL_FAIL("Bitmap::GetGreyPalette: invalid entry count (2/4/16/256 allowed)");
204 return GetGreyPalette(2);
207 Bitmap
& Bitmap::operator=( const Bitmap
& rBitmap
)
209 if (this == &rBitmap
)
212 maPrefSize
= rBitmap
.maPrefSize
;
213 maPrefMapMode
= rBitmap
.maPrefMapMode
;
214 mxSalBmp
= rBitmap
.mxSalBmp
;
219 Bitmap
& Bitmap::operator=( Bitmap
&& rBitmap
) noexcept
221 maPrefSize
= std::move(rBitmap
.maPrefSize
);
222 maPrefMapMode
= std::move(rBitmap
.maPrefMapMode
);
223 mxSalBmp
= std::move(rBitmap
.mxSalBmp
);
228 bool Bitmap::operator==( const Bitmap
& rBmp
) const
230 if (rBmp
.mxSalBmp
== mxSalBmp
) // Includes both are nullptr
232 if (!rBmp
.mxSalBmp
|| !mxSalBmp
)
234 if (rBmp
.mxSalBmp
->GetSize() != mxSalBmp
->GetSize() ||
235 rBmp
.mxSalBmp
->GetBitCount() != mxSalBmp
->GetBitCount())
237 BitmapChecksum aChecksum1
= rBmp
.mxSalBmp
->GetChecksum();
238 BitmapChecksum aChecksum2
= mxSalBmp
->GetChecksum();
239 // If the bitmaps can't calculate a checksum, best to regard them as different.
240 if (aChecksum1
== 0 || aChecksum2
== 0)
242 return aChecksum1
== aChecksum2
;
245 void Bitmap::SetEmpty()
247 maPrefMapMode
= MapMode();
252 Size
Bitmap::GetSizePixel() const
254 return( mxSalBmp
? mxSalBmp
->GetSize() : Size() );
257 vcl::PixelFormat
Bitmap::getPixelFormat() const
260 return vcl::PixelFormat::INVALID
;
262 sal_uInt16 nBitCount
= mxSalBmp
->GetBitCount();
264 return vcl::PixelFormat::N8_BPP
;
266 return vcl::PixelFormat::N24_BPP
;
268 return vcl::PixelFormat::N32_BPP
;
270 return vcl::PixelFormat::INVALID
;
273 bool Bitmap::HasGreyPaletteAny() const
277 BitmapScopedInfoAccess
pIAcc(*this);
281 bRet
= pIAcc
->HasPalette() && pIAcc
->GetPalette().IsGreyPaletteAny();
287 bool Bitmap::HasGreyPalette8Bit() const
290 BitmapScopedInfoAccess
pIAcc(*this);
294 bRet
= pIAcc
->HasPalette() && pIAcc
->GetPalette().IsGreyPalette8Bit();
300 BitmapChecksum
Bitmap::GetChecksum() const
305 BitmapChecksum nRet
= mxSalBmp
->GetChecksum();
308 // nRet == 0 => probably, we were not able to acquire
309 // the buffer in SalBitmap::updateChecksum;
310 // so, we need to update the imp bitmap for this bitmap instance
311 // as we do in BitmapInfoAccess::ImplCreate
312 std::shared_ptr
<SalBitmap
> xNewImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
313 if (xNewImpBmp
->Create(*mxSalBmp
, getPixelFormat()))
315 Bitmap
* pThis
= const_cast<Bitmap
*>(this);
316 pThis
->mxSalBmp
= std::move(xNewImpBmp
);
317 nRet
= mxSalBmp
->GetChecksum();
324 void Bitmap::ImplMakeUnique()
326 if (mxSalBmp
&& mxSalBmp
.use_count() > 1)
328 std::shared_ptr
<SalBitmap
> xOldImpBmp
= mxSalBmp
;
329 mxSalBmp
= ImplGetSVData()->mpDefInst
->CreateSalBitmap();
330 (void)mxSalBmp
->Create(*xOldImpBmp
);
334 void Bitmap::ReassignWithSize(const Bitmap
& rBitmap
)
336 const Size
aOldSizePix(GetSizePixel());
337 const Size
aNewSizePix(rBitmap
.GetSizePixel());
338 const MapMode
aOldMapMode(maPrefMapMode
);
341 if ((aOldSizePix
!= aNewSizePix
) && aOldSizePix
.Width() && aOldSizePix
.Height())
343 aNewPrefSize
.setWidth(maPrefSize
.Width() * aNewSizePix
.Width() / aOldSizePix
.Width());
344 aNewPrefSize
.setHeight(maPrefSize
.Height() * aNewSizePix
.Height() / aOldSizePix
.Height());
348 aNewPrefSize
= maPrefSize
;
353 maPrefSize
= aNewPrefSize
;
354 maPrefMapMode
= aOldMapMode
;
357 void Bitmap::ImplSetSalBitmap(const std::shared_ptr
<SalBitmap
>& xImpBmp
)
362 bool Bitmap::Crop( const tools::Rectangle
& rRectPixel
)
364 const Size
aSizePix( GetSizePixel() );
365 tools::Rectangle
aRect( rRectPixel
);
367 aRect
.Intersection( tools::Rectangle( Point(), aSizePix
) );
369 if( aRect
.IsEmpty() || aSizePix
== aRect
.GetSize())
372 BitmapScopedReadAccess
pReadAcc(*this);
376 const tools::Rectangle
aNewRect( Point(), aRect
.GetSize() );
377 Bitmap
aNewBmp(aNewRect
.GetSize(), getPixelFormat(), &pReadAcc
->GetPalette());
378 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
382 const tools::Long nOldX
= aRect
.Left();
383 const tools::Long nOldY
= aRect
.Top();
384 const tools::Long nNewWidth
= aNewRect
.GetWidth();
385 const tools::Long nNewHeight
= aNewRect
.GetHeight();
387 for( tools::Long nY
= 0, nY2
= nOldY
; nY
< nNewHeight
; nY
++, nY2
++ )
389 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
390 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY2
);
391 for( tools::Long nX
= 0, nX2
= nOldX
; nX
< nNewWidth
; nX
++, nX2
++ )
392 pWriteAcc
->SetPixelOnData( pScanline
, nX
, pReadAcc
->GetPixelFromData( pScanlineRead
, nX2
) );
398 ReassignWithSize( aNewBmp
);
403 bool Bitmap::CopyPixel( const tools::Rectangle
& rRectDst
,
404 const tools::Rectangle
& rRectSrc
)
406 const Size
aSizePix( GetSizePixel() );
407 tools::Rectangle
aRectDst( rRectDst
);
409 aRectDst
.Intersection( tools::Rectangle( Point(), aSizePix
) );
411 if( aRectDst
.IsEmpty() )
414 tools::Rectangle
aRectSrc( rRectSrc
);
416 aRectSrc
.Intersection( tools::Rectangle( Point(), aSizePix
) );
418 if( aRectSrc
.IsEmpty() || ( aRectSrc
== aRectDst
) )
421 BitmapScopedWriteAccess
pWriteAcc(*this);
425 const tools::Long nWidth
= std::min( aRectSrc
.GetWidth(), aRectDst
.GetWidth() );
426 const tools::Long nHeight
= std::min( aRectSrc
.GetHeight(), aRectDst
.GetHeight() );
427 const tools::Long nSrcX
= aRectSrc
.Left();
428 const tools::Long nSrcY
= aRectSrc
.Top();
429 const tools::Long nSrcEndX1
= nSrcX
+ nWidth
- 1;
430 const tools::Long nSrcEndY1
= nSrcY
+ nHeight
- 1;
431 const tools::Long nDstX
= aRectDst
.Left();
432 const tools::Long nDstY
= aRectDst
.Top();
433 const tools::Long nDstEndX1
= nDstX
+ nWidth
- 1;
434 const tools::Long nDstEndY1
= nDstY
+ nHeight
- 1;
436 if( ( nDstX
<= nSrcX
) && ( nDstY
<= nSrcY
) )
438 for( tools::Long nY
= nSrcY
, nYN
= nDstY
; nY
<= nSrcEndY1
; nY
++, nYN
++ )
440 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
441 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
442 for( tools::Long nX
= nSrcX
, nXN
= nDstX
; nX
<= nSrcEndX1
; nX
++, nXN
++ )
443 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
446 else if( ( nDstX
<= nSrcX
) && ( nDstY
>= nSrcY
) )
448 for( tools::Long nY
= nSrcEndY1
, nYN
= nDstEndY1
; nY
>= nSrcY
; nY
--, nYN
-- )
450 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
451 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
452 for( tools::Long nX
= nSrcX
, nXN
= nDstX
; nX
<= nSrcEndX1
; nX
++, nXN
++ )
453 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
456 else if( ( nDstX
>= nSrcX
) && ( nDstY
<= nSrcY
) )
458 for( tools::Long nY
= nSrcY
, nYN
= nDstY
; nY
<= nSrcEndY1
; nY
++, nYN
++ )
460 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
461 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
462 for( tools::Long nX
= nSrcEndX1
, nXN
= nDstEndX1
; nX
>= nSrcX
; nX
--, nXN
-- )
463 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
468 for( tools::Long nY
= nSrcEndY1
, nYN
= nDstEndY1
; nY
>= nSrcY
; nY
--, nYN
-- )
470 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
471 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
472 for( tools::Long nX
= nSrcEndX1
, nXN
= nDstEndX1
; nX
>= nSrcX
; nX
--, nXN
-- )
473 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
480 bool Bitmap::CopyPixel( const tools::Rectangle
& rRectDst
,
481 const tools::Rectangle
& rRectSrc
, const Bitmap
& rBmpSrc
)
483 const Size
aSizePix( GetSizePixel() );
484 tools::Rectangle
aRectDst( rRectDst
);
486 aRectDst
.Intersection( tools::Rectangle( Point(), aSizePix
) );
488 if( aRectDst
.IsEmpty() )
491 if( rBmpSrc
.mxSalBmp
== mxSalBmp
) // if self-copy
492 return CopyPixel(rRectDst
, rRectSrc
);
494 Bitmap
* pSrc
= &const_cast<Bitmap
&>(rBmpSrc
);
495 const Size
aCopySizePix( pSrc
->GetSizePixel() );
496 tools::Rectangle
aRectSrc( rRectSrc
);
497 const sal_uInt16 nSrcBitCount
= vcl::pixelFormatBitCount(rBmpSrc
.getPixelFormat());
498 const sal_uInt16 nDstBitCount
= vcl::pixelFormatBitCount(getPixelFormat());
500 if( nSrcBitCount
> nDstBitCount
)
504 if (nSrcBitCount
== 24)
505 Convert( BmpConversion::N24Bit
);
506 else if (nSrcBitCount
== 8)
508 Convert( BmpConversion::N8BitColors
);
511 else if (nSrcBitCount
== 4)
518 BitmapScopedReadAccess
pSrcAcc(*pSrc
);
519 BitmapScopedWriteAccess
pDstAcc(*this);
521 if( pSrcAcc
&& pDstAcc
)
523 const int nSrcCount
= pSrcAcc
->GetPaletteEntryCount();
524 const int nDstCount
= 1 << nDstBitCount
;
526 for (int i
= 0; ( i
< nSrcCount
) && ( nNextIndex
< nDstCount
); ++i
)
528 const BitmapColor
& rSrcCol
= pSrcAcc
->GetPaletteColor( static_cast<sal_uInt16
>(i
) );
532 for (int j
= 0; j
< nDstCount
; ++j
)
534 if( rSrcCol
== pDstAcc
->GetPaletteColor( static_cast<sal_uInt16
>(j
) ) )
542 pDstAcc
->SetPaletteColor( static_cast<sal_uInt16
>(nNextIndex
++), rSrcCol
);
548 aRectSrc
.Intersection( tools::Rectangle( Point(), aCopySizePix
) );
550 if( aRectSrc
.IsEmpty() )
553 BitmapScopedReadAccess
pReadAcc(*pSrc
);
557 BitmapScopedWriteAccess
pWriteAcc(*this);
561 const tools::Long nWidth
= std::min( aRectSrc
.GetWidth(), aRectDst
.GetWidth() );
562 const tools::Long nHeight
= std::min( aRectSrc
.GetHeight(), aRectDst
.GetHeight() );
563 const tools::Long nSrcEndX
= aRectSrc
.Left() + nWidth
;
564 const tools::Long nSrcEndY
= aRectSrc
.Top() + nHeight
;
565 tools::Long nDstY
= aRectDst
.Top();
567 if( pReadAcc
->HasPalette() && pWriteAcc
->HasPalette() )
569 const sal_uInt16 nCount
= pReadAcc
->GetPaletteEntryCount();
570 std::unique_ptr
<sal_uInt8
[]> pMap(new sal_uInt8
[ nCount
]);
572 // Create index map for the color table, as the bitmap should be copied
573 // retaining it's color information relatively well
574 for( sal_uInt16 i
= 0; i
< nCount
; i
++ )
575 pMap
[ i
] = static_cast<sal_uInt8
>(pWriteAcc
->GetBestPaletteIndex( pReadAcc
->GetPaletteColor( i
) ));
577 for( tools::Long nSrcY
= aRectSrc
.Top(); nSrcY
< nSrcEndY
; nSrcY
++, nDstY
++ )
579 Scanline pScanline
= pWriteAcc
->GetScanline(nDstY
);
580 Scanline pScanlineRead
= pReadAcc
->GetScanline(nSrcY
);
581 for( tools::Long nSrcX
= aRectSrc
.Left(), nDstX
= aRectDst
.Left(); nSrcX
< nSrcEndX
; nSrcX
++, nDstX
++ )
582 pWriteAcc
->SetPixelOnData( pScanline
, nDstX
, BitmapColor( pMap
[ pReadAcc
->GetIndexFromData( pScanlineRead
, nSrcX
) ] ));
585 else if( pReadAcc
->HasPalette() )
587 for( tools::Long nSrcY
= aRectSrc
.Top(); nSrcY
< nSrcEndY
; nSrcY
++, nDstY
++ )
589 Scanline pScanline
= pWriteAcc
->GetScanline(nDstY
);
590 Scanline pScanlineRead
= pReadAcc
->GetScanline(nSrcY
);
591 for( tools::Long nSrcX
= aRectSrc
.Left(), nDstX
= aRectDst
.Left(); nSrcX
< nSrcEndX
; nSrcX
++, nDstX
++ )
592 pWriteAcc
->SetPixelOnData( pScanline
, nDstX
, pReadAcc
->GetPaletteColor( pReadAcc
->GetIndexFromData( pScanlineRead
, nSrcX
) ) );
596 for( tools::Long nSrcY
= aRectSrc
.Top(); nSrcY
< nSrcEndY
; nSrcY
++, nDstY
++ )
598 Scanline pScanline
= pWriteAcc
->GetScanline(nDstY
);
599 Scanline pScanlineRead
= pReadAcc
->GetScanline(nSrcY
);
600 for( tools::Long nSrcX
= aRectSrc
.Left(), nDstX
= aRectDst
.Left(); nSrcX
< nSrcEndX
; nSrcX
++, nDstX
++ )
601 pWriteAcc
->SetPixelOnData( pScanline
, nDstX
, pReadAcc
->GetPixelFromData( pScanlineRead
, nSrcX
) );
604 bool bRet
= ( nWidth
> 0 ) && ( nHeight
> 0 );
609 bool Bitmap::CopyPixel_AlphaOptimized( const tools::Rectangle
& rRectDst
, const tools::Rectangle
& rRectSrc
)
611 assert(HasGreyPalette8Bit());
612 // Note: this code is copied from Bitmap::CopyPixel but avoids any palette lookups
613 // This optimization is possible because the palettes of AlphaMasks are always identical (8bit GreyPalette, see ctor)
614 const Size
aSizePix( GetSizePixel() );
615 tools::Rectangle
aRectDst( rRectDst
);
617 aRectDst
.Intersection( tools::Rectangle( Point(), aSizePix
) );
619 if( aRectDst
.IsEmpty() )
622 tools::Rectangle
aRectSrc( rRectSrc
);
623 aRectSrc
.Intersection( tools::Rectangle( Point(), aSizePix
) );
624 if( aRectSrc
.IsEmpty() || ( aRectSrc
== aRectDst
) )
627 BitmapScopedWriteAccess
pWriteAcc(*this);
631 const tools::Long nWidth
= std::min( aRectSrc
.GetWidth(), aRectDst
.GetWidth() );
632 const tools::Long nHeight
= std::min( aRectSrc
.GetHeight(), aRectDst
.GetHeight() );
633 const tools::Long nSrcX
= aRectSrc
.Left();
634 const tools::Long nSrcY
= aRectSrc
.Top();
635 const tools::Long nSrcEndX1
= nSrcX
+ nWidth
- 1;
636 const tools::Long nSrcEndY1
= nSrcY
+ nHeight
- 1;
637 const tools::Long nDstX
= aRectDst
.Left();
638 const tools::Long nDstY
= aRectDst
.Top();
639 const tools::Long nDstEndX1
= nDstX
+ nWidth
- 1;
640 const tools::Long nDstEndY1
= nDstY
+ nHeight
- 1;
642 if( ( nDstX
<= nSrcX
) && ( nDstY
<= nSrcY
) )
644 for( tools::Long nY
= nSrcY
, nYN
= nDstY
; nY
<= nSrcEndY1
; nY
++, nYN
++ )
646 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
647 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
648 for( tools::Long nX
= nSrcX
, nXN
= nDstX
; nX
<= nSrcEndX1
; nX
++, nXN
++ )
649 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
652 else if( ( nDstX
<= nSrcX
) && ( nDstY
>= nSrcY
) )
654 for( tools::Long nY
= nSrcEndY1
, nYN
= nDstEndY1
; nY
>= nSrcY
; nY
--, nYN
-- )
656 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
657 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
658 for( tools::Long nX
= nSrcX
, nXN
= nDstX
; nX
<= nSrcEndX1
; nX
++, nXN
++ )
659 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
662 else if( ( nDstX
>= nSrcX
) && ( nDstY
<= nSrcY
) )
664 for( tools::Long nY
= nSrcY
, nYN
= nDstY
; nY
<= nSrcEndY1
; nY
++, nYN
++ )
666 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
667 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
668 for( tools::Long nX
= nSrcEndX1
, nXN
= nDstEndX1
; nX
>= nSrcX
; nX
--, nXN
-- )
669 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
674 for( tools::Long nY
= nSrcEndY1
, nYN
= nDstEndY1
; nY
>= nSrcY
; nY
--, nYN
-- )
676 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
677 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
678 for( tools::Long nX
= nSrcEndX1
, nXN
= nDstEndX1
; nX
>= nSrcX
; nX
--, nXN
-- )
679 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
686 bool Bitmap::CopyPixel_AlphaOptimized( const tools::Rectangle
& rRectDst
, const tools::Rectangle
& rRectSrc
,
687 const AlphaMask
& rBmpSrc
)
689 assert(HasGreyPalette8Bit());
690 assert(rBmpSrc
.GetBitmap().HasGreyPalette8Bit());
691 // Note: this code is copied from Bitmap::CopyPixel but avoids any palette lookups
692 // This optimization is possible because the palettes of AlphaMasks are always identical (8bit GreyPalette, see ctor)
693 const Size
aSizePix( GetSizePixel() );
694 tools::Rectangle
aRectDst( rRectDst
);
696 aRectDst
.Intersection( tools::Rectangle( Point(), aSizePix
) );
698 if( aRectDst
.IsEmpty() )
701 if( rBmpSrc
.GetBitmap().mxSalBmp
== mxSalBmp
) // self-copy
702 return CopyPixel_AlphaOptimized(rRectDst
, rRectSrc
);
704 Bitmap
* pSrc
= &const_cast<Bitmap
&>(rBmpSrc
.GetBitmap());
705 const Size
aCopySizePix( pSrc
->GetSizePixel() );
706 tools::Rectangle
aRectSrc( rRectSrc
);
708 aRectSrc
.Intersection( tools::Rectangle( Point(), aCopySizePix
) );
709 if( aRectSrc
.IsEmpty() )
712 BitmapScopedReadAccess
pReadAcc(*pSrc
);
716 BitmapScopedWriteAccess
pWriteAcc(*this);
720 const tools::Long nWidth
= std::min( aRectSrc
.GetWidth(), aRectDst
.GetWidth() );
721 const tools::Long nHeight
= std::min( aRectSrc
.GetHeight(), aRectDst
.GetHeight() );
722 const tools::Long nSrcEndX
= aRectSrc
.Left() + nWidth
;
723 const tools::Long nSrcEndY
= aRectSrc
.Top() + nHeight
;
724 tools::Long nDstY
= aRectDst
.Top();
726 for( tools::Long nSrcY
= aRectSrc
.Top(); nSrcY
< nSrcEndY
; nSrcY
++, nDstY
++)
728 Scanline pScanline
= pWriteAcc
->GetScanline(nDstY
);
729 Scanline pScanlineRead
= pReadAcc
->GetScanline(nSrcY
);
730 for( tools::Long nSrcX
= aRectSrc
.Left(), nDstX
= aRectDst
.Left(); nSrcX
< nSrcEndX
; nSrcX
++, nDstX
++ )
731 pWriteAcc
->SetPixelOnData( pScanline
, nDstX
, pReadAcc
->GetPixelFromData( pScanlineRead
, nSrcX
) );
734 bool bRet
= ( nWidth
> 0 ) && ( nHeight
> 0 );
739 bool Bitmap::Expand( sal_Int32 nDX
, sal_Int32 nDY
, const Color
* pInitColor
)
744 const Size
aSizePixel( GetSizePixel() );
745 const tools::Long nWidth
= aSizePixel
.Width();
746 const tools::Long nHeight
= aSizePixel
.Height();
747 const Size
aNewSize( nWidth
+ nDX
, nHeight
+ nDY
);
748 BitmapScopedReadAccess
pReadAcc(*this);
752 BitmapPalette
aBmpPal( pReadAcc
->GetPalette() );
753 Bitmap
aNewBmp(aNewSize
, getPixelFormat(), &aBmpPal
);
754 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
759 const tools::Long nNewX
= nWidth
;
760 const tools::Long nNewY
= nHeight
;
761 const tools::Long nNewWidth
= pWriteAcc
->Width();
762 const tools::Long nNewHeight
= pWriteAcc
->Height();
767 aColor
= pWriteAcc
->GetBestMatchingColor( *pInitColor
);
769 for( nY
= 0; nY
< nHeight
; nY
++ )
771 pWriteAcc
->CopyScanline( nY
, *pReadAcc
);
773 if( pInitColor
&& nDX
)
775 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
776 for( nX
= nNewX
; nX
< nNewWidth
; nX
++ )
777 pWriteAcc
->SetPixelOnData( pScanline
, nX
, aColor
);
781 if( pInitColor
&& nDY
)
782 for( nY
= nNewY
; nY
< nNewHeight
; nY
++ )
784 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
785 for( nX
= 0; nX
< nNewWidth
; nX
++ )
786 pWriteAcc
->SetPixelOnData( pScanline
, nX
, aColor
);
792 ReassignWithSize(aNewBmp
);
797 Bitmap
Bitmap::CreateDisplayBitmap( OutputDevice
* pDisplay
) const
799 Bitmap
aDispBmp( *this );
801 SalGraphics
* pDispGraphics
= pDisplay
->GetGraphics();
803 if( mxSalBmp
&& pDispGraphics
)
805 std::shared_ptr
<SalBitmap
> xImpDispBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
806 if (xImpDispBmp
->Create(*mxSalBmp
, pDispGraphics
))
807 aDispBmp
.ImplSetSalBitmap(xImpDispBmp
);
813 bool Bitmap::GetSystemData( BitmapSystemData
& rData
) const
815 return mxSalBmp
&& mxSalBmp
->GetSystemData(rData
);
819 bool Bitmap::Convert( BmpConversion eConversion
)
821 // try to convert in backend
824 // avoid large chunk of obsolete and hopefully rarely used conversions.
825 if (eConversion
== BmpConversion::N8BitNoConversion
)
827 if (mxSalBmp
->GetBitCount() == 8 && HasGreyPalette8Bit())
829 std::shared_ptr
<SalBitmap
> xImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
830 // frequently used conversion for creating alpha masks
831 if (xImpBmp
->Create(*mxSalBmp
) && xImpBmp
->InterpretAs8Bit())
833 ImplSetSalBitmap(xImpBmp
);
834 SAL_INFO( "vcl.opengl", "Ref count: " << mxSalBmp
.use_count() );
838 if (eConversion
== BmpConversion::N8BitGreys
)
840 std::shared_ptr
<SalBitmap
> xImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
841 if (xImpBmp
->Create(*mxSalBmp
) && xImpBmp
->ConvertToGreyscale())
843 ImplSetSalBitmap(xImpBmp
);
844 SAL_INFO( "vcl.opengl", "Ref count: " << mxSalBmp
.use_count() );
850 const sal_uInt16 nBitCount
= vcl::pixelFormatBitCount(getPixelFormat());
853 switch( eConversion
)
855 case BmpConversion::N1BitThreshold
:
857 BitmapEx
aBmpEx(*this);
858 bRet
= BitmapFilter::Filter(aBmpEx
, BitmapMonochromeFilter(128));
859 *this = aBmpEx
.GetBitmap();
863 case BmpConversion::N8BitGreys
:
864 case BmpConversion::N8BitNoConversion
:
865 bRet
= ImplMakeGreyscales();
868 case BmpConversion::N8BitColors
:
871 bRet
= ImplConvertUp(vcl::PixelFormat::N8_BPP
);
872 else if( nBitCount
> 8 )
873 bRet
= ImplConvertDown8BPP();
879 case BmpConversion::N8BitTrans
:
881 Color
aTrans( BMP_COL_TRANS
);
884 bRet
= ImplConvertUp(vcl::PixelFormat::N8_BPP
, &aTrans
);
886 bRet
= ImplConvertDown8BPP(&aTrans
);
890 case BmpConversion::N24Bit
:
893 bRet
= ImplConvertUp(vcl::PixelFormat::N24_BPP
);
899 case BmpConversion::N32Bit
:
902 bRet
= ImplConvertUp(vcl::PixelFormat::N32_BPP
);
909 OSL_FAIL( "Bitmap::Convert(): Unsupported conversion" );
916 bool Bitmap::ImplMakeGreyscales()
918 BitmapScopedReadAccess
pReadAcc(*this);
922 const BitmapPalette
& rPal
= GetGreyPalette(256);
923 sal_uLong nShift
= 0;
924 bool bPalDiffers
= !pReadAcc
->HasPalette() || ( rPal
.GetEntryCount() != pReadAcc
->GetPaletteEntryCount() );
927 bPalDiffers
= ( rPal
!= pReadAcc
->GetPalette() );
931 const auto ePixelFormat
= vcl::PixelFormat::N8_BPP
;
932 Bitmap
aNewBmp(GetSizePixel(), ePixelFormat
, &rPal
);
933 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
937 const tools::Long nWidth
= pWriteAcc
->Width();
938 const tools::Long nHeight
= pWriteAcc
->Height();
940 if( pReadAcc
->HasPalette() )
942 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
944 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
945 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
946 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
948 const sal_uInt8 cIndex
= pReadAcc
->GetIndexFromData( pScanlineRead
, nX
);
949 pWriteAcc
->SetPixelOnData( pScanline
, nX
,
950 BitmapColor(pReadAcc
->GetPaletteColor( cIndex
).GetLuminance() >> nShift
) );
954 else if( pReadAcc
->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr
&&
955 pWriteAcc
->GetScanlineFormat() == ScanlineFormat::N8BitPal
)
959 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
961 Scanline pReadScan
= pReadAcc
->GetScanline( nY
);
962 Scanline pWriteScan
= pWriteAcc
->GetScanline( nY
);
964 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
966 const sal_uLong nB
= *pReadScan
++;
967 const sal_uLong nG
= *pReadScan
++;
968 const sal_uLong nR
= *pReadScan
++;
970 *pWriteScan
++ = static_cast<sal_uInt8
>( ( nB
* 28UL + nG
* 151UL + nR
* 77UL ) >> nShift
);
974 else if( pReadAcc
->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb
&&
975 pWriteAcc
->GetScanlineFormat() == ScanlineFormat::N8BitPal
)
979 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
981 Scanline pReadScan
= pReadAcc
->GetScanline( nY
);
982 Scanline pWriteScan
= pWriteAcc
->GetScanline( nY
);
984 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
986 const sal_uLong nR
= *pReadScan
++;
987 const sal_uLong nG
= *pReadScan
++;
988 const sal_uLong nB
= *pReadScan
++;
990 *pWriteScan
++ = static_cast<sal_uInt8
>( ( nB
* 28UL + nG
* 151UL + nR
* 77UL ) >> nShift
);
996 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
998 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
999 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1000 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1001 pWriteAcc
->SetPixelOnData( pScanline
, nX
, BitmapColor(pReadAcc
->GetPixelFromData( pScanlineRead
, nX
).GetLuminance() >> nShift
) );
1008 const MapMode
aMap( maPrefMapMode
);
1009 const Size
aSize( maPrefSize
);
1011 *this = std::move(aNewBmp
);
1013 maPrefMapMode
= aMap
;
1019 bool Bitmap::ImplConvertUp(vcl::PixelFormat ePixelFormat
, Color
const * pExtColor
)
1021 SAL_WARN_IF(ePixelFormat
<= getPixelFormat(), "vcl", "New pixel format must be greater!" );
1023 BitmapScopedReadAccess
pReadAcc(*this);
1027 BitmapPalette aPalette
;
1028 Bitmap
aNewBmp(GetSizePixel(), ePixelFormat
, pReadAcc
->HasPalette() ? &pReadAcc
->GetPalette() : &aPalette
);
1029 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
1033 const tools::Long nWidth
= pWriteAcc
->Width();
1034 const tools::Long nHeight
= pWriteAcc
->Height();
1036 if (pWriteAcc
->HasPalette())
1038 const BitmapPalette
& rOldPalette
= pReadAcc
->GetPalette();
1039 const sal_uInt16 nOldCount
= rOldPalette
.GetEntryCount();
1040 assert(nOldCount
<= (1 << vcl::pixelFormatBitCount(getPixelFormat())));
1042 aPalette
.SetEntryCount(1 << vcl::pixelFormatBitCount(ePixelFormat
));
1044 for (sal_uInt16 i
= 0; i
< nOldCount
; i
++)
1045 aPalette
[i
] = rOldPalette
[i
];
1048 aPalette
[aPalette
.GetEntryCount() - 1] = *pExtColor
;
1050 pWriteAcc
->SetPalette(aPalette
);
1052 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
1054 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1055 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1056 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1058 pWriteAcc
->SetPixelOnData(pScanline
, nX
, pReadAcc
->GetPixelFromData(pScanlineRead
, nX
));
1064 if (pReadAcc
->HasPalette())
1066 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
1068 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1069 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1070 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1072 pWriteAcc
->SetPixelOnData(pScanline
, nX
, pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nX
)));
1078 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
1080 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1081 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1082 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1084 pWriteAcc
->SetPixelOnData(pScanline
, nX
, pReadAcc
->GetPixelFromData(pScanlineRead
, nX
));
1090 const MapMode
aMap(maPrefMapMode
);
1091 const Size
aSize(maPrefSize
);
1093 *this = std::move(aNewBmp
);
1095 maPrefMapMode
= aMap
;
1101 bool Bitmap::ImplConvertDown8BPP(Color
const * pExtColor
)
1103 SAL_WARN_IF(vcl::PixelFormat::N8_BPP
> getPixelFormat(), "vcl", "New pixelformat must be lower ( or equal when pExtColor is set )!");
1105 BitmapScopedReadAccess
pReadAcc(*this);
1109 BitmapPalette aPalette
;
1110 Bitmap
aNewBmp(GetSizePixel(), vcl::PixelFormat::N8_BPP
, &aPalette
);
1111 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
1115 sal_Int16 nNewBitCount
= sal_Int16(vcl::PixelFormat::N8_BPP
);
1116 const sal_uInt16 nCount
= 1 << nNewBitCount
;
1117 const tools::Long nWidth
= pWriteAcc
->Width();
1118 const tools::Long nWidth1
= nWidth
- 1;
1119 const tools::Long nHeight
= pWriteAcc
->Height();
1120 Octree
aOctree(*pReadAcc
, pExtColor
? (nCount
- 1) : nCount
);
1121 aPalette
= aOctree
.GetPalette();
1122 InverseColorMap
aColorMap(aPalette
);
1124 ImpErrorQuad aErrQuad
;
1125 std::vector
<ImpErrorQuad
> aErrQuad1(nWidth
);
1126 std::vector
<ImpErrorQuad
> aErrQuad2(nWidth
);
1127 ImpErrorQuad
* pQLine1
= aErrQuad1
.data();
1128 ImpErrorQuad
* pQLine2
= nullptr;
1129 tools::Long nYTmp
= 0;
1135 aPalette
.SetEntryCount(aPalette
.GetEntryCount() + 1);
1136 aPalette
[aPalette
.GetEntryCount() - 1] = *pExtColor
;
1139 // set Black/White always, if we have enough space
1140 if (aPalette
.GetEntryCount() < (nCount
- 1))
1142 aPalette
.SetEntryCount(aPalette
.GetEntryCount() + 2);
1143 aPalette
[aPalette
.GetEntryCount() - 2] = COL_BLACK
;
1144 aPalette
[aPalette
.GetEntryCount() - 1] = COL_WHITE
;
1147 pWriteAcc
->SetPalette(aPalette
);
1149 for (tools::Long nY
= 0; nY
< std::min(nHeight
, tools::Long(2)); nY
++, nYTmp
++)
1151 pQLine2
= !nY
? aErrQuad1
.data() : aErrQuad2
.data();
1152 Scanline pScanlineRead
= pReadAcc
->GetScanline(nYTmp
);
1153 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1155 if (pReadAcc
->HasPalette())
1156 pQLine2
[nX
] = pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nX
));
1158 pQLine2
[nX
] = pReadAcc
->GetPixelFromData(pScanlineRead
, nX
);
1162 assert(pQLine2
|| nHeight
== 0);
1164 for (tools::Long nY
= 0; nY
< nHeight
; nY
++, nYTmp
++)
1166 // first pixel in the line
1167 cIndex
= static_cast<sal_uInt8
>(aColorMap
.GetBestPaletteIndex(pQLine1
[0].ImplGetColor()));
1168 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1169 pWriteAcc
->SetPixelOnData(pScanline
, 0, BitmapColor(cIndex
));
1172 for (nX
= 1; nX
< nWidth1
; nX
++)
1174 aColor
= pQLine1
[nX
].ImplGetColor();
1175 cIndex
= static_cast<sal_uInt8
>(aColorMap
.GetBestPaletteIndex(aColor
));
1176 aErrQuad
= (ImpErrorQuad(aColor
) -= pWriteAcc
->GetPaletteColor(cIndex
));
1177 pQLine1
[++nX
].ImplAddColorError7(aErrQuad
);
1178 pQLine2
[nX
--].ImplAddColorError1(aErrQuad
);
1179 pQLine2
[nX
--].ImplAddColorError5(aErrQuad
);
1180 pQLine2
[nX
++].ImplAddColorError3(aErrQuad
);
1181 pWriteAcc
->SetPixelOnData(pScanline
, nX
, BitmapColor(cIndex
));
1187 cIndex
= static_cast<sal_uInt8
>(aColorMap
.GetBestPaletteIndex(pQLine1
[nWidth1
].ImplGetColor()));
1188 pWriteAcc
->SetPixelOnData(pScanline
, nX
, BitmapColor(cIndex
));
1191 // Refill/copy row buffer
1194 pQLine2
= bQ1
? aErrQuad2
.data() : aErrQuad1
.data();
1196 if (nYTmp
< nHeight
)
1198 Scanline pScanlineRead
= pReadAcc
->GetScanline(nYTmp
);
1199 for (nX
= 0; nX
< nWidth
; nX
++)
1201 if (pReadAcc
->HasPalette())
1202 pQLine2
[nX
] = pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nX
));
1204 pQLine2
[nX
] = pReadAcc
->GetPixelFromData(pScanlineRead
, nX
);
1211 const MapMode
aMap(maPrefMapMode
);
1212 const Size
aSize(maPrefSize
);
1214 *this = std::move(aNewBmp
);
1216 maPrefMapMode
= aMap
;
1222 bool Bitmap::Scale( const double& rScaleX
, const double& rScaleY
, BmpScaleFlag nScaleFlag
)
1224 if(basegfx::fTools::equalZero(rScaleX
) || basegfx::fTools::equalZero(rScaleY
))
1230 if(basegfx::fTools::equal(rScaleX
, 1.0) && basegfx::fTools::equal(rScaleY
, 1.0))
1236 const auto eStartPixelFormat
= getPixelFormat();
1238 if (mxSalBmp
&& mxSalBmp
->ScalingSupported())
1240 // implementation specific scaling
1241 std::shared_ptr
<SalBitmap
> xImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
1242 if (xImpBmp
->Create(*mxSalBmp
) && xImpBmp
->Scale(rScaleX
, rScaleY
, nScaleFlag
))
1244 ImplSetSalBitmap(xImpBmp
);
1245 SAL_INFO( "vcl.opengl", "Ref count: " << mxSalBmp
.use_count() );
1246 maPrefMapMode
= MapMode( MapUnit::MapPixel
);
1247 maPrefSize
= xImpBmp
->GetSize();
1252 BitmapEx
aBmpEx(*this);
1253 bool bRetval(false);
1257 case BmpScaleFlag::Default
:
1258 if (GetSizePixel().Width() < 2 || GetSizePixel().Height() < 2)
1259 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapFastScaleFilter(rScaleX
, rScaleY
));
1261 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapScaleSuperFilter(rScaleX
, rScaleY
));
1264 case BmpScaleFlag::Fast
:
1265 case BmpScaleFlag::NearestNeighbor
:
1266 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapFastScaleFilter(rScaleX
, rScaleY
));
1269 case BmpScaleFlag::Interpolate
:
1270 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapInterpolateScaleFilter(rScaleX
, rScaleY
));
1273 case BmpScaleFlag::BestQuality
:
1274 case BmpScaleFlag::Lanczos
:
1275 bRetval
= BitmapFilter::Filter(aBmpEx
, vcl::BitmapScaleLanczos3Filter(rScaleX
, rScaleY
));
1278 case BmpScaleFlag::BiCubic
:
1279 bRetval
= BitmapFilter::Filter(aBmpEx
, vcl::BitmapScaleBicubicFilter(rScaleX
, rScaleY
));
1282 case BmpScaleFlag::BiLinear
:
1283 bRetval
= BitmapFilter::Filter(aBmpEx
, vcl::BitmapScaleBilinearFilter(rScaleX
, rScaleY
));
1288 *this = aBmpEx
.GetBitmap();
1290 OSL_ENSURE(!bRetval
|| eStartPixelFormat
== getPixelFormat(), "Bitmap::Scale has changed the ColorDepth, this should *not* happen (!)");
1294 bool Bitmap::Scale( const Size
& rNewSize
, BmpScaleFlag nScaleFlag
)
1296 const Size
aSize( GetSizePixel() );
1299 if( aSize
.Width() && aSize
.Height() )
1301 bRet
= Scale( static_cast<double>(rNewSize
.Width()) / aSize
.Width(),
1302 static_cast<double>(rNewSize
.Height()) / aSize
.Height(),
1311 bool Bitmap::HasFastScale()
1313 #if HAVE_FEATURE_SKIA
1314 if( SkiaHelper::isVCLSkiaEnabled() && SkiaHelper::renderMethodToUse() != SkiaHelper::RenderRaster
)
1320 void Bitmap::AdaptBitCount(Bitmap
& rNew
) const
1322 // aNew is the result of some operation; adapt it's BitCount to the original (this)
1323 if (getPixelFormat() == rNew
.getPixelFormat())
1326 switch (getPixelFormat())
1328 case vcl::PixelFormat::N8_BPP
:
1330 if(HasGreyPaletteAny())
1332 rNew
.Convert(BmpConversion::N8BitGreys
);
1336 rNew
.Convert(BmpConversion::N8BitColors
);
1340 case vcl::PixelFormat::N24_BPP
:
1342 rNew
.Convert(BmpConversion::N24Bit
);
1345 case vcl::PixelFormat::N32_BPP
:
1347 rNew
.Convert(BmpConversion::N32Bit
);
1350 case vcl::PixelFormat::INVALID
:
1352 SAL_WARN("vcl", "Can't adapt the pixelformat as it is invalid.");
1358 static void shiftColors(sal_Int32
* pColorArray
, const BitmapScopedReadAccess
& pReadAcc
)
1360 Scanline pScanlineRead
= pReadAcc
->GetScanline(0); // Why always 0?
1361 for (tools::Long n
= 0; n
< pReadAcc
->Width(); ++n
)
1363 const BitmapColor aColor
= pReadAcc
->GetColorFromData(pScanlineRead
, n
);
1364 *pColorArray
++ = static_cast<sal_Int32
>(aColor
.GetBlue()) << 12;
1365 *pColorArray
++ = static_cast<sal_Int32
>(aColor
.GetGreen()) << 12;
1366 *pColorArray
++ = static_cast<sal_Int32
>(aColor
.GetRed()) << 12;
1370 bool Bitmap::Dither()
1372 const Size
aSize( GetSizePixel() );
1373 if( aSize
.Width() == 1 || aSize
.Height() == 1 )
1375 if( ( aSize
.Width() <= 3 ) || ( aSize
.Height() <= 2 ) )
1378 BitmapScopedReadAccess
pReadAcc(*this);
1379 Bitmap
aNewBmp(GetSizePixel(), vcl::PixelFormat::N8_BPP
);
1380 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
1381 if( !pReadAcc
|| !pWriteAcc
)
1384 tools::Long nWidth
= pReadAcc
->Width();
1385 tools::Long nWidth1
= nWidth
- 1;
1386 tools::Long nHeight
= pReadAcc
->Height();
1387 tools::Long nW
= nWidth
* 3;
1388 tools::Long nW2
= nW
- 3;
1389 std::unique_ptr
<sal_Int32
[]> p1(new sal_Int32
[ nW
]);
1390 std::unique_ptr
<sal_Int32
[]> p2(new sal_Int32
[ nW
]);
1391 sal_Int32
* p1T
= p1
.get();
1392 sal_Int32
* p2T
= p2
.get();
1393 shiftColors(p2T
, pReadAcc
);
1394 for( tools::Long nYAcc
= 0; nYAcc
< nHeight
; nYAcc
++ )
1396 std::swap(p1T
, p2T
);
1397 if (nYAcc
< nHeight
- 1)
1398 shiftColors(p2T
, pReadAcc
);
1400 auto CalcError
= [](tools::Long n
)
1402 n
= std::clamp
<tools::Long
>(n
>> 12, 0, 255);
1403 return std::pair(FloydErrMap
[n
], FloydMap
[n
]);
1406 auto CalcErrors
= [&](tools::Long n
)
1407 { return std::tuple_cat(CalcError(p1T
[n
]), CalcError(p1T
[n
+ 1]), CalcError(p1T
[n
+ 2])); };
1409 auto CalcT
= [](sal_Int32
* dst
, const int* src
, int b
, int g
, int r
)
1416 auto Calc1
= [&](int x
, int b
, int g
, int r
) { CalcT(p2T
+ x
+ 3, FloydError1
, b
, g
, r
); };
1417 auto Calc3
= [&](int x
, int b
, int g
, int r
) { CalcT(p2T
+ x
- 3, FloydError3
, b
, g
, r
); };
1418 auto Calc5
= [&](int x
, int b
, int g
, int r
) { CalcT(p2T
+ x
, FloydError5
, b
, g
, r
); };
1419 auto Calc7
= [&](int x
, int b
, int g
, int r
) { CalcT(p1T
+ x
+ 3, FloydError7
, b
, g
, r
); };
1421 Scanline pScanline
= pWriteAcc
->GetScanline(nYAcc
);
1422 // Examine first Pixel separately
1424 auto [nBErr
, nBC
, nGErr
, nGC
, nRErr
, nRC
] = CalcErrors(0);
1425 Calc1(0, nBErr
, nGErr
, nRErr
);
1426 Calc5(0, nBErr
, nGErr
, nRErr
);
1427 Calc7(0, nBErr
, nGErr
, nRErr
);
1428 pWriteAcc
->SetPixelOnData( pScanline
, 0, BitmapColor(static_cast<sal_uInt8
>(nVCLBLut
[ nBC
] + nVCLGLut
[nGC
] + nVCLRLut
[nRC
])) );
1430 // Get middle Pixels using a loop
1431 for ( tools::Long nX
= 3, nXAcc
= 1; nX
< nW2
; nX
+= 3, nXAcc
++ )
1433 auto [nBErr
, nBC
, nGErr
, nGC
, nRErr
, nRC
] = CalcErrors(nX
);
1434 Calc1(nX
, nBErr
, nGErr
, nRErr
);
1435 Calc3(nX
, nBErr
, nGErr
, nRErr
);
1436 Calc5(nX
, nBErr
, nGErr
, nRErr
);
1437 Calc7(nX
, nBErr
, nGErr
, nRErr
);
1438 pWriteAcc
->SetPixelOnData( pScanline
, nXAcc
, BitmapColor(static_cast<sal_uInt8
>(nVCLBLut
[ nBC
] + nVCLGLut
[nGC
] + nVCLRLut
[nRC
])) );
1440 // Treat last Pixel separately
1442 auto [nBErr
, nBC
, nGErr
, nGC
, nRErr
, nRC
] = CalcErrors(nW2
);
1443 Calc3(nW2
, nBErr
, nGErr
, nRErr
);
1444 Calc5(nW2
, nBErr
, nGErr
, nRErr
);
1445 pWriteAcc
->SetPixelOnData( pScanline
, nWidth1
, BitmapColor(static_cast<sal_uInt8
>(nVCLBLut
[ nBC
] + nVCLGLut
[nGC
] + nVCLRLut
[nRC
])) );
1450 const MapMode
aMap( maPrefMapMode
);
1451 const Size
aPrefSize( maPrefSize
);
1452 *this = std::move(aNewBmp
);
1453 maPrefMapMode
= aMap
;
1454 maPrefSize
= aPrefSize
;
1458 bool Bitmap::Adjust( short nLuminancePercent
, short nContrastPercent
,
1459 short nChannelRPercent
, short nChannelGPercent
, short nChannelBPercent
,
1460 double fGamma
, bool bInvert
, bool msoBrightness
)
1462 // nothing to do => return quickly
1463 if( !nLuminancePercent
&& !nContrastPercent
&&
1464 !nChannelRPercent
&& !nChannelGPercent
&& !nChannelBPercent
&&
1465 ( fGamma
== 1.0 ) && !bInvert
)
1470 BitmapScopedWriteAccess
pAcc(*this);
1475 const tools::Long nW
= pAcc
->Width();
1476 const tools::Long nH
= pAcc
->Height();
1477 std::unique_ptr
<sal_uInt8
[]> cMapR(new sal_uInt8
[ 256 ]);
1478 std::unique_ptr
<sal_uInt8
[]> cMapG(new sal_uInt8
[ 256 ]);
1479 std::unique_ptr
<sal_uInt8
[]> cMapB(new sal_uInt8
[ 256 ]);
1480 double fM
, fROff
, fGOff
, fBOff
, fOff
;
1483 if( nContrastPercent
>= 0 )
1484 fM
= 128.0 / ( 128.0 - 1.27 * std::clamp( nContrastPercent
, short(0), short(100) ) );
1486 fM
= ( 128.0 + 1.27 * std::clamp( nContrastPercent
, short(-100), short(0) ) ) / 128.0;
1489 // total offset = luminance offset + contrast offset
1490 fOff
= std::clamp( nLuminancePercent
, short(-100), short(100) ) * 2.55 + 128.0 - fM
* 128.0;
1492 fOff
= std::clamp( nLuminancePercent
, short(-100), short(100) ) * 2.55;
1494 // channel offset = channel offset + total offset
1495 fROff
= nChannelRPercent
* 2.55 + fOff
;
1496 fGOff
= nChannelGPercent
* 2.55 + fOff
;
1497 fBOff
= nChannelBPercent
* 2.55 + fOff
;
1499 // calculate gamma value
1500 fGamma
= ( fGamma
<= 0.0 || fGamma
> 10.0 ) ? 1.0 : ( 1.0 / fGamma
);
1501 const bool bGamma
= ( fGamma
!= 1.0 );
1503 // create mapping table
1504 for( tools::Long nX
= 0; nX
< 256; nX
++ )
1508 cMapR
[nX
] = basegfx::fround
<sal_uInt8
>(nX
* fM
+ fROff
);
1509 cMapG
[nX
] = basegfx::fround
<sal_uInt8
>(nX
* fM
+ fGOff
);
1510 cMapB
[nX
] = basegfx::fround
<sal_uInt8
>(nX
* fM
+ fBOff
);
1514 // LO simply uses (in a somewhat optimized form) "newcolor = (oldcolor-128)*contrast+brightness+128"
1515 // as the formula, i.e. contrast first, brightness afterwards. MSOffice, for whatever weird reason,
1516 // use neither first, but apparently it applies half of brightness before contrast and half afterwards.
1517 cMapR
[nX
] = basegfx::fround
<sal_uInt8
>((nX
+ fROff
/ 2 - 128) * fM
+ 128 + fROff
/ 2);
1518 cMapG
[nX
] = basegfx::fround
<sal_uInt8
>((nX
+ fGOff
/ 2 - 128) * fM
+ 128 + fGOff
/ 2);
1519 cMapB
[nX
] = basegfx::fround
<sal_uInt8
>((nX
+ fBOff
/ 2 - 128) * fM
+ 128 + fBOff
/ 2);
1523 cMapR
[ nX
] = GAMMA( cMapR
[ nX
], fGamma
);
1524 cMapG
[ nX
] = GAMMA( cMapG
[ nX
], fGamma
);
1525 cMapB
[ nX
] = GAMMA( cMapB
[ nX
], fGamma
);
1530 cMapR
[ nX
] = ~cMapR
[ nX
];
1531 cMapG
[ nX
] = ~cMapG
[ nX
];
1532 cMapB
[ nX
] = ~cMapB
[ nX
];
1537 if( pAcc
->HasPalette() )
1539 BitmapColor aNewCol
;
1541 for( sal_uInt16 i
= 0, nCount
= pAcc
->GetPaletteEntryCount(); i
< nCount
; i
++ )
1543 const BitmapColor
& rCol
= pAcc
->GetPaletteColor( i
);
1544 aNewCol
.SetRed( cMapR
[ rCol
.GetRed() ] );
1545 aNewCol
.SetGreen( cMapG
[ rCol
.GetGreen() ] );
1546 aNewCol
.SetBlue( cMapB
[ rCol
.GetBlue() ] );
1547 pAcc
->SetPaletteColor( i
, aNewCol
);
1550 else if( pAcc
->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr
)
1552 for( tools::Long nY
= 0; nY
< nH
; nY
++ )
1554 Scanline pScan
= pAcc
->GetScanline( nY
);
1556 for( tools::Long nX
= 0; nX
< nW
; nX
++ )
1558 *pScan
= cMapB
[ *pScan
]; pScan
++;
1559 *pScan
= cMapG
[ *pScan
]; pScan
++;
1560 *pScan
= cMapR
[ *pScan
]; pScan
++;
1564 else if( pAcc
->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb
)
1566 for( tools::Long nY
= 0; nY
< nH
; nY
++ )
1568 Scanline pScan
= pAcc
->GetScanline( nY
);
1570 for( tools::Long nX
= 0; nX
< nW
; nX
++ )
1572 *pScan
= cMapR
[ *pScan
]; pScan
++;
1573 *pScan
= cMapG
[ *pScan
]; pScan
++;
1574 *pScan
= cMapB
[ *pScan
]; pScan
++;
1580 for( tools::Long nY
= 0; nY
< nH
; nY
++ )
1582 Scanline pScanline
= pAcc
->GetScanline(nY
);
1583 for( tools::Long nX
= 0; nX
< nW
; nX
++ )
1585 aCol
= pAcc
->GetPixelFromData( pScanline
, nX
);
1586 aCol
.SetRed( cMapR
[ aCol
.GetRed() ] );
1587 aCol
.SetGreen( cMapG
[ aCol
.GetGreen() ] );
1588 aCol
.SetBlue( cMapB
[ aCol
.GetBlue() ] );
1589 pAcc
->SetPixelOnData( pScanline
, nX
, aCol
);
1601 inline sal_uInt8
backBlendAlpha(sal_uInt16 alpha
, sal_uInt16 srcCol
, sal_uInt16 startCol
)
1603 const sal_uInt16
nAlpha((alpha
* startCol
) / 255);
1606 return static_cast<sal_uInt8
>(((srcCol
- nAlpha
) * 255) / (255 - nAlpha
));
1613 void Bitmap::RemoveBlendedStartColor(
1614 const Color
& rStartColor
,
1615 const AlphaMask
& rAlphaMask
)
1621 BitmapScopedWriteAccess
pAcc(*this);
1622 const tools::Long
nHeight(pAcc
->Height());
1623 const tools::Long
nWidth(pAcc
->Width());
1626 if(0 == nHeight
|| 0 == nWidth
)
1629 BitmapScopedReadAccess
pAlphaAcc(rAlphaMask
);
1631 // inequal sizes of content and alpha, avoid change (maybe assert?)
1632 if(pAlphaAcc
->Height() != nHeight
|| pAlphaAcc
->Width() != nWidth
)
1635 // prepare local values as sal_uInt16 to avoid multiple conversions
1636 const sal_uInt16
nStartColRed(rStartColor
.GetRed());
1637 const sal_uInt16
nStartColGreen(rStartColor
.GetGreen());
1638 const sal_uInt16
nStartColBlue(rStartColor
.GetBlue());
1640 for (tools::Long y
= 0; y
< nHeight
; ++y
)
1642 for (tools::Long x
= 0; x
< nWidth
; ++x
)
1645 const sal_uInt8
nAlpha8(pAlphaAcc
->GetColor(y
, x
).GetRed());
1647 // not or completely transparent, no adaptation needed
1648 if(0 == nAlpha8
|| 255 == nAlpha8
)
1651 // prepare local value as sal_uInt16 to avoid multiple conversions
1652 const sal_uInt16
nAlpha16(static_cast<sal_uInt16
>(nAlpha8
));
1655 BitmapColor
aColor(pAcc
->GetColor(y
, x
));
1657 // modify/blend back source color
1658 aColor
.SetRed(backBlendAlpha(nAlpha16
, static_cast<sal_uInt16
>(aColor
.GetRed()), nStartColRed
));
1659 aColor
.SetGreen(backBlendAlpha(nAlpha16
, static_cast<sal_uInt16
>(aColor
.GetGreen()), nStartColGreen
));
1660 aColor
.SetBlue(backBlendAlpha(nAlpha16
, static_cast<sal_uInt16
>(aColor
.GetBlue()), nStartColBlue
));
1662 // write result back
1663 pAcc
->SetPixel(y
, x
, aColor
);
1668 const basegfx::SystemDependentDataHolder
* Bitmap::accessSystemDependentDataHolder() const
1672 return mxSalBmp
->accessSystemDependentDataHolder();
1675 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */