/* -*- 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/. *
*/
/** 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) @param bReversColors In case the endianness of pData is wrong, you could reverse colors
*/
BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight,
sal_Int32 nStride, sal_Int8 nBitCount, bool bReversColors, bool bReverseAlpha)
{
assert(nStride >= (nWidth * nBitCount / 8));
assert(nBitCount == 1 || nBitCount == 8 || nBitCount == 24 || nBitCount == 32);
BitmapScopedWriteAccess pWrite(aBmp);
assert(pWrite.get()); if( !pWrite ) return BitmapEx();
std::optional<AlphaMask> pAlphaMask;
BitmapScopedWriteAccess xMaskAcc; if (nBitCount == 32)
{
pAlphaMask.emplace( Size(nWidth, nHeight) );
xMaskAcc = *pAlphaMask;
} if (nBitCount == 1)
{ for( tools::Long y = 0; y < nHeight; ++y )
{
sal_uInt8 const *p = pData + y * nStride / 8;
Scanline pScanline = pWrite->GetScanline(y); for (tools::Long x = 0; x < nWidth; ++x)
{ int bitIndex = (y * nStride + x) % 8;
pWrite->SetPixelOnData(pScanline, x, BitmapColor((*p >> bitIndex) & 1));
}
}
} else
{ for( tools::Long y = 0; y < nHeight; ++y )
{
sal_uInt8 const *p = pData + (y * nStride);
Scanline pScanline = pWrite->GetScanline(y); for (tools::Long x = 0; x < nWidth; ++x)
{
BitmapColor col; if (nBitCount == 8)
col = BitmapColor( *p ); elseif ( bReversColors )
col = BitmapColor( p[2], p[1], p[0] ); else
col = BitmapColor( 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 (tools::Long x = 0; x < nWidth; ++x)
{ // FIXME this parameter is badly named const sal_uInt8 nValue = bReverseAlpha ? *p : 0xff - *p;
xMaskAcc->SetPixelOnData(pMaskScanLine, x, BitmapColor(nValue));
p += 4;
}
}
}
} // Avoid further bitmap use with unfinished write access
pWrite.reset();
xMaskAcc.reset(); 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);
#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.
// 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 );
BitmapScopedWriteAccess pRGBWrite(aRGB);
assert(pRGBWrite); if (!pRGBWrite) return nullptr;
BitmapScopedWriteAccess pMaskWrite(aMask);
assert(pMaskWrite); if (!pMaskWrite) return nullptr;
cairo_surface_flush(pPixels); unsignedchar *pSrc = cairo_image_surface_get_data( pPixels ); unsignedint nStride = cairo_image_surface_get_stride( pPixels ); #if !ENABLE_WASM_STRIP_PREMULTIPLY
vcl::bitmap::lookup_table const & unpremultiply_table = vcl::bitmap::get_unpremultiply_table(); #endif for( tools::Long y = 0; y < aSize.Height(); y++ )
{
sal_uInt32 *pPix = reinterpret_cast<sal_uInt32 *>(pSrc + nStride * y); for( tools::Long x = 0; x < aSize.Width(); x++ )
{ #ifdefined 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 #if ENABLE_WASM_STRIP_PREMULTIPLY
nR = vcl::bitmap::unpremultiply(nR, nAlpha);
nG = vcl::bitmap::unpremultiply(nG, nAlpha);
nB = vcl::bitmap::unpremultiply(nB, nAlpha); #else
nR = unpremultiply_table[nAlpha][nR];
nG = unpremultiply_table[nAlpha][nG];
nB = unpremultiply_table[nAlpha][nB]; #endif
}
pRGBWrite->SetPixel( y, x, BitmapColor( nR, nG, nB ) );
pMaskWrite->SetPixelIndex( y, x, 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 );
{ // 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( tools::Long y=0; y<aDestBmpSize.Height(); ++y )
{ // differentiate mask and alpha channel (on-off // vs. multi-level transparency) if( rBitmap.IsAlpha() )
{
Scanline pScan = pWriteAccess->GetScanline( y );
Scanline pScanAlpha = pAlphaWriteAccess->GetScanline( y ); // Handling alpha and mask just the same... for( tools::Long x=0; x<aDestBmpSize.Width(); ++x )
{
::basegfx::B2DPoint aPoint(x,y);
aPoint *= aTransform;
// #i50672# Extract whole VDev content (to match size of rBitmap)
pVDev->EnableMapMode( false ); const Bitmap aVDevMask(pVDev->GetBitmap(Point(), aSizePixel));
if(aBmpEx.IsAlpha())
{ // bitmap already uses a Mask or Alpha, we need to blend that with // the new masking in pVDev. // need to blend in AlphaMask quality (8Bit)
AlphaMask fromVDev(aVDevMask);
AlphaMask fromBmpEx(aBmpEx.GetAlphaMask());
BitmapScopedReadAccess pR(fromVDev);
BitmapScopedWriteAccess pW(fromBmpEx);
// these values represent alpha (255 == no, 0 == fully transparent), // so to blend these we have to multiply const sal_uInt8 nCombined((nIndR * nIndW) >> 8);
pR.reset();
pW.reset();
aBmpEx = BitmapEx(aBmpEx.GetBitmap(), fromBmpEx);
} 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;
AlphaMask aMask = aBmpEx.GetAlphaMask(); // for backwards compatibility for extensions, we need to convert from alpha to transparency
aMask.Invert();
WriteDIB(aMask.GetBitmap(), aMem, false, true); return css::uno::Sequence< sal_Int8 >( static_cast<sal_Int8 const *>(aMem.GetData()), aMem.Tell() );
}
/** * @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, const Bitmap & aBitmap, unsignedchar*& data, bool& bHasAlpha, tools::Long& rnWidth, tools::Long& rnHeight )
{ const AlphaMask& aAlpha = aBmpEx.GetAlphaMask();
if(8 == aBitmap.GetSizePixel().Width() && 8 == aBitmap.GetSizePixel().Height())
{ // Historical 1bpp images are getting really historical, // even to the point that e.g. the png loader actually loads // them as RGB. But the pattern code in svx relies on this // assumption that any 2-color 1bpp bitmap is a pattern, and so it would // get confused by RGB. Try to detect if this image is really // just two colors and say it's a pattern bitmap if so.
BitmapScopedReadAccess access(aBitmap);
o_rBack = access->GetColor(0,0); bool foundSecondColor = false;; for(tools::Long y = 0, nHeight = access->Height(); y < nHeight; ++y) for(tools::Long x = 0, nWidth = access->Width(); x < nWidth; ++x)
{ if(!foundSecondColor)
{ if( access->GetColor(y,x) != o_rBack )
{
o_rFront = access->GetColor(y,x);
foundSecondColor = true; // Hard to know which of the two colors is the background, // select the lighter one. if( o_rFront.GetLuminance() > o_rBack.GetLuminance())
std::swap( o_rFront, o_rBack );
}
} else
{ if( access->GetColor(y,x) != o_rBack && access->GetColor(y,x) != o_rFront) returnfalse;
}
} returntrue;
}
}
return bRet;
}
#if ENABLE_WASM_STRIP_PREMULTIPLY
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;
} #else
sal_uInt8 unpremultiply(sal_uInt8 c, sal_uInt8 a)
{ return get_unpremultiply_table()[a][c];
}
static constexpr sal_uInt8 unpremultiplyImpl(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 get_premultiply_table()[a][c];
}
static constexpr sal_uInt8 premultiplyImpl(sal_uInt8 c, sal_uInt8 a)
{ return (c * a + 127) / 255;
}
template<int... Is> static constexpr std::array<sal_uInt8, 256> make_unpremultiply_table_row_( int a, std::integer_sequence<int, Is...>)
{ return {unpremultiplyImpl(Is, a)...};
}
if( !aBmp.IsEmpty() )
{ // do downsampling if necessary // #103209# Normalize size (mirroring has to happen outside of this method)
Size aDstSizeTwip(std::abs(rDstSizeTwip.Width()), std::abs(rDstSizeTwip.Height()));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.