/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include 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(aPosAry.mnSrcWidth); double nScaleY = aPosAry.mnDestHeight / static_cast(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 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 mpMapX; std::unique_ptr mpMapY; std::unique_ptr mpMapXOffset; std::unique_ptr 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((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(*pColorSample1) << 7) + nMapFX * (static_cast(*pColorSample2) - *pColorSample1); pColorSample1++; pColorSample2++; nColor2Line1 = (static_cast(*pColorSample1) << 7) + nMapFX * (static_cast(*pColorSample2) - *pColorSample1); pColorSample1++; pColorSample2++; nColor3Line1 = (static_cast(*pColorSample1) << 7) + nMapFX * (static_cast(*pColorSample2) - *pColorSample1); pColorSample1 = pLine1 + 3 * nMapX; pColorSample2 = (nMapX + 1 < pSource->Width()) ? pColorSample1 + 3 : pColorSample1; nColor1Line2 = (static_cast(*pColorSample1) << 7) + nMapFX * (static_cast(*pColorSample2) - *pColorSample1); pColorSample1++; pColorSample2++; nColor2Line2 = (static_cast(*pColorSample1) << 7) + nMapFX * (static_cast(*pColorSample2) - *pColorSample1); pColorSample1++; pColorSample2++; nColor3Line2 = (static_cast(*pColorSample1) << 7) + nMapFX * (static_cast(*pColorSample2) - *pColorSample1); pColorSample1 = pLineAlpha0 + nMapX; pColorSample2 = (nMapX + 1 < pSourceAlpha->Width()) ? pColorSample1 + 1 : pColorSample1; nAlphaLine1 = (static_cast(*pColorSample1) << 7) + nMapFX * (static_cast(*pColorSample2) - *pColorSample1); pColorSample1 = pLineAlpha1 + nMapX; pColorSample2 = (nMapX + 1 < pSourceAlpha->Width()) ? pColorSample1 + 1 : pColorSample1; nAlphaLine2 = (static_cast(*pColorSample1) << 7) + nMapFX * (static_cast(*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 mpMapX; std::unique_ptr 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(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(nSourceAlpha)*nSourceColor + static_cast(nDstAlpha)*nDestColor - static_cast(nDstAlpha)*nDestColor*nSourceAlpha/255 ) / static_cast(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(nSrcAlpha) + static_cast(nDstAlpha) - static_cast(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( 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( 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( 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: */