diff options
Diffstat (limited to '')
-rw-r--r-- | vcl/source/bitmap/BitmapTools.cxx | 1131 |
1 files changed, 1131 insertions, 0 deletions
diff --git a/vcl/source/bitmap/BitmapTools.cxx b/vcl/source/bitmap/BitmapTools.cxx new file mode 100644 index 000000000..3f0226047 --- /dev/null +++ b/vcl/source/bitmap/BitmapTools.cxx @@ -0,0 +1,1131 @@ +/* -*- 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/. + * + */ + +#include <vcl/BitmapTools.hxx> + +#include <sal/log.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/seqstream.hxx> +#include <vcl/canvastools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> + +#include <com/sun/star/graphic/SvgTools.hpp> +#include <com/sun/star/graphic/Primitive2DTools.hpp> + +#include <drawinglayer/primitive2d/baseprimitive2d.hxx> + +#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp> + +#include <vcl/dibtools.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/bitmapaccess.hxx> +#include <vcl/virdev.hxx> +#if ENABLE_CAIRO_CANVAS +#include <cairo.h> +#endif +#include <tools/diagnose_ex.h> +#include <tools/fract.hxx> +#include <tools/stream.hxx> +#include <bitmapwriteaccess.hxx> + +using namespace css; + +using drawinglayer::primitive2d::Primitive2DSequence; +using drawinglayer::primitive2d::Primitive2DReference; + +namespace vcl::bitmap +{ + +BitmapEx loadFromName(const OUString& rFileName, const ImageLoadFlags eFlags) +{ + bool bSuccess = true; + OUString aIconTheme; + BitmapEx aBitmapEx; + try + { + aIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme(); + ImageTree::get().loadImage(rFileName, aIconTheme, aBitmapEx, true, eFlags); + } + catch (...) + { + bSuccess = false; + } + + SAL_WARN_IF(!bSuccess, "vcl", "vcl::bitmap::loadFromName : could not load image " << rFileName << " via icon theme " << aIconTheme); + + return aBitmapEx; +} + +void loadFromSvg(SvStream& rStream, const OUString& sPath, BitmapEx& rBitmapEx, double fScalingFactor) +{ + uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + const uno::Reference<graphic::XSvgParser> xSvgParser = graphic::SvgTools::create(xContext); + + std::size_t nSize = rStream.remainingSize(); + std::vector<sal_Int8> aBuffer(nSize + 1); + rStream.ReadBytes(aBuffer.data(), nSize); + aBuffer[nSize] = 0; + + uno::Sequence<sal_Int8> aData(aBuffer.data(), nSize + 1); + uno::Reference<io::XInputStream> aInputStream(new comphelper::SequenceInputStream(aData)); + + const Primitive2DSequence aPrimitiveSequence = xSvgParser->getDecomposition(aInputStream, sPath); + + if (!aPrimitiveSequence.hasElements()) + return; + + uno::Sequence<beans::PropertyValue> aViewParameters; + + geometry::RealRectangle2D aRealRect; + basegfx::B2DRange aRange; + for (Primitive2DReference const & xReference : aPrimitiveSequence) + { + if (xReference.is()) + { + aRealRect = xReference->getRange(aViewParameters); + aRange.expand(basegfx::B2DRange(aRealRect.X1, aRealRect.Y1, aRealRect.X2, aRealRect.Y2)); + } + } + + aRealRect.X1 = aRange.getMinX(); + aRealRect.Y1 = aRange.getMinY(); + aRealRect.X2 = aRange.getMaxX(); + aRealRect.Y2 = aRange.getMaxY(); + + double nDPI = 96 * fScalingFactor; + + const css::uno::Reference<css::graphic::XPrimitive2DRenderer> xPrimitive2DRenderer = css::graphic::Primitive2DTools::create(xContext); + const css::uno::Reference<css::rendering::XBitmap> xBitmap( + xPrimitive2DRenderer->rasterize(aPrimitiveSequence, aViewParameters, nDPI, nDPI, aRealRect, 256*256)); + + if (xBitmap.is()) + { + const css::uno::Reference<css::rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap, uno::UNO_QUERY_THROW); + rBitmapEx = vcl::unotools::bitmapExFromXBitmap(xIntBmp); + } + +} + +/** Copy block of image data into the bitmap. + Assumes that the Bitmap has been constructed with the desired size. + + @param pData + The block of data to copy + @param nStride + The number of bytes in a scanline, must >= (width * nBitCount / 8) +*/ +BitmapEx CreateFromData( sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight, sal_Int32 nStride, sal_uInt16 nBitCount ) +{ + assert(nStride >= (nWidth * nBitCount / 8)); + assert( nBitCount == 1 || nBitCount == 24 || nBitCount == 32); + Bitmap aBmp( Size( nWidth, nHeight ), nBitCount ); + + BitmapScopedWriteAccess pWrite(aBmp); + assert(pWrite.get()); + if( !pWrite ) + return BitmapEx(); + std::unique_ptr<AlphaMask> pAlphaMask; + AlphaScopedWriteAccess xMaskAcc; + if (nBitCount == 32) + { + pAlphaMask.reset( new AlphaMask( Size(nWidth, nHeight) ) ); + xMaskAcc = AlphaScopedWriteAccess(*pAlphaMask); + } + if (nBitCount == 1) + { + for( long y = 0; y < nHeight; ++y ) + { + Scanline pScanline = pWrite->GetScanline(y); + for (long x = 0; x < nWidth; ++x) + { + sal_uInt8 const *p = pData + y * nStride / 8; + int bitIndex = (y * nStride) % 8; + pWrite->SetPixelOnData(pScanline, x, BitmapColor((*p >> bitIndex) & 1)); + } + } + } + else + { + for( long y = 0; y < nHeight; ++y ) + { + sal_uInt8 const *p = pData + (y * nStride); + Scanline pScanline = pWrite->GetScanline(y); + for (long x = 0; x < nWidth; ++x) + { + BitmapColor col(p[0], p[1], p[2]); + pWrite->SetPixelOnData(pScanline, x, col); + p += nBitCount/8; + } + if (nBitCount == 32) + { + p = pData + (y * nStride) + 3; + Scanline pMaskScanLine = xMaskAcc->GetScanline(y); + for (long x = 0; x < nWidth; ++x) + { + xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(*p)); + p += 4; + } + } + } + } + if (nBitCount == 32) + return BitmapEx(aBmp, *pAlphaMask); + else + return BitmapEx(aBmp); +} + +/** Copy block of image data into the bitmap. + Assumes that the Bitmap has been constructed with the desired size. +*/ +BitmapEx CreateFromData( RawBitmap&& rawBitmap ) +{ + auto nBitCount = rawBitmap.GetBitCount(); + assert( nBitCount == 24 || nBitCount == 32); + Bitmap aBmp( rawBitmap.maSize, nBitCount ); + + BitmapScopedWriteAccess pWrite(aBmp); + assert(pWrite.get()); + if( !pWrite ) + return BitmapEx(); + std::unique_ptr<AlphaMask> pAlphaMask; + AlphaScopedWriteAccess xMaskAcc; + if (nBitCount == 32) + { + pAlphaMask.reset( new AlphaMask( rawBitmap.maSize ) ); + xMaskAcc = AlphaScopedWriteAccess(*pAlphaMask); + } + + auto nHeight = rawBitmap.maSize.getHeight(); + auto nWidth = rawBitmap.maSize.getWidth(); + auto nStride = nWidth * nBitCount / 8; + for( long y = 0; y < nHeight; ++y ) + { + sal_uInt8 const *p = rawBitmap.mpData.get() + (y * nStride); + Scanline pScanline = pWrite->GetScanline(y); + for (long x = 0; x < nWidth; ++x) + { + BitmapColor col(p[0], p[1], p[2]); + pWrite->SetPixelOnData(pScanline, x, col); + p += nBitCount/8; + } + if (nBitCount == 32) + { + p = rawBitmap.mpData.get() + (y * nStride) + 3; + Scanline pMaskScanLine = xMaskAcc->GetScanline(y); + for (long x = 0; x < nWidth; ++x) + { + xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(*p)); + p += 4; + } + } + } + if (nBitCount == 32) + return BitmapEx(aBmp, *pAlphaMask); + else + return BitmapEx(aBmp); +} + +#if ENABLE_CAIRO_CANVAS +BitmapEx* CreateFromCairoSurface(Size aSize, cairo_surface_t * pSurface) +{ + // FIXME: if we could teach VCL/ about cairo handles, life could + // be significantly better here perhaps. + +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) + cairo_surface_t *pPixels = cairo_surface_create_similar_image(pSurface, +#else + cairo_surface_t *pPixels = cairo_image_surface_create( +#endif + CAIRO_FORMAT_ARGB32, aSize.Width(), aSize.Height()); + cairo_t *pCairo = cairo_create( pPixels ); + if( !pPixels || !pCairo || cairo_status(pCairo) != CAIRO_STATUS_SUCCESS ) + return nullptr; + + // suck ourselves from the X server to this buffer so then we can fiddle with + // Alpha to turn it into the ultra-lame vcl required format and then push it + // all back again later at vast expense [ urgh ] + cairo_set_source_surface( pCairo, pSurface, 0, 0 ); + cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE ); + cairo_paint( pCairo ); + + ::Bitmap aRGB( aSize, 24 ); + ::AlphaMask aMask( aSize ); + + BitmapScopedWriteAccess pRGBWrite(aRGB); + assert(pRGBWrite); + if (!pRGBWrite) + return nullptr; + + AlphaScopedWriteAccess pMaskWrite(aMask); + assert(pMaskWrite); + if (!pMaskWrite) + return nullptr; + + cairo_surface_flush(pPixels); + unsigned char *pSrc = cairo_image_surface_get_data( pPixels ); + unsigned int nStride = cairo_image_surface_get_stride( pPixels ); + vcl::bitmap::lookup_table unpremultiply_table = vcl::bitmap::get_unpremultiply_table(); + for( long y = 0; y < aSize.Height(); y++ ) + { + sal_uInt32 *pPix = reinterpret_cast<sal_uInt32 *>(pSrc + nStride * y); + for( long x = 0; x < aSize.Width(); x++ ) + { +#if defined OSL_BIGENDIAN + sal_uInt8 nB = (*pPix >> 24); + sal_uInt8 nG = (*pPix >> 16) & 0xff; + sal_uInt8 nR = (*pPix >> 8) & 0xff; + sal_uInt8 nAlpha = *pPix & 0xff; +#else + sal_uInt8 nAlpha = (*pPix >> 24); + sal_uInt8 nR = (*pPix >> 16) & 0xff; + sal_uInt8 nG = (*pPix >> 8) & 0xff; + sal_uInt8 nB = *pPix & 0xff; +#endif + if( nAlpha != 0 && nAlpha != 255 ) + { + // Cairo uses pre-multiplied alpha - we do not => re-multiply + nR = unpremultiply_table[nAlpha][nR]; + nG = unpremultiply_table[nAlpha][nG]; + nB = unpremultiply_table[nAlpha][nB]; + } + pRGBWrite->SetPixel( y, x, BitmapColor( nR, nG, nB ) ); + pMaskWrite->SetPixelIndex( y, x, 255 - nAlpha ); + pPix++; + } + } + + // ignore potential errors above. will get caller a + // uniformly white bitmap, but not that there would + // be error handling in calling code ... + ::BitmapEx *pBitmapEx = new ::BitmapEx( aRGB, aMask ); + + cairo_destroy( pCairo ); + cairo_surface_destroy( pPixels ); + return pBitmapEx; +} +#endif + +BitmapEx CanvasTransformBitmap( const BitmapEx& rBitmap, + const ::basegfx::B2DHomMatrix& rTransform, + ::basegfx::B2DRectangle const & rDestRect, + ::basegfx::B2DHomMatrix const & rLocalTransform ) +{ + const Size aBmpSize( rBitmap.GetSizePixel() ); + Bitmap aSrcBitmap( rBitmap.GetBitmap() ); + Bitmap aSrcAlpha; + + // differentiate mask and alpha channel (on-off + // vs. multi-level transparency) + if( rBitmap.IsTransparent() ) + { + if( rBitmap.IsAlpha() ) + aSrcAlpha = rBitmap.GetAlpha().GetBitmap(); + else + aSrcAlpha = rBitmap.GetMask(); + } + + Bitmap::ScopedReadAccess pReadAccess( aSrcBitmap ); + Bitmap::ScopedReadAccess pAlphaReadAccess( rBitmap.IsTransparent() ? + aSrcAlpha.AcquireReadAccess() : + nullptr, + aSrcAlpha ); + + if( pReadAccess.get() == nullptr || + (pAlphaReadAccess.get() == nullptr && rBitmap.IsTransparent()) ) + { + // TODO(E2): Error handling! + ENSURE_OR_THROW( false, + "transformBitmap(): could not access source bitmap" ); + } + + // mapping table, to translate pAlphaReadAccess' pixel + // values into destination alpha values (needed e.g. for + // paletted 1-bit masks). + sal_uInt8 aAlphaMap[256]; + + if( rBitmap.IsTransparent() ) + { + if( rBitmap.IsAlpha() ) + { + // source already has alpha channel - 1:1 mapping, + // i.e. aAlphaMap[0]=0,...,aAlphaMap[255]=255. + sal_uInt8 val=0; + sal_uInt8* pCur=aAlphaMap; + sal_uInt8* const pEnd=&aAlphaMap[256]; + while(pCur != pEnd) + *pCur++ = val++; + } + else + { + // mask transparency - determine used palette colors + const BitmapColor& rCol0( pAlphaReadAccess->GetPaletteColor( 0 ) ); + const BitmapColor& rCol1( pAlphaReadAccess->GetPaletteColor( 1 ) ); + + // shortcut for true luminance calculation + // (assumes that palette is grey-level) + aAlphaMap[0] = rCol0.GetRed(); + aAlphaMap[1] = rCol1.GetRed(); + } + } + // else: mapping table is not used + + const Size aDestBmpSize( ::basegfx::fround( rDestRect.getWidth() ), + ::basegfx::fround( rDestRect.getHeight() ) ); + + if( aDestBmpSize.IsEmpty() ) + return BitmapEx(); + + Bitmap aDstBitmap( aDestBmpSize, aSrcBitmap.GetBitCount(), &pReadAccess->GetPalette() ); + Bitmap aDstAlpha( AlphaMask( aDestBmpSize ).GetBitmap() ); + + { + // just to be on the safe side: let the + // ScopedAccessors get destructed before + // copy-constructing the resulting bitmap. This will + // rule out the possibility that cached accessor data + // is not yet written back. + BitmapScopedWriteAccess pWriteAccess( aDstBitmap ); + BitmapScopedWriteAccess pAlphaWriteAccess( aDstAlpha ); + + + if( pWriteAccess.get() != nullptr && + pAlphaWriteAccess.get() != nullptr && + rTransform.isInvertible() ) + { + // we're doing inverse mapping here, i.e. mapping + // points from the destination bitmap back to the + // source + ::basegfx::B2DHomMatrix aTransform( rLocalTransform ); + aTransform.invert(); + + // for the time being, always read as ARGB + for( long y=0; y<aDestBmpSize.Height(); ++y ) + { + // differentiate mask and alpha channel (on-off + // vs. multi-level transparency) + if( rBitmap.IsTransparent() ) + { + Scanline pScan = pWriteAccess->GetScanline( y ); + Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y ); + // Handling alpha and mask just the same... + for( long x=0; x<aDestBmpSize.Width(); ++x ) + { + ::basegfx::B2DPoint aPoint(x,y); + aPoint *= aTransform; + + const int nSrcX( ::basegfx::fround( aPoint.getX() ) ); + const int nSrcY( ::basegfx::fround( aPoint.getY() ) ); + if( nSrcX < 0 || nSrcX >= aBmpSize.Width() || + nSrcY < 0 || nSrcY >= aBmpSize.Height() ) + { + pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(255) ); + } + else + { + const sal_uInt8 cAlphaIdx = pAlphaReadAccess->GetPixelIndex( nSrcY, nSrcX ); + pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(aAlphaMap[ cAlphaIdx ]) ); + pWriteAccess->SetPixelOnData( pScan, x, pReadAccess->GetPixel( nSrcY, nSrcX ) ); + } + } + } + else + { + Scanline pScan = pWriteAccess->GetScanline( y ); + Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y ); + for( long x=0; x<aDestBmpSize.Width(); ++x ) + { + ::basegfx::B2DPoint aPoint(x,y); + aPoint *= aTransform; + + const int nSrcX( ::basegfx::fround( aPoint.getX() ) ); + const int nSrcY( ::basegfx::fround( aPoint.getY() ) ); + if( nSrcX < 0 || nSrcX >= aBmpSize.Width() || + nSrcY < 0 || nSrcY >= aBmpSize.Height() ) + { + pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(255) ); + } + else + { + pAlphaWriteAccess->SetPixelOnData( pScanAlpha, x, BitmapColor(0) ); + pWriteAccess->SetPixelOnData( pScan, x, pReadAccess->GetPixel( nSrcY, + nSrcX ) ); + } + } + } + } + } + else + { + // TODO(E2): Error handling! + ENSURE_OR_THROW( false, + "transformBitmap(): could not access bitmap" ); + } + } + + return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha)); +} + + +void DrawAlphaBitmapAndAlphaGradient(BitmapEx & rBitmapEx, bool bFixedTransparence, float fTransparence, AlphaMask & rNewMask) +{ + // mix existing and new alpha mask + AlphaMask aOldMask; + + if(rBitmapEx.IsAlpha()) + { + aOldMask = rBitmapEx.GetAlpha(); + } + else if(TransparentType::Bitmap == rBitmapEx.GetTransparentType()) + { + aOldMask = rBitmapEx.GetMask(); + } + else if(TransparentType::Color == rBitmapEx.GetTransparentType()) + { + aOldMask = rBitmapEx.GetBitmap().CreateMask(rBitmapEx.GetTransparentColor()); + } + + { + AlphaScopedWriteAccess pOld(aOldMask); + + assert(pOld && "Got no access to old alpha mask (!)"); + + const double fFactor(1.0 / 255.0); + + if(bFixedTransparence) + { + const double fOpNew(1.0 - fTransparence); + + for(long y(0); y < pOld->Height(); y++) + { + Scanline pScanline = pOld->GetScanline( y ); + for(long x(0); x < pOld->Width(); x++) + { + const double fOpOld(1.0 - (pOld->GetIndexFromData(pScanline, x) * fFactor)); + const sal_uInt8 aCol(basegfx::fround((1.0 - (fOpOld * fOpNew)) * 255.0)); + + pOld->SetPixelOnData(pScanline, x, BitmapColor(aCol)); + } + } + } + else + { + AlphaMask::ScopedReadAccess pNew(rNewMask); + + assert(pNew && "Got no access to new alpha mask (!)"); + + assert(pOld->Width() == pNew->Width() && pOld->Height() == pNew->Height() && + "Alpha masks have different sizes (!)"); + + for(long y(0); y < pOld->Height(); y++) + { + Scanline pScanline = pOld->GetScanline( y ); + for(long x(0); x < pOld->Width(); x++) + { + const double fOpOld(1.0 - (pOld->GetIndexFromData(pScanline, x) * fFactor)); + const double fOpNew(1.0 - (pNew->GetIndexFromData(pScanline, x) * fFactor)); + const sal_uInt8 aCol(basegfx::fround((1.0 - (fOpOld * fOpNew)) * 255.0)); + + pOld->SetPixelOnData(pScanline, x, BitmapColor(aCol)); + } + } + } + + } + + // apply combined bitmap as mask + rBitmapEx = BitmapEx(rBitmapEx.GetBitmap(), aOldMask); +} + + +void DrawAndClipBitmap(const Point& rPos, const Size& rSize, const BitmapEx& rBitmap, BitmapEx & aBmpEx, basegfx::B2DPolyPolygon const & rClipPath) +{ + ScopedVclPtrInstance< VirtualDevice > pVDev; + MapMode aMapMode( MapUnit::Map100thMM ); + aMapMode.SetOrigin( Point( -rPos.X(), -rPos.Y() ) ); + const Size aOutputSizePixel( pVDev->LogicToPixel( rSize, aMapMode ) ); + const Size aSizePixel( rBitmap.GetSizePixel() ); + if ( aOutputSizePixel.Width() && aOutputSizePixel.Height() ) + { + aMapMode.SetScaleX( Fraction( aSizePixel.Width(), aOutputSizePixel.Width() ) ); + aMapMode.SetScaleY( Fraction( aSizePixel.Height(), aOutputSizePixel.Height() ) ); + } + pVDev->SetMapMode( aMapMode ); + pVDev->SetOutputSizePixel( aSizePixel ); + pVDev->SetFillColor( COL_BLACK ); + const tools::PolyPolygon aClip( rClipPath ); + pVDev->DrawPolyPolygon( aClip ); + + // #i50672# Extract whole VDev content (to match size of rBitmap) + pVDev->EnableMapMode( false ); + const Bitmap aVDevMask(pVDev->GetBitmap(Point(), aSizePixel)); + + if(aBmpEx.IsTransparent()) + { + // bitmap already uses a Mask or Alpha, we need to blend that with + // the new masking in pVDev + if(aBmpEx.IsAlpha()) + { + // need to blend in AlphaMask quality (8Bit) + AlphaMask fromVDev(aVDevMask); + AlphaMask fromBmpEx(aBmpEx.GetAlpha()); + AlphaMask::ScopedReadAccess pR(fromVDev); + AlphaScopedWriteAccess pW(fromBmpEx); + + if(pR && pW) + { + const long nWidth(std::min(pR->Width(), pW->Width())); + const long nHeight(std::min(pR->Height(), pW->Height())); + + for(long nY(0); nY < nHeight; nY++) + { + Scanline pScanlineR = pR->GetScanline( nY ); + Scanline pScanlineW = pW->GetScanline( nY ); + for(long nX(0); nX < nWidth; nX++) + { + const sal_uInt8 nIndR(pR->GetIndexFromData(pScanlineR, nX)); + const sal_uInt8 nIndW(pW->GetIndexFromData(pScanlineW, nX)); + + // these values represent transparency (0 == no, 255 == fully transparent), + // so to blend these we have to multiply the inverse (opacity) + // and re-invert the result to transparence + const sal_uInt8 nCombined(0x00ff - (((0x00ff - nIndR) * (0x00ff - nIndW)) >> 8)); + + pW->SetPixelOnData(pScanlineW, nX, BitmapColor(nCombined)); + } + } + } + + pR.reset(); + pW.reset(); + aBmpEx = BitmapEx(aBmpEx.GetBitmap(), fromBmpEx); + } + else + { + // need to blend in Mask quality (1Bit) + Bitmap aMask(aVDevMask.CreateMask(COL_WHITE)); + + if ( rBitmap.GetTransparentColor() == COL_WHITE ) + { + aMask.CombineSimple( rBitmap.GetMask(), BmpCombine::Or ); + } + else + { + aMask.CombineSimple( rBitmap.GetMask(), BmpCombine::And ); + } + + aBmpEx = BitmapEx( rBitmap.GetBitmap(), aMask ); + } + } + else + { + // no mask yet, create and add new mask. For better quality, use Alpha, + // this allows the drawn mask being processed with AntiAliasing (AAed) + aBmpEx = BitmapEx(rBitmap.GetBitmap(), aVDevMask); + } +} + + +css::uno::Sequence< sal_Int8 > GetMaskDIB(BitmapEx const & aBmpEx) +{ + if ( aBmpEx.IsAlpha() ) + { + SvMemoryStream aMem; + WriteDIB(aBmpEx.GetAlpha().GetBitmap(), aMem, false, true); + return css::uno::Sequence< sal_Int8 >( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() ); + } + else if ( aBmpEx.IsTransparent() ) + { + SvMemoryStream aMem; + WriteDIB(aBmpEx.GetMask(), aMem, false, true); + return css::uno::Sequence< sal_Int8 >( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() ); + } + + return css::uno::Sequence< sal_Int8 >(); +} + +static bool readAlpha( BitmapReadAccess const * pAlphaReadAcc, long nY, const long nWidth, unsigned char* data, long nOff ) +{ + bool bIsAlpha = false; + long nX; + int nAlpha; + Scanline pReadScan; + + nOff += 3; + + switch( pAlphaReadAcc->GetScanlineFormat() ) + { + case ScanlineFormat::N8BitTcMask: + pReadScan = pAlphaReadAcc->GetScanline( nY ); + for( nX = 0; nX < nWidth; nX++ ) + { + nAlpha = data[ nOff ] = 255 - ( *pReadScan++ ); + if( nAlpha != 255 ) + bIsAlpha = true; + nOff += 4; + } + break; + case ScanlineFormat::N8BitPal: + pReadScan = pAlphaReadAcc->GetScanline( nY ); + for( nX = 0; nX < nWidth; nX++ ) + { + BitmapColor const& rColor( + pAlphaReadAcc->GetPaletteColor(*pReadScan)); + pReadScan++; + nAlpha = data[ nOff ] = 255 - rColor.GetIndex(); + if( nAlpha != 255 ) + bIsAlpha = true; + nOff += 4; + } + break; + default: + SAL_INFO( "canvas.cairo", "fallback to GetColor for alpha - slow, format: " << static_cast<int>(pAlphaReadAcc->GetScanlineFormat()) ); + for( nX = 0; nX < nWidth; nX++ ) + { + nAlpha = data[ nOff ] = 255 - pAlphaReadAcc->GetColor( nY, nX ).GetIndex(); + if( nAlpha != 255 ) + bIsAlpha = true; + nOff += 4; + } + } + + return bIsAlpha; +} + + + +/** + * @param data will be filled with alpha data, if xBitmap is alpha/transparent image + * @param bHasAlpha will be set to true if resulting surface has alpha + **/ +void CanvasCairoExtractBitmapData( BitmapEx const & aBmpEx, Bitmap & aBitmap, unsigned char*& data, bool& bHasAlpha, long& rnWidth, long& rnHeight ) +{ + AlphaMask aAlpha = aBmpEx.GetAlpha(); + + ::BitmapReadAccess* pBitmapReadAcc = aBitmap.AcquireReadAccess(); + ::BitmapReadAccess* pAlphaReadAcc = nullptr; + const long nWidth = rnWidth = pBitmapReadAcc->Width(); + const long nHeight = rnHeight = pBitmapReadAcc->Height(); + long nX, nY; + bool bIsAlpha = false; + + if( aBmpEx.IsTransparent() || aBmpEx.IsAlpha() ) + pAlphaReadAcc = aAlpha.AcquireReadAccess(); + + data = static_cast<unsigned char*>(malloc( nWidth*nHeight*4 )); + + long nOff = 0; + ::Color aColor; + unsigned int nAlpha = 255; + + vcl::bitmap::lookup_table premultiply_table = vcl::bitmap::get_premultiply_table(); + for( nY = 0; nY < nHeight; nY++ ) + { + ::Scanline pReadScan; + + switch( pBitmapReadAcc->GetScanlineFormat() ) + { + case ScanlineFormat::N8BitPal: + pReadScan = pBitmapReadAcc->GetScanline( nY ); + if( pAlphaReadAcc ) + if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) ) + bIsAlpha = true; + + for( nX = 0; nX < nWidth; nX++ ) + { +#ifdef OSL_BIGENDIAN + if( pAlphaReadAcc ) + nAlpha = data[ nOff++ ]; + else + nAlpha = data[ nOff++ ] = 255; +#else + if( pAlphaReadAcc ) + nAlpha = data[ nOff + 3 ]; + else + nAlpha = data[ nOff + 3 ] = 255; +#endif + aColor = pBitmapReadAcc->GetPaletteColor(*pReadScan++); + +#ifdef OSL_BIGENDIAN + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()]; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()]; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()]; +#else + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()]; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()]; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()]; + nOff++; +#endif + } + break; + case ScanlineFormat::N24BitTcBgr: + pReadScan = pBitmapReadAcc->GetScanline( nY ); + if( pAlphaReadAcc ) + if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) ) + bIsAlpha = true; + + for( nX = 0; nX < nWidth; nX++ ) + { +#ifdef OSL_BIGENDIAN + if( pAlphaReadAcc ) + nAlpha = data[ nOff ]; + else + nAlpha = data[ nOff ] = 255; + data[ nOff + 3 ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff + 2 ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff + 1 ] = premultiply_table[nAlpha][*pReadScan++]; + nOff += 4; +#else + if( pAlphaReadAcc ) + nAlpha = data[ nOff + 3 ]; + else + nAlpha = data[ nOff + 3 ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + nOff++; +#endif + } + break; + case ScanlineFormat::N24BitTcRgb: + pReadScan = pBitmapReadAcc->GetScanline( nY ); + if( pAlphaReadAcc ) + if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) ) + bIsAlpha = true; + + for( nX = 0; nX < nWidth; nX++ ) + { +#ifdef OSL_BIGENDIAN + if( pAlphaReadAcc ) + nAlpha = data[ nOff++ ]; + else + nAlpha = data[ nOff++ ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; +#else + if( pAlphaReadAcc ) + nAlpha = data[ nOff + 3 ]; + else + nAlpha = data[ nOff + 3 ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]]; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]]; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]]; + pReadScan += 3; + nOff++; +#endif + } + break; + case ScanlineFormat::N32BitTcBgra: + pReadScan = pBitmapReadAcc->GetScanline( nY ); + if( pAlphaReadAcc ) + if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) ) + bIsAlpha = true; + + for( nX = 0; nX < nWidth; nX++ ) + { +#ifdef OSL_BIGENDIAN + if( pAlphaReadAcc ) + nAlpha = data[ nOff++ ]; + else + nAlpha = data[ nOff++ ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]]; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]]; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]]; + pReadScan += 4; +#else + if( pAlphaReadAcc ) + nAlpha = data[ nOff + 3 ]; + else + nAlpha = data[ nOff + 3 ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + pReadScan++; + nOff++; +#endif + } + break; + case ScanlineFormat::N32BitTcRgba: + pReadScan = pBitmapReadAcc->GetScanline( nY ); + if( pAlphaReadAcc ) + if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) ) + bIsAlpha = true; + + for( nX = 0; nX < nWidth; nX++ ) + { +#ifdef OSL_BIGENDIAN + if( pAlphaReadAcc ) + nAlpha = data[ nOff ++ ]; + else + nAlpha = data[ nOff ++ ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + data[ nOff++ ] = premultiply_table[nAlpha][*pReadScan++]; + pReadScan++; +#else + if( pAlphaReadAcc ) + nAlpha = data[ nOff + 3 ]; + else + nAlpha = data[ nOff + 3 ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 2 ]]; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 1 ]]; + data[ nOff++ ] = premultiply_table[nAlpha][pReadScan[ 0 ]]; + pReadScan += 4; + nOff++; +#endif + } + break; + default: + SAL_INFO( "canvas.cairo", "fallback to GetColor - slow, format: " << static_cast<int>(pBitmapReadAcc->GetScanlineFormat()) ); + + if( pAlphaReadAcc ) + if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) ) + bIsAlpha = true; + + for( nX = 0; nX < nWidth; nX++ ) + { + aColor = pBitmapReadAcc->GetColor( nY, nX ); + + // cairo need premultiplied color values + // TODO(rodo) handle endianness +#ifdef OSL_BIGENDIAN + if( pAlphaReadAcc ) + nAlpha = data[ nOff++ ]; + else + nAlpha = data[ nOff++ ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()]; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()]; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()]; +#else + if( pAlphaReadAcc ) + nAlpha = data[ nOff + 3 ]; + else + nAlpha = data[ nOff + 3 ] = 255; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetBlue()]; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetGreen()]; + data[ nOff++ ] = premultiply_table[nAlpha][aColor.GetRed()]; + nOff ++; +#endif + } + } + } + + ::Bitmap::ReleaseAccess( pBitmapReadAcc ); + if( pAlphaReadAcc ) + aAlpha.ReleaseAccess( pAlphaReadAcc ); + + bHasAlpha = bIsAlpha; + +} + + uno::Sequence< sal_Int8 > CanvasExtractBitmapData(BitmapEx const & rBitmapEx, const geometry::IntegerRectangle2D& rect) + { + Bitmap aBitmap( rBitmapEx.GetBitmap() ); + Bitmap aAlpha( rBitmapEx.GetAlpha().GetBitmap() ); + + Bitmap::ScopedReadAccess pReadAccess( aBitmap ); + Bitmap::ScopedReadAccess pAlphaReadAccess( aAlpha.IsEmpty() ? + nullptr : aAlpha.AcquireReadAccess(), + aAlpha ); + + assert( pReadAccess ); + + // TODO(F1): Support more formats. + const Size aBmpSize( aBitmap.GetSizePixel() ); + + // for the time being, always return as BGRA + uno::Sequence< sal_Int8 > aRes( 4*aBmpSize.Width()*aBmpSize.Height() ); + sal_Int8* pRes = aRes.getArray(); + + int nCurrPos(0); + for( long y=rect.Y1; + y<aBmpSize.Height() && y<rect.Y2; + ++y ) + { + if( pAlphaReadAccess.get() != nullptr ) + { + Scanline pScanlineReadAlpha = pAlphaReadAccess->GetScanline( y ); + for( long x=rect.X1; + x<aBmpSize.Width() && x<rect.X2; + ++x ) + { + pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed(); + pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen(); + pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue(); + pRes[ nCurrPos++ ] = pAlphaReadAccess->GetIndexFromData( pScanlineReadAlpha, x ); + } + } + else + { + for( long x=rect.X1; + x<aBmpSize.Width() && x<rect.X2; + ++x ) + { + pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed(); + pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen(); + pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue(); + pRes[ nCurrPos++ ] = sal_uInt8(255); + } + } + } + return aRes; + } + + BitmapEx createHistorical8x8FromArray(std::array<sal_uInt8,64> const & pArray, Color aColorPix, Color aColorBack) + { + BitmapPalette aPalette(2); + + aPalette[0] = BitmapColor(aColorBack); + aPalette[1] = BitmapColor(aColorPix); + + Bitmap aBitmap(Size(8, 8), 1, &aPalette); + BitmapScopedWriteAccess pContent(aBitmap); + + for(sal_uInt16 a(0); a < 8; a++) + { + for(sal_uInt16 b(0); b < 8; b++) + { + if(pArray[(a * 8) + b]) + { + pContent->SetPixelIndex(a, b, 1); + } + else + { + pContent->SetPixelIndex(a, b, 0); + } + } + } + + return BitmapEx(aBitmap); + } + + bool isHistorical8x8(const BitmapEx& rBitmapEx, Color& o_rBack, Color& o_rFront) + { + bool bRet(false); + + if(!rBitmapEx.IsTransparent()) + { + Bitmap aBitmap(rBitmapEx.GetBitmap()); + + if(8 == aBitmap.GetSizePixel().Width() && 8 == aBitmap.GetSizePixel().Height()) + { + if(2 == aBitmap.GetColorCount()) + { + BitmapReadAccess* pRead = aBitmap.AcquireReadAccess(); + + if(pRead) + { + if(pRead->HasPalette() && 2 == pRead->GetPaletteEntryCount()) + { + const BitmapPalette& rPalette = pRead->GetPalette(); + + // #i123564# background and foreground were exchanged; of course + // rPalette[0] is the background color + o_rFront = rPalette[1]; + o_rBack = rPalette[0]; + + bRet = true; + } + + Bitmap::ReleaseAccess(pRead); + } + } + } + } + + return bRet; + } + + sal_uInt8 unpremultiply(sal_uInt8 c, sal_uInt8 a) + { + return (a == 0) ? 0 : (c * 255 + a / 2) / a; + } + + sal_uInt8 premultiply(sal_uInt8 c, sal_uInt8 a) + { + return (c * a + 127) / 255; + } + + lookup_table get_unpremultiply_table() + { + static bool inited; + static sal_uInt8 unpremultiply_table[256][256]; + + if (!inited) + { + for (int a = 0; a < 256; ++a) + for (int c = 0; c < 256; ++c) + unpremultiply_table[a][c] = unpremultiply(c, a); + inited = true; + } + + return unpremultiply_table; + } + + lookup_table get_premultiply_table() + { + static bool inited; + static sal_uInt8 premultiply_table[256][256]; + + if (!inited) + { + for (int a = 0; a < 256; ++a) + for (int c = 0; c < 256; ++c) + premultiply_table[a][c] = premultiply(c, a); + inited = true; + } + + return premultiply_table; + } + +bool convertBitmap32To24Plus8(BitmapEx const & rInput, BitmapEx & rResult) +{ + Bitmap aBitmap(rInput.GetBitmap()); + if (aBitmap.GetBitCount() != 32) + return false; + + Size aSize = aBitmap.GetSizePixel(); + Bitmap aResultBitmap(aSize, 24); + AlphaMask aResultAlpha(aSize); + { + BitmapScopedWriteAccess pResultBitmapAccess(aResultBitmap); + AlphaScopedWriteAccess pResultAlphaAccess(aResultAlpha); + + Bitmap::ScopedReadAccess pReadAccess(aBitmap); + + for (long nY = 0; nY < aSize.Height(); ++nY) + { + Scanline aResultScan = pResultBitmapAccess->GetScanline(nY); + Scanline aResultScanAlpha = pResultAlphaAccess->GetScanline(nY); + + Scanline aReadScan = pReadAccess->GetScanline(nY); + + for (long nX = 0; nX < aSize.Width(); ++nX) + { + const BitmapColor aColor = pReadAccess->GetPixelFromData(aReadScan, nX); + BitmapColor aResultColor(aColor.GetRed(), aColor.GetGreen(), aColor.GetBlue()); + BitmapColor aResultColorAlpha(aColor.GetAlpha(), aColor.GetAlpha(), aColor.GetAlpha()); + + pResultBitmapAccess->SetPixelOnData(aResultScan, nX, aResultColor); + pResultAlphaAccess->SetPixelOnData(aResultScanAlpha, nX, aResultColorAlpha); + } + } + } + if (rInput.IsTransparent()) + rResult = BitmapEx(aResultBitmap, rInput.GetAlpha()); + else + rResult = BitmapEx(aResultBitmap, aResultAlpha); + return true; +} + +} // end vcl::bitmap + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |