Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / source / gdi / bitmap.cxx
blob71d5d73f15abc9aab609883775cc868a87253887
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 <algorithm>
21 #include <rtl/crc.h>
22 #include <tools/stream.hxx>
23 #include <tools/poly.hxx>
24 #include <vcl/salbtype.hxx>
25 #include <vcl/bitmapaccess.hxx>
26 #include <vcl/outdev.hxx>
27 #include <vcl/bitmap.hxx>
28 #include <vcl/bitmapex.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/image.hxx>
32 #include <impbmp.hxx>
33 #include <salbmp.hxx>
34 #include <memory>
36 Bitmap::Bitmap()
40 Bitmap::Bitmap(const Bitmap& rBitmap)
41 : mxImpBmp(rBitmap.mxImpBmp)
42 , maPrefMapMode(rBitmap.maPrefMapMode)
43 , maPrefSize(rBitmap.maPrefSize)
47 Bitmap::Bitmap(SalBitmap* pSalBitmap)
48 : mxImpBmp(new ImpBitmap(pSalBitmap))
49 , maPrefMapMode(MapMode(MapUnit::MapPixel))
50 , maPrefSize(mxImpBmp->ImplGetSize())
54 Bitmap::Bitmap( const Size& rSizePixel, sal_uInt16 nBitCount, const BitmapPalette* pPal )
56 if (rSizePixel.Width() && rSizePixel.Height())
58 BitmapPalette aPal;
59 BitmapPalette* pRealPal = nullptr;
61 if( nBitCount <= 8 )
63 if( !pPal )
65 if( 1 == nBitCount )
67 aPal.SetEntryCount( 2 );
68 aPal[ 0 ] = Color( COL_BLACK );
69 aPal[ 1 ] = Color( COL_WHITE );
71 else if( ( 4 == nBitCount ) || ( 8 == nBitCount ) )
73 aPal.SetEntryCount( 1 << nBitCount );
74 aPal[ 0 ] = Color( COL_BLACK );
75 aPal[ 1 ] = Color( COL_BLUE );
76 aPal[ 2 ] = Color( COL_GREEN );
77 aPal[ 3 ] = Color( COL_CYAN );
78 aPal[ 4 ] = Color( COL_RED );
79 aPal[ 5 ] = Color( COL_MAGENTA );
80 aPal[ 6 ] = Color( COL_BROWN );
81 aPal[ 7 ] = Color( COL_GRAY );
82 aPal[ 8 ] = Color( COL_LIGHTGRAY );
83 aPal[ 9 ] = Color( COL_LIGHTBLUE );
84 aPal[ 10 ] = Color( COL_LIGHTGREEN );
85 aPal[ 11 ] = Color( COL_LIGHTCYAN );
86 aPal[ 12 ] = Color( COL_LIGHTRED );
87 aPal[ 13 ] = Color( COL_LIGHTMAGENTA );
88 aPal[ 14 ] = Color( COL_YELLOW );
89 aPal[ 15 ] = Color( COL_WHITE );
91 // Create dither palette
92 if( 8 == nBitCount )
94 sal_uInt16 nActCol = 16;
96 for( sal_uInt16 nB = 0; nB < 256; nB += 51 )
97 for( sal_uInt16 nG = 0; nG < 256; nG += 51 )
98 for( sal_uInt16 nR = 0; nR < 256; nR += 51 )
99 aPal[ nActCol++ ] = BitmapColor( (sal_uInt8) nR, (sal_uInt8) nG, (sal_uInt8) nB );
101 // Set standard Office colors
102 aPal[ nActCol++ ] = BitmapColor( 0, 184, 255 );
106 else
107 pRealPal = const_cast<BitmapPalette*>(pPal);
110 mxImpBmp.reset(new ImpBitmap);
111 mxImpBmp->ImplCreate( rSizePixel, nBitCount, pRealPal ? *pRealPal : aPal );
115 Bitmap::~Bitmap()
119 const BitmapPalette& Bitmap::GetGreyPalette( int nEntries )
121 static BitmapPalette aGreyPalette2;
122 static BitmapPalette aGreyPalette4;
123 static BitmapPalette aGreyPalette16;
124 static BitmapPalette aGreyPalette256;
126 // Create greyscale palette with 2, 4, 16 or 256 entries
127 if( 2 == nEntries || 4 == nEntries || 16 == nEntries || 256 == nEntries )
129 if( 2 == nEntries )
131 if( !aGreyPalette2.GetEntryCount() )
133 aGreyPalette2.SetEntryCount( 2 );
134 aGreyPalette2[ 0 ] = BitmapColor( 0, 0, 0 );
135 aGreyPalette2[ 1 ] = BitmapColor( 255, 255, 255 );
138 return aGreyPalette2;
140 else if( 4 == nEntries )
142 if( !aGreyPalette4.GetEntryCount() )
144 aGreyPalette4.SetEntryCount( 4 );
145 aGreyPalette4[ 0 ] = BitmapColor( 0, 0, 0 );
146 aGreyPalette4[ 1 ] = BitmapColor( 85, 85, 85 );
147 aGreyPalette4[ 2 ] = BitmapColor( 170, 170, 170 );
148 aGreyPalette4[ 3 ] = BitmapColor( 255, 255, 255 );
151 return aGreyPalette4;
153 else if( 16 == nEntries )
155 if( !aGreyPalette16.GetEntryCount() )
157 sal_uInt8 cGrey = 0, cGreyInc = 17;
159 aGreyPalette16.SetEntryCount( 16 );
161 for( sal_uInt16 i = 0; i < 16; i++, cGrey = sal::static_int_cast<sal_uInt8>(cGrey + cGreyInc) )
162 aGreyPalette16[ i ] = BitmapColor( cGrey, cGrey, cGrey );
165 return aGreyPalette16;
167 else
169 if( !aGreyPalette256.GetEntryCount() )
171 aGreyPalette256.SetEntryCount( 256 );
173 for( sal_uInt16 i = 0; i < 256; i++ )
174 aGreyPalette256[ i ] = BitmapColor( (sal_uInt8) i, (sal_uInt8) i, (sal_uInt8) i );
177 return aGreyPalette256;
180 else
182 OSL_FAIL( "Bitmap::GetGreyPalette: invalid entry count (2/4/16/256 allowed)" );
183 return aGreyPalette2;
187 bool BitmapPalette::IsGreyPalette() const
189 const int nEntryCount = GetEntryCount();
190 if( !nEntryCount ) // NOTE: an empty palette means 1:1 mapping
191 return true;
192 // See above: only certain entry values will result in a valid call to GetGreyPalette
193 if( nEntryCount == 2 || nEntryCount == 4 || nEntryCount == 16 || nEntryCount == 256 )
195 const BitmapPalette& rGreyPalette = Bitmap::GetGreyPalette( nEntryCount );
196 if( rGreyPalette == *this )
197 return true;
200 bool bRet = false;
201 // TODO: is it worth to compare the entries for the general case?
202 if (nEntryCount == 2)
204 const BitmapColor& rCol0(maBitmapColor[0]);
205 const BitmapColor& rCol1(maBitmapColor[1]);
206 bRet = rCol0.GetRed() == rCol0.GetGreen() && rCol0.GetRed() == rCol0.GetBlue() &&
207 rCol1.GetRed() == rCol1.GetGreen() && rCol1.GetRed() == rCol1.GetBlue();
209 return bRet;
212 Bitmap& Bitmap::operator=( const Bitmap& rBitmap )
214 if (this == &rBitmap)
215 return *this;
217 maPrefSize = rBitmap.maPrefSize;
218 maPrefMapMode = rBitmap.maPrefMapMode;
219 mxImpBmp = rBitmap.mxImpBmp;
221 return *this;
224 Bitmap& Bitmap::operator=( Bitmap&& rBitmap )
226 maPrefSize = std::move(rBitmap.maPrefSize);
227 maPrefMapMode = std::move(rBitmap.maPrefMapMode);
228 mxImpBmp = std::move(rBitmap.mxImpBmp);
230 return *this;
233 bool Bitmap::IsEqual( const Bitmap& rBmp ) const
235 return rBmp.mxImpBmp == mxImpBmp || // Includes both are nullptr
236 (rBmp.mxImpBmp && mxImpBmp && mxImpBmp->ImplIsEqual(*rBmp.mxImpBmp));
239 void Bitmap::SetEmpty()
241 maPrefMapMode = MapMode();
242 maPrefSize = Size();
243 mxImpBmp.reset();
246 Size Bitmap::GetSizePixel() const
248 return( mxImpBmp ? mxImpBmp->ImplGetSize() : Size() );
251 sal_uInt16 Bitmap::GetBitCount() const
253 return( mxImpBmp ? mxImpBmp->ImplGetBitCount() : 0 );
256 bool Bitmap::HasGreyPalette() const
258 const sal_uInt16 nBitCount = GetBitCount();
259 bool bRet = nBitCount == 1;
261 ScopedInfoAccess pIAcc(const_cast<Bitmap&>(*this));
263 if( pIAcc )
265 bRet = pIAcc->HasPalette() && pIAcc->GetPalette().IsGreyPalette();
268 return bRet;
271 BitmapChecksum Bitmap::GetChecksum() const
273 BitmapChecksum nRet = 0;
275 if( mxImpBmp )
277 nRet = mxImpBmp->ImplGetChecksum();
279 if (!nRet)
281 // nRet == 0 => probably, we were not able to acquire
282 // the buffer in SalBitmap::updateChecksum;
283 // so, we need to update the imp bitmap for this bitmap instance
284 // as we do in BitmapInfoAccess::ImplCreate
285 std::shared_ptr<ImpBitmap> xNewImpBmp(new ImpBitmap);
286 if (xNewImpBmp->ImplCreate(*mxImpBmp, GetBitCount()))
288 Bitmap* pThis = const_cast<Bitmap*>(this);
289 pThis->mxImpBmp = xNewImpBmp;
290 nRet = mxImpBmp->ImplGetChecksum();
295 return nRet;
298 void Bitmap::ImplMakeUnique()
300 if (mxImpBmp && !mxImpBmp.unique())
302 std::shared_ptr<ImpBitmap> xOldImpBmp = mxImpBmp;
303 mxImpBmp.reset(new ImpBitmap);
304 mxImpBmp->ImplCreate(*xOldImpBmp);
308 void Bitmap::ImplAssignWithSize( const Bitmap& rBitmap )
310 const Size aOldSizePix( GetSizePixel() );
311 const Size aNewSizePix( rBitmap.GetSizePixel() );
312 const MapMode aOldMapMode( maPrefMapMode );
313 Size aNewPrefSize;
315 if( ( aOldSizePix != aNewSizePix ) && aOldSizePix.Width() && aOldSizePix.Height() )
317 aNewPrefSize.Width() = FRound( maPrefSize.Width() * aNewSizePix.Width() / aOldSizePix.Width() );
318 aNewPrefSize.Height() = FRound( maPrefSize.Height() * aNewSizePix.Height() / aOldSizePix.Height() );
320 else
321 aNewPrefSize = maPrefSize;
323 *this = rBitmap;
325 maPrefSize = aNewPrefSize;
326 maPrefMapMode = aOldMapMode;
330 void Bitmap::ImplSetImpBitmap(const std::shared_ptr<ImpBitmap>& xImpBmp)
332 mxImpBmp = xImpBmp;
335 BitmapInfoAccess* Bitmap::AcquireInfoAccess()
337 BitmapInfoAccess* pInfoAccess = new BitmapInfoAccess( *this );
339 if( !*pInfoAccess )
341 delete pInfoAccess;
342 pInfoAccess = nullptr;
345 return pInfoAccess;
348 BitmapReadAccess* Bitmap::AcquireReadAccess()
350 BitmapReadAccess* pReadAccess = new BitmapReadAccess( *this );
352 if( !*pReadAccess )
354 delete pReadAccess;
355 pReadAccess = nullptr;
358 return pReadAccess;
361 BitmapWriteAccess* Bitmap::AcquireWriteAccess()
363 BitmapWriteAccess* pWriteAccess = new BitmapWriteAccess( *this );
365 if( !*pWriteAccess )
367 delete pWriteAccess;
368 pWriteAccess = nullptr;
371 return pWriteAccess;
374 void Bitmap::ReleaseAccess( BitmapInfoAccess* pBitmapAccess )
376 delete pBitmapAccess;
379 bool Bitmap::Erase(const Color& rFillColor)
381 if (IsEmpty())
382 return true;
384 Bitmap::ScopedWriteAccess pWriteAcc(*this);
385 bool bRet = false;
387 if (pWriteAcc)
389 const ScanlineFormat nFormat = pWriteAcc->GetScanlineFormat();
390 sal_uInt8 cIndex = 0;
391 bool bFast = false;
393 switch (nFormat)
395 case ScanlineFormat::N1BitMsbPal:
396 case ScanlineFormat::N1BitLsbPal:
398 cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
399 cIndex = (cIndex ? 255 : 0);
400 bFast = true;
402 break;
404 case ScanlineFormat::N4BitMsnPal:
405 case ScanlineFormat::N4BitLsnPal:
407 cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
408 cIndex = cIndex | ( cIndex << 4 );
409 bFast = true;
411 break;
413 case ScanlineFormat::N8BitPal:
415 cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor));
416 bFast = true;
418 break;
420 case ScanlineFormat::N24BitTcBgr:
421 case ScanlineFormat::N24BitTcRgb:
423 if (rFillColor.GetRed() == rFillColor.GetGreen() &&
424 rFillColor.GetRed() == rFillColor.GetBlue())
426 cIndex = rFillColor.GetRed();
427 bFast = true;
429 else
430 bFast = false;
432 break;
434 default:
435 bFast = false;
436 break;
439 if( bFast )
441 const sal_uLong nBufSize = pWriteAcc->GetScanlineSize() * pWriteAcc->Height();
442 memset( pWriteAcc->GetBuffer(), cIndex, nBufSize );
444 else
446 Point aTmpPoint;
447 const tools::Rectangle aRect( aTmpPoint, Size( pWriteAcc->Width(), pWriteAcc->Height() ) );
448 pWriteAcc->SetFillColor( rFillColor );
449 pWriteAcc->FillRect( aRect );
452 bRet = true;
455 return bRet;
458 bool Bitmap::Invert()
460 ScopedWriteAccess pAcc(*this);
461 bool bRet = false;
463 if( pAcc )
465 if( pAcc->HasPalette() )
467 BitmapPalette aBmpPal( pAcc->GetPalette() );
468 const sal_uInt16 nCount = aBmpPal.GetEntryCount();
470 for( sal_uInt16 i = 0; i < nCount; i++ )
471 aBmpPal[ i ].Invert();
473 pAcc->SetPalette( aBmpPal );
475 else
477 const long nWidth = pAcc->Width();
478 const long nHeight = pAcc->Height();
480 for( long nX = 0L; nX < nWidth; nX++ )
481 for( long nY = 0L; nY < nHeight; nY++ )
482 pAcc->SetPixel( nY, nX, pAcc->GetPixel( nY, nX ).Invert() );
485 mxImpBmp->ImplInvalidateChecksum();
486 pAcc.reset();
487 bRet = true;
490 return bRet;
493 bool Bitmap::Mirror( BmpMirrorFlags nMirrorFlags )
495 bool bHorz( nMirrorFlags & BmpMirrorFlags::Horizontal );
496 bool bVert( nMirrorFlags & BmpMirrorFlags::Vertical );
497 bool bRet = false;
499 if( bHorz && !bVert )
501 ScopedWriteAccess pAcc(*this);
503 if( pAcc )
505 const long nWidth = pAcc->Width();
506 const long nHeight = pAcc->Height();
507 const long nWidth1 = nWidth - 1L;
508 const long nWidth_2 = nWidth >> 1L;
510 for( long nY = 0L; nY < nHeight; nY++ )
512 for( long nX = 0L, nOther = nWidth1; nX < nWidth_2; nX++, nOther-- )
514 const BitmapColor aTemp( pAcc->GetPixel( nY, nX ) );
516 pAcc->SetPixel( nY, nX, pAcc->GetPixel( nY, nOther ) );
517 pAcc->SetPixel( nY, nOther, aTemp );
521 pAcc.reset();
522 bRet = true;
525 else if( bVert && !bHorz )
527 ScopedWriteAccess pAcc(*this);
529 if( pAcc )
531 const long nScanSize = pAcc->GetScanlineSize();
532 std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[ nScanSize ]);
533 const long nHeight = pAcc->Height();
534 const long nHeight1 = nHeight - 1L;
535 const long nHeight_2 = nHeight >> 1L;
537 for( long nY = 0L, nOther = nHeight1; nY < nHeight_2; nY++, nOther-- )
539 memcpy( pBuffer.get(), pAcc->GetScanline( nY ), nScanSize );
540 memcpy( pAcc->GetScanline( nY ), pAcc->GetScanline( nOther ), nScanSize );
541 memcpy( pAcc->GetScanline( nOther ), pBuffer.get(), nScanSize );
544 pAcc.reset();
545 bRet = true;
548 else if( bHorz && bVert )
550 ScopedWriteAccess pAcc(*this);
552 if( pAcc )
554 const long nWidth = pAcc->Width();
555 const long nWidth1 = nWidth - 1L;
556 const long nHeight = pAcc->Height();
557 long nHeight_2 = nHeight >> 1;
559 for( long nY = 0L, nOtherY = nHeight - 1L; nY < nHeight_2; nY++, nOtherY-- )
561 for( long nX = 0L, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX-- )
563 const BitmapColor aTemp( pAcc->GetPixel( nY, nX ) );
565 pAcc->SetPixel( nY, nX, pAcc->GetPixel( nOtherY, nOtherX ) );
566 pAcc->SetPixel( nOtherY, nOtherX, aTemp );
570 // ggf. noch mittlere Zeile horizontal spiegeln
571 if( nHeight & 1 )
573 for( long nX = 0L, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2; nX++, nOtherX-- )
575 const BitmapColor aTemp( pAcc->GetPixel( nHeight_2, nX ) );
576 pAcc->SetPixel( nHeight_2, nX, pAcc->GetPixel( nHeight_2, nOtherX ) );
577 pAcc->SetPixel( nHeight_2, nOtherX, aTemp );
581 pAcc.reset();
582 bRet = true;
585 else
586 bRet = true;
588 return bRet;
591 bool Bitmap::Rotate( long nAngle10, const Color& rFillColor )
593 bool bRet = false;
595 nAngle10 %= 3600L;
596 nAngle10 = ( nAngle10 < 0L ) ? ( 3599L + nAngle10 ) : nAngle10;
598 if( !nAngle10 )
599 bRet = true;
600 else if( 1800L == nAngle10 )
601 bRet = Mirror( BmpMirrorFlags::Horizontal | BmpMirrorFlags::Vertical );
602 else
604 ScopedReadAccess pReadAcc(*this);
605 Bitmap aRotatedBmp;
607 if( pReadAcc )
609 const Size aSizePix( GetSizePixel() );
611 if( ( 900L == nAngle10 ) || ( 2700L == nAngle10 ) )
613 const Size aNewSizePix( aSizePix.Height(), aSizePix.Width() );
614 Bitmap aNewBmp( aNewSizePix, GetBitCount(), &pReadAcc->GetPalette() );
615 ScopedWriteAccess pWriteAcc(aNewBmp);
617 if( pWriteAcc )
619 const long nWidth = aSizePix.Width();
620 const long nWidth1 = nWidth - 1L;
621 const long nHeight = aSizePix.Height();
622 const long nHeight1 = nHeight - 1L;
623 const long nNewWidth = aNewSizePix.Width();
624 const long nNewHeight = aNewSizePix.Height();
626 if( 900L == nAngle10 )
628 for( long nY = 0L, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX-- )
629 for( long nX = 0L, nOtherY = 0L; nX < nNewWidth; nX++ )
630 pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nOtherY++, nOtherX ) );
632 else if( 2700L == nAngle10 )
634 for( long nY = 0L, nOtherX = 0L; nY < nNewHeight; nY++, nOtherX++ )
635 for( long nX = 0L, nOtherY = nHeight1; nX < nNewWidth; nX++ )
636 pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nOtherY--, nOtherX ) );
639 pWriteAcc.reset();
642 aRotatedBmp = aNewBmp;
644 else
646 Point aTmpPoint;
647 tools::Rectangle aTmpRectangle( aTmpPoint, aSizePix );
648 tools::Polygon aPoly( aTmpRectangle );
649 aPoly.Rotate( aTmpPoint, (sal_uInt16) nAngle10 );
651 tools::Rectangle aNewBound( aPoly.GetBoundRect() );
652 const Size aNewSizePix( aNewBound.GetSize() );
653 Bitmap aNewBmp( aNewSizePix, GetBitCount(), &pReadAcc->GetPalette() );
654 ScopedWriteAccess pWriteAcc(aNewBmp);
656 if( pWriteAcc )
658 const BitmapColor aFillColor( pWriteAcc->GetBestMatchingColor( rFillColor ) );
659 const double fCosAngle = cos( nAngle10 * F_PI1800 );
660 const double fSinAngle = sin( nAngle10 * F_PI1800 );
661 const double fXMin = aNewBound.Left();
662 const double fYMin = aNewBound.Top();
663 const long nWidth = aSizePix.Width();
664 const long nHeight = aSizePix.Height();
665 const long nNewWidth = aNewSizePix.Width();
666 const long nNewHeight = aNewSizePix.Height();
667 long nX;
668 long nY;
669 long nRotX;
670 long nRotY;
671 std::unique_ptr<long[]> pCosX(new long[ nNewWidth ]);
672 std::unique_ptr<long[]> pSinX(new long[ nNewWidth ]);
673 std::unique_ptr<long[]> pCosY(new long[ nNewHeight ]);
674 std::unique_ptr<long[]> pSinY(new long[ nNewHeight ]);
676 for ( nX = 0; nX < nNewWidth; nX++ )
678 const double fTmp = ( fXMin + nX ) * 64.;
680 pCosX[ nX ] = FRound( fCosAngle * fTmp );
681 pSinX[ nX ] = FRound( fSinAngle * fTmp );
684 for ( nY = 0; nY < nNewHeight; nY++ )
686 const double fTmp = ( fYMin + nY ) * 64.;
688 pCosY[ nY ] = FRound( fCosAngle * fTmp );
689 pSinY[ nY ] = FRound( fSinAngle * fTmp );
692 for( nY = 0L; nY < nNewHeight; nY++ )
694 long nSinY = pSinY[ nY ];
695 long nCosY = pCosY[ nY ];
697 for( nX = 0L; nX < nNewWidth; nX++ )
699 nRotX = ( pCosX[ nX ] - nSinY ) >> 6;
700 nRotY = ( pSinX[ nX ] + nCosY ) >> 6;
702 if ( ( nRotX > -1L ) && ( nRotX < nWidth ) && ( nRotY > -1L ) && ( nRotY < nHeight ) )
703 pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nRotY, nRotX ) );
704 else
705 pWriteAcc->SetPixel( nY, nX, aFillColor );
709 pWriteAcc.reset();
712 aRotatedBmp = aNewBmp;
715 pReadAcc.reset();
718 bRet = !!aRotatedBmp;
719 if( bRet )
720 ImplAssignWithSize( aRotatedBmp );
723 return bRet;
726 bool Bitmap::Crop( const tools::Rectangle& rRectPixel )
728 const Size aSizePix( GetSizePixel() );
729 tools::Rectangle aRect( rRectPixel );
730 bool bRet = false;
732 aRect.Intersection( tools::Rectangle( Point(), aSizePix ) );
734 if( !aRect.IsEmpty() && aSizePix != aRect.GetSize())
736 ScopedReadAccess pReadAcc(*this);
738 if( pReadAcc )
740 Point aTmpPoint;
741 const tools::Rectangle aNewRect( aTmpPoint, aRect.GetSize() );
742 Bitmap aNewBmp( aNewRect.GetSize(), GetBitCount(), &pReadAcc->GetPalette() );
743 ScopedWriteAccess pWriteAcc(aNewBmp);
745 if( pWriteAcc )
747 const long nOldX = aRect.Left();
748 const long nOldY = aRect.Top();
749 const long nNewWidth = aNewRect.GetWidth();
750 const long nNewHeight = aNewRect.GetHeight();
752 for( long nY = 0, nY2 = nOldY; nY < nNewHeight; nY++, nY2++ )
753 for( long nX = 0, nX2 = nOldX; nX < nNewWidth; nX++, nX2++ )
754 pWriteAcc->SetPixel( nY, nX, pReadAcc->GetPixel( nY2, nX2 ) );
756 pWriteAcc.reset();
757 bRet = true;
760 pReadAcc.reset();
762 if( bRet )
763 ImplAssignWithSize( aNewBmp );
767 return bRet;
770 bool Bitmap::CopyPixel( const tools::Rectangle& rRectDst,
771 const tools::Rectangle& rRectSrc, const Bitmap* pBmpSrc )
773 const Size aSizePix( GetSizePixel() );
774 tools::Rectangle aRectDst( rRectDst );
775 bool bRet = false;
777 aRectDst.Intersection( tools::Rectangle( Point(), aSizePix ) );
779 if( !aRectDst.IsEmpty() )
781 if( pBmpSrc && ( *pBmpSrc != *this ) )
783 Bitmap* pSrc = const_cast<Bitmap*>(pBmpSrc);
784 const Size aCopySizePix( pSrc->GetSizePixel() );
785 tools::Rectangle aRectSrc( rRectSrc );
786 const sal_uInt16 nSrcBitCount = pBmpSrc->GetBitCount();
787 const sal_uInt16 nDstBitCount = GetBitCount();
789 if( nSrcBitCount > nDstBitCount )
791 int nNextIndex = 0;
793 if( ( nSrcBitCount == 24 ) && ( nDstBitCount < 24 ) )
794 Convert( BmpConversion::N24Bit );
795 else if( ( nSrcBitCount == 8 ) && ( nDstBitCount < 8 ) )
797 Convert( BmpConversion::N8BitColors );
798 nNextIndex = 16;
800 else if( ( nSrcBitCount == 4 ) && ( nDstBitCount < 4 ) )
802 Convert( BmpConversion::N4BitColors );
803 nNextIndex = 2;
806 if( nNextIndex )
808 ScopedReadAccess pSrcAcc(*pSrc);
809 ScopedWriteAccess pDstAcc(*this);
811 if( pSrcAcc && pDstAcc )
813 const int nSrcCount = pDstAcc->GetPaletteEntryCount();
814 const int nDstCount = 1 << nDstBitCount;
816 for (int i = 0; ( i < nSrcCount ) && ( nNextIndex < nSrcCount ); ++i)
818 const BitmapColor& rSrcCol = pSrcAcc->GetPaletteColor( (sal_uInt16) i );
820 bool bFound = false;
822 for (int j = 0; j < nDstCount; ++j)
824 if( rSrcCol == pDstAcc->GetPaletteColor( (sal_uInt16) j ) )
826 bFound = true;
827 break;
831 if( !bFound )
832 pDstAcc->SetPaletteColor( (sal_uInt16) nNextIndex++, rSrcCol );
838 aRectSrc.Intersection( tools::Rectangle( Point(), aCopySizePix ) );
840 if( !aRectSrc.IsEmpty() )
842 ScopedReadAccess pReadAcc(*pSrc);
844 if( pReadAcc )
846 ScopedWriteAccess pWriteAcc(*this);
848 if( pWriteAcc )
850 const long nWidth = std::min( aRectSrc.GetWidth(), aRectDst.GetWidth() );
851 const long nHeight = std::min( aRectSrc.GetHeight(), aRectDst.GetHeight() );
852 const long nSrcEndX = aRectSrc.Left() + nWidth;
853 const long nSrcEndY = aRectSrc.Top() + nHeight;
854 long nDstY = aRectDst.Top();
856 if( pReadAcc->HasPalette() && pWriteAcc->HasPalette() )
858 const sal_uInt16 nCount = pReadAcc->GetPaletteEntryCount();
859 std::unique_ptr<sal_uInt8[]> pMap(new sal_uInt8[ nCount ]);
861 // Create index map for the color table, as the bitmap should be copied
862 // retaining it's color information relatively well
863 for( sal_uInt16 i = 0; i < nCount; i++ )
864 pMap[ i ] = (sal_uInt8) pWriteAcc->GetBestPaletteIndex( pReadAcc->GetPaletteColor( i ) );
866 for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ )
867 for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ )
868 pWriteAcc->SetPixelIndex( nDstY, nDstX, pMap[ pReadAcc->GetPixelIndex( nSrcY, nSrcX ) ] );
870 else if( pReadAcc->HasPalette() )
872 for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ )
873 for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ )
874 pWriteAcc->SetPixel( nDstY, nDstX, pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( nSrcY, nSrcX ) ) );
876 else
877 for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ )
878 for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ )
879 pWriteAcc->SetPixel( nDstY, nDstX, pReadAcc->GetPixel( nSrcY, nSrcX ) );
881 pWriteAcc.reset();
882 bRet = ( nWidth > 0L ) && ( nHeight > 0L );
885 pReadAcc.reset();
889 else
891 tools::Rectangle aRectSrc( rRectSrc );
893 aRectSrc.Intersection( tools::Rectangle( Point(), aSizePix ) );
895 if( !aRectSrc.IsEmpty() && ( aRectSrc != aRectDst ) )
897 ScopedWriteAccess pWriteAcc(*this);
899 if( pWriteAcc )
901 const long nWidth = std::min( aRectSrc.GetWidth(), aRectDst.GetWidth() );
902 const long nHeight = std::min( aRectSrc.GetHeight(), aRectDst.GetHeight() );
903 const long nSrcX = aRectSrc.Left();
904 const long nSrcY = aRectSrc.Top();
905 const long nSrcEndX1 = nSrcX + nWidth - 1L;
906 const long nSrcEndY1 = nSrcY + nHeight - 1L;
907 const long nDstX = aRectDst.Left();
908 const long nDstY = aRectDst.Top();
909 const long nDstEndX1 = nDstX + nWidth - 1L;
910 const long nDstEndY1 = nDstY + nHeight - 1L;
912 if( ( nDstX <= nSrcX ) && ( nDstY <= nSrcY ) )
914 for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ )
915 for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ )
916 pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) );
918 else if( ( nDstX <= nSrcX ) && ( nDstY >= nSrcY ) )
920 for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- )
921 for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ )
922 pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) );
924 else if( ( nDstX >= nSrcX ) && ( nDstY <= nSrcY ) )
926 for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ )
927 for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- )
928 pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) );
930 else
932 for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- )
933 for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- )
934 pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) );
937 pWriteAcc.reset();
938 bRet = true;
944 return bRet;
947 bool Bitmap::CopyPixel_AlphaOptimized( const tools::Rectangle& rRectDst, const tools::Rectangle& rRectSrc,
948 const Bitmap* pBmpSrc )
950 // Note: this code is copied from Bitmap::CopyPixel but avoids any palette lookups
951 // This optimization is possible because the palettes of AlphaMasks are always identical (8bit GreyPalette, see ctor)
952 const Size aSizePix( GetSizePixel() );
953 tools::Rectangle aRectDst( rRectDst );
954 bool bRet = false;
956 aRectDst.Intersection( tools::Rectangle( Point(), aSizePix ) );
958 if( !aRectDst.IsEmpty() )
960 if( pBmpSrc && ( *pBmpSrc != *this ) )
962 Bitmap* pSrc = const_cast<Bitmap*>(pBmpSrc);
963 const Size aCopySizePix( pSrc->GetSizePixel() );
964 tools::Rectangle aRectSrc( rRectSrc );
966 aRectSrc.Intersection( tools::Rectangle( Point(), aCopySizePix ) );
968 if( !aRectSrc.IsEmpty() )
970 ScopedReadAccess pReadAcc(*pSrc);
972 if( pReadAcc )
974 ScopedWriteAccess pWriteAcc(*this);
976 if( pWriteAcc )
978 const long nWidth = std::min( aRectSrc.GetWidth(), aRectDst.GetWidth() );
979 const long nHeight = std::min( aRectSrc.GetHeight(), aRectDst.GetHeight() );
980 const long nSrcEndX = aRectSrc.Left() + nWidth;
981 const long nSrcEndY = aRectSrc.Top() + nHeight;
982 long nDstY = aRectDst.Top();
984 for( long nSrcY = aRectSrc.Top(); nSrcY < nSrcEndY; nSrcY++, nDstY++ )
985 for( long nSrcX = aRectSrc.Left(), nDstX = aRectDst.Left(); nSrcX < nSrcEndX; nSrcX++, nDstX++ )
986 pWriteAcc->SetPixel( nDstY, nDstX, pReadAcc->GetPixel( nSrcY, nSrcX ) );
988 pWriteAcc.reset();
989 bRet = ( nWidth > 0L ) && ( nHeight > 0L );
992 pReadAcc.reset();
996 else
998 tools::Rectangle aRectSrc( rRectSrc );
1000 aRectSrc.Intersection( tools::Rectangle( Point(), aSizePix ) );
1002 if( !aRectSrc.IsEmpty() && ( aRectSrc != aRectDst ) )
1004 ScopedWriteAccess pWriteAcc(*this);
1006 if( pWriteAcc )
1008 const long nWidth = std::min( aRectSrc.GetWidth(), aRectDst.GetWidth() );
1009 const long nHeight = std::min( aRectSrc.GetHeight(), aRectDst.GetHeight() );
1010 const long nSrcX = aRectSrc.Left();
1011 const long nSrcY = aRectSrc.Top();
1012 const long nSrcEndX1 = nSrcX + nWidth - 1L;
1013 const long nSrcEndY1 = nSrcY + nHeight - 1L;
1014 const long nDstX = aRectDst.Left();
1015 const long nDstY = aRectDst.Top();
1016 const long nDstEndX1 = nDstX + nWidth - 1L;
1017 const long nDstEndY1 = nDstY + nHeight - 1L;
1019 if( ( nDstX <= nSrcX ) && ( nDstY <= nSrcY ) )
1021 for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ )
1022 for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ )
1023 pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) );
1025 else if( ( nDstX <= nSrcX ) && ( nDstY >= nSrcY ) )
1027 for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- )
1028 for( long nX = nSrcX, nXN = nDstX; nX <= nSrcEndX1; nX++, nXN++ )
1029 pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) );
1031 else if( ( nDstX >= nSrcX ) && ( nDstY <= nSrcY ) )
1033 for( long nY = nSrcY, nYN = nDstY; nY <= nSrcEndY1; nY++, nYN++ )
1034 for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- )
1035 pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) );
1037 else
1039 for( long nY = nSrcEndY1, nYN = nDstEndY1; nY >= nSrcY; nY--, nYN-- )
1040 for( long nX = nSrcEndX1, nXN = nDstEndX1; nX >= nSrcX; nX--, nXN-- )
1041 pWriteAcc->SetPixel( nYN, nXN, pWriteAcc->GetPixel( nY, nX ) );
1044 pWriteAcc.reset();
1045 bRet = true;
1051 return bRet;
1055 bool Bitmap::Expand( sal_uLong nDX, sal_uLong nDY, const Color* pInitColor )
1057 bool bRet = false;
1059 if( nDX || nDY )
1061 const Size aSizePixel( GetSizePixel() );
1062 const long nWidth = aSizePixel.Width();
1063 const long nHeight = aSizePixel.Height();
1064 const Size aNewSize( nWidth + nDX, nHeight + nDY );
1065 ScopedReadAccess pReadAcc(*this);
1067 if( pReadAcc )
1069 BitmapPalette aBmpPal( pReadAcc->GetPalette() );
1070 Bitmap aNewBmp( aNewSize, GetBitCount(), &aBmpPal );
1071 ScopedWriteAccess pWriteAcc(aNewBmp);
1073 if( pWriteAcc )
1075 BitmapColor aColor;
1076 const long nNewX = nWidth;
1077 const long nNewY = nHeight;
1078 const long nNewWidth = pWriteAcc->Width();
1079 const long nNewHeight = pWriteAcc->Height();
1080 long nX;
1081 long nY;
1083 if( pInitColor )
1084 aColor = pWriteAcc->GetBestMatchingColor( *pInitColor );
1086 for( nY = 0L; nY < nHeight; nY++ )
1088 pWriteAcc->CopyScanline( nY, *pReadAcc );
1090 if( pInitColor && nDX )
1091 for( nX = nNewX; nX < nNewWidth; nX++ )
1092 pWriteAcc->SetPixel( nY, nX, aColor );
1095 if( pInitColor && nDY )
1096 for( nY = nNewY; nY < nNewHeight; nY++ )
1097 for( nX = 0; nX < nNewWidth; nX++ )
1098 pWriteAcc->SetPixel( nY, nX, aColor );
1100 pWriteAcc.reset();
1101 bRet = true;
1104 pReadAcc.reset();
1106 if( bRet )
1107 ImplAssignWithSize( aNewBmp );
1111 return bRet;
1114 Bitmap Bitmap::CreateMask( const Color& rTransColor, sal_uLong nTol ) const
1116 ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
1118 if (!nTol && pReadAcc && (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal)
1119 && pReadAcc->GetBestMatchingColor(Color(COL_WHITE)) == pReadAcc->GetBestMatchingColor(rTransColor))
1121 //if we're a 1 bit pixel already, and the transcolor matches the color that would replace it already already, then just return a copy
1122 return *this;
1125 Bitmap aNewBmp(GetSizePixel(), 1);
1126 ScopedWriteAccess pWriteAcc(aNewBmp);
1127 bool bRet = false;
1129 if (pWriteAcc && pReadAcc)
1131 const long nWidth = pReadAcc->Width();
1132 const long nHeight = pReadAcc->Height();
1133 const BitmapColor aBlack( pWriteAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
1134 const BitmapColor aWhite( pWriteAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
1136 if( !nTol )
1138 const BitmapColor aTest( pReadAcc->GetBestMatchingColor( rTransColor ) );
1139 long nX, nY;
1141 if( pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal ||
1142 pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitLsnPal )
1144 // optimized for 4Bit-MSN/LSN source palette
1145 const sal_uInt8 cTest = aTest.GetIndex();
1146 const long nShiftInit = ( ( pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal ) ? 4 : 0 );
1148 if( pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal &&
1149 aWhite.GetIndex() == 1 )
1151 // optimized for 1Bit-MSB destination palette
1152 for( nY = 0L; nY < nHeight; nY++ )
1154 Scanline pSrc = pReadAcc->GetScanline( nY );
1155 Scanline pDst = pWriteAcc->GetScanline( nY );
1156 long nShift = 0;
1157 for( nX = 0L, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4 )
1159 if( cTest == ( ( pSrc[ nX >> 1 ] >> nShift ) & 0x0f ) )
1160 pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) );
1161 else
1162 pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) );
1166 else
1168 for( nY = 0L; nY < nHeight; nY++ )
1170 Scanline pSrc = pReadAcc->GetScanline( nY );
1171 long nShift = 0;
1172 for( nX = 0L, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4 )
1174 if( cTest == ( ( pSrc[ nX >> 1 ] >> nShift ) & 0x0f ) )
1175 pWriteAcc->SetPixel( nY, nX, aWhite );
1176 else
1177 pWriteAcc->SetPixel( nY, nX, aBlack );
1182 else if( pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal )
1184 // optimized for 8Bit source palette
1185 const sal_uInt8 cTest = aTest.GetIndex();
1187 if( pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal &&
1188 aWhite.GetIndex() == 1 )
1190 // optimized for 1Bit-MSB destination palette
1191 for( nY = 0L; nY < nHeight; nY++ )
1193 Scanline pSrc = pReadAcc->GetScanline( nY );
1194 Scanline pDst = pWriteAcc->GetScanline( nY );
1195 for( nX = 0L; nX < nWidth; nX++ )
1197 if( cTest == pSrc[ nX ] )
1198 pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) );
1199 else
1200 pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) );
1204 else
1206 for( nY = 0L; nY < nHeight; nY++ )
1208 Scanline pSrc = pReadAcc->GetScanline( nY );
1209 for( nX = 0L; nX < nWidth; nX++ )
1211 if( cTest == pSrc[ nX ] )
1212 pWriteAcc->SetPixel( nY, nX, aWhite );
1213 else
1214 pWriteAcc->SetPixel( nY, nX, aBlack );
1219 else
1221 // not optimized
1222 for( nY = 0L; nY < nHeight; nY++ )
1224 for( nX = 0L; nX < nWidth; nX++ )
1226 if( aTest == pReadAcc->GetPixel( nY, nX ) )
1227 pWriteAcc->SetPixel( nY, nX, aWhite );
1228 else
1229 pWriteAcc->SetPixel( nY, nX, aBlack );
1234 else
1236 BitmapColor aCol;
1237 long nR, nG, nB;
1238 const long nMinR = MinMax<long>(rTransColor.GetRed() - nTol, 0, 255);
1239 const long nMaxR = MinMax<long>(rTransColor.GetRed() + nTol, 0, 255);
1240 const long nMinG = MinMax<long>(rTransColor.GetGreen() - nTol, 0, 255);
1241 const long nMaxG = MinMax<long>(rTransColor.GetGreen() + nTol, 0, 255);
1242 const long nMinB = MinMax<long>(rTransColor.GetBlue() - nTol, 0, 255);
1243 const long nMaxB = MinMax<long>(rTransColor.GetBlue() + nTol, 0, 255);
1245 if( pReadAcc->HasPalette() )
1247 for( long nY = 0L; nY < nHeight; nY++ )
1249 for( long nX = 0L; nX < nWidth; nX++ )
1251 aCol = pReadAcc->GetPaletteColor( pReadAcc->GetPixelIndex( nY, nX ) );
1252 nR = aCol.GetRed();
1253 nG = aCol.GetGreen();
1254 nB = aCol.GetBlue();
1256 if( nMinR <= nR && nMaxR >= nR &&
1257 nMinG <= nG && nMaxG >= nG &&
1258 nMinB <= nB && nMaxB >= nB )
1260 pWriteAcc->SetPixel( nY, nX, aWhite );
1262 else
1263 pWriteAcc->SetPixel( nY, nX, aBlack );
1267 else
1269 for( long nY = 0L; nY < nHeight; nY++ )
1271 for( long nX = 0L; nX < nWidth; nX++ )
1273 aCol = pReadAcc->GetPixel( nY, nX );
1274 nR = aCol.GetRed();
1275 nG = aCol.GetGreen();
1276 nB = aCol.GetBlue();
1278 if( nMinR <= nR && nMaxR >= nR &&
1279 nMinG <= nG && nMaxG >= nG &&
1280 nMinB <= nB && nMaxB >= nB )
1282 pWriteAcc->SetPixel( nY, nX, aWhite );
1284 else
1285 pWriteAcc->SetPixel( nY, nX, aBlack );
1291 bRet = true;
1294 pWriteAcc.reset();
1295 pReadAcc.reset();
1297 if( bRet )
1299 aNewBmp.maPrefSize = maPrefSize;
1300 aNewBmp.maPrefMapMode = maPrefMapMode;
1302 else
1303 aNewBmp = Bitmap();
1305 return aNewBmp;
1308 vcl::Region Bitmap::CreateRegion( const Color& rColor, const tools::Rectangle& rRect ) const
1310 vcl::Region aRegion;
1311 tools::Rectangle aRect( rRect );
1312 ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this));
1314 aRect.Intersection( tools::Rectangle( Point(), GetSizePixel() ) );
1315 aRect.Justify();
1317 if( pReadAcc )
1319 //Rectangle aSubRect;
1320 const long nLeft = aRect.Left();
1321 const long nTop = aRect.Top();
1322 const long nRight = aRect.Right();
1323 const long nBottom = aRect.Bottom();
1324 const BitmapColor aMatch( pReadAcc->GetBestMatchingColor( rColor ) );
1326 //RectangleVector aRectangles;
1327 //aRegion.ImplBeginAddRect();
1328 std::vector< long > aLine;
1329 long nYStart(nTop);
1330 long nY(nTop);
1332 for( ; nY <= nBottom; nY++ )
1334 //aSubRect.Top() = aSubRect.Bottom() = nY;
1335 std::vector< long > aNewLine;
1336 long nX(nLeft);
1338 for( ; nX <= nRight; )
1340 while( ( nX <= nRight ) && ( aMatch != pReadAcc->GetPixel( nY, nX ) ) )
1341 nX++;
1343 if( nX <= nRight )
1345 aNewLine.push_back(nX);
1346 //aSubRect.Left() = nX;
1348 while( ( nX <= nRight ) && ( aMatch == pReadAcc->GetPixel( nY, nX ) ) )
1349 nX++;
1351 //aSubRect.Right() = nX - 1L;
1352 aNewLine.push_back(nX - 1);
1354 //aRegion.ImplAddRect( aSubRect );
1355 //aRectangles.push_back(aSubRect);
1356 //aRegion.Union(aSubRect);
1360 if(aNewLine != aLine)
1362 // need to write aLine, it's different from the next line
1363 if(aLine.size())
1365 tools::Rectangle aSubRect;
1367 // enter y values and proceed ystart
1368 aSubRect.Top() = nYStart;
1369 aSubRect.Bottom() = nY ? nY - 1 : 0;
1371 for(size_t a(0); a < aLine.size();)
1373 aSubRect.Left() = aLine[a++];
1374 aSubRect.Right() = aLine[a++];
1375 aRegion.Union(aSubRect);
1379 // copy line as new line
1380 aLine = aNewLine;
1381 nYStart = nY;
1385 // write last line if used
1386 if(aLine.size())
1388 tools::Rectangle aSubRect;
1390 // enter y values
1391 aSubRect.Top() = nYStart;
1392 aSubRect.Bottom() = nY ? nY - 1 : 0;
1394 for(size_t a(0); a < aLine.size();)
1396 aSubRect.Left() = aLine[a++];
1397 aSubRect.Right() = aLine[a++];
1398 aRegion.Union(aSubRect);
1402 //aRegion.ImplEndAddRect();
1403 //aRegion.SetRegionRectangles(aRectangles);
1405 pReadAcc.reset();
1407 else
1408 aRegion = aRect;
1410 return aRegion;
1413 bool Bitmap::Replace( const Bitmap& rMask, const Color& rReplaceColor )
1415 ScopedReadAccess pMaskAcc( const_cast<Bitmap&>(rMask) );
1416 ScopedWriteAccess pAcc(*this);
1417 bool bRet = false;
1419 if( pMaskAcc && pAcc )
1421 const long nWidth = std::min( pMaskAcc->Width(), pAcc->Width() );
1422 const long nHeight = std::min( pMaskAcc->Height(), pAcc->Height() );
1423 const BitmapColor aMaskWhite( pMaskAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
1424 BitmapColor aReplace;
1426 if( pAcc->HasPalette() )
1428 const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount();
1429 const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount();
1431 // default to the nearest color
1432 aReplace = pAcc->GetBestMatchingColor( rReplaceColor );
1434 // for paletted images without a matching palette entry
1435 // look for an unused palette entry (NOTE: expensive!)
1436 if( pAcc->GetPaletteColor( aReplace.GetIndex() ) != BitmapColor( rReplaceColor ) )
1438 // if the palette has empty entries use the last one
1439 if( nActColors < nMaxColors )
1441 pAcc->SetPaletteEntryCount( nActColors + 1 );
1442 pAcc->SetPaletteColor( nActColors, rReplaceColor );
1443 aReplace = BitmapColor( (sal_uInt8) nActColors );
1445 else
1447 std::unique_ptr<bool[]> pFlags(new bool[ nMaxColors ]);
1449 // Set all entries to false
1450 std::fill( pFlags.get(), pFlags.get()+nMaxColors, false );
1452 for( long nY = 0L; nY < nHeight; nY++ )
1453 for( long nX = 0L; nX < nWidth; nX++ )
1454 pFlags[ pAcc->GetPixelIndex( nY, nX ) ] = true;
1456 for( sal_uInt16 i = 0UL; i < nMaxColors; i++ )
1458 // Hurray, we do have an unused entry
1459 if( !pFlags[ i ] )
1461 pAcc->SetPaletteColor( i, rReplaceColor );
1462 aReplace = BitmapColor( (sal_uInt8) i );
1468 else
1469 aReplace = rReplaceColor;
1471 for( long nY = 0L; nY < nHeight; nY++ )
1472 for( long nX = 0L; nX < nWidth; nX++ )
1473 if( pMaskAcc->GetPixel( nY, nX ) == aMaskWhite )
1474 pAcc->SetPixel( nY, nX, aReplace );
1476 bRet = true;
1479 return bRet;
1482 bool Bitmap::Replace( const AlphaMask& rAlpha, const Color& rMergeColor )
1484 Bitmap aNewBmp( GetSizePixel(), 24 );
1485 ScopedReadAccess pAcc(*this);
1486 AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
1487 ScopedWriteAccess pNewAcc(aNewBmp);
1488 bool bRet = false;
1490 if( pAcc && pAlphaAcc && pNewAcc )
1492 BitmapColor aCol;
1493 const long nWidth = std::min( pAlphaAcc->Width(), pAcc->Width() );
1494 const long nHeight = std::min( pAlphaAcc->Height(), pAcc->Height() );
1496 for( long nY = 0L; nY < nHeight; nY++ )
1498 for( long nX = 0L; nX < nWidth; nX++ )
1500 aCol = pAcc->GetColor( nY, nX );
1501 pNewAcc->SetPixel( nY, nX, aCol.Merge( rMergeColor, 255 - pAlphaAcc->GetPixelIndex( nY, nX ) ) );
1505 bRet = true;
1508 pAcc.reset();
1509 pAlphaAcc.reset();
1510 pNewAcc.reset();
1512 if( bRet )
1514 const MapMode aMap( maPrefMapMode );
1515 const Size aSize( maPrefSize );
1517 *this = aNewBmp;
1519 maPrefMapMode = aMap;
1520 maPrefSize = aSize;
1523 return bRet;
1526 bool Bitmap::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uLong nTol )
1528 if( mxImpBmp )
1530 // implementation specific replace
1531 std::shared_ptr<ImpBitmap> xImpBmp(new ImpBitmap);
1532 if (xImpBmp->ImplCreate(*mxImpBmp) && xImpBmp->ImplReplace(rSearchColor, rReplaceColor, nTol))
1534 ImplSetImpBitmap(xImpBmp);
1535 maPrefMapMode = MapMode( MapUnit::MapPixel );
1536 maPrefSize = xImpBmp->ImplGetSize();
1537 return true;
1541 // Bitmaps with 1 bit color depth can cause problems
1542 // if they have other entries than black/white in their palette
1543 if( 1 == GetBitCount() )
1544 Convert( BmpConversion::N4BitColors );
1546 ScopedWriteAccess pAcc(*this);
1547 bool bRet = false;
1549 if( pAcc )
1551 const long nMinR = MinMax<long>(rSearchColor.GetRed() - nTol, 0, 255);
1552 const long nMaxR = MinMax<long>(rSearchColor.GetRed() + nTol, 0, 255);
1553 const long nMinG = MinMax<long>(rSearchColor.GetGreen() - nTol, 0, 255);
1554 const long nMaxG = MinMax<long>(rSearchColor.GetGreen() + nTol, 0, 255);
1555 const long nMinB = MinMax<long>(rSearchColor.GetBlue() - nTol, 0, 255);
1556 const long nMaxB = MinMax<long>(rSearchColor.GetBlue() + nTol, 0, 255);
1558 if( pAcc->HasPalette() )
1560 for( sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++ )
1562 const BitmapColor& rCol = pAcc->GetPaletteColor( i );
1564 if( nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() &&
1565 nMinG <= rCol.GetGreen() && nMaxG >= rCol.GetGreen() &&
1566 nMinB <= rCol.GetBlue() && nMaxB >= rCol.GetBlue() )
1568 pAcc->SetPaletteColor( i, rReplaceColor );
1572 else
1574 BitmapColor aCol;
1575 const BitmapColor aReplace( pAcc->GetBestMatchingColor( rReplaceColor ) );
1577 for( long nY = 0L, nHeight = pAcc->Height(); nY < nHeight; nY++ )
1579 for( long nX = 0L, nWidth = pAcc->Width(); nX < nWidth; nX++ )
1581 aCol = pAcc->GetPixel( nY, nX );
1583 if( nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() &&
1584 nMinG <= aCol.GetGreen() && nMaxG >= aCol.GetGreen() &&
1585 nMinB <= aCol.GetBlue() && nMaxB >= aCol.GetBlue() )
1587 pAcc->SetPixel( nY, nX, aReplace );
1593 pAcc.reset();
1594 bRet = true;
1597 return bRet;
1600 bool Bitmap::Replace( const Color* pSearchColors, const Color* pReplaceColors,
1601 sal_uLong nColorCount, sal_uLong* _pTols )
1603 // Bitmaps with 1 bit color depth can cause problems
1604 // if they have other entries than black/white in their palette
1605 if( 1 == GetBitCount() )
1606 Convert( BmpConversion::N4BitColors );
1608 ScopedWriteAccess pAcc(*this);
1609 bool bRet = false;
1611 if( pAcc )
1613 std::unique_ptr<long[]> pMinR(new long[ nColorCount ]);
1614 std::unique_ptr<long[]> pMaxR(new long[ nColorCount ]);
1615 std::unique_ptr<long[]> pMinG(new long[ nColorCount ]);
1616 std::unique_ptr<long[]> pMaxG(new long[ nColorCount ]);
1617 std::unique_ptr<long[]> pMinB(new long[ nColorCount ]);
1618 std::unique_ptr<long[]> pMaxB(new long[ nColorCount ]);
1619 long* pTols;
1620 sal_uLong i;
1622 if( !_pTols )
1624 pTols = new long[ nColorCount ];
1625 memset( pTols, 0, nColorCount * sizeof( long ) );
1627 else
1628 pTols = reinterpret_cast<long*>(_pTols);
1630 for( i = 0UL; i < nColorCount; i++ )
1632 const Color& rCol = pSearchColors[ i ];
1633 const long nTol = pTols[ i ];
1635 pMinR[ i ] = MinMax<long>(rCol.GetRed() - nTol, 0, 255);
1636 pMaxR[ i ] = MinMax<long>(rCol.GetRed() + nTol, 0, 255);
1637 pMinG[ i ] = MinMax<long>(rCol.GetGreen() - nTol, 0, 255);
1638 pMaxG[ i ] = MinMax<long>(rCol.GetGreen() + nTol, 0, 255);
1639 pMinB[ i ] = MinMax<long>(rCol.GetBlue() - nTol, 0, 255);
1640 pMaxB[ i ] = MinMax<long>(rCol.GetBlue() + nTol, 0, 255);
1643 if( pAcc->HasPalette() )
1645 for( sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount(); nEntry < nPalCount; nEntry++ )
1647 const BitmapColor& rCol = pAcc->GetPaletteColor( nEntry );
1649 for( i = 0UL; i < nColorCount; i++ )
1651 if( pMinR[ i ] <= rCol.GetRed() && pMaxR[ i ] >= rCol.GetRed() &&
1652 pMinG[ i ] <= rCol.GetGreen() && pMaxG[ i ] >= rCol.GetGreen() &&
1653 pMinB[ i ] <= rCol.GetBlue() && pMaxB[ i ] >= rCol.GetBlue() )
1655 pAcc->SetPaletteColor( nEntry, pReplaceColors[ i ] );
1656 break;
1661 else
1663 BitmapColor aCol;
1664 std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[ nColorCount ]);
1666 for( i = 0UL; i < nColorCount; i++ )
1667 pReplaces[ i ] = pAcc->GetBestMatchingColor( pReplaceColors[ i ] );
1669 for( long nY = 0L, nHeight = pAcc->Height(); nY < nHeight; nY++ )
1671 for( long nX = 0L, nWidth = pAcc->Width(); nX < nWidth; nX++ )
1673 aCol = pAcc->GetPixel( nY, nX );
1675 for( i = 0UL; i < nColorCount; i++ )
1677 if( pMinR[ i ] <= aCol.GetRed() && pMaxR[ i ] >= aCol.GetRed() &&
1678 pMinG[ i ] <= aCol.GetGreen() && pMaxG[ i ] >= aCol.GetGreen() &&
1679 pMinB[ i ] <= aCol.GetBlue() && pMaxB[ i ] >= aCol.GetBlue() )
1681 pAcc->SetPixel( nY, nX, pReplaces[ i ] );
1682 break;
1689 if( !_pTols )
1690 delete[] pTols;
1692 pAcc.reset();
1693 bRet = true;
1696 return bRet;
1699 Bitmap Bitmap::CreateDisplayBitmap( OutputDevice* pDisplay )
1701 Bitmap aDispBmp( *this );
1703 SalGraphics* pDispGraphics = pDisplay->GetGraphics();
1705 if( mxImpBmp && pDispGraphics )
1707 std::shared_ptr<ImpBitmap> xImpDispBmp(new ImpBitmap);
1708 if (xImpDispBmp->ImplCreate(*mxImpBmp, pDispGraphics))
1709 aDispBmp.ImplSetImpBitmap(xImpDispBmp);
1712 return aDispBmp;
1715 bool Bitmap::CombineSimple( const Bitmap& rMask, BmpCombine eCombine )
1717 ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask));
1718 ScopedWriteAccess pAcc(*this);
1719 bool bRet = false;
1721 if( pMaskAcc && pAcc )
1723 const long nWidth = std::min( pMaskAcc->Width(), pAcc->Width() );
1724 const long nHeight = std::min( pMaskAcc->Height(), pAcc->Height() );
1725 const Color aColBlack( COL_BLACK );
1726 BitmapColor aPixel;
1727 BitmapColor aMaskPixel;
1728 const BitmapColor aWhite( pAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
1729 const BitmapColor aBlack( pAcc->GetBestMatchingColor( aColBlack ) );
1730 const BitmapColor aMaskBlack( pMaskAcc->GetBestMatchingColor( aColBlack ) );
1732 switch( eCombine )
1734 case BmpCombine::And:
1736 for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ )
1738 if( pMaskAcc->GetPixel( nY, nX ) != aMaskBlack && pAcc->GetPixel( nY, nX ) != aBlack )
1739 pAcc->SetPixel( nY, nX, aWhite );
1740 else
1741 pAcc->SetPixel( nY, nX, aBlack );
1744 break;
1746 case BmpCombine::Or:
1748 for( long nY = 0L; nY < nHeight; nY++ ) for( long nX = 0L; nX < nWidth; nX++ )
1750 if( pMaskAcc->GetPixel( nY, nX ) != aMaskBlack || pAcc->GetPixel( nY, nX ) != aBlack )
1751 pAcc->SetPixel( nY, nX, aWhite );
1752 else
1753 pAcc->SetPixel( nY, nX, aBlack );
1756 break;
1760 bRet = true;
1763 return bRet;
1766 // TODO: Have a look at OutputDevice::ImplDrawAlpha() for some
1767 // optimizations. Might even consolidate the code here and there.
1768 bool Bitmap::Blend( const AlphaMask& rAlpha, const Color& rBackgroundColor )
1770 // Convert to a truecolor bitmap, if we're a paletted one. There's
1771 // room for tradeoff decision here, maybe later for an overload (or a flag)
1772 if( GetBitCount() <= 8 )
1773 Convert( BmpConversion::N24Bit );
1775 AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha));
1777 ScopedWriteAccess pAcc(*this);
1778 bool bRet = false;
1780 if( pAlphaAcc && pAcc )
1782 const long nWidth = std::min( pAlphaAcc->Width(), pAcc->Width() );
1783 const long nHeight = std::min( pAlphaAcc->Height(), pAcc->Height() );
1785 for( long nY = 0L; nY < nHeight; ++nY )
1786 for( long nX = 0L; nX < nWidth; ++nX )
1787 pAcc->SetPixel( nY, nX,
1788 pAcc->GetPixel( nY, nX ).Merge( rBackgroundColor,
1789 255 - pAlphaAcc->GetPixelIndex( nY, nX ) ) );
1791 bRet = true;
1794 return bRet;
1797 bool Bitmap::MakeMono( sal_uInt8 cThreshold )
1799 return ImplMakeMono( cThreshold );
1802 bool Bitmap::GetSystemData( BitmapSystemData& rData ) const
1804 bool bRet = false;
1805 if (mxImpBmp)
1807 SalBitmap* pSalBitmap = mxImpBmp->ImplGetSalBitmap();
1808 if( pSalBitmap )
1809 bRet = pSalBitmap->GetSystemData( rData );
1812 return bRet;
1815 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */