diff options
author | Chris Sherlock <chris.sherlock79@gmail.com> | 2018-03-01 06:07:39 +1100 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2018-03-06 05:08:19 +0100 |
commit | bdd62b4c327ab894dbba00fe2e07696c1b7d9de6 (patch) | |
tree | e6da9e7783142a37b0edf190946688a8073a5a65 /vcl | |
parent | 221b17794c1cb15ce7f5a0b5a543fc79eee7085f (diff) |
vcl: split painting bitmap functions to bitmappaint.cxx
Change-Id: Ib5f52e4b7b5121de15cdb205165fcbdb8b09bc8a
Reviewed-on: https://gerrit.libreoffice.org/50530
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 3 | ||||
-rw-r--r-- | vcl/source/bitmap/bitmap.cxx | 1088 | ||||
-rw-r--r-- | vcl/source/bitmap/bitmappaint.cxx | 1158 |
3 files changed, 1164 insertions, 1085 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index b116fd15705a..9e292916f843 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -300,8 +300,9 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/gdi/wall \ vcl/source/gdi/scrptrun \ vcl/source/gdi/CommonSalLayout \ - vcl/source/bitmap/bitmap \ + vcl/source/bitmap/bitmap \ vcl/source/bitmap/bitmapfilter \ + vcl/source/bitmap/bitmappaint \ vcl/source/bitmap/bitmapscalesuper \ vcl/source/bitmap/BitmapScaleConvolution \ vcl/source/bitmap/BitmapSymmetryCheck \ diff --git a/vcl/source/bitmap/bitmap.cxx b/vcl/source/bitmap/bitmap.cxx index bf670f20ffce..b5602327f75d 100644 --- a/vcl/source/bitmap/bitmap.cxx +++ b/vcl/source/bitmap/bitmap.cxx @@ -17,20 +17,15 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#include <algorithm> -#include <rtl/crc.h> -#include <tools/stream.hxx> -#include <tools/poly.hxx> -#include <vcl/salbtype.hxx> +#include <osl/diagnose.h> +#include <vcl/bitmap.hxx> #include <vcl/bitmapaccess.hxx> #include <vcl/outdev.hxx> -#include <vcl/bitmap.hxx> -#include <vcl/bitmapex.hxx> -#include <vcl/svapp.hxx> -#include <vcl/image.hxx> #include <impbmp.hxx> #include <salbmp.hxx> + +#include <algorithm> #include <memory> Bitmap::Bitmap() @@ -377,366 +372,6 @@ void Bitmap::ReleaseAccess( BitmapInfoAccess* pBitmapAccess ) delete pBitmapAccess; } -bool Bitmap::Erase(const Color& rFillColor) -{ - if (IsEmpty()) - return true; - - Bitmap::ScopedWriteAccess pWriteAcc(*this); - bool bRet = false; - - if (pWriteAcc) - { - const ScanlineFormat nFormat = pWriteAcc->GetScanlineFormat(); - sal_uInt8 cIndex = 0; - bool bFast = false; - - switch (nFormat) - { - case ScanlineFormat::N1BitMsbPal: - case ScanlineFormat::N1BitLsbPal: - { - cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor)); - cIndex = (cIndex ? 255 : 0); - bFast = true; - } - break; - - case ScanlineFormat::N4BitMsnPal: - case ScanlineFormat::N4BitLsnPal: - { - cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor)); - cIndex = cIndex | ( cIndex << 4 ); - bFast = true; - } - break; - - case ScanlineFormat::N8BitPal: - { - cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor)); - bFast = true; - } - break; - - case ScanlineFormat::N24BitTcBgr: - case ScanlineFormat::N24BitTcRgb: - { - if (rFillColor.GetRed() == rFillColor.GetGreen() && - rFillColor.GetRed() == rFillColor.GetBlue()) - { - cIndex = rFillColor.GetRed(); - bFast = true; - } - else - bFast = false; - } - break; - - default: - bFast = false; - break; - } - - if( bFast ) - { - const sal_uLong nBufSize = pWriteAcc->GetScanlineSize() * pWriteAcc->Height(); - memset( pWriteAcc->GetBuffer(), cIndex, nBufSize ); - } - else - { - const tools::Rectangle aRect( Point(), Size( pWriteAcc->Width(), pWriteAcc->Height() ) ); - pWriteAcc->SetFillColor( rFillColor ); - pWriteAcc->FillRect( aRect ); - } - - bRet = true; - } - - return bRet; -} - -bool Bitmap::Invert() -{ - ScopedWriteAccess pAcc(*this); - bool bRet = false; - - if( pAcc ) - { - if( pAcc->HasPalette() ) - { - BitmapPalette aBmpPal( pAcc->GetPalette() ); - const sal_uInt16 nCount = aBmpPal.GetEntryCount(); - - for( sal_uInt16 i = 0; i < nCount; i++ ) - aBmpPal[ i ].Invert(); - - pAcc->SetPalette( aBmpPal ); - } - else - { - const long nWidth = pAcc->Width(); - const long nHeight = pAcc->Height(); - - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pAcc->GetScanline(nY); - for( long nX = 0; nX < nWidth; nX++ ) - pAcc->SetPixelOnData( pScanline, nX, pAcc->GetPixelFromData( pScanline, nX ).Invert() ); - } - } - - mxImpBmp->ImplInvalidateChecksum(); - pAcc.reset(); - bRet = true; - } - - return bRet; -} - -bool Bitmap::Mirror( BmpMirrorFlags nMirrorFlags ) -{ - bool bHorz( nMirrorFlags & BmpMirrorFlags::Horizontal ); - bool bVert( nMirrorFlags & BmpMirrorFlags::Vertical ); - bool bRet = false; - - if( bHorz && !bVert ) - { - ScopedWriteAccess pAcc(*this); - - if( pAcc ) - { - const long nWidth = pAcc->Width(); - const long nHeight = pAcc->Height(); - const long nWidth1 = nWidth - 1; - const long nWidth_2 = nWidth >> 1; - - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pAcc->GetScanline(nY); - for( long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther-- ) - { - const BitmapColor aTemp( pAcc->GetPixelFromData( pScanline, nX ) ); - - pAcc->SetPixelOnData( pScanline, nX, pAcc->GetPixelFromData( pScanline, nOther ) ); - pAcc->SetPixelOnData( pScanline, nOther, aTemp ); - } - } - - pAcc.reset(); - bRet = true; - } - } - else if( bVert && !bHorz ) - { - ScopedWriteAccess pAcc(*this); - - if( pAcc ) - { - const long nScanSize = pAcc->GetScanlineSize(); - std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[ nScanSize ]); - const long nHeight = pAcc->Height(); - const long nHeight1 = nHeight - 1; - const long nHeight_2 = nHeight >> 1; - - for( long nY = 0, nOther = nHeight1; nY < nHeight_2; nY++, nOther-- ) - { - memcpy( pBuffer.get(), pAcc->GetScanline( nY ), nScanSize ); - memcpy( pAcc->GetScanline( nY ), pAcc->GetScanline( nOther ), nScanSize ); - memcpy( pAcc->GetScanline( nOther ), pBuffer.get(), nScanSize ); - } - - pAcc.reset(); - bRet = true; - } - } - else if( bHorz && bVert ) - { - ScopedWriteAccess pAcc(*this); - - if( pAcc ) - { - const long nWidth = pAcc->Width(); - const long nWidth1 = nWidth - 1; - const long nHeight = pAcc->Height(); - long nHeight_2 = nHeight >> 1; - - for( long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY-- ) - { - Scanline pScanline = pAcc->GetScanline(nY); - Scanline pScanlineOther = pAcc->GetScanline(nOtherY); - for( long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX-- ) - { - const BitmapColor aTemp( pAcc->GetPixelFromData( pScanline, nX ) ); - - pAcc->SetPixelOnData( pScanline, nX, pAcc->GetPixelFromData( pScanlineOther, nOtherX ) ); - pAcc->SetPixelOnData( pScanlineOther, nOtherX, aTemp ); - } - } - - // if necessary, also mirror the middle line horizontally - if( nHeight & 1 ) - { - Scanline pScanline = pAcc->GetScanline(nHeight_2); - for( long nX = 0, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2; nX++, nOtherX-- ) - { - const BitmapColor aTemp( pAcc->GetPixelFromData( pScanline, nX ) ); - pAcc->SetPixelOnData( pScanline, nX, pAcc->GetPixelFromData( pScanline, nOtherX ) ); - pAcc->SetPixelOnData( pScanline, nOtherX, aTemp ); - } - } - - pAcc.reset(); - bRet = true; - } - } - else - bRet = true; - - return bRet; -} - -bool Bitmap::Rotate( long nAngle10, const Color& rFillColor ) -{ - bool bRet = false; - - nAngle10 %= 3600; - nAngle10 = ( nAngle10 < 0 ) ? ( 3599L + nAngle10 ) : nAngle10; - - if( !nAngle10 ) - bRet = true; - else if( 1800 == nAngle10 ) - bRet = Mirror( BmpMirrorFlags::Horizontal | BmpMirrorFlags::Vertical ); - else - { - ScopedReadAccess pReadAcc(*this); - Bitmap aRotatedBmp; - - if( pReadAcc ) - { - const Size aSizePix( GetSizePixel() ); - - if( ( 900 == nAngle10 ) || ( 2700 == nAngle10 ) ) - { - const Size aNewSizePix( aSizePix.Height(), aSizePix.Width() ); - Bitmap aNewBmp( aNewSizePix, GetBitCount(), &pReadAcc->GetPalette() ); - ScopedWriteAccess pWriteAcc(aNewBmp); - - if( pWriteAcc ) - { - const long nWidth = aSizePix.Width(); - const long nWidth1 = nWidth - 1; - const long nHeight = aSizePix.Height(); - const long nHeight1 = nHeight - 1; - const long nNewWidth = aNewSizePix.Width(); - const long nNewHeight = aNewSizePix.Height(); - - if( 900 == nAngle10 ) - { - for( long nY = 0, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX-- ) - { - Scanline pScanline = pWriteAcc->GetScanline(nY); - for( long nX = 0, nOtherY = 0; nX < nNewWidth; nX++ ) - pWriteAcc->SetPixelOnData( pScanline, nX, pReadAcc->GetPixel( nOtherY++, nOtherX ) ); - } - } - else if( 2700 == nAngle10 ) - { - for( long nY = 0, nOtherX = 0; nY < nNewHeight; nY++, nOtherX++ ) - { - Scanline pScanline = pWriteAcc->GetScanline(nY); - for( long nX = 0, nOtherY = nHeight1; nX < nNewWidth; nX++ ) - pWriteAcc->SetPixelOnData( pScanline, nX, pReadAcc->GetPixel( nOtherY--, nOtherX ) ); - } - } - - pWriteAcc.reset(); - } - - aRotatedBmp = aNewBmp; - } - else - { - Point aTmpPoint; - tools::Rectangle aTmpRectangle( aTmpPoint, aSizePix ); - tools::Polygon aPoly( aTmpRectangle ); - aPoly.Rotate( aTmpPoint, static_cast<sal_uInt16>(nAngle10) ); - - tools::Rectangle aNewBound( aPoly.GetBoundRect() ); - const Size aNewSizePix( aNewBound.GetSize() ); - Bitmap aNewBmp( aNewSizePix, GetBitCount(), &pReadAcc->GetPalette() ); - ScopedWriteAccess pWriteAcc(aNewBmp); - - if( pWriteAcc ) - { - const BitmapColor aFillColor( pWriteAcc->GetBestMatchingColor( rFillColor ) ); - const double fCosAngle = cos( nAngle10 * F_PI1800 ); - const double fSinAngle = sin( nAngle10 * F_PI1800 ); - const double fXMin = aNewBound.Left(); - const double fYMin = aNewBound.Top(); - const long nWidth = aSizePix.Width(); - const long nHeight = aSizePix.Height(); - const long nNewWidth = aNewSizePix.Width(); - const long nNewHeight = aNewSizePix.Height(); - long nX; - long nY; - long nRotX; - long nRotY; - std::unique_ptr<long[]> pCosX(new long[ nNewWidth ]); - std::unique_ptr<long[]> pSinX(new long[ nNewWidth ]); - std::unique_ptr<long[]> pCosY(new long[ nNewHeight ]); - std::unique_ptr<long[]> pSinY(new long[ nNewHeight ]); - - for ( nX = 0; nX < nNewWidth; nX++ ) - { - const double fTmp = ( fXMin + nX ) * 64.; - - pCosX[ nX ] = FRound( fCosAngle * fTmp ); - pSinX[ nX ] = FRound( fSinAngle * fTmp ); - } - - for ( nY = 0; nY < nNewHeight; nY++ ) - { - const double fTmp = ( fYMin + nY ) * 64.; - - pCosY[ nY ] = FRound( fCosAngle * fTmp ); - pSinY[ nY ] = FRound( fSinAngle * fTmp ); - } - - for( nY = 0; nY < nNewHeight; nY++ ) - { - long nSinY = pSinY[ nY ]; - long nCosY = pCosY[ nY ]; - Scanline pScanline = pWriteAcc->GetScanline(nY); - - for( nX = 0; nX < nNewWidth; nX++ ) - { - nRotX = ( pCosX[ nX ] - nSinY ) >> 6; - nRotY = ( pSinX[ nX ] + nCosY ) >> 6; - - if ( ( nRotX > -1 ) && ( nRotX < nWidth ) && ( nRotY > -1 ) && ( nRotY < nHeight ) ) - pWriteAcc->SetPixelOnData( pScanline, nX, pReadAcc->GetPixel( nRotY, nRotX ) ); - else - pWriteAcc->SetPixelOnData( pScanline, nX, aFillColor ); - } - } - - pWriteAcc.reset(); - } - - aRotatedBmp = aNewBmp; - } - - pReadAcc.reset(); - } - - bRet = !!aRotatedBmp; - if( bRet ) - ImplAssignWithSize( aRotatedBmp ); - } - - return bRet; -}; - bool Bitmap::Crop( const tools::Rectangle& rRectPixel ) { const Size aSizePix( GetSizePixel() ); @@ -1182,625 +817,6 @@ bool Bitmap::Expand( sal_uLong nDX, sal_uLong nDY, const Color* pInitColor ) return bRet; } -Bitmap Bitmap::CreateMask( const Color& rTransColor, sal_uInt8 nTol ) const -{ - ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this)); - - if (!nTol && pReadAcc && (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal) - && pReadAcc->GetBestMatchingColor(COL_WHITE) == pReadAcc->GetBestMatchingColor(rTransColor)) - { - //if we're a 1 bit pixel already, and the transcolor matches the color that would replace it already, then just return a copy - return *this; - } - - Bitmap aNewBmp(GetSizePixel(), 1); - ScopedWriteAccess pWriteAcc(aNewBmp); - bool bRet = false; - - if (pWriteAcc && pReadAcc) - { - const long nWidth = pReadAcc->Width(); - const long nHeight = pReadAcc->Height(); - const BitmapColor aBlack( pWriteAcc->GetBestMatchingColor( COL_BLACK ) ); - const BitmapColor aWhite( pWriteAcc->GetBestMatchingColor( COL_WHITE ) ); - - if( !nTol ) - { - const BitmapColor aTest( pReadAcc->GetBestMatchingColor( rTransColor ) ); - - if( pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal || - pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitLsnPal ) - { - // optimized for 4Bit-MSN/LSN source palette - const sal_uInt8 cTest = aTest.GetIndex(); - const long nShiftInit = ( ( pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal ) ? 4 : 0 ); - - if( pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal && - aWhite.GetIndex() == 1 ) - { - // optimized for 1Bit-MSB destination palette - for (long nY = 0; nY < nHeight; ++nY) - { - Scanline pSrc = pReadAcc->GetScanline( nY ); - Scanline pDst = pWriteAcc->GetScanline( nY ); - for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4) - { - if( cTest == ( ( pSrc[ nX >> 1 ] >> nShift ) & 0x0f ) ) - pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) ); - else - pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) ); - } - } - } - else - { - for (long nY = 0; nY < nHeight; ++nY) - { - Scanline pSrc = pReadAcc->GetScanline( nY ); - Scanline pDst = pWriteAcc->GetScanline( nY ); - for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4) - { - if( cTest == ( ( pSrc[ nX >> 1 ] >> nShift ) & 0x0f ) ) - pWriteAcc->SetPixelOnData( pDst, nX, aWhite ); - else - pWriteAcc->SetPixelOnData( pDst, nX, aBlack ); - } - } - } - } - else if( pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal ) - { - // optimized for 8Bit source palette - const sal_uInt8 cTest = aTest.GetIndex(); - - if( pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal && - aWhite.GetIndex() == 1 ) - { - // optimized for 1Bit-MSB destination palette - for (long nY = 0; nY < nHeight; ++nY) - { - Scanline pSrc = pReadAcc->GetScanline( nY ); - Scanline pDst = pWriteAcc->GetScanline( nY ); - for (long nX = 0; nX < nWidth; ++nX) - { - if( cTest == pSrc[ nX ] ) - pDst[ nX >> 3 ] |= 1 << ( 7 - ( nX & 7 ) ); - else - pDst[ nX >> 3 ] &= ~( 1 << ( 7 - ( nX & 7 ) ) ); - } - } - } - else - { - for (long nY = 0; nY < nHeight; ++nY) - { - Scanline pSrc = pReadAcc->GetScanline( nY ); - Scanline pDst = pWriteAcc->GetScanline( nY ); - for (long nX = 0; nX < nWidth; ++nX) - { - if( cTest == pSrc[ nX ] ) - pWriteAcc->SetPixelOnData( pDst, nX, aWhite ); - else - pWriteAcc->SetPixelOnData( pDst, nX, aBlack ); - } - } - } - } - else if (pWriteAcc->GetScanlineFormat() == pReadAcc->GetScanlineFormat() && aWhite.GetIndex() == 1 && - (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal)) - { - for (long nY = 0; nY < nHeight; ++nY) - { - Scanline pSrc = pReadAcc->GetScanline(nY); - Scanline pDst = pWriteAcc->GetScanline(nY); - assert(pWriteAcc->GetScanlineSize() == pReadAcc->GetScanlineSize()); - const long nScanlineSize = pWriteAcc->GetScanlineSize(); - for (long nX = 0; nX < nScanlineSize; ++nX) - pDst[nX] = ~pSrc[nX]; - } - } - else - { - // not optimized - for (long nY = 0; nY < nHeight; ++nY) - { - Scanline pScanline = pWriteAcc->GetScanline( nY ); - Scanline pScanlineRead = pReadAcc->GetScanline( nY ); - for (long nX = 0; nX < nWidth; ++nX) - { - if( aTest == pReadAcc->GetPixelFromData( pScanlineRead, nX ) ) - pWriteAcc->SetPixelOnData( pScanline, nX, aWhite ); - else - pWriteAcc->SetPixelOnData( pScanline, nX, aBlack ); - } - } - } - } - else - { - BitmapColor aCol; - long nR, nG, nB; - const long nMinR = MinMax<long>(rTransColor.GetRed() - nTol, 0, 255); - const long nMaxR = MinMax<long>(rTransColor.GetRed() + nTol, 0, 255); - const long nMinG = MinMax<long>(rTransColor.GetGreen() - nTol, 0, 255); - const long nMaxG = MinMax<long>(rTransColor.GetGreen() + nTol, 0, 255); - const long nMinB = MinMax<long>(rTransColor.GetBlue() - nTol, 0, 255); - const long nMaxB = MinMax<long>(rTransColor.GetBlue() + nTol, 0, 255); - - if( pReadAcc->HasPalette() ) - { - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pWriteAcc->GetScanline( nY ); - Scanline pScanlineRead = pReadAcc->GetScanline(nY); - for( long nX = 0; nX < nWidth; nX++ ) - { - aCol = pReadAcc->GetPaletteColor( pReadAcc->GetIndexFromData( pScanlineRead, nX ) ); - nR = aCol.GetRed(); - nG = aCol.GetGreen(); - nB = aCol.GetBlue(); - - if( nMinR <= nR && nMaxR >= nR && - nMinG <= nG && nMaxG >= nG && - nMinB <= nB && nMaxB >= nB ) - { - pWriteAcc->SetPixelOnData( pScanline, nX, aWhite ); - } - else - pWriteAcc->SetPixelOnData( pScanline, nX, aBlack ); - } - } - } - else - { - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pWriteAcc->GetScanline( nY ); - Scanline pScanlineRead = pReadAcc->GetScanline(nY); - for( long nX = 0; nX < nWidth; nX++ ) - { - aCol = pReadAcc->GetPixelFromData( pScanlineRead, nX ); - nR = aCol.GetRed(); - nG = aCol.GetGreen(); - nB = aCol.GetBlue(); - - if( nMinR <= nR && nMaxR >= nR && - nMinG <= nG && nMaxG >= nG && - nMinB <= nB && nMaxB >= nB ) - { - pWriteAcc->SetPixelOnData( pScanline, nX, aWhite ); - } - else - pWriteAcc->SetPixelOnData( pScanline, nX, aBlack ); - } - } - } - } - - bRet = true; - } - - pWriteAcc.reset(); - pReadAcc.reset(); - - if( bRet ) - { - aNewBmp.maPrefSize = maPrefSize; - aNewBmp.maPrefMapMode = maPrefMapMode; - } - else - aNewBmp = Bitmap(); - - return aNewBmp; -} - -vcl::Region Bitmap::CreateRegion( const Color& rColor, const tools::Rectangle& rRect ) const -{ - vcl::Region aRegion; - tools::Rectangle aRect( rRect ); - ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this)); - - aRect.Intersection( tools::Rectangle( Point(), GetSizePixel() ) ); - aRect.Justify(); - - if( pReadAcc ) - { - //Rectangle aSubRect; - const long nLeft = aRect.Left(); - const long nTop = aRect.Top(); - const long nRight = aRect.Right(); - const long nBottom = aRect.Bottom(); - const BitmapColor aMatch( pReadAcc->GetBestMatchingColor( rColor ) ); - - //RectangleVector aRectangles; - //aRegion.ImplBeginAddRect(); - std::vector< long > aLine; - long nYStart(nTop); - long nY(nTop); - - for( ; nY <= nBottom; nY++ ) - { - //aSubRect.Top() = aSubRect.Bottom() = nY; - std::vector< long > aNewLine; - long nX(nLeft); - Scanline pScanlineRead = pReadAcc->GetScanline(nY); - - for( ; nX <= nRight; ) - { - while( ( nX <= nRight ) && ( aMatch != pReadAcc->GetPixelFromData( pScanlineRead, nX ) ) ) - nX++; - - if( nX <= nRight ) - { - aNewLine.push_back(nX); - //aSubRect.Left() = nX; - - while( ( nX <= nRight ) && ( aMatch == pReadAcc->GetPixelFromData( pScanlineRead, nX ) ) ) - nX++; - - //aSubRect.Right() = nX - 1; - aNewLine.push_back(nX - 1); - - //aRegion.ImplAddRect( aSubRect ); - //aRectangles.push_back(aSubRect); - //aRegion.Union(aSubRect); - } - } - - if(aNewLine != aLine) - { - // need to write aLine, it's different from the next line - if(aLine.size()) - { - tools::Rectangle aSubRect; - - // enter y values and proceed ystart - aSubRect.SetTop( nYStart ); - aSubRect.SetBottom( nY ? nY - 1 : 0 ); - - for(size_t a(0); a < aLine.size();) - { - aSubRect.SetLeft( aLine[a++] ); - aSubRect.SetRight( aLine[a++] ); - aRegion.Union(aSubRect); - } - } - - // copy line as new line - aLine = aNewLine; - nYStart = nY; - } - } - - // write last line if used - if(aLine.size()) - { - tools::Rectangle aSubRect; - - // enter y values - aSubRect.SetTop( nYStart ); - aSubRect.SetBottom( nY ? nY - 1 : 0 ); - - for(size_t a(0); a < aLine.size();) - { - aSubRect.SetLeft( aLine[a++] ); - aSubRect.SetRight( aLine[a++] ); - aRegion.Union(aSubRect); - } - } - - //aRegion.ImplEndAddRect(); - //aRegion.SetRegionRectangles(aRectangles); - - pReadAcc.reset(); - } - else - aRegion = aRect; - - return aRegion; -} - -bool Bitmap::Replace( const Bitmap& rMask, const Color& rReplaceColor ) -{ - ScopedReadAccess pMaskAcc( const_cast<Bitmap&>(rMask) ); - ScopedWriteAccess pAcc(*this); - bool bRet = false; - - if( pMaskAcc && pAcc ) - { - const long nWidth = std::min( pMaskAcc->Width(), pAcc->Width() ); - const long nHeight = std::min( pMaskAcc->Height(), pAcc->Height() ); - const BitmapColor aMaskWhite( pMaskAcc->GetBestMatchingColor( COL_WHITE ) ); - BitmapColor aReplace; - - if( pAcc->HasPalette() ) - { - const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount(); - const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount(); - - // default to the nearest color - aReplace = pAcc->GetBestMatchingColor( rReplaceColor ); - - // for paletted images without a matching palette entry - // look for an unused palette entry (NOTE: expensive!) - if( pAcc->GetPaletteColor( aReplace.GetIndex() ) != BitmapColor( rReplaceColor ) ) - { - // if the palette has empty entries use the last one - if( nActColors < nMaxColors ) - { - pAcc->SetPaletteEntryCount( nActColors + 1 ); - pAcc->SetPaletteColor( nActColors, rReplaceColor ); - aReplace = BitmapColor( static_cast<sal_uInt8>(nActColors) ); - } - else - { - std::unique_ptr<bool[]> pFlags(new bool[ nMaxColors ]); - - // Set all entries to false - std::fill( pFlags.get(), pFlags.get()+nMaxColors, false ); - - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pAcc->GetScanline(nY); - for( long nX = 0; nX < nWidth; nX++ ) - pFlags[ pAcc->GetIndexFromData( pScanline, nX ) ] = true; - } - - for( sal_uInt16 i = 0; i < nMaxColors; i++ ) - { - // Hurray, we do have an unused entry - if( !pFlags[ i ] ) - { - pAcc->SetPaletteColor( i, rReplaceColor ); - aReplace = BitmapColor( static_cast<sal_uInt8>(i) ); - } - } - } - } - } - else - aReplace = rReplaceColor; - - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pAcc->GetScanline( nY ); - Scanline pScanlineMask = pMaskAcc->GetScanline( nY ); - for( long nX = 0; nX < nWidth; nX++ ) - if( pMaskAcc->GetPixelFromData( pScanlineMask, nX ) == aMaskWhite ) - pAcc->SetPixelOnData( pScanline, nX, aReplace ); - } - - bRet = true; - } - - return bRet; -} - -bool Bitmap::Replace( const AlphaMask& rAlpha, const Color& rMergeColor ) -{ - Bitmap aNewBmp( GetSizePixel(), 24 ); - ScopedReadAccess pAcc(*this); - AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha)); - ScopedWriteAccess pNewAcc(aNewBmp); - bool bRet = false; - - if( pAcc && pAlphaAcc && pNewAcc ) - { - BitmapColor aCol; - const long nWidth = std::min( pAlphaAcc->Width(), pAcc->Width() ); - const long nHeight = std::min( pAlphaAcc->Height(), pAcc->Height() ); - - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pNewAcc->GetScanline( nY ); - Scanline pScanlineAlpha = pAlphaAcc->GetScanline( nY ); - for( long nX = 0; nX < nWidth; nX++ ) - { - aCol = pAcc->GetColor( nY, nX ); - pNewAcc->SetPixelOnData( pScanline, nX, aCol.Merge( rMergeColor, 255 - pAlphaAcc->GetIndexFromData( pScanlineAlpha, nX ) ) ); - } - } - - bRet = true; - } - - pAcc.reset(); - pAlphaAcc.reset(); - pNewAcc.reset(); - - if( bRet ) - { - const MapMode aMap( maPrefMapMode ); - const Size aSize( maPrefSize ); - - *this = aNewBmp; - - maPrefMapMode = aMap; - maPrefSize = aSize; - } - - return bRet; -} - -bool Bitmap::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol ) -{ - if( mxImpBmp ) - { - // implementation specific replace - std::shared_ptr<ImpBitmap> xImpBmp(new ImpBitmap); - if (xImpBmp->ImplCreate(*mxImpBmp) && xImpBmp->ImplReplace(rSearchColor, rReplaceColor, nTol)) - { - ImplSetImpBitmap(xImpBmp); - maPrefMapMode = MapMode( MapUnit::MapPixel ); - maPrefSize = xImpBmp->ImplGetSize(); - return true; - } - } - - // Bitmaps with 1 bit color depth can cause problems - // if they have other entries than black/white in their palette - if( 1 == GetBitCount() ) - Convert( BmpConversion::N4BitColors ); - - ScopedWriteAccess pAcc(*this); - bool bRet = false; - - if( pAcc ) - { - const long nMinR = MinMax<long>(rSearchColor.GetRed() - nTol, 0, 255); - const long nMaxR = MinMax<long>(rSearchColor.GetRed() + nTol, 0, 255); - const long nMinG = MinMax<long>(rSearchColor.GetGreen() - nTol, 0, 255); - const long nMaxG = MinMax<long>(rSearchColor.GetGreen() + nTol, 0, 255); - const long nMinB = MinMax<long>(rSearchColor.GetBlue() - nTol, 0, 255); - const long nMaxB = MinMax<long>(rSearchColor.GetBlue() + nTol, 0, 255); - - if( pAcc->HasPalette() ) - { - for( sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++ ) - { - const BitmapColor& rCol = pAcc->GetPaletteColor( i ); - - if( nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && - nMinG <= rCol.GetGreen() && nMaxG >= rCol.GetGreen() && - nMinB <= rCol.GetBlue() && nMaxB >= rCol.GetBlue() ) - { - pAcc->SetPaletteColor( i, rReplaceColor ); - } - } - } - else - { - BitmapColor aCol; - const BitmapColor aReplace( pAcc->GetBestMatchingColor( rReplaceColor ) ); - - for( long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++ ) - { - Scanline pScanline = pAcc->GetScanline( nY ); - for( long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++ ) - { - aCol = pAcc->GetPixelFromData( pScanline, nX ); - - if( nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && - nMinG <= aCol.GetGreen() && nMaxG >= aCol.GetGreen() && - nMinB <= aCol.GetBlue() && nMaxB >= aCol.GetBlue() ) - { - pAcc->SetPixelOnData( pScanline, nX, aReplace ); - } - } - } - } - - pAcc.reset(); - bRet = true; - } - - return bRet; -} - -bool Bitmap::Replace( const Color* pSearchColors, const Color* pReplaceColors, - sal_uLong nColorCount, sal_uInt8 const * pTols ) -{ - // Bitmaps with 1 bit color depth can cause problems - // if they have other entries than black/white in their palette - if( 1 == GetBitCount() ) - Convert( BmpConversion::N4BitColors ); - - ScopedWriteAccess pAcc(*this); - bool bRet = false; - - if( pAcc ) - { - std::unique_ptr<long[]> pMinR(new long[ nColorCount ]); - std::unique_ptr<long[]> pMaxR(new long[ nColorCount ]); - std::unique_ptr<long[]> pMinG(new long[ nColorCount ]); - std::unique_ptr<long[]> pMaxG(new long[ nColorCount ]); - std::unique_ptr<long[]> pMinB(new long[ nColorCount ]); - std::unique_ptr<long[]> pMaxB(new long[ nColorCount ]); - - if( pTols ) - { - for( sal_uLong i = 0; i < nColorCount; i++ ) - { - const Color& rCol = pSearchColors[ i ]; - const sal_uInt8 nTol = pTols[ i ]; - - pMinR[ i ] = MinMax<long>(rCol.GetRed() - nTol, 0, 255); - pMaxR[ i ] = MinMax<long>(rCol.GetRed() + nTol, 0, 255); - pMinG[ i ] = MinMax<long>(rCol.GetGreen() - nTol, 0, 255); - pMaxG[ i ] = MinMax<long>(rCol.GetGreen() + nTol, 0, 255); - pMinB[ i ] = MinMax<long>(rCol.GetBlue() - nTol, 0, 255); - pMaxB[ i ] = MinMax<long>(rCol.GetBlue() + nTol, 0, 255); - } - } - else - { - for( sal_uLong i = 0; i < nColorCount; i++ ) - { - const Color& rCol = pSearchColors[ i ]; - - pMinR[ i ] = rCol.GetRed(); - pMaxR[ i ] = rCol.GetRed(); - pMinG[ i ] = rCol.GetGreen(); - pMaxG[ i ] = rCol.GetGreen(); - pMinB[ i ] = rCol.GetBlue(); - pMaxB[ i ] = rCol.GetBlue(); - } - } - - if( pAcc->HasPalette() ) - { - for( sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount(); nEntry < nPalCount; nEntry++ ) - { - const BitmapColor& rCol = pAcc->GetPaletteColor( nEntry ); - - for( sal_uLong i = 0; i < nColorCount; i++ ) - { - if( pMinR[ i ] <= rCol.GetRed() && pMaxR[ i ] >= rCol.GetRed() && - pMinG[ i ] <= rCol.GetGreen() && pMaxG[ i ] >= rCol.GetGreen() && - pMinB[ i ] <= rCol.GetBlue() && pMaxB[ i ] >= rCol.GetBlue() ) - { - pAcc->SetPaletteColor( nEntry, pReplaceColors[ i ] ); - break; - } - } - } - } - else - { - BitmapColor aCol; - std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[ nColorCount ]); - - for( sal_uLong i = 0; i < nColorCount; i++ ) - pReplaces[ i ] = pAcc->GetBestMatchingColor( pReplaceColors[ i ] ); - - for( long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++ ) - { - Scanline pScanline = pAcc->GetScanline( nY ); - for( long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++ ) - { - aCol = pAcc->GetPixelFromData( pScanline, nX ); - - for( sal_uLong i = 0; i < nColorCount; i++ ) - { - if( pMinR[ i ] <= aCol.GetRed() && pMaxR[ i ] >= aCol.GetRed() && - pMinG[ i ] <= aCol.GetGreen() && pMaxG[ i ] >= aCol.GetGreen() && - pMinB[ i ] <= aCol.GetBlue() && pMaxB[ i ] >= aCol.GetBlue() ) - { - pAcc->SetPixelOnData( pScanline, nX, pReplaces[ i ] ); - break; - } - } - } - } - } - - pAcc.reset(); - bRet = true; - } - - return bRet; -} - Bitmap Bitmap::CreateDisplayBitmap( OutputDevice* pDisplay ) { Bitmap aDispBmp( *this ); @@ -1817,102 +833,6 @@ Bitmap Bitmap::CreateDisplayBitmap( OutputDevice* pDisplay ) return aDispBmp; } -bool Bitmap::CombineSimple( const Bitmap& rMask, BmpCombine eCombine ) -{ - ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask)); - ScopedWriteAccess pAcc(*this); - bool bRet = false; - - if( pMaskAcc && pAcc ) - { - const long nWidth = std::min( pMaskAcc->Width(), pAcc->Width() ); - const long nHeight = std::min( pMaskAcc->Height(), pAcc->Height() ); - const Color aColBlack( COL_BLACK ); - BitmapColor aPixel; - BitmapColor aMaskPixel; - const BitmapColor aWhite( pAcc->GetBestMatchingColor( COL_WHITE ) ); - const BitmapColor aBlack( pAcc->GetBestMatchingColor( aColBlack ) ); - const BitmapColor aMaskBlack( pMaskAcc->GetBestMatchingColor( aColBlack ) ); - - switch( eCombine ) - { - case BmpCombine::And: - { - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pAcc->GetScanline( nY ); - Scanline pScanlineMask = pMaskAcc->GetScanline( nY ); - for( long nX = 0; nX < nWidth; nX++ ) - { - if( pMaskAcc->GetPixelFromData( pScanlineMask, nX ) != aMaskBlack && pAcc->GetPixelFromData( pScanline, nX ) != aBlack ) - pAcc->SetPixelOnData( pScanline, nX, aWhite ); - else - pAcc->SetPixelOnData( pScanline, nX, aBlack ); - } - } - } - break; - - case BmpCombine::Or: - { - for( long nY = 0; nY < nHeight; nY++ ) - { - Scanline pScanline = pAcc->GetScanline( nY ); - Scanline pScanlineMask = pMaskAcc->GetScanline( nY ); - for( long nX = 0; nX < nWidth; nX++ ) - { - if( pMaskAcc->GetPixelFromData( pScanlineMask, nX ) != aMaskBlack || pAcc->GetPixelFromData( pScanline, nX ) != aBlack ) - pAcc->SetPixelOnData( pScanline, nX, aWhite ); - else - pAcc->SetPixelOnData( pScanline, nX, aBlack ); - } - } - } - break; - - } - - bRet = true; - } - - return bRet; -} - -// TODO: Have a look at OutputDevice::ImplDrawAlpha() for some -// optimizations. Might even consolidate the code here and there. -bool Bitmap::Blend( const AlphaMask& rAlpha, const Color& rBackgroundColor ) -{ - // Convert to a truecolor bitmap, if we're a paletted one. There's - // room for tradeoff decision here, maybe later for an overload (or a flag) - if( GetBitCount() <= 8 ) - Convert( BmpConversion::N24Bit ); - - AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha)); - - ScopedWriteAccess pAcc(*this); - bool bRet = false; - - if( pAlphaAcc && pAcc ) - { - const long nWidth = std::min( pAlphaAcc->Width(), pAcc->Width() ); - const long nHeight = std::min( pAlphaAcc->Height(), pAcc->Height() ); - - for( long nY = 0; nY < nHeight; ++nY ) - { - Scanline pScanline = pAcc->GetScanline( nY ); - Scanline pScanlineAlpha = pAlphaAcc->GetScanline( nY ); - for( long nX = 0; nX < nWidth; ++nX ) - pAcc->SetPixelOnData( pScanline, nX, - pAcc->GetPixelFromData( pScanline, nX ).Merge( rBackgroundColor, - 255 - pAlphaAcc->GetIndexFromData( pScanlineAlpha, nX ) ) ); - } - - bRet = true; - } - - return bRet; -} - bool Bitmap::MakeMonochrome(sal_uInt8 cThreshold) { ScopedReadAccess pReadAcc(*this); diff --git a/vcl/source/bitmap/bitmappaint.cxx b/vcl/source/bitmap/bitmappaint.cxx new file mode 100644 index 000000000000..38472333a0c3 --- /dev/null +++ b/vcl/source/bitmap/bitmappaint.cxx @@ -0,0 +1,1158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/poly.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/bitmapaccess.hxx> +#include <vcl/alpha.hxx> + +#include <impbmp.hxx> + +#include <algorithm> +#include <memory> + +bool Bitmap::Erase(const Color& rFillColor) +{ + if (IsEmpty()) + return true; + + Bitmap::ScopedWriteAccess pWriteAcc(*this); + bool bRet = false; + + if (pWriteAcc) + { + const ScanlineFormat nFormat = pWriteAcc->GetScanlineFormat(); + sal_uInt8 cIndex = 0; + bool bFast = false; + + switch (nFormat) + { + case ScanlineFormat::N1BitMsbPal: + case ScanlineFormat::N1BitLsbPal: + { + cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor)); + cIndex = (cIndex ? 255 : 0); + bFast = true; + } + break; + + case ScanlineFormat::N4BitMsnPal: + case ScanlineFormat::N4BitLsnPal: + { + cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor)); + cIndex = cIndex | (cIndex << 4); + bFast = true; + } + break; + + case ScanlineFormat::N8BitPal: + { + cIndex = static_cast<sal_uInt8>(pWriteAcc->GetBestPaletteIndex(rFillColor)); + bFast = true; + } + break; + + case ScanlineFormat::N24BitTcBgr: + case ScanlineFormat::N24BitTcRgb: + { + if (rFillColor.GetRed() == rFillColor.GetGreen() + && rFillColor.GetRed() == rFillColor.GetBlue()) + { + cIndex = rFillColor.GetRed(); + bFast = true; + } + else + { + bFast = false; + } + } + break; + + default: + bFast = false; + break; + } + + if (bFast) + { + const sal_uLong nBufSize = pWriteAcc->GetScanlineSize() * pWriteAcc->Height(); + memset(pWriteAcc->GetBuffer(), cIndex, nBufSize); + } + else + { + const tools::Rectangle aRect(Point(), Size(pWriteAcc->Width(), pWriteAcc->Height())); + pWriteAcc->SetFillColor(rFillColor); + pWriteAcc->FillRect(aRect); + } + + bRet = true; + } + + return bRet; +} + +bool Bitmap::Invert() +{ + ScopedWriteAccess pAcc(*this); + bool bRet = false; + + if (pAcc) + { + if (pAcc->HasPalette()) + { + BitmapPalette aBmpPal(pAcc->GetPalette()); + const sal_uInt16 nCount = aBmpPal.GetEntryCount(); + + for (sal_uInt16 i = 0; i < nCount; i++) + { + aBmpPal[i].Invert(); + } + + pAcc->SetPalette(aBmpPal); + } + else + { + const long nWidth = pAcc->Width(); + const long nHeight = pAcc->Height(); + + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; nX++) + { + pAcc->SetPixelOnData(pScanline, nX, + pAcc->GetPixelFromData(pScanline, nX).Invert()); + } + } + } + + mxImpBmp->ImplInvalidateChecksum(); + pAcc.reset(); + bRet = true; + } + + return bRet; +} + +bool Bitmap::Mirror(BmpMirrorFlags nMirrorFlags) +{ + bool bHorz(nMirrorFlags & BmpMirrorFlags::Horizontal); + bool bVert(nMirrorFlags & BmpMirrorFlags::Vertical); + bool bRet = false; + + if (bHorz && !bVert) + { + ScopedWriteAccess pAcc(*this); + + if (pAcc) + { + const long nWidth = pAcc->Width(); + const long nHeight = pAcc->Height(); + const long nWidth1 = nWidth - 1; + const long nWidth_2 = nWidth >> 1; + + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pAcc->GetScanline(nY); + for (long nX = 0, nOther = nWidth1; nX < nWidth_2; nX++, nOther--) + { + const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX)); + + pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOther)); + pAcc->SetPixelOnData(pScanline, nOther, aTemp); + } + } + + pAcc.reset(); + bRet = true; + } + } + else if (bVert && !bHorz) + { + ScopedWriteAccess pAcc(*this); + + if (pAcc) + { + const long nScanSize = pAcc->GetScanlineSize(); + std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nScanSize]); + const long nHeight = pAcc->Height(); + const long nHeight1 = nHeight - 1; + const long nHeight_2 = nHeight >> 1; + + for (long nY = 0, nOther = nHeight1; nY < nHeight_2; nY++, nOther--) + { + memcpy(pBuffer.get(), pAcc->GetScanline(nY), nScanSize); + memcpy(pAcc->GetScanline(nY), pAcc->GetScanline(nOther), nScanSize); + memcpy(pAcc->GetScanline(nOther), pBuffer.get(), nScanSize); + } + + pAcc.reset(); + bRet = true; + } + } + else if (bHorz && bVert) + { + ScopedWriteAccess pAcc(*this); + + if (pAcc) + { + const long nWidth = pAcc->Width(); + const long nWidth1 = nWidth - 1; + const long nHeight = pAcc->Height(); + long nHeight_2 = nHeight >> 1; + + for (long nY = 0, nOtherY = nHeight - 1; nY < nHeight_2; nY++, nOtherY--) + { + Scanline pScanline = pAcc->GetScanline(nY); + Scanline pScanlineOther = pAcc->GetScanline(nOtherY); + for (long nX = 0, nOtherX = nWidth1; nX < nWidth; nX++, nOtherX--) + { + const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX)); + + pAcc->SetPixelOnData(pScanline, nX, + pAcc->GetPixelFromData(pScanlineOther, nOtherX)); + pAcc->SetPixelOnData(pScanlineOther, nOtherX, aTemp); + } + } + + // if necessary, also mirror the middle line horizontally + if (nHeight & 1) + { + Scanline pScanline = pAcc->GetScanline(nHeight_2); + for (long nX = 0, nOtherX = nWidth1, nWidth_2 = nWidth >> 1; nX < nWidth_2; + nX++, nOtherX--) + { + const BitmapColor aTemp(pAcc->GetPixelFromData(pScanline, nX)); + pAcc->SetPixelOnData(pScanline, nX, pAcc->GetPixelFromData(pScanline, nOtherX)); + pAcc->SetPixelOnData(pScanline, nOtherX, aTemp); + } + } + + pAcc.reset(); + bRet = true; + } + } + else + bRet = true; + + return bRet; +} + +bool Bitmap::Rotate(long nAngle10, const Color& rFillColor) +{ + bool bRet = false; + + nAngle10 %= 3600; + nAngle10 = (nAngle10 < 0) ? (3599L + nAngle10) : nAngle10; + + if (!nAngle10) + bRet = true; + else if (nAngle10 == 1800) + bRet = Mirror(BmpMirrorFlags::Horizontal | BmpMirrorFlags::Vertical); + else + { + ScopedReadAccess pReadAcc(*this); + Bitmap aRotatedBmp; + + if (pReadAcc) + { + const Size aSizePix(GetSizePixel()); + + if (nAngle10 == 900 || nAngle10 == 2700) + { + const Size aNewSizePix(aSizePix.Height(), aSizePix.Width()); + Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette()); + ScopedWriteAccess pWriteAcc(aNewBmp); + + if (pWriteAcc) + { + const long nWidth = aSizePix.Width(); + const long nWidth1 = nWidth - 1; + const long nHeight = aSizePix.Height(); + const long nHeight1 = nHeight - 1; + const long nNewWidth = aNewSizePix.Width(); + const long nNewHeight = aNewSizePix.Height(); + + if (nAngle10 == 900) + { + for (long nY = 0, nOtherX = nWidth1; nY < nNewHeight; nY++, nOtherX--) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + for (long nX = 0, nOtherY = 0; nX < nNewWidth; nX++) + { + pWriteAcc->SetPixelOnData(pScanline, nX, + pReadAcc->GetPixel(nOtherY++, nOtherX)); + } + } + } + else if (nAngle10 == 2700) + { + for (long nY = 0, nOtherX = 0; nY < nNewHeight; nY++, nOtherX++) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + for (long nX = 0, nOtherY = nHeight1; nX < nNewWidth; nX++) + { + pWriteAcc->SetPixelOnData(pScanline, nX, + pReadAcc->GetPixel(nOtherY--, nOtherX)); + } + } + } + + pWriteAcc.reset(); + } + + aRotatedBmp = aNewBmp; + } + else + { + Point aTmpPoint; + tools::Rectangle aTmpRectangle(aTmpPoint, aSizePix); + tools::Polygon aPoly(aTmpRectangle); + aPoly.Rotate(aTmpPoint, static_cast<sal_uInt16>(nAngle10)); + + tools::Rectangle aNewBound(aPoly.GetBoundRect()); + const Size aNewSizePix(aNewBound.GetSize()); + Bitmap aNewBmp(aNewSizePix, GetBitCount(), &pReadAcc->GetPalette()); + ScopedWriteAccess pWriteAcc(aNewBmp); + + if (pWriteAcc) + { + const BitmapColor aFillColor(pWriteAcc->GetBestMatchingColor(rFillColor)); + const double fCosAngle = cos(nAngle10 * F_PI1800); + const double fSinAngle = sin(nAngle10 * F_PI1800); + const double fXMin = aNewBound.Left(); + const double fYMin = aNewBound.Top(); + const long nWidth = aSizePix.Width(); + const long nHeight = aSizePix.Height(); + const long nNewWidth = aNewSizePix.Width(); + const long nNewHeight = aNewSizePix.Height(); + long nX; + long nY; + long nRotX; + long nRotY; + std::unique_ptr<long[]> pCosX(new long[nNewWidth]); + std::unique_ptr<long[]> pSinX(new long[nNewWidth]); + std::unique_ptr<long[]> pCosY(new long[nNewHeight]); + std::unique_ptr<long[]> pSinY(new long[nNewHeight]); + + for (nX = 0; nX < nNewWidth; nX++) + { + const double fTmp = (fXMin + nX) * 64.; + + pCosX[nX] = FRound(fCosAngle * fTmp); + pSinX[nX] = FRound(fSinAngle * fTmp); + } + + for (nY = 0; nY < nNewHeight; nY++) + { + const double fTmp = (fYMin + nY) * 64.; + + pCosY[nY] = FRound(fCosAngle * fTmp); + pSinY[nY] = FRound(fSinAngle * fTmp); + } + + for (nY = 0; nY < nNewHeight; nY++) + { + long nSinY = pSinY[nY]; + long nCosY = pCosY[nY]; + Scanline pScanline = pWriteAcc->GetScanline(nY); + + for (nX = 0; nX < nNewWidth; nX++) + { + nRotX = (pCosX[nX] - nSinY) >> 6; + nRotY = (pSinX[nX] + nCosY) >> 6; + + if ((nRotX > -1) && (nRotX < nWidth) && (nRotY > -1) + && (nRotY < nHeight)) + { + pWriteAcc->SetPixelOnData(pScanline, nX, + pReadAcc->GetPixel(nRotY, nRotX)); + } + else + { + pWriteAcc->SetPixelOnData(pScanline, nX, aFillColor); + } + } + } + + pWriteAcc.reset(); + } + + aRotatedBmp = aNewBmp; + } + + pReadAcc.reset(); + } + + bRet = !!aRotatedBmp; + if (bRet) + ImplAssignWithSize(aRotatedBmp); + } + + return bRet; +}; + +Bitmap Bitmap::CreateMask(const Color& rTransColor, sal_uInt8 nTol) const +{ + ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this)); + + if (!nTol && pReadAcc + && (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal + || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal) + && pReadAcc->GetBestMatchingColor(COL_WHITE) == pReadAcc->GetBestMatchingColor(rTransColor)) + { + // if we're a 1 bit pixel already, and the transcolor matches the color that would replace it + // already, then just return a copy + return *this; + } + + Bitmap aNewBmp(GetSizePixel(), 1); + ScopedWriteAccess pWriteAcc(aNewBmp); + bool bRet = false; + + if (pWriteAcc && pReadAcc) + { + const long nWidth = pReadAcc->Width(); + const long nHeight = pReadAcc->Height(); + const BitmapColor aBlack(pWriteAcc->GetBestMatchingColor(COL_BLACK)); + const BitmapColor aWhite(pWriteAcc->GetBestMatchingColor(COL_WHITE)); + + if (!nTol) + { + const BitmapColor aTest(pReadAcc->GetBestMatchingColor(rTransColor)); + + if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal + || pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitLsnPal) + { + // optimized for 4Bit-MSN/LSN source palette + const sal_uInt8 cTest = aTest.GetIndex(); + const long nShiftInit + = ((pReadAcc->GetScanlineFormat() == ScanlineFormat::N4BitMsnPal) ? 4 : 0); + + if (pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal + && aWhite.GetIndex() == 1) + { + // optimized for 1Bit-MSB destination palette + for (long nY = 0; nY < nHeight; ++nY) + { + Scanline pSrc = pReadAcc->GetScanline(nY); + Scanline pDst = pWriteAcc->GetScanline(nY); + for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4) + { + if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f)) + pDst[nX >> 3] |= 1 << (7 - (nX & 7)); + else + pDst[nX >> 3] &= ~(1 << (7 - (nX & 7))); + } + } + } + else + { + for (long nY = 0; nY < nHeight; ++nY) + { + Scanline pSrc = pReadAcc->GetScanline(nY); + Scanline pDst = pWriteAcc->GetScanline(nY); + for (long nX = 0, nShift = nShiftInit; nX < nWidth; nX++, nShift ^= 4) + { + if (cTest == ((pSrc[nX >> 1] >> nShift) & 0x0f)) + pWriteAcc->SetPixelOnData(pDst, nX, aWhite); + else + pWriteAcc->SetPixelOnData(pDst, nX, aBlack); + } + } + } + } + else if (pReadAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal) + { + // optimized for 8Bit source palette + const sal_uInt8 cTest = aTest.GetIndex(); + + if (pWriteAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal + && aWhite.GetIndex() == 1) + { + // optimized for 1Bit-MSB destination palette + for (long nY = 0; nY < nHeight; ++nY) + { + Scanline pSrc = pReadAcc->GetScanline(nY); + Scanline pDst = pWriteAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; ++nX) + { + if (cTest == pSrc[nX]) + pDst[nX >> 3] |= 1 << (7 - (nX & 7)); + else + pDst[nX >> 3] &= ~(1 << (7 - (nX & 7))); + } + } + } + else + { + for (long nY = 0; nY < nHeight; ++nY) + { + Scanline pSrc = pReadAcc->GetScanline(nY); + Scanline pDst = pWriteAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; ++nX) + { + if (cTest == pSrc[nX]) + pWriteAcc->SetPixelOnData(pDst, nX, aWhite); + else + pWriteAcc->SetPixelOnData(pDst, nX, aBlack); + } + } + } + } + else if (pWriteAcc->GetScanlineFormat() == pReadAcc->GetScanlineFormat() + && aWhite.GetIndex() == 1 + && (pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitLsbPal + || pReadAcc->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal)) + { + for (long nY = 0; nY < nHeight; ++nY) + { + Scanline pSrc = pReadAcc->GetScanline(nY); + Scanline pDst = pWriteAcc->GetScanline(nY); + assert(pWriteAcc->GetScanlineSize() == pReadAcc->GetScanlineSize()); + const long nScanlineSize = pWriteAcc->GetScanlineSize(); + for (long nX = 0; nX < nScanlineSize; ++nX) + pDst[nX] = ~pSrc[nX]; + } + } + else + { + // not optimized + for (long nY = 0; nY < nHeight; ++nY) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + Scanline pScanlineRead = pReadAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; ++nX) + { + if (aTest == pReadAcc->GetPixelFromData(pScanlineRead, nX)) + pWriteAcc->SetPixelOnData(pScanline, nX, aWhite); + else + pWriteAcc->SetPixelOnData(pScanline, nX, aBlack); + } + } + } + } + else + { + BitmapColor aCol; + long nR, nG, nB; + const long nMinR = MinMax<long>(rTransColor.GetRed() - nTol, 0, 255); + const long nMaxR = MinMax<long>(rTransColor.GetRed() + nTol, 0, 255); + const long nMinG = MinMax<long>(rTransColor.GetGreen() - nTol, 0, 255); + const long nMaxG = MinMax<long>(rTransColor.GetGreen() + nTol, 0, 255); + const long nMinB = MinMax<long>(rTransColor.GetBlue() - nTol, 0, 255); + const long nMaxB = MinMax<long>(rTransColor.GetBlue() + nTol, 0, 255); + + if (pReadAcc->HasPalette()) + { + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + Scanline pScanlineRead = pReadAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; nX++) + { + aCol = pReadAcc->GetPaletteColor( + pReadAcc->GetIndexFromData(pScanlineRead, nX)); + nR = aCol.GetRed(); + nG = aCol.GetGreen(); + nB = aCol.GetBlue(); + + if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB + && nMaxB >= nB) + { + pWriteAcc->SetPixelOnData(pScanline, nX, aWhite); + } + else + { + pWriteAcc->SetPixelOnData(pScanline, nX, aBlack); + } + } + } + } + else + { + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pWriteAcc->GetScanline(nY); + Scanline pScanlineRead = pReadAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; nX++) + { + aCol = pReadAcc->GetPixelFromData(pScanlineRead, nX); + nR = aCol.GetRed(); + nG = aCol.GetGreen(); + nB = aCol.GetBlue(); + + if (nMinR <= nR && nMaxR >= nR && nMinG <= nG && nMaxG >= nG && nMinB <= nB + && nMaxB >= nB) + { + pWriteAcc->SetPixelOnData(pScanline, nX, aWhite); + } + else + { + pWriteAcc->SetPixelOnData(pScanline, nX, aBlack); + } + } + } + } + } + + bRet = true; + } + + pWriteAcc.reset(); + pReadAcc.reset(); + + if (bRet) + { + aNewBmp.maPrefSize = maPrefSize; + aNewBmp.maPrefMapMode = maPrefMapMode; + } + else + aNewBmp = Bitmap(); + + return aNewBmp; +} + +vcl::Region Bitmap::CreateRegion(const Color& rColor, const tools::Rectangle& rRect) const +{ + vcl::Region aRegion; + tools::Rectangle aRect(rRect); + ScopedReadAccess pReadAcc(const_cast<Bitmap&>(*this)); + + aRect.Intersection(tools::Rectangle(Point(), GetSizePixel())); + aRect.Justify(); + + if (pReadAcc) + { + const long nLeft = aRect.Left(); + const long nTop = aRect.Top(); + const long nRight = aRect.Right(); + const long nBottom = aRect.Bottom(); + const BitmapColor aMatch(pReadAcc->GetBestMatchingColor(rColor)); + + //RectangleVector aRectangles; + //aRegion.ImplBeginAddRect(); + std::vector<long> aLine; + long nYStart(nTop); + long nY(nTop); + + for (; nY <= nBottom; nY++) + { + //aSubRect.Top() = aSubRect.Bottom() = nY; + std::vector<long> aNewLine; + long nX(nLeft); + Scanline pScanlineRead = pReadAcc->GetScanline(nY); + + for (; nX <= nRight;) + { + while ((nX <= nRight) && (aMatch != pReadAcc->GetPixelFromData(pScanlineRead, nX))) + nX++; + + if (nX <= nRight) + { + aNewLine.push_back(nX); + //aSubRect.Left() = nX; + + while ((nX <= nRight) + && (aMatch == pReadAcc->GetPixelFromData(pScanlineRead, nX))) + { + nX++; + } + + //aSubRect.Right() = nX - 1; + aNewLine.push_back(nX - 1); + + //aRegion.ImplAddRect( aSubRect ); + //aRectangles.push_back(aSubRect); + //aRegion.Union(aSubRect); + } + } + + if (aNewLine != aLine) + { + // need to write aLine, it's different from the next line + if (aLine.size()) + { + tools::Rectangle aSubRect; + + // enter y values and proceed ystart + aSubRect.SetTop(nYStart); + aSubRect.SetBottom(nY ? nY - 1 : 0); + + for (size_t a(0); a < aLine.size();) + { + aSubRect.SetLeft(aLine[a++]); + aSubRect.SetRight(aLine[a++]); + aRegion.Union(aSubRect); + } + } + + // copy line as new line + aLine = aNewLine; + nYStart = nY; + } + } + + // write last line if used + if (aLine.size()) + { + tools::Rectangle aSubRect; + + // enter y values + aSubRect.SetTop(nYStart); + aSubRect.SetBottom(nY ? nY - 1 : 0); + + for (size_t a(0); a < aLine.size();) + { + aSubRect.SetLeft(aLine[a++]); + aSubRect.SetRight(aLine[a++]); + aRegion.Union(aSubRect); + } + } + + //aRegion.ImplEndAddRect(); + //aRegion.SetRegionRectangles(aRectangles); + + pReadAcc.reset(); + } + else + { + aRegion = aRect; + } + + return aRegion; +} + +bool Bitmap::Replace(const Bitmap& rMask, const Color& rReplaceColor) +{ + ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask)); + ScopedWriteAccess pAcc(*this); + bool bRet = false; + + if (pMaskAcc && pAcc) + { + const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width()); + const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height()); + const BitmapColor aMaskWhite(pMaskAcc->GetBestMatchingColor(COL_WHITE)); + BitmapColor aReplace; + + if (pAcc->HasPalette()) + { + const sal_uInt16 nActColors = pAcc->GetPaletteEntryCount(); + const sal_uInt16 nMaxColors = 1 << pAcc->GetBitCount(); + + // default to the nearest color + aReplace = pAcc->GetBestMatchingColor(rReplaceColor); + + // for paletted images without a matching palette entry + // look for an unused palette entry (NOTE: expensive!) + if (pAcc->GetPaletteColor(aReplace.GetIndex()) != BitmapColor(rReplaceColor)) + { + // if the palette has empty entries use the last one + if (nActColors < nMaxColors) + { + pAcc->SetPaletteEntryCount(nActColors + 1); + pAcc->SetPaletteColor(nActColors, rReplaceColor); + aReplace = BitmapColor(static_cast<sal_uInt8>(nActColors)); + } + else + { + std::unique_ptr<bool[]> pFlags(new bool[nMaxColors]); + + // Set all entries to false + std::fill(pFlags.get(), pFlags.get() + nMaxColors, false); + + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; nX++) + pFlags[pAcc->GetIndexFromData(pScanline, nX)] = true; + } + + for (sal_uInt16 i = 0; i < nMaxColors; i++) + { + // Hurray, we do have an unused entry + if (!pFlags[i]) + { + pAcc->SetPaletteColor(i, rReplaceColor); + aReplace = BitmapColor(static_cast<sal_uInt8>(i)); + } + } + } + } + } + else + aReplace = rReplaceColor; + + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pAcc->GetScanline(nY); + Scanline pScanlineMask = pMaskAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; nX++) + { + if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) == aMaskWhite) + pAcc->SetPixelOnData(pScanline, nX, aReplace); + } + } + + bRet = true; + } + + return bRet; +} + +bool Bitmap::Replace(const AlphaMask& rAlpha, const Color& rMergeColor) +{ + Bitmap aNewBmp(GetSizePixel(), 24); + ScopedReadAccess pAcc(*this); + AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha)); + ScopedWriteAccess pNewAcc(aNewBmp); + bool bRet = false; + + if (pAcc && pAlphaAcc && pNewAcc) + { + BitmapColor aCol; + const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width()); + const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height()); + + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pNewAcc->GetScanline(nY); + Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; nX++) + { + aCol = pAcc->GetColor(nY, nX); + pNewAcc->SetPixelOnData( + pScanline, nX, + aCol.Merge(rMergeColor, 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX))); + } + } + + bRet = true; + } + + pAcc.reset(); + pAlphaAcc.reset(); + pNewAcc.reset(); + + if (bRet) + { + const MapMode aMap(maPrefMapMode); + const Size aSize(maPrefSize); + + *this = aNewBmp; + + maPrefMapMode = aMap; + maPrefSize = aSize; + } + + return bRet; +} + +bool Bitmap::Replace(const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol) +{ + if (mxImpBmp) + { + // implementation specific replace + std::shared_ptr<ImpBitmap> xImpBmp(new ImpBitmap); + if (xImpBmp->ImplCreate(*mxImpBmp) + && xImpBmp->ImplReplace(rSearchColor, rReplaceColor, nTol)) + { + ImplSetImpBitmap(xImpBmp); + maPrefMapMode = MapMode(MapUnit::MapPixel); + maPrefSize = xImpBmp->ImplGetSize(); + return true; + } + } + + // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white + // in their palette + if (GetBitCount() == 1) + Convert(BmpConversion::N4BitColors); + + ScopedWriteAccess pAcc(*this); + bool bRet = false; + + if (pAcc) + { + const long nMinR = MinMax<long>(rSearchColor.GetRed() - nTol, 0, 255); + const long nMaxR = MinMax<long>(rSearchColor.GetRed() + nTol, 0, 255); + const long nMinG = MinMax<long>(rSearchColor.GetGreen() - nTol, 0, 255); + const long nMaxG = MinMax<long>(rSearchColor.GetGreen() + nTol, 0, 255); + const long nMinB = MinMax<long>(rSearchColor.GetBlue() - nTol, 0, 255); + const long nMaxB = MinMax<long>(rSearchColor.GetBlue() + nTol, 0, 255); + + if (pAcc->HasPalette()) + { + for (sal_uInt16 i = 0, nPalCount = pAcc->GetPaletteEntryCount(); i < nPalCount; i++) + { + const BitmapColor& rCol = pAcc->GetPaletteColor(i); + + if (nMinR <= rCol.GetRed() && nMaxR >= rCol.GetRed() && nMinG <= rCol.GetGreen() + && nMaxG >= rCol.GetGreen() && nMinB <= rCol.GetBlue() + && nMaxB >= rCol.GetBlue()) + { + pAcc->SetPaletteColor(i, rReplaceColor); + } + } + } + else + { + BitmapColor aCol; + const BitmapColor aReplace(pAcc->GetBestMatchingColor(rReplaceColor)); + + for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++) + { + Scanline pScanline = pAcc->GetScanline(nY); + for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++) + { + aCol = pAcc->GetPixelFromData(pScanline, nX); + + if (nMinR <= aCol.GetRed() && nMaxR >= aCol.GetRed() && nMinG <= aCol.GetGreen() + && nMaxG >= aCol.GetGreen() && nMinB <= aCol.GetBlue() + && nMaxB >= aCol.GetBlue()) + { + pAcc->SetPixelOnData(pScanline, nX, aReplace); + } + } + } + } + + pAcc.reset(); + bRet = true; + } + + return bRet; +} + +bool Bitmap::Replace(const Color* pSearchColors, const Color* pReplaceColors, sal_uLong nColorCount, + sal_uInt8 const* pTols) +{ + // Bitmaps with 1 bit color depth can cause problems if they have other entries than black/white + // in their palette + if (GetBitCount() == 1) + Convert(BmpConversion::N4BitColors); + + ScopedWriteAccess pAcc(*this); + bool bRet = false; + + if (pAcc) + { + std::unique_ptr<long[]> pMinR(new long[nColorCount]); + std::unique_ptr<long[]> pMaxR(new long[nColorCount]); + std::unique_ptr<long[]> pMinG(new long[nColorCount]); + std::unique_ptr<long[]> pMaxG(new long[nColorCount]); + std::unique_ptr<long[]> pMinB(new long[nColorCount]); + std::unique_ptr<long[]> pMaxB(new long[nColorCount]); + + if (pTols) + { + for (sal_uLong i = 0; i < nColorCount; i++) + { + const Color& rCol = pSearchColors[i]; + const sal_uInt8 nTol = pTols[i]; + + pMinR[i] = MinMax<long>(rCol.GetRed() - nTol, 0, 255); + pMaxR[i] = MinMax<long>(rCol.GetRed() + nTol, 0, 255); + pMinG[i] = MinMax<long>(rCol.GetGreen() - nTol, 0, 255); + pMaxG[i] = MinMax<long>(rCol.GetGreen() + nTol, 0, 255); + pMinB[i] = MinMax<long>(rCol.GetBlue() - nTol, 0, 255); + pMaxB[i] = MinMax<long>(rCol.GetBlue() + nTol, 0, 255); + } + } + else + { + for (sal_uLong i = 0; i < nColorCount; i++) + { + const Color& rCol = pSearchColors[i]; + + pMinR[i] = rCol.GetRed(); + pMaxR[i] = rCol.GetRed(); + pMinG[i] = rCol.GetGreen(); + pMaxG[i] = rCol.GetGreen(); + pMinB[i] = rCol.GetBlue(); + pMaxB[i] = rCol.GetBlue(); + } + } + + if (pAcc->HasPalette()) + { + for (sal_uInt16 nEntry = 0, nPalCount = pAcc->GetPaletteEntryCount(); + nEntry < nPalCount; nEntry++) + { + const BitmapColor& rCol = pAcc->GetPaletteColor(nEntry); + + for (sal_uLong i = 0; i < nColorCount; i++) + { + if (pMinR[i] <= rCol.GetRed() && pMaxR[i] >= rCol.GetRed() + && pMinG[i] <= rCol.GetGreen() && pMaxG[i] >= rCol.GetGreen() + && pMinB[i] <= rCol.GetBlue() && pMaxB[i] >= rCol.GetBlue()) + { + pAcc->SetPaletteColor(nEntry, pReplaceColors[i]); + break; + } + } + } + } + else + { + BitmapColor aCol; + std::unique_ptr<BitmapColor[]> pReplaces(new BitmapColor[nColorCount]); + + for (sal_uLong i = 0; i < nColorCount; i++) + pReplaces[i] = pAcc->GetBestMatchingColor(pReplaceColors[i]); + + for (long nY = 0, nHeight = pAcc->Height(); nY < nHeight; nY++) + { + Scanline pScanline = pAcc->GetScanline(nY); + for (long nX = 0, nWidth = pAcc->Width(); nX < nWidth; nX++) + { + aCol = pAcc->GetPixelFromData(pScanline, nX); + + for (sal_uLong i = 0; i < nColorCount; i++) + { + if (pMinR[i] <= aCol.GetRed() && pMaxR[i] >= aCol.GetRed() + && pMinG[i] <= aCol.GetGreen() && pMaxG[i] >= aCol.GetGreen() + && pMinB[i] <= aCol.GetBlue() && pMaxB[i] >= aCol.GetBlue()) + { + pAcc->SetPixelOnData(pScanline, nX, pReplaces[i]); + break; + } + } + } + } + } + + pAcc.reset(); + bRet = true; + } + + return bRet; +} + +bool Bitmap::CombineSimple(const Bitmap& rMask, BmpCombine eCombine) +{ + ScopedReadAccess pMaskAcc(const_cast<Bitmap&>(rMask)); + ScopedWriteAccess pAcc(*this); + bool bRet = false; + + if (pMaskAcc && pAcc) + { + const long nWidth = std::min(pMaskAcc->Width(), pAcc->Width()); + const long nHeight = std::min(pMaskAcc->Height(), pAcc->Height()); + const Color aColBlack(COL_BLACK); + BitmapColor aPixel; + BitmapColor aMaskPixel; + const BitmapColor aWhite(pAcc->GetBestMatchingColor(COL_WHITE)); + const BitmapColor aBlack(pAcc->GetBestMatchingColor(aColBlack)); + const BitmapColor aMaskBlack(pMaskAcc->GetBestMatchingColor(aColBlack)); + + switch (eCombine) + { + case BmpCombine::And: + { + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pAcc->GetScanline(nY); + Scanline pScanlineMask = pMaskAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; nX++) + { + if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack + && pAcc->GetPixelFromData(pScanline, nX) != aBlack) + { + pAcc->SetPixelOnData(pScanline, nX, aWhite); + } + else + { + pAcc->SetPixelOnData(pScanline, nX, aBlack); + } + } + } + } + break; + + case BmpCombine::Or: + { + for (long nY = 0; nY < nHeight; nY++) + { + Scanline pScanline = pAcc->GetScanline(nY); + Scanline pScanlineMask = pMaskAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; nX++) + { + if (pMaskAcc->GetPixelFromData(pScanlineMask, nX) != aMaskBlack + || pAcc->GetPixelFromData(pScanline, nX) != aBlack) + { + pAcc->SetPixelOnData(pScanline, nX, aWhite); + } + else + { + pAcc->SetPixelOnData(pScanline, nX, aBlack); + } + } + } + } + break; + } + + bRet = true; + } + + return bRet; +} + +// TODO: Have a look at OutputDevice::ImplDrawAlpha() for some +// optimizations. Might even consolidate the code here and there. +bool Bitmap::Blend(const AlphaMask& rAlpha, const Color& rBackgroundColor) +{ + // Convert to a truecolor bitmap, if we're a paletted one. There's room for tradeoff decision here, + // maybe later for an overload (or a flag) + if (GetBitCount() <= 8) + Convert(BmpConversion::N24Bit); + + AlphaMask::ScopedReadAccess pAlphaAcc(const_cast<AlphaMask&>(rAlpha)); + + ScopedWriteAccess pAcc(*this); + bool bRet = false; + + if (pAlphaAcc && pAcc) + { + const long nWidth = std::min(pAlphaAcc->Width(), pAcc->Width()); + const long nHeight = std::min(pAlphaAcc->Height(), pAcc->Height()); + + for (long nY = 0; nY < nHeight; ++nY) + { + Scanline pScanline = pAcc->GetScanline(nY); + Scanline pScanlineAlpha = pAlphaAcc->GetScanline(nY); + for (long nX = 0; nX < nWidth; ++nX) + { + pAcc->SetPixelOnData( + pScanline, nX, + pAcc->GetPixelFromData(pScanline, nX) + .Merge(rBackgroundColor, + 255 - pAlphaAcc->GetIndexFromData(pScanlineAlpha, nX))); + } + } + + bRet = true; + } + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |