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;

Reply via email to