diff options
Diffstat (limited to 'vcl/source/outdev/bitmap.cxx')
-rw-r--r-- | vcl/source/outdev/bitmap.cxx | 993 |
1 files changed, 993 insertions, 0 deletions
diff --git a/vcl/source/outdev/bitmap.cxx b/vcl/source/outdev/bitmap.cxx new file mode 100644 index 0000000000..86ac231375 --- /dev/null +++ b/vcl/source/outdev/bitmap.cxx @@ -0,0 +1,993 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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 <config_features.h> + +#include <osl/diagnose.h> +#include <tools/debug.hxx> +#include <tools/helpers.hxx> + +#include <vcl/image.hxx> +#include <vcl/metaact.hxx> +#include <vcl/skia/SkiaHelper.hxx> +#include <vcl/virdev.hxx> +#include <vcl/BitmapWriteAccess.hxx> + +#include <bitmap/bmpfast.hxx> +#include <drawmode.hxx> +#include <salbmp.hxx> +#include <salgdi.hxx> + +void OutputDevice::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap ) +{ + assert(!is_double_buffered_window()); + + const Size aSizePix( rBitmap.GetSizePixel() ); + DrawBitmap( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, MetaActionType::BMP ); +} + +void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, const Bitmap& rBitmap ) +{ + assert(!is_double_buffered_window()); + + DrawBitmap( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, MetaActionType::BMPSCALE ); +} + +void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel, + const Bitmap& rBitmap) +{ + assert(!is_double_buffered_window()); + + DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, MetaActionType::BMPSCALEPART ); +} + +void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel, + const Bitmap& rBitmap, const MetaActionType nAction ) +{ + assert(!is_double_buffered_window()); + + if( ImplIsRecordLayout() ) + return; + + if ( RasterOp::Invert == meRasterOp ) + { + DrawRect( tools::Rectangle( rDestPt, rDestSize ) ); + return; + } + + Bitmap aBmp( rBitmap ); + + if ( mnDrawMode & ( DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap | + DrawModeFlags::GrayBitmap ) ) + { + if ( mnDrawMode & ( DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap ) ) + { + sal_uInt8 cCmpVal; + + if ( mnDrawMode & DrawModeFlags::BlackBitmap ) + cCmpVal = 0; + else + cCmpVal = 255; + + Color aCol( cCmpVal, cCmpVal, cCmpVal ); + Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR ); + SetLineColor( aCol ); + SetFillColor( aCol ); + DrawRect( tools::Rectangle( rDestPt, rDestSize ) ); + Pop(); + return; + } + else if( !aBmp.IsEmpty() ) + { + if ( mnDrawMode & DrawModeFlags::GrayBitmap ) + aBmp.Convert( BmpConversion::N8BitGreys ); + } + } + + if ( mpMetaFile ) + { + switch( nAction ) + { + case MetaActionType::BMP: + mpMetaFile->AddAction( new MetaBmpAction( rDestPt, aBmp ) ); + break; + + case MetaActionType::BMPSCALE: + mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) ); + break; + + case MetaActionType::BMPSCALEPART: + mpMetaFile->AddAction( new MetaBmpScalePartAction( + rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp ) ); + break; + + default: break; + } + } + + if ( !IsDeviceOutputNecessary() ) + return; + + if (!mpGraphics && !AcquireGraphics()) + return; + assert(mpGraphics); + + if ( mbInitClipRegion ) + InitClipRegion(); + + if ( mbOutputClipped ) + return; + + if( !aBmp.IsEmpty() ) + { + SalTwoRect aPosAry(rSrcPtPixel.X(), rSrcPtPixel.Y(), rSrcSizePixel.Width(), rSrcSizePixel.Height(), + ImplLogicXToDevicePixel(rDestPt.X()), ImplLogicYToDevicePixel(rDestPt.Y()), + ImplLogicWidthToDevicePixel(rDestSize.Width()), + ImplLogicHeightToDevicePixel(rDestSize.Height())); + + if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) + { + const BmpMirrorFlags nMirrFlags = AdjustTwoRect( aPosAry, aBmp.GetSizePixel() ); + + if ( nMirrFlags != BmpMirrorFlags::NONE ) + aBmp.Mirror( nMirrFlags ); + + if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight ) + { + if (nAction == MetaActionType::BMPSCALE && CanSubsampleBitmap()) + { + double nScaleX = aPosAry.mnDestWidth / static_cast<double>(aPosAry.mnSrcWidth); + double nScaleY = aPosAry.mnDestHeight / static_cast<double>(aPosAry.mnSrcHeight); + + // If subsampling, use Bitmap::Scale() for subsampling of better quality. + + // but hidpi surfaces like the cairo one have their own scale, so don't downscale + // past the surface scaling which can retain the extra detail + double fScale(1.0); + if (mpGraphics->ShouldDownscaleIconsAtSurface(&fScale)) + { + nScaleX *= fScale; + nScaleY *= fScale; + } + + if ( nScaleX < 1.0 || nScaleY < 1.0 ) + { + aBmp.Scale(nScaleX, nScaleY); + aPosAry.mnSrcWidth = aPosAry.mnDestWidth * fScale; + aPosAry.mnSrcHeight = aPosAry.mnDestHeight * fScale; + } + } + + mpGraphics->DrawBitmap( aPosAry, *aBmp.ImplGetSalBitmap(), *this ); + } + } + } + + if( mpAlphaVDev ) + { + // #i32109#: Make bitmap area opaque + mpAlphaVDev->ImplFillOpaqueRectangle( tools::Rectangle(rDestPt, rDestSize) ); + } +} + +Bitmap OutputDevice::GetBitmap( const Point& rSrcPt, const Size& rSize ) const +{ + if ( !mpGraphics && !AcquireGraphics() ) + return Bitmap(); + + assert(mpGraphics); + + tools::Long nX = ImplLogicXToDevicePixel( rSrcPt.X() ); + tools::Long nY = ImplLogicYToDevicePixel( rSrcPt.Y() ); + tools::Long nWidth = ImplLogicWidthToDevicePixel( rSize.Width() ); + tools::Long nHeight = ImplLogicHeightToDevicePixel( rSize.Height() ); + if ( nWidth <= 0 || nHeight <= 0 || nX > (mnOutWidth + mnOutOffX) || nY > (mnOutHeight + mnOutOffY)) + return Bitmap(); + + Bitmap aBmp; + tools::Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + bool bClipped = false; + + // X-Coordinate outside of draw area? + if ( nX < mnOutOffX ) + { + nWidth -= ( mnOutOffX - nX ); + nX = mnOutOffX; + bClipped = true; + } + + // Y-Coordinate outside of draw area? + if ( nY < mnOutOffY ) + { + nHeight -= ( mnOutOffY - nY ); + nY = mnOutOffY; + bClipped = true; + } + + // Width outside of draw area? + if ( (nWidth + nX) > (mnOutWidth + mnOutOffX) ) + { + nWidth = mnOutOffX + mnOutWidth - nX; + bClipped = true; + } + + // Height outside of draw area? + if ( (nHeight + nY) > (mnOutHeight + mnOutOffY) ) + { + nHeight = mnOutOffY + mnOutHeight - nY; + bClipped = true; + } + + if ( bClipped ) + { + // If the visible part has been clipped, we have to create a + // Bitmap with the correct size in which we copy the clipped + // Bitmap to the correct position. + ScopedVclPtrInstance< VirtualDevice > aVDev( *this ); + + if ( aVDev->SetOutputSizePixel( aRect.GetSize() ) ) + { + if ( aVDev->mpGraphics || aVDev->AcquireGraphics() ) + { + if ( (nWidth > 0) && (nHeight > 0) ) + { + SalTwoRect aPosAry(nX, nY, nWidth, nHeight, + (aRect.Left() < mnOutOffX) ? (mnOutOffX - aRect.Left()) : 0L, + (aRect.Top() < mnOutOffY) ? (mnOutOffY - aRect.Top()) : 0L, + nWidth, nHeight); + aVDev->mpGraphics->CopyBits(aPosAry, *mpGraphics, *this, *this); + } + else + { + OSL_ENSURE(false, "CopyBits with zero or negative width or height"); + } + + aBmp = aVDev->GetBitmap( Point(), aVDev->GetOutputSizePixel() ); + } + else + bClipped = false; + } + else + bClipped = false; + } + + if ( !bClipped ) + { + std::shared_ptr<SalBitmap> pSalBmp = mpGraphics->GetBitmap( nX, nY, nWidth, nHeight, *this ); + + if( pSalBmp ) + { + aBmp.ImplSetSalBitmap(pSalBmp); + } + } + + return aBmp; +} + +void OutputDevice::DrawDeviceAlphaBitmap( const Bitmap& rBmp, const AlphaMask& rAlpha, + const Point& rDestPt, const Size& rDestSize, + const Point& rSrcPtPixel, const Size& rSrcSizePixel ) +{ + assert(!is_double_buffered_window()); + + Point aOutPt(LogicToPixel(rDestPt)); + Size aOutSz(LogicToPixel(rDestSize)); + tools::Rectangle aDstRect(Point(), GetOutputSizePixel()); + + const bool bHMirr = aOutSz.Width() < 0; + const bool bVMirr = aOutSz.Height() < 0; + + ClipToPaintRegion(aDstRect); + + BmpMirrorFlags mirrorFlags = BmpMirrorFlags::NONE; + if (bHMirr) + { + aOutSz.setWidth( -aOutSz.Width() ); + aOutPt.AdjustX( -(aOutSz.Width() - 1) ); + mirrorFlags |= BmpMirrorFlags::Horizontal; + } + + if (bVMirr) + { + aOutSz.setHeight( -aOutSz.Height() ); + aOutPt.AdjustY( -(aOutSz.Height() - 1) ); + mirrorFlags |= BmpMirrorFlags::Vertical; + } + + if (aDstRect.Intersection(tools::Rectangle(aOutPt, aOutSz)).IsEmpty()) + return; + + { + Point aRelPt = aOutPt + Point(mnOutOffX, mnOutOffY); + SalTwoRect aTR( + rSrcPtPixel.X(), rSrcPtPixel.Y(), + rSrcSizePixel.Width(), rSrcSizePixel.Height(), + aRelPt.X(), aRelPt.Y(), + aOutSz.Width(), aOutSz.Height()); + + Bitmap bitmap(rBmp); + AlphaMask alpha(rAlpha); + if(bHMirr || bVMirr) + { + bitmap.Mirror(mirrorFlags); + alpha.Mirror(mirrorFlags); + } + SalBitmap* pSalSrcBmp = bitmap.ImplGetSalBitmap().get(); + SalBitmap* pSalAlphaBmp = alpha.GetBitmap().ImplGetSalBitmap().get(); + + // #i83087# Naturally, system alpha blending (SalGraphics::DrawAlphaBitmap) cannot work + // with separate alpha VDev + + // try to blend the alpha bitmap with the alpha virtual device + if (mpAlphaVDev) + { + if (ImplLogicToDevicePixel(aOutSz).IsEmpty()) // nothing to draw + return; + Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aRelPt, aOutSz ) ); + if (SalBitmap* pSalAlphaBmp2 = aAlphaBitmap.ImplGetSalBitmap().get()) + { + if (mpGraphics->BlendAlphaBitmap(aTR, *pSalSrcBmp, *pSalAlphaBmp, *pSalAlphaBmp2, *this)) + { + mpAlphaVDev->BlendBitmap(aTR, rAlpha.GetBitmap()); + return; + } + } + } + else + { + if (mpGraphics->DrawAlphaBitmap(aTR, *pSalSrcBmp, *pSalAlphaBmp, *this)) + return; + } + + // we need to make sure Skia never reaches this slow code path + // (but do not fail in no-op cases) + assert(!SkiaHelper::isVCLSkiaEnabled() || !SkiaHelper::isAlphaMaskBlendingEnabled() + || tools::Rectangle(Point(), rBmp.GetSizePixel()) + .Intersection(tools::Rectangle(rSrcPtPixel, rSrcSizePixel)).IsEmpty() + || mpAlphaVDev->LogicToPixel(mpAlphaVDev->GetOutputSizePixel()).IsEmpty()); + } + + tools::Rectangle aBmpRect(Point(), rBmp.GetSizePixel()); + if (aBmpRect.Intersection(tools::Rectangle(rSrcPtPixel, rSrcSizePixel)).IsEmpty()) + return; + + Point auxOutPt(LogicToPixel(rDestPt)); + Size auxOutSz(LogicToPixel(rDestSize)); + + // HACK: The function is broken with alpha vdev and mirroring, mirror here. + Bitmap bitmap(rBmp); + AlphaMask alpha(rAlpha); + if(mpAlphaVDev && (bHMirr || bVMirr)) + { + bitmap.Mirror(mirrorFlags); + alpha.Mirror(mirrorFlags); + auxOutPt = aOutPt; + auxOutSz = aOutSz; + } + DrawDeviceAlphaBitmapSlowPath(bitmap, alpha, aDstRect, aBmpRect, auxOutSz, auxOutPt); +} + +namespace +{ + +struct LinearScaleContext +{ + std::unique_ptr<sal_Int32[]> mpMapX; + std::unique_ptr<sal_Int32[]> mpMapY; + + std::unique_ptr<sal_Int32[]> mpMapXOffset; + std::unique_ptr<sal_Int32[]> mpMapYOffset; + + LinearScaleContext(tools::Rectangle const & aDstRect, tools::Rectangle const & aBitmapRect, + Size const & aOutSize, tools::Long nOffX, tools::Long nOffY) + + : mpMapX(new sal_Int32[aDstRect.GetWidth()]) + , mpMapY(new sal_Int32[aDstRect.GetHeight()]) + , mpMapXOffset(new sal_Int32[aDstRect.GetWidth()]) + , mpMapYOffset(new sal_Int32[aDstRect.GetHeight()]) + { + const tools::Long nSrcWidth = aBitmapRect.GetWidth(); + const tools::Long nSrcHeight = aBitmapRect.GetHeight(); + + generateSimpleMap( + nSrcWidth, aDstRect.GetWidth(), aBitmapRect.Left(), + aOutSize.Width(), nOffX, mpMapX.get(), mpMapXOffset.get()); + + generateSimpleMap( + nSrcHeight, aDstRect.GetHeight(), aBitmapRect.Top(), + aOutSize.Height(), nOffY, mpMapY.get(), mpMapYOffset.get()); + } + +private: + + static void generateSimpleMap(tools::Long nSrcDimension, tools::Long nDstDimension, tools::Long nDstLocation, + tools::Long nOutDimension, tools::Long nOffset, sal_Int32* pMap, sal_Int32* pMapOffset) + { + + const double fReverseScale = (std::abs(nOutDimension) > 1) ? (nSrcDimension - 1) / double(std::abs(nOutDimension) - 1) : 0.0; + + tools::Long nSampleRange = std::max(tools::Long(0), nSrcDimension - 2); + + for (tools::Long i = 0; i < nDstDimension; i++) + { + double fTemp = std::abs((nOffset + i) * fReverseScale); + + pMap[i] = std::clamp(nDstLocation + tools::Long(fTemp), tools::Long(0), nSampleRange); + pMapOffset[i] = static_cast<tools::Long>((fTemp - pMap[i]) * 128.0); + } + } + +public: + bool blendBitmap( + const BitmapWriteAccess* pDestination, + const BitmapReadAccess* pSource, + const BitmapReadAccess* pSourceAlpha, + const tools::Long nDstWidth, + const tools::Long nDstHeight) + { + if (!pSource || !pSourceAlpha || !pDestination) + return false; + + ScanlineFormat nSourceFormat = pSource->GetScanlineFormat(); + ScanlineFormat nDestinationFormat = pDestination->GetScanlineFormat(); + + switch (nSourceFormat) + { + case ScanlineFormat::N24BitTcRgb: + case ScanlineFormat::N24BitTcBgr: + { + if ( (nSourceFormat == ScanlineFormat::N24BitTcBgr && nDestinationFormat == ScanlineFormat::N32BitTcBgra) + || (nSourceFormat == ScanlineFormat::N24BitTcRgb && nDestinationFormat == ScanlineFormat::N32BitTcRgba)) + { + blendBitmap24(pDestination, pSource, pSourceAlpha, nDstWidth, nDstHeight); + return true; + } + } + break; + default: break; + } + return false; + } + + void blendBitmap24( + const BitmapWriteAccess* pDestination, + const BitmapReadAccess* pSource, + const BitmapReadAccess* pSourceAlpha, + const tools::Long nDstWidth, + const tools::Long nDstHeight) + { + Scanline pLine0, pLine1; + Scanline pLineAlpha0, pLineAlpha1; + Scanline pColorSample1, pColorSample2; + Scanline pDestScanline; + + tools::Long nColor1Line1, nColor2Line1, nColor3Line1; + tools::Long nColor1Line2, nColor2Line2, nColor3Line2; + tools::Long nAlphaLine1, nAlphaLine2; + + sal_uInt8 nColor1, nColor2, nColor3, nAlpha; + + for (tools::Long nY = 0; nY < nDstHeight; nY++) + { + const tools::Long nMapY = mpMapY[nY]; + const tools::Long nMapFY = mpMapYOffset[nY]; + + pLine0 = pSource->GetScanline(nMapY); + // tdf#95481 guard nMapY + 1 to be within bounds + pLine1 = (nMapY + 1 < pSource->Height()) ? pSource->GetScanline(nMapY + 1) : pLine0; + + pLineAlpha0 = pSourceAlpha->GetScanline(nMapY); + // tdf#95481 guard nMapY + 1 to be within bounds + pLineAlpha1 = (nMapY + 1 < pSourceAlpha->Height()) ? pSourceAlpha->GetScanline(nMapY + 1) : pLineAlpha0; + + pDestScanline = pDestination->GetScanline(nY); + + for (tools::Long nX = 0; nX < nDstWidth; nX++) + { + const tools::Long nMapX = mpMapX[nX]; + const tools::Long nMapFX = mpMapXOffset[nX]; + + pColorSample1 = pLine0 + 3 * nMapX; + pColorSample2 = (nMapX + 1 < pSource->Width()) ? pColorSample1 + 3 : pColorSample1; + nColor1Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1); + + pColorSample1++; + pColorSample2++; + nColor2Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1); + + pColorSample1++; + pColorSample2++; + nColor3Line1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1); + + pColorSample1 = pLine1 + 3 * nMapX; + pColorSample2 = (nMapX + 1 < pSource->Width()) ? pColorSample1 + 3 : pColorSample1; + nColor1Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1); + + pColorSample1++; + pColorSample2++; + nColor2Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1); + + pColorSample1++; + pColorSample2++; + nColor3Line2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1); + + pColorSample1 = pLineAlpha0 + nMapX; + pColorSample2 = (nMapX + 1 < pSourceAlpha->Width()) ? pColorSample1 + 1 : pColorSample1; + nAlphaLine1 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1); + + pColorSample1 = pLineAlpha1 + nMapX; + pColorSample2 = (nMapX + 1 < pSourceAlpha->Width()) ? pColorSample1 + 1 : pColorSample1; + nAlphaLine2 = (static_cast<tools::Long>(*pColorSample1) << 7) + nMapFX * (static_cast<tools::Long>(*pColorSample2) - *pColorSample1); + + nColor1 = (nColor1Line1 + nMapFY * ((nColor1Line2 >> 7) - (nColor1Line1 >> 7))) >> 7; + nColor2 = (nColor2Line1 + nMapFY * ((nColor2Line2 >> 7) - (nColor2Line1 >> 7))) >> 7; + nColor3 = (nColor3Line1 + nMapFY * ((nColor3Line2 >> 7) - (nColor3Line1 >> 7))) >> 7; + + nAlpha = (nAlphaLine1 + nMapFY * ((nAlphaLine2 >> 7) - (nAlphaLine1 >> 7))) >> 7; + + *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor1, nAlpha); + pDestScanline++; + *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor2, nAlpha); + pDestScanline++; + *pDestScanline = color::ColorChannelMerge(*pDestScanline, nColor3, nAlpha); + pDestScanline++; + pDestScanline++; + } + } + } +}; + +struct TradScaleContext +{ + std::unique_ptr<sal_Int32[]> mpMapX; + std::unique_ptr<sal_Int32[]> mpMapY; + + TradScaleContext(tools::Rectangle const & aDstRect, tools::Rectangle const & aBitmapRect, + Size const & aOutSize, tools::Long nOffX, tools::Long nOffY) + + : mpMapX(new sal_Int32[aDstRect.GetWidth()]) + , mpMapY(new sal_Int32[aDstRect.GetHeight()]) + { + const tools::Long nSrcWidth = aBitmapRect.GetWidth(); + const tools::Long nSrcHeight = aBitmapRect.GetHeight(); + + const bool bHMirr = aOutSize.Width() < 0; + const bool bVMirr = aOutSize.Height() < 0; + + generateSimpleMap( + nSrcWidth, aDstRect.GetWidth(), aBitmapRect.Left(), + aOutSize.Width(), nOffX, bHMirr, mpMapX.get()); + + generateSimpleMap( + nSrcHeight, aDstRect.GetHeight(), aBitmapRect.Top(), + aOutSize.Height(), nOffY, bVMirr, mpMapY.get()); + } + +private: + + static void generateSimpleMap(tools::Long nSrcDimension, tools::Long nDstDimension, tools::Long nDstLocation, + tools::Long nOutDimension, tools::Long nOffset, bool bMirror, sal_Int32* pMap) + { + tools::Long nMirrorOffset = 0; + + if (bMirror) + nMirrorOffset = (nDstLocation << 1) + nSrcDimension - 1; + + for (tools::Long i = 0; i < nDstDimension; ++i, ++nOffset) + { + pMap[i] = nDstLocation + nOffset * nSrcDimension / nOutDimension; + if (bMirror) + pMap[i] = nMirrorOffset - pMap[i]; + } + } +}; + + +} // end anonymous namespace + +void OutputDevice::DrawDeviceAlphaBitmapSlowPath(const Bitmap& rBitmap, + const AlphaMask& rAlpha, tools::Rectangle aDstRect, tools::Rectangle aBmpRect, Size const & aOutSize, Point const & aOutPoint) +{ + assert(!is_double_buffered_window()); + + VirtualDevice* pOldVDev = mpAlphaVDev; + + const bool bHMirr = aOutSize.Width() < 0; + const bool bVMirr = aOutSize.Height() < 0; + + // The scaling in this code path produces really ugly results - it + // does the most trivial scaling with no smoothing. + GDIMetaFile* pOldMetaFile = mpMetaFile; + const bool bOldMap = mbMap; + + mpMetaFile = nullptr; // fdo#55044 reset before GetBitmap! + mbMap = false; + + Bitmap aBmp(GetBitmap(aDstRect.TopLeft(), aDstRect.GetSize())); + + // #109044# The generated bitmap need not necessarily be + // of aDstRect dimensions, it's internally clipped to + // window bounds. Thus, we correct the dest size here, + // since we later use it (in nDstWidth/Height) for pixel + // access) + // #i38887# reading from screen may sometimes fail + if (aBmp.ImplGetSalBitmap()) + { + aDstRect.SetSize(aBmp.GetSizePixel()); + } + + const tools::Long nDstWidth = aDstRect.GetWidth(); + const tools::Long nDstHeight = aDstRect.GetHeight(); + + // calculate offset in original bitmap + // in RTL case this is a little more complicated since the contents of the + // bitmap is not mirrored (it never is), however the paint region and bmp region + // are in mirrored coordinates, so the intersection of (aOutPt,aOutSz) with these + // is content wise somewhere else and needs to take mirroring into account + const tools::Long nOffX = IsRTLEnabled() + ? aOutSize.Width() - aDstRect.GetWidth() - (aDstRect.Left() - aOutPoint.X()) + : aDstRect.Left() - aOutPoint.X(); + + const tools::Long nOffY = aDstRect.Top() - aOutPoint.Y(); + + TradScaleContext aTradContext(aDstRect, aBmpRect, aOutSize, nOffX, nOffY); + + BitmapScopedReadAccess pBitmapReadAccess(rBitmap); + BitmapScopedReadAccess pAlphaReadAccess(rAlpha); + + SAL_WARN_IF(pAlphaReadAccess->GetScanlineFormat() != ScanlineFormat::N8BitPal, "vcl.gdi", "non-8bit alpha no longer supported!"); + assert(pAlphaReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal); + + // #i38887# reading from screen may sometimes fail + if (aBmp.ImplGetSalBitmap()) + { + Bitmap aNewBitmap; + + if (mpAlphaVDev) + { + aNewBitmap = BlendBitmapWithAlpha( + aBmp, pBitmapReadAccess.get(), pAlphaReadAccess.get(), + aDstRect, + nOffY, nDstHeight, + nOffX, nDstWidth, + aTradContext.mpMapX.get(), aTradContext.mpMapY.get() ); + } + else + { + LinearScaleContext aLinearContext(aDstRect, aBmpRect, aOutSize, nOffX, nOffY); + + if (aLinearContext.blendBitmap( BitmapScopedWriteAccess(aBmp).get(), pBitmapReadAccess.get(), pAlphaReadAccess.get(), + nDstWidth, nDstHeight)) + { + aNewBitmap = aBmp; + } + else + { + aNewBitmap = BlendBitmap( + aBmp, pBitmapReadAccess.get(), pAlphaReadAccess.get(), + nOffY, nDstHeight, + nOffX, nDstWidth, + aBmpRect, aOutSize, + bHMirr, bVMirr, + aTradContext.mpMapX.get(), aTradContext.mpMapY.get() ); + } + } + + // #110958# Disable alpha VDev, we're doing the necessary + // stuff explicitly further below + if (mpAlphaVDev) + mpAlphaVDev = nullptr; + + DrawBitmap(aDstRect.TopLeft(), aNewBitmap); + + // #110958# Enable alpha VDev again + mpAlphaVDev = pOldVDev; + } + + mbMap = bOldMap; + mpMetaFile = pOldMetaFile; +} + +bool OutputDevice::HasFastDrawTransformedBitmap() const +{ + if( ImplIsRecordLayout() ) + return false; + + if (!mpGraphics && !AcquireGraphics()) + return false; + assert(mpGraphics); + + return mpGraphics->HasFastDrawTransformedBitmap(); +} + +void OutputDevice::DrawImage( const Point& rPos, const Image& rImage, DrawImageFlags nStyle ) +{ + assert(!is_double_buffered_window()); + + DrawImage( rPos, Size(), rImage, nStyle ); +} + +void OutputDevice::DrawImage( const Point& rPos, const Size& rSize, + const Image& rImage, DrawImageFlags nStyle ) +{ + assert(!is_double_buffered_window()); + + bool bIsSizeValid = !rSize.IsEmpty(); + + if (!ImplIsRecordLayout()) + { + Image& rNonConstImage = const_cast<Image&>(rImage); + if (bIsSizeValid) + rNonConstImage.Draw(this, rPos, nStyle, &rSize); + else + rNonConstImage.Draw(this, rPos, nStyle); + } +} + +namespace +{ + // Co = Cs + Cd*(1-As) premultiplied alpha -or- + // Co = (AsCs + AdCd*(1-As)) / Ao + sal_uInt8 CalcColor( const sal_uInt8 nSourceColor, const sal_uInt8 nSourceAlpha, + const sal_uInt8 nDstAlpha, const sal_uInt8 nResAlpha, const sal_uInt8 nDestColor ) + { + int c = nResAlpha ? ( static_cast<int>(nSourceAlpha)*nSourceColor + static_cast<int>(nDstAlpha)*nDestColor - + static_cast<int>(nDstAlpha)*nDestColor*nSourceAlpha/255 ) / static_cast<int>(nResAlpha) : 0; + return sal_uInt8( c ); + } + + BitmapColor AlphaBlend( int nX, int nY, + const tools::Long nMapX, + const tools::Long nMapY, + BitmapReadAccess const * pP, + BitmapReadAccess const * pA, + BitmapReadAccess const * pB, + BitmapWriteAccess const * pAlphaW, + sal_uInt8& nResAlpha ) + { + BitmapColor aDstCol,aSrcCol; + aSrcCol = pP->GetColor( nMapY, nMapX ); + aDstCol = pB->GetColor( nY, nX ); + + const sal_uInt8 nSrcAlpha = pA->GetPixelIndex( nMapY, nMapX ); + const sal_uInt8 nDstAlpha = pAlphaW->GetPixelIndex( nY, nX ); + + // Perform porter-duff compositing 'over' operation + + // Co = Cs + Cd*(1-As) + // Ad = As + Ad*(1-As) + nResAlpha = static_cast<int>(nSrcAlpha) + static_cast<int>(nDstAlpha) - static_cast<int>(nDstAlpha)*nSrcAlpha/255; + + aDstCol.SetRed( CalcColor( aSrcCol.GetRed(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetRed() ) ); + aDstCol.SetBlue( CalcColor( aSrcCol.GetBlue(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetBlue() ) ); + aDstCol.SetGreen( CalcColor( aSrcCol.GetGreen(), nSrcAlpha, nDstAlpha, nResAlpha, aDstCol.GetGreen() ) ); + + return aDstCol; + } +} + +void OutputDevice::BlendBitmap( + const SalTwoRect& rPosAry, + const Bitmap& rBmp ) +{ + mpGraphics->BlendBitmap( rPosAry, *rBmp.ImplGetSalBitmap(), *this ); +} + +Bitmap OutputDevice::BlendBitmapWithAlpha( + Bitmap& aBmp, + BitmapReadAccess const * pP, + BitmapReadAccess const * pA, + const tools::Rectangle& aDstRect, + const sal_Int32 nOffY, + const sal_Int32 nDstHeight, + const sal_Int32 nOffX, + const sal_Int32 nDstWidth, + const sal_Int32* pMapX, + const sal_Int32* pMapY ) + +{ + BitmapColor aDstCol; + Bitmap res; + int nX, nY; + sal_uInt8 nResAlpha; + + SAL_WARN_IF( !mpAlphaVDev, "vcl.gdi", "BlendBitmapWithAlpha(): call me only with valid alpha VirtualDevice!" ); + + bool bOldMapMode( mpAlphaVDev->IsMapModeEnabled() ); + mpAlphaVDev->EnableMapMode(false); + + Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) ); + BitmapScopedWriteAccess pAlphaW(aAlphaBitmap); + + if( GetBitCount() <= 8 ) + { + Bitmap aDither(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP); + BitmapColor aIndex( 0 ); + BitmapScopedReadAccess pB(aBmp); + BitmapScopedWriteAccess pW(aDither); + + if (pB && pP && pA && pW && pAlphaW) + { + int nOutY; + + for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) + { + const tools::Long nMapY = pMapY[ nY ]; + const tools::Long nModY = ( nOutY & 0x0FL ) << 4; + int nOutX; + + Scanline pScanline = pW->GetScanline(nY); + Scanline pScanlineAlpha = pAlphaW->GetScanline(nY); + for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ ) + { + const tools::Long nMapX = pMapX[ nX ]; + const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ]; + + aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha ); + + aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] + + nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] + + nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) ); + pW->SetPixelOnData( pScanline, nX, aIndex ); + + aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ nResAlpha ] + nD ) >> 16 ] + + nVCLGLut[ ( nVCLLut[ nResAlpha ] + nD ) >> 16 ] + + nVCLBLut[ ( nVCLLut[ nResAlpha ] + nD ) >> 16 ] ) ); + pAlphaW->SetPixelOnData( pScanlineAlpha, nX, aIndex ); + } + } + } + pB.reset(); + pW.reset(); + res = aDither; + } + else + { + BitmapScopedWriteAccess pB(aBmp); + if (pB && pP && pA && pAlphaW) + { + for( nY = 0; nY < nDstHeight; nY++ ) + { + const tools::Long nMapY = pMapY[ nY ]; + Scanline pScanlineB = pB->GetScanline(nY); + Scanline pScanlineAlpha = pAlphaW->GetScanline(nY); + + for( nX = 0; nX < nDstWidth; nX++ ) + { + const tools::Long nMapX = pMapX[ nX ]; + aDstCol = AlphaBlend( nX, nY, nMapX, nMapY, pP, pA, pB.get(), pAlphaW.get(), nResAlpha ); + + pB->SetPixelOnData(pScanlineB, nX, pB->GetBestMatchingColor(aDstCol)); + pAlphaW->SetPixelOnData(pScanlineAlpha, nX, pB->GetBestMatchingColor(Color(nResAlpha, nResAlpha, nResAlpha))); + } + } + } + pB.reset(); + res = aBmp; + } + + pAlphaW.reset(); + mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap ); + mpAlphaVDev->EnableMapMode( bOldMapMode ); + + return res; +} + +Bitmap OutputDevice::BlendBitmap( + Bitmap& aBmp, + BitmapReadAccess const * pP, + BitmapReadAccess const * pA, + const sal_Int32 nOffY, + const sal_Int32 nDstHeight, + const sal_Int32 nOffX, + const sal_Int32 nDstWidth, + const tools::Rectangle& aBmpRect, + const Size& aOutSz, + const bool bHMirr, + const bool bVMirr, + const sal_Int32* pMapX, + const sal_Int32* pMapY ) +{ + if( !pP || !pA ) + return aBmp; + + if( GetBitCount() <= 8 ) + { + Bitmap aDither(aBmp.GetSizePixel(), vcl::PixelFormat::N8_BPP); + BitmapColor aIndex( 0 ); + BitmapScopedReadAccess pB(aBmp); + BitmapScopedWriteAccess pW(aDither); + + for( int nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ ) + { + tools::Long nMapY = pMapY[ nY ]; + if (bVMirr) + { + nMapY = aBmpRect.Bottom() - nMapY; + } + const tools::Long nModY = ( nOutY & 0x0FL ) << 4; + + Scanline pScanline = pW->GetScanline(nY); + Scanline pScanlineAlpha = pA->GetScanline(nMapY); + for( int nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ ) + { + tools::Long nMapX = pMapX[ nX ]; + if (bHMirr) + { + nMapX = aBmpRect.Right() - nMapX; + } + const sal_uLong nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ]; + + BitmapColor aDstCol = pB->GetColor( nY, nX ); + aDstCol.Merge( pP->GetColor( nMapY, nMapX ), 255 - pA->GetIndexFromData( pScanlineAlpha, nMapX ) ); + aIndex.SetIndex( static_cast<sal_uInt8>( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16 ] + + nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16 ] + + nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16 ] ) ); + pW->SetPixelOnData( pScanline, nX, aIndex ); + } + } + + pB.reset(); + pW.reset(); + return aDither; + } + + BitmapScopedWriteAccess pB(aBmp); + + bool bFastBlend = false; + if (!bHMirr && !bVMirr) + { + SalTwoRect aTR(aBmpRect.Left(), aBmpRect.Top(), aBmpRect.GetWidth(), aBmpRect.GetHeight(), + nOffX, nOffY, aOutSz.Width(), aOutSz.Height()); + + bFastBlend = ImplFastBitmapBlending(*pB, *pP, *pA, aTR); + } + + if (!bFastBlend) + { + for (int nY = 0; nY < nDstHeight; nY++) + { + tools::Long nMapY = pMapY[nY]; + + if (bVMirr) + nMapY = aBmpRect.Bottom() - nMapY; + + Scanline pAScan = pA->GetScanline(nMapY); + Scanline pBScan = pB->GetScanline(nY); + for(int nX = 0; nX < nDstWidth; nX++) + { + tools::Long nMapX = pMapX[nX]; + + if (bHMirr) + nMapX = aBmpRect.Right() - nMapX; + + BitmapColor aDstCol = pB->GetPixelFromData(pBScan, nX); + aDstCol.Merge(pP->GetColor(nMapY, nMapX), pAScan[nMapX]); + pB->SetPixelOnData(pBScan, nX, aDstCol); + } + } + } + + pB.reset(); + return aBmp; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |