compilerplugins/clang/unusedenumconstants.writeonly.results | 2 include/vcl/BitmapBuffer.hxx | 1 include/vcl/BitmapInfoAccess.hxx | 9 include/vcl/BitmapReadAccess.hxx | 6 include/vcl/Scanline.hxx | 6 vcl/headless/BitmapHelper.cxx | 3 vcl/headless/CairoCommon.cxx | 1 vcl/headless/svpbmp.cxx | 1 vcl/qt5/QtBitmap.cxx | 1 vcl/skia/salbmp.cxx | 2 vcl/source/bitmap/bmpfast.cxx | 36 +++ vcl/source/bitmap/dibtools.cxx | 111 +++++++----- vcl/source/bitmap/salbmp.cxx | 16 + vcl/source/filter/webp/reader.cxx | 16 + vcl/source/filter/webp/writer.cxx | 2 vcl/source/gdi/salmisc.cxx | 27 ++ vcl/source/helper/canvasbitmap.cxx | 5 vcl/source/opengl/OpenGLHelper.cxx | 4 vcl/win/gdi/gdiimpl.cxx | 3 vcl/win/gdi/salbmp.cxx | 15 - vcl/win/gdi/salgdi2.cxx | 10 - vcl/win/gdi/salvd.cxx | 4 22 files changed, 215 insertions(+), 66 deletions(-)
New commits: commit 18df81e1568f4944a8fd79fd81e85d263dc0325b Author: Noel Grandin <noelgran...@gmail.com> AuthorDate: Wed Jan 22 20:12:41 2025 +0200 Commit: Noel Grandin <noelgran...@gmail.com> CommitDate: Thu Jan 23 08:28:14 2025 +0100 Revert "ScanlineDirection is unnecessary" This reverts commit a525438eb62f59801899c5ea45e451963b2ec248 Author: Noel Grandin <noelgran...@collabora.co.uk> Date: Wed Jan 22 09:50:43 2025 +0200 fix GDI and Quartz backends and commit 828a0dcdf6fd1600baaf3103583633006a90d9f9 Author: Noel Grandin <noel.gran...@collabora.co.uk> Date: Thu Jan 16 08:23:19 2025 +0200 ScanlineDirection is unnecessary because macOS uses bottom-up coordinate system, and I could not find a way to make the bitmaps stored top-down. Change-Id: Idc05b7473eca5fae0e33d634117de810146da3b5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180603 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk> diff --git a/compilerplugins/clang/unusedenumconstants.writeonly.results b/compilerplugins/clang/unusedenumconstants.writeonly.results index 961e7f0a5d4b..f950b0ba18aa 100644 --- a/compilerplugins/clang/unusedenumconstants.writeonly.results +++ b/compilerplugins/clang/unusedenumconstants.writeonly.results @@ -4066,6 +4066,8 @@ include/vcl/salnativewidgets.hxx:198 enum ControlPart BackgroundDialog include/vcl/salnativewidgets.hxx:237 enum ButtonValue DontKnow +include/vcl/Scanline.hxx:48 + enum ScanlineDirection BottomUp include/vcl/sysdata.hxx:54 enum SystemEnvData::Toolkit Qt include/vcl/syswin.hxx:50 diff --git a/include/vcl/BitmapBuffer.hxx b/include/vcl/BitmapBuffer.hxx index 120c3140909c..a9e91c54505f 100644 --- a/include/vcl/BitmapBuffer.hxx +++ b/include/vcl/BitmapBuffer.hxx @@ -40,6 +40,7 @@ struct VCL_DLLPUBLIC BitmapBuffer BitmapPalette maPalette; sal_uInt8* mpBits; ScanlineFormat meFormat = ScanlineFormat::NONE; + ScanlineDirection meDirection = ScanlineDirection::BottomUp; ColorMask maColorMask; sal_uInt16 mnBitCount; }; diff --git a/include/vcl/BitmapInfoAccess.hxx b/include/vcl/BitmapInfoAccess.hxx index 1896dc9bfc44..f3ac582b6a16 100644 --- a/include/vcl/BitmapInfoAccess.hxx +++ b/include/vcl/BitmapInfoAccess.hxx @@ -48,6 +48,15 @@ public: tools::Long Height() const { return mpBuffer ? mpBuffer->mnHeight : 0L; } + bool IsTopDown() const + { + assert(mpBuffer && "Access is not valid!"); + + return mpBuffer && mpBuffer->meDirection == ScanlineDirection::TopDown; + } + + bool IsBottomUp() const { return !IsTopDown(); } + ScanlineFormat GetScanlineFormat() const { assert(mpBuffer && "Access is not valid!"); diff --git a/include/vcl/BitmapReadAccess.hxx b/include/vcl/BitmapReadAccess.hxx index b3895afa5c2e..0227e4d1df4d 100644 --- a/include/vcl/BitmapReadAccess.hxx +++ b/include/vcl/BitmapReadAccess.hxx @@ -49,7 +49,11 @@ public: assert(mpBuffer && "Access is not valid!"); assert(nY < mpBuffer->mnHeight && "y-coordinate out of range!"); - return mpBuffer->mpBits + (nY * mpBuffer->mnScanlineSize); + if (mpBuffer->meDirection == ScanlineDirection::TopDown) + { + return mpBuffer->mpBits + (nY * mpBuffer->mnScanlineSize); + } + return mpBuffer->mpBits + ((mpBuffer->mnHeight - 1 - nY) * mpBuffer->mnScanlineSize); } BitmapColor GetPixelFromData(const sal_uInt8* pData, tools::Long nX) const diff --git a/include/vcl/Scanline.hxx b/include/vcl/Scanline.hxx index b754e581d0c5..9dc7af46b904 100644 --- a/include/vcl/Scanline.hxx +++ b/include/vcl/Scanline.hxx @@ -47,4 +47,10 @@ enum class ScanlineFormat : sal_uInt8 N32BitTcMask, }; +enum class ScanlineDirection : sal_uInt8 +{ + BottomUp, + TopDown +}; + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/headless/BitmapHelper.cxx b/vcl/headless/BitmapHelper.cxx index 0f21780144e8..cfa9f1556eeb 100644 --- a/vcl/headless/BitmapHelper.cxx +++ b/vcl/headless/BitmapHelper.cxx @@ -40,7 +40,8 @@ BitmapHelper::BitmapHelper(const SalBitmap& rSourceBitmap, const bool bForceARGB const SalTwoRect aTwoRect = { 0, 0, pSrc->mnWidth, pSrc->mnHeight, 0, 0, pSrc->mnWidth, pSrc->mnHeight }; std::optional<BitmapBuffer> pTmp - = (pSrc->meFormat == SVP_24BIT_FORMAT) + = (pSrc->meFormat == SVP_24BIT_FORMAT + && pSrc->meDirection == ScanlineDirection::TopDown) ? FastConvert24BitRgbTo32BitCairo(pSrc) : StretchAndConvert(*pSrc, aTwoRect, SVP_CAIRO_FORMAT); aTmpBmp.Create(std::move(pTmp)); diff --git a/vcl/headless/CairoCommon.cxx b/vcl/headless/CairoCommon.cxx index bf12617ba23e..78a16b729a9e 100644 --- a/vcl/headless/CairoCommon.cxx +++ b/vcl/headless/CairoCommon.cxx @@ -1979,6 +1979,7 @@ std::optional<BitmapBuffer> FastConvert24BitRgbTo32BitCairo(const BitmapBuffer* const tools::Long nHeight = pSrc->mnHeight; std::optional<BitmapBuffer> pDst(std::in_place); pDst->meFormat = ScanlineFormat::N32BitTcArgb; + pDst->meDirection = ScanlineDirection::TopDown; pDst->mnWidth = nWidth; pDst->mnHeight = nHeight; pDst->mnBitCount = 32; diff --git a/vcl/headless/svpbmp.cxx b/vcl/headless/svpbmp.cxx index 1fcf6f1e5c58..23a0d094d009 100644 --- a/vcl/headless/svpbmp.cxx +++ b/vcl/headless/svpbmp.cxx @@ -70,6 +70,7 @@ static std::optional<BitmapBuffer> ImplCreateDIB( if (ePixelFormat <= vcl::PixelFormat::N8_BPP) nColors = vcl::numberOfColors(ePixelFormat); + pDIB->meDirection = ScanlineDirection::TopDown; pDIB->mnWidth = rSize.Width(); pDIB->mnHeight = rSize.Height(); tools::Long nScanlineBase; diff --git a/vcl/qt5/QtBitmap.cxx b/vcl/qt5/QtBitmap.cxx index 26cb42154042..235f94715e04 100644 --- a/vcl/qt5/QtBitmap.cxx +++ b/vcl/qt5/QtBitmap.cxx @@ -120,6 +120,7 @@ BitmapBuffer* QtBitmap::AcquireBuffer(BitmapAccessMode /*nMode*/) pBuffer->mnBitCount = getFormatBits(m_pImage->format()); pBuffer->mpBits = m_pImage->bits(); pBuffer->mnScanlineSize = m_pImage->bytesPerLine(); + pBuffer->meDirection = ScanlineDirection::TopDown; switch (pBuffer->mnBitCount) { diff --git a/vcl/skia/salbmp.cxx b/vcl/skia/salbmp.cxx index ca1bcf863241..144f3496d44b 100644 --- a/vcl/skia/salbmp.cxx +++ b/vcl/skia/salbmp.cxx @@ -312,6 +312,7 @@ BitmapBuffer* SkiaSalBitmap::AcquireBuffer(BitmapAccessMode nMode) default: abort(); } + buffer->meDirection = ScanlineDirection::TopDown; // Refcount all read/write accesses, to catch problems with existing accesses while // a bitmap changes, and also to detect when we can free mBuffer if wanted. // Write mode implies also reading. It would be probably a good idea to count even @@ -1146,6 +1147,7 @@ void SkiaSalBitmap::PerformErase() if (!ImplFastEraseBitmap(*bitmapBuffer, fastColor)) { FncSetPixel setPixel = BitmapReadAccess::SetPixelFunction(bitmapBuffer->meFormat); + assert(bitmapBuffer->meDirection == ScanlineDirection::TopDown); // Set first scanline, copy to others. Scanline scanline = bitmapBuffer->mpBits; for (tools::Long x = 0; x < bitmapBuffer->mnWidth; ++x) diff --git a/vcl/source/bitmap/bmpfast.cxx b/vcl/source/bitmap/bmpfast.cxx index bae0ed1f5b3f..74da808b96cb 100644 --- a/vcl/source/bitmap/bmpfast.cxx +++ b/vcl/source/bitmap/bmpfast.cxx @@ -268,7 +268,13 @@ static bool ImplCopyImage( BitmapBuffer& rDstBuffer, const BitmapBuffer& rSrcBuf const PIXBYTE* pRawSrc = rSrcBuffer.mpBits; PIXBYTE* pRawDst = rDstBuffer.mpBits; - if( nSrcLinestep == nDstLinestep ) + // source and destination don't match upside down + if (rSrcBuffer.meDirection != rDstBuffer.meDirection) + { + pRawDst += (rSrcBuffer.mnHeight - 1) * nDstLinestep; + nDstLinestep = -rDstBuffer.mnScanlineSize; + } + else if( nSrcLinestep == nDstLinestep ) { memcpy( pRawDst, pRawSrc, rSrcBuffer.mnHeight * nDstLinestep ); return true; @@ -302,6 +308,13 @@ static bool ImplConvertToBitmap( TrueColorPixelPtr<SRCFMT>& rSrcLine, TrueColorPixelPtr<DSTFMT> aDstLine; aDstLine.SetRawPtr( rDstBuffer.mpBits ); + // source and destination don't match upside down + if (rSrcBuffer.meDirection != rDstBuffer.meDirection) + { + aDstLine.AddByteOffset( (rSrcBuffer.mnHeight - 1) * nDstLinestep ); + nDstLinestep = -nDstLinestep; + } + for( int y = rSrcBuffer.mnHeight; --y >= 0; ) { ImplConvertLine( aDstLine, rSrcLine, rSrcBuffer.mnWidth ); @@ -366,6 +379,7 @@ bool ImplFastBitmapConversion( BitmapBuffer& rDst, const BitmapBuffer& rSrc, return false; // vertical mirroring if( rTR.mnDestHeight < 0 ) + // TODO: rDst.meDirection != ScanlineDirection::TopDown; return false; // offsetted conversion is not implemented yet @@ -449,7 +463,10 @@ bool ImplFastBitmapConversion( BitmapBuffer& rDst, const BitmapBuffer& rSrc, static inline ConstScanline ImplGetScanline( const BitmapBuffer& rBuf, tools::Long nY ) { - return rBuf.mpBits + nY * rBuf.mnScanlineSize; + if (rBuf.meDirection == ScanlineDirection::TopDown) + return rBuf.mpBits + nY * rBuf.mnScanlineSize; + else + return rBuf.mpBits + (rBuf.mnHeight - 1 - nY) * rBuf.mnScanlineSize; } static inline Scanline ImplGetScanline( BitmapBuffer& rBuf, tools::Long nY ) @@ -567,6 +584,20 @@ static bool ImplBlendToBitmap( TrueColorPixelPtr<SRCFMT>& rSrcLine, if( rMskBuffer.mnHeight == 1 ) nMskLinestep = 0; + // source and mask don't match: upside down + if (rSrcBuffer.meDirection != rMskBuffer.meDirection) + { + aMskLine.AddByteOffset( (rSrcBuffer.mnHeight - 1) * nMskLinestep ); + nMskLinestep = -nMskLinestep; + } + + // source and destination don't match: upside down + if (rSrcBuffer.meDirection != rDstBuffer.meDirection) + { + aDstLine.AddByteOffset( (rDstBuffer.mnHeight - 1) * nDstLinestep ); + nDstLinestep = -nDstLinestep; + } + assert(rDstBuffer.mnHeight <= rSrcBuffer.mnHeight && "not sure about that?"); for (int y = rDstBuffer.mnHeight; --y >= 0;) { @@ -669,6 +700,7 @@ bool ImplFastBitmapBlending( BitmapWriteAccess const & rDstWA, return false; // vertical mirroring if( rTR.mnDestHeight < 0 ) + // TODO: rDst.meDirection != ScanlineDirection::TopDown; return false; // offsetted blending is not implemented yet diff --git a/vcl/source/bitmap/dibtools.cxx b/vcl/source/bitmap/dibtools.cxx index cd7155e47935..97bcb86b0feb 100644 --- a/vcl/source/bitmap/dibtools.cxx +++ b/vcl/source/bitmap/dibtools.cxx @@ -499,7 +499,7 @@ bool ImplReadDIBBits(SvStream& rIStm, DIBV5Header& rHeader, BitmapWriteAccess& r { // we can't trust arbitrary-sourced index based formats to have correct indexes, so we exclude the pal formats // from raw read and force checking their colormap indexes - bNative = bTopDown && !bRLE && !bTCMask && ( rAcc.GetScanlineSize() == nAlignedWidth ); + bNative = ( ( rAcc.IsBottomUp() != bTopDown ) && !bRLE && !bTCMask && ( rAcc.GetScanlineSize() == nAlignedWidth ) ); break; } @@ -1246,8 +1246,13 @@ bool ImplWriteDIBBits(SvStream& rOStm, BitmapReadAccess const & rAcc, sal_uLong rImageSize = rOStm.Tell(); - for( tools::Long nY = rAcc.Height() - 1, nScanlineSize = rAcc.GetScanlineSize(); nY >= 0; nY-- ) - rOStm.WriteBytes( rAcc.GetScanline(nY), nScanlineSize ); + if( rAcc.IsBottomUp() ) + rOStm.WriteBytes(rAcc.GetBuffer(), rAcc.Height() * rAcc.GetScanlineSize()); + else + { + for( tools::Long nY = rAcc.Height() - 1, nScanlineSize = rAcc.GetScanlineSize(); nY >= 0; nY-- ) + rOStm.WriteBytes( rAcc.GetScanline(nY), nScanlineSize ); + } } else if((RLE_4 == nCompression) || (RLE_8 == nCompression)) { @@ -1266,60 +1271,88 @@ bool ImplWriteDIBBits(SvStream& rOStm, BitmapReadAccess const & rAcc, sal_uLong // (other cases are not written below) const auto ePixelFormat(convertToBPP(rAcc.GetBitCount())); const sal_uLong nAlignedWidth(AlignedWidth4Bytes(rAcc.Width() * sal_Int32(ePixelFormat))); + bool bNative(false); - rImageSize = rOStm.Tell(); - - const tools::Long nWidth(rAcc.Width()); - const tools::Long nHeight(rAcc.Height()); - std::vector<sal_uInt8> aBuf(nAlignedWidth); - switch(ePixelFormat) + switch(rAcc.GetScanlineFormat()) { - case vcl::PixelFormat::N8_BPP: + case ScanlineFormat::N1BitMsbPal: + case ScanlineFormat::N8BitPal: + case ScanlineFormat::N24BitTcBgr: { - for( tools::Long nY = nHeight - 1; nY >= 0; nY-- ) + if(rAcc.IsBottomUp() && (rAcc.GetScanlineSize() == nAlignedWidth)) { - sal_uInt8* pTmp = aBuf.data(); - Scanline pScanline = rAcc.GetScanline( nY ); - - for( tools::Long nX = 0; nX < nWidth; nX++ ) - *pTmp++ = rAcc.GetIndexFromData( pScanline, nX ); - - rOStm.WriteBytes(aBuf.data(), nAlignedWidth); + bNative = true; } + + break; } - break; - case vcl::PixelFormat::N24_BPP: + default: { - //valgrind, zero out the trailing unused alignment bytes - size_t nUnusedBytes = nAlignedWidth - nWidth * 3; - memset(aBuf.data() + nAlignedWidth - nUnusedBytes, 0, nUnusedBytes); + break; } - [[fallthrough]]; - // #i59239# fallback to 24 bit format, if bitcount is non-default - default: + } + + rImageSize = rOStm.Tell(); + + if(bNative) + { + rOStm.WriteBytes(rAcc.GetBuffer(), nAlignedWidth * rAcc.Height()); + } + else + { + const tools::Long nWidth(rAcc.Width()); + const tools::Long nHeight(rAcc.Height()); + std::vector<sal_uInt8> aBuf(nAlignedWidth); + switch(ePixelFormat) { - BitmapColor aPixelColor; + case vcl::PixelFormat::N8_BPP: + { + for( tools::Long nY = nHeight - 1; nY >= 0; nY-- ) + { + sal_uInt8* pTmp = aBuf.data(); + Scanline pScanline = rAcc.GetScanline( nY ); - for( tools::Long nY = nHeight - 1; nY >= 0; nY-- ) + for( tools::Long nX = 0; nX < nWidth; nX++ ) + *pTmp++ = rAcc.GetIndexFromData( pScanline, nX ); + + rOStm.WriteBytes(aBuf.data(), nAlignedWidth); + } + } + break; + + case vcl::PixelFormat::N24_BPP: + { + //valgrind, zero out the trailing unused alignment bytes + size_t nUnusedBytes = nAlignedWidth - nWidth * 3; + memset(aBuf.data() + nAlignedWidth - nUnusedBytes, 0, nUnusedBytes); + } + [[fallthrough]]; + // #i59239# fallback to 24 bit format, if bitcount is non-default + default: { - sal_uInt8* pTmp = aBuf.data(); + BitmapColor aPixelColor; - for( tools::Long nX = 0; nX < nWidth; nX++ ) + for( tools::Long nY = nHeight - 1; nY >= 0; nY-- ) { - // when alpha is used, this may be non-24bit main bitmap, so use GetColor - // instead of GetPixel to ensure RGB value - aPixelColor = rAcc.GetColor( nY, nX ); + sal_uInt8* pTmp = aBuf.data(); - *pTmp++ = aPixelColor.GetBlue(); - *pTmp++ = aPixelColor.GetGreen(); - *pTmp++ = aPixelColor.GetRed(); - } + for( tools::Long nX = 0; nX < nWidth; nX++ ) + { + // when alpha is used, this may be non-24bit main bitmap, so use GetColor + // instead of GetPixel to ensure RGB value + aPixelColor = rAcc.GetColor( nY, nX ); - rOStm.WriteBytes(aBuf.data(), nAlignedWidth); + *pTmp++ = aPixelColor.GetBlue(); + *pTmp++ = aPixelColor.GetGreen(); + *pTmp++ = aPixelColor.GetRed(); + } + + rOStm.WriteBytes(aBuf.data(), nAlignedWidth); + } } + break; } - break; } } diff --git a/vcl/source/bitmap/salbmp.cxx b/vcl/source/bitmap/salbmp.cxx index a84fc63f4042..3e8f0b255d2d 100644 --- a/vcl/source/bitmap/salbmp.cxx +++ b/vcl/source/bitmap/salbmp.cxx @@ -62,11 +62,19 @@ void SalBitmap::updateChecksum() const break; } } - if( pBuf->mnScanlineSize == lineBitsCount / 8 ) - nCrc = rtl_crc32(nCrc, pBuf->mpBits, pBuf->mnScanlineSize * pBuf->mnHeight); - else // Do not include padding with undefined content in the checksum. - for( tools::Long y = 0; y < pBuf->mnHeight; ++y ) + if (pBuf->meDirection == ScanlineDirection::TopDown) + { + if( pBuf->mnScanlineSize == lineBitsCount / 8 ) + nCrc = rtl_crc32(nCrc, pBuf->mpBits, pBuf->mnScanlineSize * pBuf->mnHeight); + else // Do not include padding with undefined content in the checksum. + for( tools::Long y = 0; y < pBuf->mnHeight; ++y ) + nCrc = scanlineChecksum(nCrc, pBuf->mpBits + y * pBuf->mnScanlineSize, lineBitsCount, extraBitsMask); + } + else // Compute checksum in the order of scanlines, to make it consistent between different bitmap implementations. + { + for( tools::Long y = pBuf->mnHeight - 1; y >= 0; --y ) nCrc = scanlineChecksum(nCrc, pBuf->mpBits + y * pBuf->mnScanlineSize, lineBitsCount, extraBitsMask); + } pThis->ReleaseBuffer(pBuf, BitmapAccessMode::Read); pThis->mnChecksum = nCrc; pThis->mbChecksumValid = true; diff --git a/vcl/source/filter/webp/reader.cxx b/vcl/source/filter/webp/reader.cxx index 2cc3f8c6a08e..cfc28a18440b 100644 --- a/vcl/source/filter/webp/reader.cxx +++ b/vcl/source/filter/webp/reader.cxx @@ -215,7 +215,23 @@ static bool readWebp(SvStream& stream, Graphic& graphic) switch (pixelMode) { case PixelMode::DirectRead: + { + // Adjust for IsBottomUp() if necessary. + if (access->IsBottomUp()) + { + std::vector<char> tmp; + const sal_uInt32 lineSize = access->GetScanlineSize(); + tmp.resize(lineSize); + for (tools::Long y = 0; y < access->Height() / 2; ++y) + { + tools::Long otherY = access->Height() - 1 - y; + memcpy(tmp.data(), access->GetScanline(y), lineSize); + memcpy(access->GetScanline(y), access->GetScanline(otherY), lineSize); + memcpy(access->GetScanline(otherY), tmp.data(), lineSize); + } + } break; + } case PixelMode::Split: { // Split to normal and alpha bitmaps. diff --git a/vcl/source/filter/webp/writer.cxx b/vcl/source/filter/webp/writer.cxx index 6a4e772eafd4..cd63cd2d2786 100644 --- a/vcl/source/filter/webp/writer.cxx +++ b/vcl/source/filter/webp/writer.cxx @@ -99,7 +99,7 @@ static bool writeWebp(SvStream& rStream, const BitmapEx& bitmapEx, bool lossless BitmapScopedReadAccess access(bitmap); BitmapScopedReadAccess accessAlpha(bitmapAlpha); bool dataDone = false; - if (bitmapAlpha.IsEmpty()) + if (!access->IsBottomUp() && bitmapAlpha.IsEmpty()) { // Try to directly copy the bitmap data. switch (access->GetScanlineFormat()) diff --git a/vcl/source/gdi/salmisc.cxx b/vcl/source/gdi/salmisc.cxx index 2ab6f77a23f1..79d976ac25db 100644 --- a/vcl/source/gdi/salmisc.cxx +++ b/vcl/source/gdi/salmisc.cxx @@ -235,6 +235,7 @@ std::optional<BitmapBuffer> StretchAndConvert( std::optional<BitmapBuffer> pDstBuffer(std::in_place); + pDstBuffer->meDirection = rSrcBuffer.meDirection; // set function for getting pixels pFncGetPixel = BitmapReadAccess::GetPixelFunction(rSrcBuffer.meFormat); if( !pFncGetPixel ) @@ -378,15 +379,33 @@ std::optional<BitmapBuffer> StretchAndConvert( } // source scanline buffer - Scanline pTmpScan = rSrcBuffer.mpBits; - tools::Long nOffset = rSrcBuffer.mnScanlineSize; + Scanline pTmpScan; + tools::Long nOffset; + if (rSrcBuffer.meDirection == ScanlineDirection::TopDown) + { + pTmpScan = rSrcBuffer.mpBits; + nOffset = rSrcBuffer.mnScanlineSize; + } + else + { + pTmpScan = rSrcBuffer.mpBits + ( rSrcBuffer.mnHeight - 1 ) * rSrcBuffer.mnScanlineSize; + nOffset = -rSrcBuffer.mnScanlineSize; + } for (tools::Long i = 0; i < rSrcBuffer.mnHeight; i++, pTmpScan += nOffset) pSrcScan[ i ] = pTmpScan; // destination scanline buffer - pTmpScan = pDstBuffer->mpBits; - nOffset = pDstBuffer->mnScanlineSize; + if (pDstBuffer->meDirection == ScanlineDirection::TopDown) + { + pTmpScan = pDstBuffer->mpBits; + nOffset = pDstBuffer->mnScanlineSize; + } + else + { + pTmpScan = pDstBuffer->mpBits + ( pDstBuffer->mnHeight - 1 ) * pDstBuffer->mnScanlineSize; + nOffset = -pDstBuffer->mnScanlineSize; + } for (tools::Long i = 0; i < pDstBuffer->mnHeight; i++, pTmpScan += nOffset) pDstScan[ i ] = pTmpScan; diff --git a/vcl/source/helper/canvasbitmap.cxx b/vcl/source/helper/canvasbitmap.cxx index 55a606a466e4..fc260b591773 100644 --- a/vcl/source/helper/canvasbitmap.cxx +++ b/vcl/source/helper/canvasbitmap.cxx @@ -399,6 +399,11 @@ uno::Sequence< sal_Int8 > SAL_CALL VclCanvasBitmap::getData( rendering::IntegerB bitmapLayout.ScanLineStride= aRequestedBytes.getOpenWidth(); sal_Int32 nScanlineStride=bitmapLayout.ScanLineStride; + if (m_pBmpAcc->IsBottomUp()) + { + pOutBuf += bitmapLayout.ScanLineStride*(aRequestedBytes.getOpenHeight()-1); + nScanlineStride *= -1; + } if( !m_aBmpEx.IsAlpha() ) { diff --git a/vcl/source/opengl/OpenGLHelper.cxx b/vcl/source/opengl/OpenGLHelper.cxx index 7dd10addea1d..19fb34df5d96 100644 --- a/vcl/source/opengl/OpenGLHelper.cxx +++ b/vcl/source/opengl/OpenGLHelper.cxx @@ -561,8 +561,12 @@ BitmapEx OpenGLHelper::ConvertBufferToBitmapEx(const sal_uInt8* const pBuffer, t BitmapScopedWriteAccess pAlphaWriteAccess( aAlpha ); #ifdef _WIN32 assert(pWriteAccess->GetScanlineFormat() == ScanlineFormat::N24BitTcBgr); + assert(pWriteAccess->IsTopDown()); + assert(pAlphaWriteAccess->IsTopDown()); #else assert(pWriteAccess->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb); + assert(!pWriteAccess->IsTopDown()); + assert(!pAlphaWriteAccess->IsTopDown()); #endif assert(pAlphaWriteAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal); diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx index e72414f70abb..beb3834459ec 100644 --- a/vcl/win/gdi/gdiimpl.cxx +++ b/vcl/win/gdi/gdiimpl.cxx @@ -538,11 +538,10 @@ void ImplDrawBitmap( HDC hDC, const SalTwoRect& rPosAry, const WinSalBitmap& rSa WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD ); const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS ); - int nHeight = -pBI->bmiHeader.biHeight; // height is negative for top-down bitmap StretchDIBits( hDC, static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY), static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight), - static_cast<int>(rPosAry.mnSrcX), static_cast<int>(nHeight - rPosAry.mnSrcHeight - rPosAry.mnSrcY), + static_cast<int>(rPosAry.mnSrcX), static_cast<int>(pBI->bmiHeader.biHeight - rPosAry.mnSrcHeight - rPosAry.mnSrcY), static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight), pBits, pBI, DIB_RGB_COLORS, nDrawMode ); diff --git a/vcl/win/gdi/salbmp.cxx b/vcl/win/gdi/salbmp.cxx index 1008cb83da30..3fe13d5254e3 100644 --- a/vcl/win/gdi/salbmp.cxx +++ b/vcl/win/gdi/salbmp.cxx @@ -251,6 +251,7 @@ std::shared_ptr<Gdiplus::Bitmap> WinSalBitmap::ImplCreateGdiPlusBitmap() { sal_uInt8* pSrcRGB(pRGB->mpBits); const sal_uInt32 nExtraRGB(pRGB->mnScanlineSize - (nW * 3)); + const bool bTopDown(pRGB->meDirection == ScanlineDirection::TopDown); const Gdiplus::Rect aAllRect(0, 0, nW, nH); Gdiplus::BitmapData aGdiPlusBitmapData; pRetval->LockBits(&aAllRect, Gdiplus::ImageLockModeWrite, PixelFormat24bppRGB, &aGdiPlusBitmapData); @@ -258,7 +259,8 @@ std::shared_ptr<Gdiplus::Bitmap> WinSalBitmap::ImplCreateGdiPlusBitmap() // copy data to Gdiplus::Bitmap; format is BGR here in both cases, so memcpy is possible for(sal_uInt32 y(0); y < nH; y++) { - sal_uInt8* targetPixels = static_cast<sal_uInt8*>(aGdiPlusBitmapData.Scan0) + (y * aGdiPlusBitmapData.Stride); + const sal_uInt32 nYInsert(bTopDown ? y : nH - y - 1); + sal_uInt8* targetPixels = static_cast<sal_uInt8*>(aGdiPlusBitmapData.Scan0) + (nYInsert * aGdiPlusBitmapData.Stride); memcpy(targetPixels, pSrcRGB, nW * 3); pSrcRGB += nW * 3 + nExtraRGB; @@ -368,6 +370,7 @@ std::shared_ptr<Gdiplus::Bitmap> WinSalBitmap::ImplCreateGdiPlusBitmap(const Win sal_uInt8* pSrcA(pA->mpBits); const sal_uInt32 nExtraRGB(pRGB->mnScanlineSize - (nW * 3)); const sal_uInt32 nExtraA(pA->mnScanlineSize - nW); + const bool bTopDown(pRGB->meDirection == ScanlineDirection::TopDown); const Gdiplus::Rect aAllRect(0, 0, nW, nH); Gdiplus::BitmapData aGdiPlusBitmapData; pRetval->LockBits(&aAllRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &aGdiPlusBitmapData); @@ -376,7 +379,8 @@ std::shared_ptr<Gdiplus::Bitmap> WinSalBitmap::ImplCreateGdiPlusBitmap(const Win // A from alpha, so inner loop is needed (who invented BitmapEx..?) for(sal_uInt32 y(0); y < nH; y++) { - sal_uInt8* targetPixels = static_cast<sal_uInt8*>(aGdiPlusBitmapData.Scan0) + (y * aGdiPlusBitmapData.Stride); + const sal_uInt32 nYInsert(bTopDown ? y : nH - y - 1); + sal_uInt8* targetPixels = static_cast<sal_uInt8*>(aGdiPlusBitmapData.Scan0) + (nYInsert * aGdiPlusBitmapData.Stride); for(sal_uInt32 x(0); x < nW; x++) { @@ -517,11 +521,10 @@ bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, SalGraphics* pSGraphics ) if( pBI->bmiHeader.biBitCount == 1 ) { - int nHeight = -pBI->bmiHeader.biHeight; // height is negative for top-down bitmap - hNewDDB = CreateBitmap( pBI->bmiHeader.biWidth, nHeight, 1, 1, nullptr ); + hNewDDB = CreateBitmap( pBI->bmiHeader.biWidth, pBI->bmiHeader.biHeight, 1, 1, nullptr ); if( hNewDDB ) - SetDIBits( hDC, hNewDDB, 0, nHeight, pBits, pBI, DIB_RGB_COLORS ); + SetDIBits( hDC, hNewDDB, 0, pBI->bmiHeader.biHeight, pBits, pBI, DIB_RGB_COLORS ); } else hNewDDB = CreateDIBitmap( hDC, &pBI->bmiHeader, CBM_INIT, pBits, pBI, DIB_RGB_COLORS ); @@ -688,7 +691,7 @@ HGLOBAL WinSalBitmap::ImplCreateDIB(const Size& rSize, vcl::PixelFormat ePixelFo pBIH->biSize = sizeof( BITMAPINFOHEADER ); pBIH->biWidth = rSize.Width(); - pBIH->biHeight = -rSize.Height(); // negative for top-down bitmap + pBIH->biHeight = rSize.Height(); pBIH->biPlanes = 1; pBIH->biBitCount = nBits; pBIH->biCompression = BI_RGB; diff --git a/vcl/win/gdi/salgdi2.cxx b/vcl/win/gdi/salgdi2.cxx index b4aa68fa29cb..ba4afc157bc3 100644 --- a/vcl/win/gdi/salgdi2.cxx +++ b/vcl/win/gdi/salgdi2.cxx @@ -116,10 +116,14 @@ void convertToWinSalBitmap(SalBitmap& rSalBitmap, WinSalBitmap& rWinSalBitmap) rWinSalBitmap.Create(rSalBitmap.GetSize(), vcl::bitDepthToPixelFormat(rSalBitmap.GetBitCount()), aBitmapPalette); BitmapBuffer* pWrite = rWinSalBitmap.AcquireBuffer(BitmapAccessMode::Write); - // convert to bottom-up data - sal_uInt8* pSource = pRead->mpBits + pRead->mnScanlineSize * (pRead->mnHeight - 1); + sal_uInt8* pSource(pRead->mpBits); sal_uInt8* pDestination(pWrite->mpBits); - tools::Long readRowChange = -pRead->mnScanlineSize; + tools::Long readRowChange = pRead->mnScanlineSize; + if (pRead->meDirection == ScanlineDirection::TopDown) + { + pSource += pRead->mnScanlineSize * (pRead->mnHeight - 1); + readRowChange = -readRowChange; + } std::unique_ptr<ColorScanlineConverter> pConverter; diff --git a/vcl/win/gdi/salvd.cxx b/vcl/win/gdi/salvd.cxx index 9d87ed82425a..2f6d3456a748 100644 --- a/vcl/win/gdi/salvd.cxx +++ b/vcl/win/gdi/salvd.cxx @@ -33,8 +33,6 @@ HBITMAP WinSalVirtualDevice::ImplCreateVirDevBitmap(HDC hDC, tools::Long nDX, tools::Long nDY, sal_uInt16 nBitCount, void **ppData) { - assert(nDX >= 0); - assert(nDY >= 0); HBITMAP hBitmap; if ( nBitCount == 1 ) @@ -54,7 +52,7 @@ HBITMAP WinSalVirtualDevice::ImplCreateVirDevBitmap(HDC hDC, tools::Long nDX, to BITMAPINFO aBitmapInfo; aBitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); aBitmapInfo.bmiHeader.biWidth = nDX; - aBitmapInfo.bmiHeader.biHeight = -nDY; // negative for top-down + aBitmapInfo.bmiHeader.biHeight = nDY; aBitmapInfo.bmiHeader.biPlanes = 1; aBitmapInfo.bmiHeader.biBitCount = nBitCount; aBitmapInfo.bmiHeader.biCompression = BI_RGB;