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