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>
26 #include <vcl/bitmap.hxx>
27 #include <vcl/bitmapex.hxx>
28 #include <vcl/outdev.hxx>
31 #include <salinst.hxx>
34 #include <vcl/skia/SkiaHelper.hxx>
36 #include <vcl/BitmapMonochromeFilter.hxx>
38 #include <bitmap/BitmapScaleSuperFilter.hxx>
39 #include <bitmap/BitmapScaleConvolutionFilter.hxx>
40 #include <bitmap/BitmapFastScaleFilter.hxx>
41 #include <bitmap/BitmapInterpolateScaleFilter.hxx>
42 #include <bitmap/BitmapWriteAccess.hxx>
43 #include <bitmap/impoctree.hxx>
44 #include <bitmap/Octree.hxx>
46 #include "impvect.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
> const & pSalBitmap
)
71 : mxSalBmp(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()))
83 BitmapPalette
* pRealPal
= nullptr;
85 if (vcl::isPalettePixelFormat(ePixelFormat
))
89 if (ePixelFormat
== vcl::PixelFormat::N1_BPP
)
91 aPal
.SetEntryCount( 2 );
92 aPal
[ 0 ] = COL_BLACK
;
93 aPal
[ 1 ] = COL_WHITE
;
95 else if (ePixelFormat
== vcl::PixelFormat::N8_BPP
)
97 aPal
.SetEntryCount(1 << sal_uInt16(ePixelFormat
));
98 aPal
[ 0 ] = COL_BLACK
;
100 aPal
[ 2 ] = COL_GREEN
;
101 aPal
[ 3 ] = COL_CYAN
;
103 aPal
[ 5 ] = COL_MAGENTA
;
104 aPal
[ 6 ] = COL_BROWN
;
105 aPal
[ 7 ] = COL_GRAY
;
106 aPal
[ 8 ] = COL_LIGHTGRAY
;
107 aPal
[ 9 ] = COL_LIGHTBLUE
;
108 aPal
[ 10 ] = COL_LIGHTGREEN
;
109 aPal
[ 11 ] = COL_LIGHTCYAN
;
110 aPal
[ 12 ] = COL_LIGHTRED
;
111 aPal
[ 13 ] = COL_LIGHTMAGENTA
;
112 aPal
[ 14 ] = COL_YELLOW
;
113 aPal
[ 15 ] = COL_WHITE
;
115 // Create dither palette
116 if (ePixelFormat
== vcl::PixelFormat::N8_BPP
)
118 sal_uInt16 nActCol
= 16;
120 for( sal_uInt16 nB
= 0; nB
< 256; nB
+= 51 )
121 for( sal_uInt16 nG
= 0; nG
< 256; nG
+= 51 )
122 for( sal_uInt16 nR
= 0; nR
< 256; nR
+= 51 )
123 aPal
[ nActCol
++ ] = BitmapColor( static_cast<sal_uInt8
>(nR
), static_cast<sal_uInt8
>(nG
), static_cast<sal_uInt8
>(nB
) );
125 // Set standard Office colors
126 aPal
[ nActCol
++ ] = BitmapColor( 0, 184, 255 );
131 pRealPal
= const_cast<BitmapPalette
*>(pPal
);
134 mxSalBmp
= ImplGetSVData()->mpDefInst
->CreateSalBitmap();
135 mxSalBmp
->Create(rSizePixel
, ePixelFormat
, pRealPal
? *pRealPal
: aPal
);
142 void savePNG(const OUString
& sWhere
, const Bitmap
& rBmp
)
144 SvFileStream
aStream(sWhere
, StreamMode::WRITE
| StreamMode::TRUNC
);
145 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
146 rFilter
.compressAsPNG(BitmapEx(rBmp
), aStream
);
155 // VCL_DUMP_BMP_PATH should be like C:/bmpDump.png or ~/bmpDump.png
156 static const OUString
sDumpPath(OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
157 // Stepping into the dtor of a bitmap you need, and setting the volatile variable to true in
158 // debugger, would dump the bitmap in question
159 static volatile bool save(false);
160 if (!sDumpPath
.isEmpty() && save
)
163 savePNG(sDumpPath
, *this);
168 const BitmapPalette
& Bitmap::GetGreyPalette( int nEntries
)
170 // Create greyscale palette with 2, 4, 16 or 256 entries
175 static const BitmapPalette aGreyPalette2
= [] {
176 BitmapPalette
aPalette(2);
177 aPalette
[0] = BitmapColor(0, 0, 0);
178 aPalette
[1] = BitmapColor(255, 255, 255);
182 return aGreyPalette2
;
186 static const BitmapPalette aGreyPalette4
= [] {
187 BitmapPalette
aPalette(4);
188 aPalette
[0] = BitmapColor(0, 0, 0);
189 aPalette
[1] = BitmapColor(85, 85, 85);
190 aPalette
[2] = BitmapColor(170, 170, 170);
191 aPalette
[3] = BitmapColor(255, 255, 255);
195 return aGreyPalette4
;
199 static const BitmapPalette aGreyPalette16
= [] {
201 sal_uInt8
const cGreyInc
= 17;
203 BitmapPalette
aPalette(16);
205 for (sal_uInt16 i
= 0; i
< 16; ++i
, cGrey
+= cGreyInc
)
206 aPalette
[i
] = BitmapColor(cGrey
, cGrey
, cGrey
);
211 return aGreyPalette16
;
215 static const BitmapPalette aGreyPalette256
= [] {
216 BitmapPalette
aPalette(256);
218 for (sal_uInt16 i
= 0; i
< 256; ++i
)
219 aPalette
[i
] = BitmapColor(static_cast<sal_uInt8
>(i
), static_cast<sal_uInt8
>(i
),
220 static_cast<sal_uInt8
>(i
));
225 return aGreyPalette256
;
228 OSL_FAIL("Bitmap::GetGreyPalette: invalid entry count (2/4/16/256 allowed)");
229 return GetGreyPalette(2);
232 bool BitmapPalette::IsGreyPaletteAny() const
234 const int nEntryCount
= GetEntryCount();
235 if( !nEntryCount
) // NOTE: an empty palette means 1:1 mapping
237 // See above: only certain entry values will result in a valid call to GetGreyPalette
238 if( nEntryCount
== 2 || nEntryCount
== 4 || nEntryCount
== 16 || nEntryCount
== 256 )
240 const BitmapPalette
& rGreyPalette
= Bitmap::GetGreyPalette( nEntryCount
);
241 if( rGreyPalette
== *this )
246 // TODO: is it worth to compare the entries for the general case?
247 if (nEntryCount
== 2)
249 const BitmapColor
& rCol0(maBitmapColor
[0]);
250 const BitmapColor
& rCol1(maBitmapColor
[1]);
251 bRet
= rCol0
.GetRed() == rCol0
.GetGreen() && rCol0
.GetRed() == rCol0
.GetBlue() &&
252 rCol1
.GetRed() == rCol1
.GetGreen() && rCol1
.GetRed() == rCol1
.GetBlue();
257 bool BitmapPalette::IsGreyPalette8Bit() const
259 const int nEntryCount
= GetEntryCount();
260 if( !nEntryCount
) // NOTE: an empty palette means 1:1 mapping
262 if( nEntryCount
!= 256 )
264 for (sal_uInt16 i
= 0; i
< 256; ++i
)
266 if( maBitmapColor
[i
] != BitmapColor(i
, i
, i
))
272 Bitmap
& Bitmap::operator=( const Bitmap
& rBitmap
)
274 if (this == &rBitmap
)
277 maPrefSize
= rBitmap
.maPrefSize
;
278 maPrefMapMode
= rBitmap
.maPrefMapMode
;
279 mxSalBmp
= rBitmap
.mxSalBmp
;
284 Bitmap
& Bitmap::operator=( Bitmap
&& rBitmap
) noexcept
286 maPrefSize
= std::move(rBitmap
.maPrefSize
);
287 maPrefMapMode
= std::move(rBitmap
.maPrefMapMode
);
288 mxSalBmp
= std::move(rBitmap
.mxSalBmp
);
293 bool Bitmap::operator==( const Bitmap
& rBmp
) const
295 if (rBmp
.mxSalBmp
== mxSalBmp
) // Includes both are nullptr
297 if (!rBmp
.mxSalBmp
|| !mxSalBmp
)
299 if (rBmp
.mxSalBmp
->GetSize() != mxSalBmp
->GetSize() ||
300 rBmp
.mxSalBmp
->GetBitCount() != mxSalBmp
->GetBitCount())
302 BitmapChecksum aChecksum1
, aChecksum2
;
303 rBmp
.mxSalBmp
->GetChecksum(aChecksum1
);
304 mxSalBmp
->GetChecksum(aChecksum2
);
305 // If the bitmaps can't calculate a checksum, best to regard them as different.
306 if (aChecksum1
== 0 || aChecksum2
== 0)
308 return aChecksum1
== aChecksum2
;
311 void Bitmap::SetEmpty()
313 maPrefMapMode
= MapMode();
318 Size
Bitmap::GetSizePixel() const
320 return( mxSalBmp
? mxSalBmp
->GetSize() : Size() );
323 vcl::PixelFormat
Bitmap::getPixelFormat() const
326 return vcl::PixelFormat::INVALID
;
328 sal_uInt16 nBitCount
= mxSalBmp
->GetBitCount();
330 return vcl::PixelFormat::N1_BPP
;
332 return vcl::PixelFormat::N8_BPP
;
334 return vcl::PixelFormat::N24_BPP
;
336 return vcl::PixelFormat::N32_BPP
;
338 return vcl::PixelFormat::INVALID
;
341 bool Bitmap::HasGreyPaletteAny() const
343 bool bRet
= getPixelFormat() == vcl::PixelFormat::N1_BPP
;
345 ScopedInfoAccess
pIAcc(const_cast<Bitmap
&>(*this));
349 bRet
= pIAcc
->HasPalette() && pIAcc
->GetPalette().IsGreyPaletteAny();
355 bool Bitmap::HasGreyPalette8Bit() const
358 ScopedInfoAccess
pIAcc(const_cast<Bitmap
&>(*this));
362 bRet
= pIAcc
->HasPalette() && pIAcc
->GetPalette().IsGreyPalette8Bit();
368 BitmapChecksum
Bitmap::GetChecksum() const
370 BitmapChecksum nRet
= 0;
374 mxSalBmp
->GetChecksum(nRet
);
378 // nRet == 0 => probably, we were not able to acquire
379 // the buffer in SalBitmap::updateChecksum;
380 // so, we need to update the imp bitmap for this bitmap instance
381 // as we do in BitmapInfoAccess::ImplCreate
382 std::shared_ptr
<SalBitmap
> xNewImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
383 if (xNewImpBmp
->Create(*mxSalBmp
, getPixelFormat()))
385 Bitmap
* pThis
= const_cast<Bitmap
*>(this);
386 pThis
->mxSalBmp
= xNewImpBmp
;
387 mxSalBmp
->GetChecksum(nRet
);
395 void Bitmap::ImplMakeUnique()
397 if (mxSalBmp
&& mxSalBmp
.use_count() > 1)
399 std::shared_ptr
<SalBitmap
> xOldImpBmp
= mxSalBmp
;
400 mxSalBmp
= ImplGetSVData()->mpDefInst
->CreateSalBitmap();
401 (void)mxSalBmp
->Create(*xOldImpBmp
);
405 void Bitmap::ReassignWithSize(const Bitmap
& rBitmap
)
407 const Size
aOldSizePix(GetSizePixel());
408 const Size
aNewSizePix(rBitmap
.GetSizePixel());
409 const MapMode
aOldMapMode(maPrefMapMode
);
412 if ((aOldSizePix
!= aNewSizePix
) && aOldSizePix
.Width() && aOldSizePix
.Height())
414 aNewPrefSize
.setWidth(FRound(maPrefSize
.Width() * aNewSizePix
.Width() / aOldSizePix
.Width()));
415 aNewPrefSize
.setHeight(FRound(maPrefSize
.Height() * aNewSizePix
.Height() / aOldSizePix
.Height()));
419 aNewPrefSize
= maPrefSize
;
424 maPrefSize
= aNewPrefSize
;
425 maPrefMapMode
= aOldMapMode
;
428 void Bitmap::ImplSetSalBitmap(const std::shared_ptr
<SalBitmap
>& xImpBmp
)
433 BitmapInfoAccess
* Bitmap::AcquireInfoAccess()
435 std::unique_ptr
<BitmapInfoAccess
> pInfoAccess(new BitmapInfoAccess( *this ));
442 return pInfoAccess
.release();
445 BitmapReadAccess
* Bitmap::AcquireReadAccess()
447 std::unique_ptr
<BitmapReadAccess
> pReadAccess(new BitmapReadAccess( *this ));
454 return pReadAccess
.release();
457 BitmapWriteAccess
* Bitmap::AcquireWriteAccess()
459 std::unique_ptr
<BitmapWriteAccess
> pWriteAccess(new BitmapWriteAccess( *this ));
466 return pWriteAccess
.release();
469 void Bitmap::ReleaseAccess( BitmapInfoAccess
* pBitmapAccess
)
471 delete pBitmapAccess
;
474 bool Bitmap::Crop( const tools::Rectangle
& rRectPixel
)
476 const Size
aSizePix( GetSizePixel() );
477 tools::Rectangle
aRect( rRectPixel
);
480 aRect
.Intersection( tools::Rectangle( Point(), aSizePix
) );
482 if( !aRect
.IsEmpty() && aSizePix
!= aRect
.GetSize())
484 ScopedReadAccess
pReadAcc(*this);
488 const tools::Rectangle
aNewRect( Point(), aRect
.GetSize() );
489 Bitmap
aNewBmp(aNewRect
.GetSize(), getPixelFormat(), &pReadAcc
->GetPalette());
490 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
494 const tools::Long nOldX
= aRect
.Left();
495 const tools::Long nOldY
= aRect
.Top();
496 const tools::Long nNewWidth
= aNewRect
.GetWidth();
497 const tools::Long nNewHeight
= aNewRect
.GetHeight();
499 for( tools::Long nY
= 0, nY2
= nOldY
; nY
< nNewHeight
; nY
++, nY2
++ )
501 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
502 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY2
);
503 for( tools::Long nX
= 0, nX2
= nOldX
; nX
< nNewWidth
; nX
++, nX2
++ )
504 pWriteAcc
->SetPixelOnData( pScanline
, nX
, pReadAcc
->GetPixelFromData( pScanlineRead
, nX2
) );
514 ReassignWithSize( aNewBmp
);
521 bool Bitmap::CopyPixel( const tools::Rectangle
& rRectDst
,
522 const tools::Rectangle
& rRectSrc
, const Bitmap
* pBmpSrc
)
524 const Size
aSizePix( GetSizePixel() );
525 tools::Rectangle
aRectDst( rRectDst
);
528 aRectDst
.Intersection( tools::Rectangle( Point(), aSizePix
) );
530 if( !aRectDst
.IsEmpty() )
532 if( pBmpSrc
&& ( pBmpSrc
->mxSalBmp
!= mxSalBmp
) )
534 Bitmap
* pSrc
= const_cast<Bitmap
*>(pBmpSrc
);
535 const Size
aCopySizePix( pSrc
->GetSizePixel() );
536 tools::Rectangle
aRectSrc( rRectSrc
);
537 const sal_uInt16 nSrcBitCount
= vcl::pixelFormatBitCount(pBmpSrc
->getPixelFormat());
538 const sal_uInt16 nDstBitCount
= vcl::pixelFormatBitCount(getPixelFormat());
540 if( nSrcBitCount
> nDstBitCount
)
544 if (nSrcBitCount
== 24)
545 Convert( BmpConversion::N24Bit
);
546 else if (nSrcBitCount
== 8)
548 Convert( BmpConversion::N8BitColors
);
551 else if (nSrcBitCount
== 4)
558 ScopedReadAccess
pSrcAcc(*pSrc
);
559 BitmapScopedWriteAccess
pDstAcc(*this);
561 if( pSrcAcc
&& pDstAcc
)
563 const int nSrcCount
= pSrcAcc
->GetPaletteEntryCount();
564 const int nDstCount
= 1 << nDstBitCount
;
566 for (int i
= 0; ( i
< nSrcCount
) && ( nNextIndex
< nDstCount
); ++i
)
568 const BitmapColor
& rSrcCol
= pSrcAcc
->GetPaletteColor( static_cast<sal_uInt16
>(i
) );
572 for (int j
= 0; j
< nDstCount
; ++j
)
574 if( rSrcCol
== pDstAcc
->GetPaletteColor( static_cast<sal_uInt16
>(j
) ) )
582 pDstAcc
->SetPaletteColor( static_cast<sal_uInt16
>(nNextIndex
++), rSrcCol
);
588 aRectSrc
.Intersection( tools::Rectangle( Point(), aCopySizePix
) );
590 if( !aRectSrc
.IsEmpty() )
592 ScopedReadAccess
pReadAcc(*pSrc
);
596 BitmapScopedWriteAccess
pWriteAcc(*this);
600 const tools::Long nWidth
= std::min( aRectSrc
.GetWidth(), aRectDst
.GetWidth() );
601 const tools::Long nHeight
= std::min( aRectSrc
.GetHeight(), aRectDst
.GetHeight() );
602 const tools::Long nSrcEndX
= aRectSrc
.Left() + nWidth
;
603 const tools::Long nSrcEndY
= aRectSrc
.Top() + nHeight
;
604 tools::Long nDstY
= aRectDst
.Top();
606 if( pReadAcc
->HasPalette() && pWriteAcc
->HasPalette() )
608 const sal_uInt16 nCount
= pReadAcc
->GetPaletteEntryCount();
609 std::unique_ptr
<sal_uInt8
[]> pMap(new sal_uInt8
[ nCount
]);
611 // Create index map for the color table, as the bitmap should be copied
612 // retaining it's color information relatively well
613 for( sal_uInt16 i
= 0; i
< nCount
; i
++ )
614 pMap
[ i
] = static_cast<sal_uInt8
>(pWriteAcc
->GetBestPaletteIndex( pReadAcc
->GetPaletteColor( i
) ));
616 for( tools::Long nSrcY
= aRectSrc
.Top(); nSrcY
< nSrcEndY
; nSrcY
++, nDstY
++ )
618 Scanline pScanline
= pWriteAcc
->GetScanline(nDstY
);
619 Scanline pScanlineRead
= pReadAcc
->GetScanline(nSrcY
);
620 for( tools::Long nSrcX
= aRectSrc
.Left(), nDstX
= aRectDst
.Left(); nSrcX
< nSrcEndX
; nSrcX
++, nDstX
++ )
621 pWriteAcc
->SetPixelOnData( pScanline
, nDstX
, BitmapColor( pMap
[ pReadAcc
->GetIndexFromData( pScanlineRead
, nSrcX
) ] ));
624 else if( pReadAcc
->HasPalette() )
626 for( tools::Long nSrcY
= aRectSrc
.Top(); nSrcY
< nSrcEndY
; nSrcY
++, nDstY
++ )
628 Scanline pScanline
= pWriteAcc
->GetScanline(nDstY
);
629 Scanline pScanlineRead
= pReadAcc
->GetScanline(nSrcY
);
630 for( tools::Long nSrcX
= aRectSrc
.Left(), nDstX
= aRectDst
.Left(); nSrcX
< nSrcEndX
; nSrcX
++, nDstX
++ )
631 pWriteAcc
->SetPixelOnData( pScanline
, nDstX
, pReadAcc
->GetPaletteColor( pReadAcc
->GetIndexFromData( pScanlineRead
, nSrcX
) ) );
635 for( tools::Long nSrcY
= aRectSrc
.Top(); nSrcY
< nSrcEndY
; nSrcY
++, nDstY
++ )
637 Scanline pScanline
= pWriteAcc
->GetScanline(nDstY
);
638 Scanline pScanlineRead
= pReadAcc
->GetScanline(nSrcY
);
639 for( tools::Long nSrcX
= aRectSrc
.Left(), nDstX
= aRectDst
.Left(); nSrcX
< nSrcEndX
; nSrcX
++, nDstX
++ )
640 pWriteAcc
->SetPixelOnData( pScanline
, nDstX
, pReadAcc
->GetPixelFromData( pScanlineRead
, nSrcX
) );
644 bRet
= ( nWidth
> 0 ) && ( nHeight
> 0 );
653 tools::Rectangle
aRectSrc( rRectSrc
);
655 aRectSrc
.Intersection( tools::Rectangle( Point(), aSizePix
) );
657 if( !aRectSrc
.IsEmpty() && ( aRectSrc
!= aRectDst
) )
659 BitmapScopedWriteAccess
pWriteAcc(*this);
663 const tools::Long nWidth
= std::min( aRectSrc
.GetWidth(), aRectDst
.GetWidth() );
664 const tools::Long nHeight
= std::min( aRectSrc
.GetHeight(), aRectDst
.GetHeight() );
665 const tools::Long nSrcX
= aRectSrc
.Left();
666 const tools::Long nSrcY
= aRectSrc
.Top();
667 const tools::Long nSrcEndX1
= nSrcX
+ nWidth
- 1;
668 const tools::Long nSrcEndY1
= nSrcY
+ nHeight
- 1;
669 const tools::Long nDstX
= aRectDst
.Left();
670 const tools::Long nDstY
= aRectDst
.Top();
671 const tools::Long nDstEndX1
= nDstX
+ nWidth
- 1;
672 const tools::Long nDstEndY1
= nDstY
+ nHeight
- 1;
674 if( ( nDstX
<= nSrcX
) && ( nDstY
<= nSrcY
) )
676 for( tools::Long nY
= nSrcY
, nYN
= nDstY
; nY
<= nSrcEndY1
; nY
++, nYN
++ )
678 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
679 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
680 for( tools::Long nX
= nSrcX
, nXN
= nDstX
; nX
<= nSrcEndX1
; nX
++, nXN
++ )
681 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
684 else if( ( nDstX
<= nSrcX
) && ( nDstY
>= nSrcY
) )
686 for( tools::Long nY
= nSrcEndY1
, nYN
= nDstEndY1
; nY
>= nSrcY
; nY
--, nYN
-- )
688 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
689 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
690 for( tools::Long nX
= nSrcX
, nXN
= nDstX
; nX
<= nSrcEndX1
; nX
++, nXN
++ )
691 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
694 else if( ( nDstX
>= nSrcX
) && ( nDstY
<= nSrcY
) )
696 for( tools::Long nY
= nSrcY
, nYN
= nDstY
; nY
<= nSrcEndY1
; nY
++, nYN
++ )
698 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
699 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
700 for( tools::Long nX
= nSrcEndX1
, nXN
= nDstEndX1
; nX
>= nSrcX
; nX
--, nXN
-- )
701 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
706 for( tools::Long nY
= nSrcEndY1
, nYN
= nDstEndY1
; nY
>= nSrcY
; nY
--, nYN
-- )
708 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
709 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
710 for( tools::Long nX
= nSrcEndX1
, nXN
= nDstEndX1
; nX
>= nSrcX
; nX
--, nXN
-- )
711 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
725 bool Bitmap::CopyPixel_AlphaOptimized( const tools::Rectangle
& rRectDst
, const tools::Rectangle
& rRectSrc
,
726 const Bitmap
* pBmpSrc
)
728 // Note: this code is copied from Bitmap::CopyPixel but avoids any palette lookups
729 // This optimization is possible because the palettes of AlphaMasks are always identical (8bit GreyPalette, see ctor)
730 const Size
aSizePix( GetSizePixel() );
731 tools::Rectangle
aRectDst( rRectDst
);
734 aRectDst
.Intersection( tools::Rectangle( Point(), aSizePix
) );
736 if( !aRectDst
.IsEmpty() )
738 if( pBmpSrc
&& ( pBmpSrc
->mxSalBmp
!= mxSalBmp
) )
740 Bitmap
* pSrc
= const_cast<Bitmap
*>(pBmpSrc
);
741 const Size
aCopySizePix( pSrc
->GetSizePixel() );
742 tools::Rectangle
aRectSrc( rRectSrc
);
744 aRectSrc
.Intersection( tools::Rectangle( Point(), aCopySizePix
) );
746 if( !aRectSrc
.IsEmpty() )
748 ScopedReadAccess
pReadAcc(*pSrc
);
752 BitmapScopedWriteAccess
pWriteAcc(*this);
756 const tools::Long nWidth
= std::min( aRectSrc
.GetWidth(), aRectDst
.GetWidth() );
757 const tools::Long nHeight
= std::min( aRectSrc
.GetHeight(), aRectDst
.GetHeight() );
758 const tools::Long nSrcEndX
= aRectSrc
.Left() + nWidth
;
759 const tools::Long nSrcEndY
= aRectSrc
.Top() + nHeight
;
760 tools::Long nDstY
= aRectDst
.Top();
762 for( tools::Long nSrcY
= aRectSrc
.Top(); nSrcY
< nSrcEndY
; nSrcY
++, nDstY
++)
764 Scanline pScanline
= pWriteAcc
->GetScanline(nDstY
);
765 Scanline pScanlineRead
= pReadAcc
->GetScanline(nSrcY
);
766 for( tools::Long nSrcX
= aRectSrc
.Left(), nDstX
= aRectDst
.Left(); nSrcX
< nSrcEndX
; nSrcX
++, nDstX
++ )
767 pWriteAcc
->SetPixelOnData( pScanline
, nDstX
, pReadAcc
->GetPixelFromData( pScanlineRead
, nSrcX
) );
771 bRet
= ( nWidth
> 0 ) && ( nHeight
> 0 );
780 tools::Rectangle
aRectSrc( rRectSrc
);
782 aRectSrc
.Intersection( tools::Rectangle( Point(), aSizePix
) );
784 if( !aRectSrc
.IsEmpty() && ( aRectSrc
!= aRectDst
) )
786 BitmapScopedWriteAccess
pWriteAcc(*this);
790 const tools::Long nWidth
= std::min( aRectSrc
.GetWidth(), aRectDst
.GetWidth() );
791 const tools::Long nHeight
= std::min( aRectSrc
.GetHeight(), aRectDst
.GetHeight() );
792 const tools::Long nSrcX
= aRectSrc
.Left();
793 const tools::Long nSrcY
= aRectSrc
.Top();
794 const tools::Long nSrcEndX1
= nSrcX
+ nWidth
- 1;
795 const tools::Long nSrcEndY1
= nSrcY
+ nHeight
- 1;
796 const tools::Long nDstX
= aRectDst
.Left();
797 const tools::Long nDstY
= aRectDst
.Top();
798 const tools::Long nDstEndX1
= nDstX
+ nWidth
- 1;
799 const tools::Long nDstEndY1
= nDstY
+ nHeight
- 1;
801 if( ( nDstX
<= nSrcX
) && ( nDstY
<= nSrcY
) )
803 for( tools::Long nY
= nSrcY
, nYN
= nDstY
; nY
<= nSrcEndY1
; nY
++, nYN
++ )
805 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
806 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
807 for( tools::Long nX
= nSrcX
, nXN
= nDstX
; nX
<= nSrcEndX1
; nX
++, nXN
++ )
808 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
811 else if( ( nDstX
<= nSrcX
) && ( nDstY
>= nSrcY
) )
813 for( tools::Long nY
= nSrcEndY1
, nYN
= nDstEndY1
; nY
>= nSrcY
; nY
--, nYN
-- )
815 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
816 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
817 for( tools::Long nX
= nSrcX
, nXN
= nDstX
; nX
<= nSrcEndX1
; nX
++, nXN
++ )
818 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
821 else if( ( nDstX
>= nSrcX
) && ( nDstY
<= nSrcY
) )
823 for( tools::Long nY
= nSrcY
, nYN
= nDstY
; nY
<= nSrcEndY1
; nY
++, nYN
++ )
825 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
826 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
827 for( tools::Long nX
= nSrcEndX1
, nXN
= nDstEndX1
; nX
>= nSrcX
; nX
--, nXN
-- )
828 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
833 for( tools::Long nY
= nSrcEndY1
, nYN
= nDstEndY1
; nY
>= nSrcY
; nY
--, nYN
-- )
835 Scanline pScanline
= pWriteAcc
->GetScanline(nYN
);
836 Scanline pScanlineSrc
= pWriteAcc
->GetScanline(nY
);
837 for( tools::Long nX
= nSrcEndX1
, nXN
= nDstEndX1
; nX
>= nSrcX
; nX
--, nXN
-- )
838 pWriteAcc
->SetPixelOnData( pScanline
, nXN
, pWriteAcc
->GetPixelFromData( pScanlineSrc
, nX
) );
853 bool Bitmap::Expand( sal_Int32 nDX
, sal_Int32 nDY
, const Color
* pInitColor
)
859 const Size
aSizePixel( GetSizePixel() );
860 const tools::Long nWidth
= aSizePixel
.Width();
861 const tools::Long nHeight
= aSizePixel
.Height();
862 const Size
aNewSize( nWidth
+ nDX
, nHeight
+ nDY
);
863 ScopedReadAccess
pReadAcc(*this);
867 BitmapPalette
aBmpPal( pReadAcc
->GetPalette() );
868 Bitmap
aNewBmp(aNewSize
, getPixelFormat(), &aBmpPal
);
869 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
874 const tools::Long nNewX
= nWidth
;
875 const tools::Long nNewY
= nHeight
;
876 const tools::Long nNewWidth
= pWriteAcc
->Width();
877 const tools::Long nNewHeight
= pWriteAcc
->Height();
882 aColor
= pWriteAcc
->GetBestMatchingColor( *pInitColor
);
884 for( nY
= 0; nY
< nHeight
; nY
++ )
886 pWriteAcc
->CopyScanline( nY
, *pReadAcc
);
888 if( pInitColor
&& nDX
)
890 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
891 for( nX
= nNewX
; nX
< nNewWidth
; nX
++ )
892 pWriteAcc
->SetPixelOnData( pScanline
, nX
, aColor
);
896 if( pInitColor
&& nDY
)
897 for( nY
= nNewY
; nY
< nNewHeight
; nY
++ )
899 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
900 for( nX
= 0; nX
< nNewWidth
; nX
++ )
901 pWriteAcc
->SetPixelOnData( pScanline
, nX
, aColor
);
911 ReassignWithSize(aNewBmp
);
918 Bitmap
Bitmap::CreateDisplayBitmap( OutputDevice
* pDisplay
) const
920 Bitmap
aDispBmp( *this );
922 SalGraphics
* pDispGraphics
= pDisplay
->GetGraphics();
924 if( mxSalBmp
&& pDispGraphics
)
926 std::shared_ptr
<SalBitmap
> xImpDispBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
927 if (xImpDispBmp
->Create(*mxSalBmp
, pDispGraphics
))
928 aDispBmp
.ImplSetSalBitmap(xImpDispBmp
);
934 bool Bitmap::GetSystemData( BitmapSystemData
& rData
) const
936 return mxSalBmp
&& mxSalBmp
->GetSystemData(rData
);
940 bool Bitmap::Convert( BmpConversion eConversion
)
942 // try to convert in backend
945 // avoid large chunk of obsolete and hopefully rarely used conversions.
946 if (eConversion
== BmpConversion::N8BitNoConversion
)
948 if (mxSalBmp
->GetBitCount() == 8 && HasGreyPalette8Bit())
950 std::shared_ptr
<SalBitmap
> xImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
951 // frequently used conversion for creating alpha masks
952 if (xImpBmp
->Create(*mxSalBmp
) && xImpBmp
->InterpretAs8Bit())
954 ImplSetSalBitmap(xImpBmp
);
955 SAL_INFO( "vcl.opengl", "Ref count: " << mxSalBmp
.use_count() );
959 if (eConversion
== BmpConversion::N8BitGreys
)
961 std::shared_ptr
<SalBitmap
> xImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
962 if (xImpBmp
->Create(*mxSalBmp
) && xImpBmp
->ConvertToGreyscale())
964 ImplSetSalBitmap(xImpBmp
);
965 SAL_INFO( "vcl.opengl", "Ref count: " << mxSalBmp
.use_count() );
971 const sal_uInt16 nBitCount
= vcl::pixelFormatBitCount(getPixelFormat());
974 switch( eConversion
)
976 case BmpConversion::N1BitThreshold
:
978 BitmapEx
aBmpEx(*this);
979 bRet
= BitmapFilter::Filter(aBmpEx
, BitmapMonochromeFilter(128));
980 *this = aBmpEx
.GetBitmap();
984 case BmpConversion::N8BitGreys
:
985 case BmpConversion::N8BitNoConversion
:
986 bRet
= ImplMakeGreyscales();
989 case BmpConversion::N8BitColors
:
992 bRet
= ImplConvertUp(vcl::PixelFormat::N8_BPP
);
993 else if( nBitCount
> 8 )
994 bRet
= ImplConvertDown8BPP();
1000 case BmpConversion::N8BitTrans
:
1002 Color
aTrans( BMP_COL_TRANS
);
1005 bRet
= ImplConvertUp(vcl::PixelFormat::N8_BPP
, &aTrans
);
1007 bRet
= ImplConvertDown8BPP(&aTrans
);
1011 case BmpConversion::N24Bit
:
1013 if( nBitCount
< 24 )
1014 bRet
= ImplConvertUp(vcl::PixelFormat::N24_BPP
);
1020 case BmpConversion::N32Bit
:
1022 if( nBitCount
< 32 )
1023 bRet
= ImplConvertUp(vcl::PixelFormat::N32_BPP
);
1030 OSL_FAIL( "Bitmap::Convert(): Unsupported conversion" );
1037 bool Bitmap::ImplMakeGreyscales()
1039 ScopedReadAccess
pReadAcc(*this);
1044 const BitmapPalette
& rPal
= GetGreyPalette(256);
1045 sal_uLong nShift
= 0;
1046 bool bPalDiffers
= !pReadAcc
->HasPalette() || ( rPal
.GetEntryCount() != pReadAcc
->GetPaletteEntryCount() );
1049 bPalDiffers
= ( rPal
!= pReadAcc
->GetPalette() );
1053 const auto ePixelFormat
= vcl::PixelFormat::N8_BPP
;
1054 Bitmap
aNewBmp(GetSizePixel(), ePixelFormat
, &rPal
);
1055 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
1059 const tools::Long nWidth
= pWriteAcc
->Width();
1060 const tools::Long nHeight
= pWriteAcc
->Height();
1062 if( pReadAcc
->HasPalette() )
1064 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
1066 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1067 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1068 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1070 const sal_uInt8 cIndex
= pReadAcc
->GetIndexFromData( pScanlineRead
, nX
);
1071 pWriteAcc
->SetPixelOnData( pScanline
, nX
,
1072 BitmapColor(pReadAcc
->GetPaletteColor( cIndex
).GetLuminance() >> nShift
) );
1076 else if( pReadAcc
->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr
&&
1077 pWriteAcc
->GetScanlineFormat() == ScanlineFormat::N8BitPal
)
1081 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
1083 Scanline pReadScan
= pReadAcc
->GetScanline( nY
);
1084 Scanline pWriteScan
= pWriteAcc
->GetScanline( nY
);
1086 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1088 const sal_uLong nB
= *pReadScan
++;
1089 const sal_uLong nG
= *pReadScan
++;
1090 const sal_uLong nR
= *pReadScan
++;
1092 *pWriteScan
++ = static_cast<sal_uInt8
>( ( nB
* 28UL + nG
* 151UL + nR
* 77UL ) >> nShift
);
1096 else if( pReadAcc
->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb
&&
1097 pWriteAcc
->GetScanlineFormat() == ScanlineFormat::N8BitPal
)
1101 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
1103 Scanline pReadScan
= pReadAcc
->GetScanline( nY
);
1104 Scanline pWriteScan
= pWriteAcc
->GetScanline( nY
);
1106 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1108 const sal_uLong nR
= *pReadScan
++;
1109 const sal_uLong nG
= *pReadScan
++;
1110 const sal_uLong nB
= *pReadScan
++;
1112 *pWriteScan
++ = static_cast<sal_uInt8
>( ( nB
* 28UL + nG
* 151UL + nR
* 77UL ) >> nShift
);
1118 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
1120 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1121 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1122 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
1123 pWriteAcc
->SetPixelOnData( pScanline
, nX
, BitmapColor(pReadAcc
->GetPixelFromData( pScanlineRead
, nX
).GetLuminance() >> nShift
) );
1135 const MapMode
aMap( maPrefMapMode
);
1136 const Size
aSize( maPrefSize
);
1140 maPrefMapMode
= aMap
;
1154 bool Bitmap::ImplConvertUp(vcl::PixelFormat ePixelFormat
, Color
const * pExtColor
)
1156 SAL_WARN_IF(ePixelFormat
<= getPixelFormat(), "vcl", "New pixel format must be greater!" );
1158 Bitmap::ScopedReadAccess
pReadAcc(*this);
1163 BitmapPalette aPalette
;
1164 Bitmap
aNewBmp(GetSizePixel(), ePixelFormat
, pReadAcc
->HasPalette() ? &pReadAcc
->GetPalette() : &aPalette
);
1165 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
1169 const tools::Long nWidth
= pWriteAcc
->Width();
1170 const tools::Long nHeight
= pWriteAcc
->Height();
1172 if (pWriteAcc
->HasPalette())
1174 const BitmapPalette
& rOldPalette
= pReadAcc
->GetPalette();
1175 const sal_uInt16 nOldCount
= rOldPalette
.GetEntryCount();
1176 assert(nOldCount
<= (1 << vcl::pixelFormatBitCount(getPixelFormat())));
1178 aPalette
.SetEntryCount(1 << vcl::pixelFormatBitCount(ePixelFormat
));
1180 for (sal_uInt16 i
= 0; i
< nOldCount
; i
++)
1181 aPalette
[i
] = rOldPalette
[i
];
1184 aPalette
[aPalette
.GetEntryCount() - 1] = *pExtColor
;
1186 pWriteAcc
->SetPalette(aPalette
);
1188 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
1190 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1191 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1192 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1194 pWriteAcc
->SetPixelOnData(pScanline
, nX
, pReadAcc
->GetPixelFromData(pScanlineRead
, nX
));
1200 if (pReadAcc
->HasPalette())
1202 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
1204 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1205 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1206 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1208 pWriteAcc
->SetPixelOnData(pScanline
, nX
, pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nX
)));
1214 for (tools::Long nY
= 0; nY
< nHeight
; nY
++)
1216 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1217 Scanline pScanlineRead
= pReadAcc
->GetScanline(nY
);
1218 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1220 pWriteAcc
->SetPixelOnData(pScanline
, nX
, pReadAcc
->GetPixelFromData(pScanlineRead
, nX
));
1230 const MapMode
aMap(maPrefMapMode
);
1231 const Size
aSize(maPrefSize
);
1235 maPrefMapMode
= aMap
;
1243 bool Bitmap::ImplConvertDown8BPP(Color
const * pExtColor
)
1245 SAL_WARN_IF(vcl::PixelFormat::N8_BPP
> getPixelFormat(), "vcl", "New pixelformat must be lower ( or equal when pExtColor is set )!");
1247 Bitmap::ScopedReadAccess
pReadAcc(*this);
1252 BitmapPalette aPalette
;
1253 Bitmap
aNewBmp(GetSizePixel(), vcl::PixelFormat::N8_BPP
, &aPalette
);
1254 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
1258 sal_Int16 nNewBitCount
= sal_Int16(vcl::PixelFormat::N8_BPP
);
1259 const sal_uInt16 nCount
= 1 << nNewBitCount
;
1260 const tools::Long nWidth
= pWriteAcc
->Width();
1261 const tools::Long nWidth1
= nWidth
- 1;
1262 const tools::Long nHeight
= pWriteAcc
->Height();
1263 Octree
aOctree(*pReadAcc
, pExtColor
? (nCount
- 1) : nCount
);
1264 aPalette
= aOctree
.GetPalette();
1265 InverseColorMap
aColorMap(aPalette
);
1267 ImpErrorQuad aErrQuad
;
1268 std::vector
<ImpErrorQuad
> aErrQuad1(nWidth
);
1269 std::vector
<ImpErrorQuad
> aErrQuad2(nWidth
);
1270 ImpErrorQuad
* pQLine1
= aErrQuad1
.data();
1271 ImpErrorQuad
* pQLine2
= nullptr;
1272 tools::Long nYTmp
= 0;
1278 aPalette
.SetEntryCount(aPalette
.GetEntryCount() + 1);
1279 aPalette
[aPalette
.GetEntryCount() - 1] = *pExtColor
;
1282 // set Black/White always, if we have enough space
1283 if (aPalette
.GetEntryCount() < (nCount
- 1))
1285 aPalette
.SetEntryCount(aPalette
.GetEntryCount() + 2);
1286 aPalette
[aPalette
.GetEntryCount() - 2] = COL_BLACK
;
1287 aPalette
[aPalette
.GetEntryCount() - 1] = COL_WHITE
;
1290 pWriteAcc
->SetPalette(aPalette
);
1292 for (tools::Long nY
= 0; nY
< std::min(nHeight
, tools::Long(2)); nY
++, nYTmp
++)
1294 pQLine2
= !nY
? aErrQuad1
.data() : aErrQuad2
.data();
1295 Scanline pScanlineRead
= pReadAcc
->GetScanline(nYTmp
);
1296 for (tools::Long nX
= 0; nX
< nWidth
; nX
++)
1298 if (pReadAcc
->HasPalette())
1299 pQLine2
[nX
] = pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nX
));
1301 pQLine2
[nX
] = pReadAcc
->GetPixelFromData(pScanlineRead
, nX
);
1305 assert(pQLine2
|| nHeight
== 0);
1307 for (tools::Long nY
= 0; nY
< nHeight
; nY
++, nYTmp
++)
1309 // first pixel in the line
1310 cIndex
= static_cast<sal_uInt8
>(aColorMap
.GetBestPaletteIndex(pQLine1
[0].ImplGetColor()));
1311 Scanline pScanline
= pWriteAcc
->GetScanline(nY
);
1312 pWriteAcc
->SetPixelOnData(pScanline
, 0, BitmapColor(cIndex
));
1315 for (nX
= 1; nX
< nWidth1
; nX
++)
1317 aColor
= pQLine1
[nX
].ImplGetColor();
1318 cIndex
= static_cast<sal_uInt8
>(aColorMap
.GetBestPaletteIndex(aColor
));
1319 aErrQuad
= (ImpErrorQuad(aColor
) -= pWriteAcc
->GetPaletteColor(cIndex
));
1320 pQLine1
[++nX
].ImplAddColorError7(aErrQuad
);
1321 pQLine2
[nX
--].ImplAddColorError1(aErrQuad
);
1322 pQLine2
[nX
--].ImplAddColorError5(aErrQuad
);
1323 pQLine2
[nX
++].ImplAddColorError3(aErrQuad
);
1324 pWriteAcc
->SetPixelOnData(pScanline
, nX
, BitmapColor(cIndex
));
1330 cIndex
= static_cast<sal_uInt8
>(aColorMap
.GetBestPaletteIndex(pQLine1
[nWidth1
].ImplGetColor()));
1331 pWriteAcc
->SetPixelOnData(pScanline
, nX
, BitmapColor(cIndex
));
1334 // Refill/copy row buffer
1337 pQLine2
= bQ1
? aErrQuad2
.data() : aErrQuad1
.data();
1339 if (nYTmp
< nHeight
)
1341 Scanline pScanlineRead
= pReadAcc
->GetScanline(nYTmp
);
1342 for (nX
= 0; nX
< nWidth
; nX
++)
1344 if (pReadAcc
->HasPalette())
1345 pQLine2
[nX
] = pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nX
));
1347 pQLine2
[nX
] = pReadAcc
->GetPixelFromData(pScanlineRead
, nX
);
1358 const MapMode
aMap(maPrefMapMode
);
1359 const Size
aSize(maPrefSize
);
1363 maPrefMapMode
= aMap
;
1371 bool Bitmap::Scale( const double& rScaleX
, const double& rScaleY
, BmpScaleFlag nScaleFlag
)
1373 if(basegfx::fTools::equalZero(rScaleX
) || basegfx::fTools::equalZero(rScaleY
))
1379 if(basegfx::fTools::equal(rScaleX
, 1.0) && basegfx::fTools::equal(rScaleY
, 1.0))
1385 const auto eStartPixelFormat
= getPixelFormat();
1387 if (mxSalBmp
&& mxSalBmp
->ScalingSupported())
1389 // implementation specific scaling
1390 std::shared_ptr
<SalBitmap
> xImpBmp(ImplGetSVData()->mpDefInst
->CreateSalBitmap());
1391 if (xImpBmp
->Create(*mxSalBmp
) && xImpBmp
->Scale(rScaleX
, rScaleY
, nScaleFlag
))
1393 ImplSetSalBitmap(xImpBmp
);
1394 SAL_INFO( "vcl.opengl", "Ref count: " << mxSalBmp
.use_count() );
1395 maPrefMapMode
= MapMode( MapUnit::MapPixel
);
1396 maPrefSize
= xImpBmp
->GetSize();
1403 // If we start with a 1 bit image, then after scaling it in any mode except
1404 // BmpScaleFlag::Fast we have a 24bit image which is perfectly correct, but we
1405 // are going to down-shift it to mono again and Bitmap::MakeMonochrome just
1406 // has "Bitmap aNewBmp( GetSizePixel(), 1 );" to create a 1 bit bitmap which
1407 // will default to black/white and the colors mapped to which ever is closer
1410 // So the easiest thing to do to retain the colors of 1 bit bitmaps is to
1411 // just use the fast scale rather than attempting to count unique colors in
1412 // the other converters and pass all the info down through
1413 // Bitmap::MakeMonochrome
1414 if (eStartPixelFormat
== vcl::PixelFormat::N1_BPP
)
1415 nScaleFlag
= BmpScaleFlag::Fast
;
1417 BitmapEx
aBmpEx(*this);
1418 bool bRetval(false);
1422 case BmpScaleFlag::Default
:
1423 if (GetSizePixel().Width() < 2 || GetSizePixel().Height() < 2)
1424 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapFastScaleFilter(rScaleX
, rScaleY
));
1426 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapScaleSuperFilter(rScaleX
, rScaleY
));
1429 case BmpScaleFlag::Fast
:
1430 case BmpScaleFlag::NearestNeighbor
:
1431 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapFastScaleFilter(rScaleX
, rScaleY
));
1434 case BmpScaleFlag::Interpolate
:
1435 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapInterpolateScaleFilter(rScaleX
, rScaleY
));
1438 case BmpScaleFlag::Super
:
1439 bRetval
= BitmapFilter::Filter(aBmpEx
, BitmapScaleSuperFilter(rScaleX
, rScaleY
));
1441 case BmpScaleFlag::BestQuality
:
1442 case BmpScaleFlag::Lanczos
:
1443 bRetval
= BitmapFilter::Filter(aBmpEx
, vcl::BitmapScaleLanczos3Filter(rScaleX
, rScaleY
));
1446 case BmpScaleFlag::BiCubic
:
1447 bRetval
= BitmapFilter::Filter(aBmpEx
, vcl::BitmapScaleBicubicFilter(rScaleX
, rScaleY
));
1450 case BmpScaleFlag::BiLinear
:
1451 bRetval
= BitmapFilter::Filter(aBmpEx
, vcl::BitmapScaleBilinearFilter(rScaleX
, rScaleY
));
1456 *this = aBmpEx
.GetBitmap();
1458 OSL_ENSURE(!bRetval
|| eStartPixelFormat
== getPixelFormat(), "Bitmap::Scale has changed the ColorDepth, this should *not* happen (!)");
1462 bool Bitmap::Scale( const Size
& rNewSize
, BmpScaleFlag nScaleFlag
)
1464 const Size
aSize( GetSizePixel() );
1467 if( aSize
.Width() && aSize
.Height() )
1469 bRet
= Scale( static_cast<double>(rNewSize
.Width()) / aSize
.Width(),
1470 static_cast<double>(rNewSize
.Height()) / aSize
.Height(),
1479 bool Bitmap::HasFastScale()
1481 #if HAVE_FEATURE_SKIA
1482 if( SkiaHelper::isVCLSkiaEnabled() && SkiaHelper::renderMethodToUse() != SkiaHelper::RenderRaster
)
1488 void Bitmap::AdaptBitCount(Bitmap
& rNew
) const
1490 // aNew is the result of some operation; adapt it's BitCount to the original (this)
1491 if (getPixelFormat() == rNew
.getPixelFormat())
1494 switch (getPixelFormat())
1496 case vcl::PixelFormat::N1_BPP
:
1498 rNew
.Convert(BmpConversion::N1BitThreshold
);
1501 case vcl::PixelFormat::N8_BPP
:
1503 if(HasGreyPaletteAny())
1505 rNew
.Convert(BmpConversion::N8BitGreys
);
1509 rNew
.Convert(BmpConversion::N8BitColors
);
1513 case vcl::PixelFormat::N24_BPP
:
1515 rNew
.Convert(BmpConversion::N24Bit
);
1518 case vcl::PixelFormat::N32_BPP
:
1520 rNew
.Convert(BmpConversion::N32Bit
);
1523 case vcl::PixelFormat::INVALID
:
1525 SAL_WARN("vcl", "Can't adapt the pixelformat as it is invalid.");
1531 static tools::Long
* shiftColor(tools::Long
* pColorArray
, BitmapColor
const& rColor
)
1533 *pColorArray
++ = static_cast<tools::Long
>(rColor
.GetBlue()) << 12;
1534 *pColorArray
++ = static_cast<tools::Long
>(rColor
.GetGreen()) << 12;
1535 *pColorArray
++ = static_cast<tools::Long
>(rColor
.GetRed()) << 12;
1538 static BitmapColor
getColor(BitmapReadAccess
*pReadAcc
, tools::Long nZ
)
1540 Scanline pScanlineRead
= pReadAcc
->GetScanline(0);
1541 if (pReadAcc
->HasPalette())
1542 return pReadAcc
->GetPaletteColor(pReadAcc
->GetIndexFromData(pScanlineRead
, nZ
));
1544 return pReadAcc
->GetPixelFromData(pScanlineRead
, nZ
);
1547 bool Bitmap::Dither()
1549 const Size
aSize( GetSizePixel() );
1550 if( aSize
.Width() == 1 || aSize
.Height() == 1 )
1552 if( ( aSize
.Width() > 3 ) && ( aSize
.Height() > 2 ) )
1554 ScopedReadAccess
pReadAcc(*this);
1555 Bitmap
aNewBmp(GetSizePixel(), vcl::PixelFormat::N8_BPP
);
1556 BitmapScopedWriteAccess
pWriteAcc(aNewBmp
);
1557 if( pReadAcc
&& pWriteAcc
)
1560 tools::Long nWidth
= pReadAcc
->Width();
1561 tools::Long nWidth1
= nWidth
- 1;
1562 tools::Long nHeight
= pReadAcc
->Height();
1563 tools::Long nW
= nWidth
* 3;
1564 tools::Long nW2
= nW
- 3;
1565 std::unique_ptr
<tools::Long
[]> p1(new tools::Long
[ nW
]);
1566 std::unique_ptr
<tools::Long
[]> p2(new tools::Long
[ nW
]);
1567 tools::Long
* p1T
= p1
.get();
1568 tools::Long
* p2T
= p2
.get();
1571 for (tools::Long nZ
= 0; nZ
< nWidth
; nZ
++)
1573 pTmp
= shiftColor(pTmp
, getColor(pReadAcc
.get(), nZ
));
1575 tools::Long nRErr
, nGErr
, nBErr
;
1576 tools::Long nRC
, nGC
, nBC
;
1577 for( tools::Long nY
= 1, nYAcc
= 0; nY
<= nHeight
; nY
++, nYAcc
++ )
1584 for (tools::Long nZ
= 0; nZ
< nWidth
; nZ
++)
1586 pTmp
= shiftColor(pTmp
, getColor(pReadAcc
.get(), nZ
));
1589 // Examine first Pixel separately
1596 Scanline pScanline
= pWriteAcc
->GetScanline(nYAcc
);
1597 pWriteAcc
->SetPixelOnData( pScanline
, 0, BitmapColor(static_cast<sal_uInt8
>(nVCLBLut
[ nBC
] + nVCLGLut
[nGC
] + nVCLRLut
[nRC
])) );
1598 // Get middle Pixels using a loop
1600 for ( nX
= 3, nXAcc
= 1; nX
< nW2
; nXAcc
++ )
1607 pWriteAcc
->SetPixelOnData( pScanline
, nXAcc
, BitmapColor(static_cast<sal_uInt8
>(nVCLBLut
[ nBC
] + nVCLGLut
[nGC
] + nVCLRLut
[nRC
])) );
1609 // Treat last Pixel separately
1614 pWriteAcc
->SetPixelOnData( pScanline
, nWidth1
, BitmapColor(static_cast<sal_uInt8
>(nVCLBLut
[ nBC
] + nVCLGLut
[nGC
] + nVCLRLut
[nRC
])) );
1618 const MapMode
aMap( maPrefMapMode
);
1619 const Size
aPrefSize( maPrefSize
);
1621 maPrefMapMode
= aMap
;
1622 maPrefSize
= aPrefSize
;
1631 void Bitmap::Vectorize( GDIMetaFile
& rMtf
, sal_uInt8 cReduce
, const Link
<tools::Long
,void>* pProgress
)
1633 ImplVectorizer::ImplVectorize( *this, rMtf
, cReduce
, pProgress
);
1636 bool Bitmap::Adjust( short nLuminancePercent
, short nContrastPercent
,
1637 short nChannelRPercent
, short nChannelGPercent
, short nChannelBPercent
,
1638 double fGamma
, bool bInvert
, bool msoBrightness
)
1642 // nothing to do => return quickly
1643 if( !nLuminancePercent
&& !nContrastPercent
&&
1644 !nChannelRPercent
&& !nChannelGPercent
&& !nChannelBPercent
&&
1645 ( fGamma
== 1.0 ) && !bInvert
)
1651 BitmapScopedWriteAccess
pAcc(*this);
1656 const tools::Long nW
= pAcc
->Width();
1657 const tools::Long nH
= pAcc
->Height();
1658 std::unique_ptr
<sal_uInt8
[]> cMapR(new sal_uInt8
[ 256 ]);
1659 std::unique_ptr
<sal_uInt8
[]> cMapG(new sal_uInt8
[ 256 ]);
1660 std::unique_ptr
<sal_uInt8
[]> cMapB(new sal_uInt8
[ 256 ]);
1661 double fM
, fROff
, fGOff
, fBOff
, fOff
;
1664 if( nContrastPercent
>= 0 )
1665 fM
= 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent
, 0, 100 ) );
1667 fM
= ( 128.0 + 1.27 * MinMax( nContrastPercent
, -100, 0 ) ) / 128.0;
1670 // total offset = luminance offset + contrast offset
1671 fOff
= MinMax( nLuminancePercent
, -100, 100 ) * 2.55 + 128.0 - fM
* 128.0;
1673 fOff
= MinMax( nLuminancePercent
, -100, 100 ) * 2.55;
1675 // channel offset = channel offset + total offset
1676 fROff
= nChannelRPercent
* 2.55 + fOff
;
1677 fGOff
= nChannelGPercent
* 2.55 + fOff
;
1678 fBOff
= nChannelBPercent
* 2.55 + fOff
;
1680 // calculate gamma value
1681 fGamma
= ( fGamma
<= 0.0 || fGamma
> 10.0 ) ? 1.0 : ( 1.0 / fGamma
);
1682 const bool bGamma
= ( fGamma
!= 1.0 );
1684 // create mapping table
1685 for( tools::Long nX
= 0; nX
< 256; nX
++ )
1689 cMapR
[ nX
] = static_cast<sal_uInt8
>(MinMax( FRound( nX
* fM
+ fROff
), 0, 255 ));
1690 cMapG
[ nX
] = static_cast<sal_uInt8
>(MinMax( FRound( nX
* fM
+ fGOff
), 0, 255 ));
1691 cMapB
[ nX
] = static_cast<sal_uInt8
>(MinMax( FRound( nX
* fM
+ fBOff
), 0, 255 ));
1695 // LO simply uses (in a somewhat optimized form) "newcolor = (oldcolor-128)*contrast+brightness+128"
1696 // as the formula, i.e. contrast first, brightness afterwards. MSOffice, for whatever weird reason,
1697 // use neither first, but apparently it applies half of brightness before contrast and half afterwards.
1698 cMapR
[ nX
] = static_cast<sal_uInt8
>(MinMax( FRound( (nX
+fROff
/2-128) * fM
+ 128 + fROff
/2 ), 0, 255 ));
1699 cMapG
[ nX
] = static_cast<sal_uInt8
>(MinMax( FRound( (nX
+fGOff
/2-128) * fM
+ 128 + fGOff
/2 ), 0, 255 ));
1700 cMapB
[ nX
] = static_cast<sal_uInt8
>(MinMax( FRound( (nX
+fBOff
/2-128) * fM
+ 128 + fBOff
/2 ), 0, 255 ));
1704 cMapR
[ nX
] = GAMMA( cMapR
[ nX
], fGamma
);
1705 cMapG
[ nX
] = GAMMA( cMapG
[ nX
], fGamma
);
1706 cMapB
[ nX
] = GAMMA( cMapB
[ nX
], fGamma
);
1711 cMapR
[ nX
] = ~cMapR
[ nX
];
1712 cMapG
[ nX
] = ~cMapG
[ nX
];
1713 cMapB
[ nX
] = ~cMapB
[ nX
];
1718 if( pAcc
->HasPalette() )
1720 BitmapColor aNewCol
;
1722 for( sal_uInt16 i
= 0, nCount
= pAcc
->GetPaletteEntryCount(); i
< nCount
; i
++ )
1724 const BitmapColor
& rCol
= pAcc
->GetPaletteColor( i
);
1725 aNewCol
.SetRed( cMapR
[ rCol
.GetRed() ] );
1726 aNewCol
.SetGreen( cMapG
[ rCol
.GetGreen() ] );
1727 aNewCol
.SetBlue( cMapB
[ rCol
.GetBlue() ] );
1728 pAcc
->SetPaletteColor( i
, aNewCol
);
1731 else if( pAcc
->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr
)
1733 for( tools::Long nY
= 0; nY
< nH
; nY
++ )
1735 Scanline pScan
= pAcc
->GetScanline( nY
);
1737 for( tools::Long nX
= 0; nX
< nW
; nX
++ )
1739 *pScan
= cMapB
[ *pScan
]; pScan
++;
1740 *pScan
= cMapG
[ *pScan
]; pScan
++;
1741 *pScan
= cMapR
[ *pScan
]; pScan
++;
1745 else if( pAcc
->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb
)
1747 for( tools::Long nY
= 0; nY
< nH
; nY
++ )
1749 Scanline pScan
= pAcc
->GetScanline( nY
);
1751 for( tools::Long nX
= 0; nX
< nW
; nX
++ )
1753 *pScan
= cMapR
[ *pScan
]; pScan
++;
1754 *pScan
= cMapG
[ *pScan
]; pScan
++;
1755 *pScan
= cMapB
[ *pScan
]; pScan
++;
1761 for( tools::Long nY
= 0; nY
< nH
; nY
++ )
1763 Scanline pScanline
= pAcc
->GetScanline(nY
);
1764 for( tools::Long nX
= 0; nX
< nW
; nX
++ )
1766 aCol
= pAcc
->GetPixelFromData( pScanline
, nX
);
1767 aCol
.SetRed( cMapR
[ aCol
.GetRed() ] );
1768 aCol
.SetGreen( cMapG
[ aCol
.GetGreen() ] );
1769 aCol
.SetBlue( cMapB
[ aCol
.GetBlue() ] );
1770 pAcc
->SetPixelOnData( pScanline
, nX
, aCol
);
1783 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */