bump product version to 7.2.5.1
[LibreOffice.git] / vcl / source / bitmap / bitmap.cxx
blobe093e0afabb54eb6786552b5d4974ce0d2c6e664
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
30 #include <svdata.hxx>
31 #include <salinst.hxx>
32 #include <salbmp.hxx>
33 #if HAVE_FEATURE_SKIA
34 #include <vcl/skia/SkiaHelper.hxx>
35 #endif
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"
47 #include "floyd.hxx"
49 #include <math.h>
50 #include <algorithm>
51 #include <memory>
53 #ifdef DBG_UTIL
54 #include <cstdlib>
55 #include <tools/stream.hxx>
56 #include <vcl/graphicfilter.hxx>
57 #endif
59 Bitmap::Bitmap()
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()))
80 return;
82 BitmapPalette aPal;
83 BitmapPalette* pRealPal = nullptr;
85 if (vcl::isPalettePixelFormat(ePixelFormat))
87 if( !pPal )
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;
99 aPal[ 1 ] = COL_BLUE;
100 aPal[ 2 ] = COL_GREEN;
101 aPal[ 3 ] = COL_CYAN;
102 aPal[ 4 ] = COL_RED;
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 );
130 else
131 pRealPal = const_cast<BitmapPalette*>(pPal);
134 mxSalBmp = ImplGetSVData()->mpDefInst->CreateSalBitmap();
135 mxSalBmp->Create(rSizePixel, ePixelFormat, pRealPal ? *pRealPal : aPal);
138 #ifdef DBG_UTIL
140 namespace
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);
150 #endif
152 Bitmap::~Bitmap()
154 #ifdef DBG_UTIL
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)
162 save = false;
163 savePNG(sDumpPath, *this);
165 #endif
168 const BitmapPalette& Bitmap::GetGreyPalette( int nEntries )
170 // Create greyscale palette with 2, 4, 16 or 256 entries
171 switch (nEntries)
173 case 2:
175 static const BitmapPalette aGreyPalette2 = [] {
176 BitmapPalette aPalette(2);
177 aPalette[0] = BitmapColor(0, 0, 0);
178 aPalette[1] = BitmapColor(255, 255, 255);
179 return aPalette;
180 }();
182 return aGreyPalette2;
184 case 4:
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);
192 return aPalette;
193 }();
195 return aGreyPalette4;
197 case 16:
199 static const BitmapPalette aGreyPalette16 = [] {
200 sal_uInt8 cGrey = 0;
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);
208 return aPalette;
209 }();
211 return aGreyPalette16;
213 case 256:
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));
222 return aPalette;
223 }();
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
236 return true;
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 )
242 return true;
245 bool bRet = false;
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();
254 return bRet;
257 bool BitmapPalette::IsGreyPalette8Bit() const
259 const int nEntryCount = GetEntryCount();
260 if( !nEntryCount ) // NOTE: an empty palette means 1:1 mapping
261 return true;
262 if( nEntryCount != 256 )
263 return false;
264 for (sal_uInt16 i = 0; i < 256; ++i)
266 if( maBitmapColor[i] != BitmapColor(i, i, i))
267 return false;
269 return true;
272 Bitmap& Bitmap::operator=( const Bitmap& rBitmap )
274 if (this == &rBitmap)
275 return *this;
277 maPrefSize = rBitmap.maPrefSize;
278 maPrefMapMode = rBitmap.maPrefMapMode;
279 mxSalBmp = rBitmap.mxSalBmp;
281 return *this;
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);
290 return *this;
293 bool Bitmap::operator==( const Bitmap& rBmp ) const
295 if (rBmp.mxSalBmp == mxSalBmp) // Includes both are nullptr
296 return true;
297 if (!rBmp.mxSalBmp || !mxSalBmp)
298 return false;
299 if (rBmp.mxSalBmp->GetSize() != mxSalBmp->GetSize() ||
300 rBmp.mxSalBmp->GetBitCount() != mxSalBmp->GetBitCount())
301 return false;
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)
307 return false;
308 return aChecksum1 == aChecksum2;
311 void Bitmap::SetEmpty()
313 maPrefMapMode = MapMode();
314 maPrefSize = Size();
315 mxSalBmp.reset();
318 Size Bitmap::GetSizePixel() const
320 return( mxSalBmp ? mxSalBmp->GetSize() : Size() );
323 vcl::PixelFormat Bitmap::getPixelFormat() const
325 if (!mxSalBmp)
326 return vcl::PixelFormat::INVALID;
328 sal_uInt16 nBitCount = mxSalBmp->GetBitCount();
329 if (nBitCount <= 1)
330 return vcl::PixelFormat::N1_BPP;
331 if (nBitCount <= 8)
332 return vcl::PixelFormat::N8_BPP;
333 if (nBitCount <= 24)
334 return vcl::PixelFormat::N24_BPP;
335 if (nBitCount <= 32)
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));
347 if( pIAcc )
349 bRet = pIAcc->HasPalette() && pIAcc->GetPalette().IsGreyPaletteAny();
352 return bRet;
355 bool Bitmap::HasGreyPalette8Bit() const
357 bool bRet = false;
358 ScopedInfoAccess pIAcc(const_cast<Bitmap&>(*this));
360 if( pIAcc )
362 bRet = pIAcc->HasPalette() && pIAcc->GetPalette().IsGreyPalette8Bit();
365 return bRet;
368 BitmapChecksum Bitmap::GetChecksum() const
370 BitmapChecksum nRet = 0;
372 if( mxSalBmp )
374 mxSalBmp->GetChecksum(nRet);
376 if (!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);
392 return 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);
410 Size aNewPrefSize;
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()));
417 else
419 aNewPrefSize = maPrefSize;
422 *this = rBitmap;
424 maPrefSize = aNewPrefSize;
425 maPrefMapMode = aOldMapMode;
428 void Bitmap::ImplSetSalBitmap(const std::shared_ptr<SalBitmap>& xImpBmp)
430 mxSalBmp = xImpBmp;
433 BitmapInfoAccess* Bitmap::AcquireInfoAccess()
435 std::unique_ptr<BitmapInfoAccess> pInfoAccess(new BitmapInfoAccess( *this ));
437 if( !*pInfoAccess )
439 return nullptr;
442 return pInfoAccess.release();
445 BitmapReadAccess* Bitmap::AcquireReadAccess()
447 std::unique_ptr<BitmapReadAccess> pReadAccess(new BitmapReadAccess( *this ));
449 if( !*pReadAccess )
451 return nullptr;
454 return pReadAccess.release();
457 BitmapWriteAccess* Bitmap::AcquireWriteAccess()
459 std::unique_ptr<BitmapWriteAccess> pWriteAccess(new BitmapWriteAccess( *this ));
461 if( !*pWriteAccess )
463 return nullptr;
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 );
478 bool bRet = false;
480 aRect.Intersection( tools::Rectangle( Point(), aSizePix ) );
482 if( !aRect.IsEmpty() && aSizePix != aRect.GetSize())
484 ScopedReadAccess pReadAcc(*this);
486 if( pReadAcc )
488 const tools::Rectangle aNewRect( Point(), aRect.GetSize() );
489 Bitmap aNewBmp(aNewRect.GetSize(), getPixelFormat(), &pReadAcc->GetPalette());
490 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
492 if( pWriteAcc )
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 ) );
507 pWriteAcc.reset();
508 bRet = true;
511 pReadAcc.reset();
513 if( bRet )
514 ReassignWithSize( aNewBmp );
518 return bRet;
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 );
526 bool bRet = false;
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 )
542 int nNextIndex = 0;
544 if (nSrcBitCount == 24)
545 Convert( BmpConversion::N24Bit );
546 else if (nSrcBitCount == 8)
548 Convert( BmpConversion::N8BitColors );
549 nNextIndex = 16;
551 else if (nSrcBitCount == 4)
553 assert(false);
556 if( nNextIndex )
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) );
570 bool bFound = false;
572 for (int j = 0; j < nDstCount; ++j)
574 if( rSrcCol == pDstAcc->GetPaletteColor( static_cast<sal_uInt16>(j) ) )
576 bFound = true;
577 break;
581 if( !bFound )
582 pDstAcc->SetPaletteColor( static_cast<sal_uInt16>(nNextIndex++), rSrcCol );
588 aRectSrc.Intersection( tools::Rectangle( Point(), aCopySizePix ) );
590 if( !aRectSrc.IsEmpty() )
592 ScopedReadAccess pReadAcc(*pSrc);
594 if( pReadAcc )
596 BitmapScopedWriteAccess pWriteAcc(*this);
598 if( pWriteAcc )
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 ) ) );
634 else
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 ) );
643 pWriteAcc.reset();
644 bRet = ( nWidth > 0 ) && ( nHeight > 0 );
647 pReadAcc.reset();
651 else
653 tools::Rectangle aRectSrc( rRectSrc );
655 aRectSrc.Intersection( tools::Rectangle( Point(), aSizePix ) );
657 if( !aRectSrc.IsEmpty() && ( aRectSrc != aRectDst ) )
659 BitmapScopedWriteAccess pWriteAcc(*this);
661 if( pWriteAcc )
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 ) );
704 else
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 ) );
715 pWriteAcc.reset();
716 bRet = true;
722 return bRet;
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 );
732 bool bRet = false;
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);
750 if( pReadAcc )
752 BitmapScopedWriteAccess pWriteAcc(*this);
754 if( pWriteAcc )
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 ) );
770 pWriteAcc.reset();
771 bRet = ( nWidth > 0 ) && ( nHeight > 0 );
774 pReadAcc.reset();
778 else
780 tools::Rectangle aRectSrc( rRectSrc );
782 aRectSrc.Intersection( tools::Rectangle( Point(), aSizePix ) );
784 if( !aRectSrc.IsEmpty() && ( aRectSrc != aRectDst ) )
786 BitmapScopedWriteAccess pWriteAcc(*this);
788 if( pWriteAcc )
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 ) );
831 else
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 ) );
842 pWriteAcc.reset();
843 bRet = true;
849 return bRet;
853 bool Bitmap::Expand( sal_Int32 nDX, sal_Int32 nDY, const Color* pInitColor )
855 bool bRet = false;
857 if( nDX || nDY )
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);
865 if( pReadAcc )
867 BitmapPalette aBmpPal( pReadAcc->GetPalette() );
868 Bitmap aNewBmp(aNewSize, getPixelFormat(), &aBmpPal);
869 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
871 if( pWriteAcc )
873 BitmapColor aColor;
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();
878 tools::Long nX;
879 tools::Long nY;
881 if( pInitColor )
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 );
904 pWriteAcc.reset();
905 bRet = true;
908 pReadAcc.reset();
910 if (bRet)
911 ReassignWithSize(aNewBmp);
915 return bRet;
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);
931 return aDispBmp;
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
943 if (mxSalBmp)
945 // avoid large chunk of obsolete and hopefully rarely used conversions.
946 if (eConversion == BmpConversion::N8BitNoConversion)
948 if (mxSalBmp->GetBitCount() == 8 && HasGreyPalette8Bit())
949 return true;
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() );
956 return true;
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() );
966 return true;
971 const sal_uInt16 nBitCount = vcl::pixelFormatBitCount(getPixelFormat());
972 bool bRet = false;
974 switch( eConversion )
976 case BmpConversion::N1BitThreshold:
978 BitmapEx aBmpEx(*this);
979 bRet = BitmapFilter::Filter(aBmpEx, BitmapMonochromeFilter(128));
980 *this = aBmpEx.GetBitmap();
982 break;
984 case BmpConversion::N8BitGreys:
985 case BmpConversion::N8BitNoConversion:
986 bRet = ImplMakeGreyscales();
987 break;
989 case BmpConversion::N8BitColors:
991 if( nBitCount < 8 )
992 bRet = ImplConvertUp(vcl::PixelFormat::N8_BPP);
993 else if( nBitCount > 8 )
994 bRet = ImplConvertDown8BPP();
995 else
996 bRet = true;
998 break;
1000 case BmpConversion::N8BitTrans:
1002 Color aTrans( BMP_COL_TRANS );
1004 if( nBitCount < 8 )
1005 bRet = ImplConvertUp(vcl::PixelFormat::N8_BPP, &aTrans );
1006 else
1007 bRet = ImplConvertDown8BPP(&aTrans );
1009 break;
1011 case BmpConversion::N24Bit:
1013 if( nBitCount < 24 )
1014 bRet = ImplConvertUp(vcl::PixelFormat::N24_BPP);
1015 else
1016 bRet = true;
1018 break;
1020 case BmpConversion::N32Bit:
1022 if( nBitCount < 32 )
1023 bRet = ImplConvertUp(vcl::PixelFormat::N32_BPP);
1024 else
1025 bRet = true;
1027 break;
1029 default:
1030 OSL_FAIL( "Bitmap::Convert(): Unsupported conversion" );
1031 break;
1034 return bRet;
1037 bool Bitmap::ImplMakeGreyscales()
1039 ScopedReadAccess pReadAcc(*this);
1040 bool bRet = false;
1042 if( pReadAcc )
1044 const BitmapPalette& rPal = GetGreyPalette(256);
1045 sal_uLong nShift = 0;
1046 bool bPalDiffers = !pReadAcc->HasPalette() || ( rPal.GetEntryCount() != pReadAcc->GetPaletteEntryCount() );
1048 if( !bPalDiffers )
1049 bPalDiffers = ( rPal != pReadAcc->GetPalette() );
1051 if( bPalDiffers )
1053 const auto ePixelFormat = vcl::PixelFormat::N8_BPP;
1054 Bitmap aNewBmp(GetSizePixel(), ePixelFormat, &rPal );
1055 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
1057 if( pWriteAcc )
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 )
1079 nShift += 8;
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 )
1099 nShift += 8;
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 );
1116 else
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) );
1127 pWriteAcc.reset();
1128 bRet = true;
1131 pReadAcc.reset();
1133 if( bRet )
1135 const MapMode aMap( maPrefMapMode );
1136 const Size aSize( maPrefSize );
1138 *this = aNewBmp;
1140 maPrefMapMode = aMap;
1141 maPrefSize = aSize;
1144 else
1146 pReadAcc.reset();
1147 bRet = true;
1151 return bRet;
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);
1159 bool bRet = false;
1161 if (pReadAcc)
1163 BitmapPalette aPalette;
1164 Bitmap aNewBmp(GetSizePixel(), ePixelFormat, pReadAcc->HasPalette() ? &pReadAcc->GetPalette() : &aPalette);
1165 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
1167 if (pWriteAcc)
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];
1183 if (pExtColor)
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));
1198 else
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)));
1212 else
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));
1225 bRet = true;
1228 if (bRet)
1230 const MapMode aMap(maPrefMapMode);
1231 const Size aSize(maPrefSize);
1233 *this = aNewBmp;
1235 maPrefMapMode = aMap;
1236 maPrefSize = aSize;
1240 return bRet;
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);
1248 bool bRet = false;
1250 if (pReadAcc)
1252 BitmapPalette aPalette;
1253 Bitmap aNewBmp(GetSizePixel(), vcl::PixelFormat::N8_BPP, &aPalette);
1254 BitmapScopedWriteAccess pWriteAcc(aNewBmp);
1256 if (pWriteAcc)
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);
1266 BitmapColor aColor;
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;
1273 sal_uInt8 cIndex;
1274 bool bQ1 = true;
1276 if (pExtColor)
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));
1300 else
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));
1314 tools::Long nX;
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));
1327 // Last RowPixel
1328 if (nX < nWidth)
1330 cIndex = static_cast<sal_uInt8>(aColorMap.GetBestPaletteIndex(pQLine1[nWidth1].ImplGetColor()));
1331 pWriteAcc->SetPixelOnData(pScanline, nX, BitmapColor(cIndex));
1334 // Refill/copy row buffer
1335 pQLine1 = pQLine2;
1336 bQ1 = !bQ1;
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));
1346 else
1347 pQLine2[nX] = pReadAcc->GetPixelFromData(pScanlineRead, nX);
1352 bRet = true;
1354 pWriteAcc.reset();
1356 if(bRet)
1358 const MapMode aMap(maPrefMapMode);
1359 const Size aSize(maPrefSize);
1361 *this = aNewBmp;
1363 maPrefMapMode = aMap;
1364 maPrefSize = aSize;
1368 return bRet;
1371 bool Bitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
1373 if(basegfx::fTools::equalZero(rScaleX) || basegfx::fTools::equalZero(rScaleY))
1375 // no scale
1376 return true;
1379 if(basegfx::fTools::equal(rScaleX, 1.0) && basegfx::fTools::equal(rScaleY, 1.0))
1381 // no scale
1382 return true;
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();
1397 return true;
1401 // fdo#33455
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
1408 // to black/white
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);
1420 switch(nScaleFlag)
1422 case BmpScaleFlag::Default:
1423 if (GetSizePixel().Width() < 2 || GetSizePixel().Height() < 2)
1424 bRetval = BitmapFilter::Filter(aBmpEx, BitmapFastScaleFilter(rScaleX, rScaleY));
1425 else
1426 bRetval = BitmapFilter::Filter(aBmpEx, BitmapScaleSuperFilter(rScaleX, rScaleY));
1427 break;
1429 case BmpScaleFlag::Fast:
1430 case BmpScaleFlag::NearestNeighbor:
1431 bRetval = BitmapFilter::Filter(aBmpEx, BitmapFastScaleFilter(rScaleX, rScaleY));
1432 break;
1434 case BmpScaleFlag::Interpolate:
1435 bRetval = BitmapFilter::Filter(aBmpEx, BitmapInterpolateScaleFilter(rScaleX, rScaleY));
1436 break;
1438 case BmpScaleFlag::Super:
1439 bRetval = BitmapFilter::Filter(aBmpEx, BitmapScaleSuperFilter(rScaleX, rScaleY));
1440 break;
1441 case BmpScaleFlag::BestQuality:
1442 case BmpScaleFlag::Lanczos:
1443 bRetval = BitmapFilter::Filter(aBmpEx, vcl::BitmapScaleLanczos3Filter(rScaleX, rScaleY));
1444 break;
1446 case BmpScaleFlag::BiCubic:
1447 bRetval = BitmapFilter::Filter(aBmpEx, vcl::BitmapScaleBicubicFilter(rScaleX, rScaleY));
1448 break;
1450 case BmpScaleFlag::BiLinear:
1451 bRetval = BitmapFilter::Filter(aBmpEx, vcl::BitmapScaleBilinearFilter(rScaleX, rScaleY));
1452 break;
1455 if (bRetval)
1456 *this = aBmpEx.GetBitmap();
1458 OSL_ENSURE(!bRetval || eStartPixelFormat == getPixelFormat(), "Bitmap::Scale has changed the ColorDepth, this should *not* happen (!)");
1459 return bRetval;
1462 bool Bitmap::Scale( const Size& rNewSize, BmpScaleFlag nScaleFlag )
1464 const Size aSize( GetSizePixel() );
1465 bool bRet;
1467 if( aSize.Width() && aSize.Height() )
1469 bRet = Scale( static_cast<double>(rNewSize.Width()) / aSize.Width(),
1470 static_cast<double>(rNewSize.Height()) / aSize.Height(),
1471 nScaleFlag );
1473 else
1474 bRet = true;
1476 return bRet;
1479 bool Bitmap::HasFastScale()
1481 #if HAVE_FEATURE_SKIA
1482 if( SkiaHelper::isVCLSkiaEnabled() && SkiaHelper::renderMethodToUse() != SkiaHelper::RenderRaster)
1483 return true;
1484 #endif
1485 return false;
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())
1492 return;
1494 switch (getPixelFormat())
1496 case vcl::PixelFormat::N1_BPP:
1498 rNew.Convert(BmpConversion::N1BitThreshold);
1499 break;
1501 case vcl::PixelFormat::N8_BPP:
1503 if(HasGreyPaletteAny())
1505 rNew.Convert(BmpConversion::N8BitGreys);
1507 else
1509 rNew.Convert(BmpConversion::N8BitColors);
1511 break;
1513 case vcl::PixelFormat::N24_BPP:
1515 rNew.Convert(BmpConversion::N24Bit);
1516 break;
1518 case vcl::PixelFormat::N32_BPP:
1520 rNew.Convert(BmpConversion::N32Bit);
1521 break;
1523 case vcl::PixelFormat::INVALID:
1525 SAL_WARN("vcl", "Can't adapt the pixelformat as it is invalid.");
1526 break;
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;
1536 return pColorArray;
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));
1543 else
1544 return pReadAcc->GetPixelFromData(pScanlineRead, nZ);
1547 bool Bitmap::Dither()
1549 const Size aSize( GetSizePixel() );
1550 if( aSize.Width() == 1 || aSize.Height() == 1 )
1551 return true;
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 )
1559 BitmapColor aColor;
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();
1569 tools::Long* pTmp;
1570 pTmp = p2T;
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++ )
1579 pTmp = p1T;
1580 p1T = p2T;
1581 p2T = pTmp;
1582 if (nY < nHeight)
1584 for (tools::Long nZ = 0; nZ < nWidth; nZ++)
1586 pTmp = shiftColor(pTmp, getColor(pReadAcc.get(), nZ));
1589 // Examine first Pixel separately
1590 tools::Long nX = 0;
1591 tools::Long nTemp;
1592 CALC_ERRORS;
1593 CALC_TABLES7;
1594 nX -= 5;
1595 CALC_TABLES5;
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
1599 tools::Long nXAcc;
1600 for ( nX = 3, nXAcc = 1; nX < nW2; nXAcc++ )
1602 CALC_ERRORS;
1603 CALC_TABLES7;
1604 nX -= 8;
1605 CALC_TABLES3;
1606 CALC_TABLES5;
1607 pWriteAcc->SetPixelOnData( pScanline, nXAcc, BitmapColor(static_cast<sal_uInt8>(nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ])) );
1609 // Treat last Pixel separately
1610 CALC_ERRORS;
1611 nX -= 5;
1612 CALC_TABLES3;
1613 CALC_TABLES5;
1614 pWriteAcc->SetPixelOnData( pScanline, nWidth1, BitmapColor(static_cast<sal_uInt8>(nVCLBLut[ nBC ] + nVCLGLut[nGC ] + nVCLRLut[nRC ])) );
1616 pReadAcc.reset();
1617 pWriteAcc.reset();
1618 const MapMode aMap( maPrefMapMode );
1619 const Size aPrefSize( maPrefSize );
1620 *this = aNewBmp;
1621 maPrefMapMode = aMap;
1622 maPrefSize = aPrefSize;
1623 return true;
1625 pReadAcc.reset();
1626 pWriteAcc.reset();
1628 return false;
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 )
1640 bool bRet = false;
1642 // nothing to do => return quickly
1643 if( !nLuminancePercent && !nContrastPercent &&
1644 !nChannelRPercent && !nChannelGPercent && !nChannelBPercent &&
1645 ( fGamma == 1.0 ) && !bInvert )
1647 bRet = true;
1649 else
1651 BitmapScopedWriteAccess pAcc(*this);
1653 if( pAcc )
1655 BitmapColor aCol;
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;
1663 // calculate slope
1664 if( nContrastPercent >= 0 )
1665 fM = 128.0 / ( 128.0 - 1.27 * MinMax( nContrastPercent, 0, 100 ) );
1666 else
1667 fM = ( 128.0 + 1.27 * MinMax( nContrastPercent, -100, 0 ) ) / 128.0;
1669 if(!msoBrightness)
1670 // total offset = luminance offset + contrast offset
1671 fOff = MinMax( nLuminancePercent, -100, 100 ) * 2.55 + 128.0 - fM * 128.0;
1672 else
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++ )
1687 if(!msoBrightness)
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 ));
1693 else
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 ));
1702 if( bGamma )
1704 cMapR[ nX ] = GAMMA( cMapR[ nX ], fGamma );
1705 cMapG[ nX ] = GAMMA( cMapG[ nX ], fGamma );
1706 cMapB[ nX ] = GAMMA( cMapB[ nX ], fGamma );
1709 if( bInvert )
1711 cMapR[ nX ] = ~cMapR[ nX ];
1712 cMapG[ nX ] = ~cMapG[ nX ];
1713 cMapB[ nX ] = ~cMapB[ nX ];
1717 // do modifying
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++;
1759 else
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 );
1775 pAcc.reset();
1776 bRet = true;
1780 return bRet;
1783 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */