vcl/skia/osx/gdiimpl.cxx |   49 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 39 insertions(+), 10 deletions(-)

New commits:
commit 9ec57a3dc7925d6f86a19d0e179e065df53e542f
Author:     Patrick Luby <plub...@neooffice.org>
AuthorDate: Wed Dec 14 13:11:57 2022 -0500
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Thu Dec 15 05:33:36 2022 +0000

    tdf#145843 Do not use CGBitmapContextCreate() to create a bitmap context
    
    As described in the comment in the above code, CGBitmapContextCreate()
    and CGBitmapContextCreateWithData() will try to access pixels up to
    mDirtyRect.x() + pixmap.bounds.width() for each row. When reading the
    last line in the SkPixmap, the buffer allocated for the SkPixmap ends at
    mDirtyRect.x() + mDirtyRect.width() and mDirtyRect.width() is clamped to
    pixmap.bounds.width() - mDirtyRect.x().
    
    This behavior looks like an optimization within CGBitmapContextCreate()
    to draw with a single memcpy() so fix this bug by chaining the
    CGDataProvider(), CGImageCreate(), and CGImageCreateWithImageInRect()
    functions to create the screen image.
    
    Change-Id: Ic83f6162f8eb63de22693c0c40015da3b88aee65
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144194
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>

diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx
index 38927e92c9ca..9d3765c3cc03 100644
--- a/vcl/skia/osx/gdiimpl.cxx
+++ b/vcl/skia/osx/gdiimpl.cxx
@@ -142,7 +142,7 @@ void AquaSkiaSalGraphicsImpl::flushSurfaceToScreenCG()
     if (pixmap.bounds() != mDirtyRect && pixmap.bounds().bottom() == 
mDirtyRect.bottom())
     {
         // HACK for tdf#145843: If mDirtyRect includes the last line but not 
the first pixel of it,
-        // then the rowBytes() trick would lead to the GC* functions thinking 
that even pixels after
+        // then the rowBytes() trick would lead to the CG* functions thinking 
that even pixels after
         // the pixmap data belong to the area (since the shifted 
x()+rowBytes() points there) and
         // at least on Intel Mac they would actually read those data, even 
though I see no good reason
         // to do that, as that's beyond the x()+width() for the last line. 
That could be handled
@@ -151,22 +151,49 @@ void AquaSkiaSalGraphicsImpl::flushSurfaceToScreenCG()
         mDirtyRect.fLeft = 0;
         assert(mDirtyRect.width() == pixmap.bounds().width());
     }
-    CGContextRef context = CGBitmapContextCreate(
-        pixmap.writable_addr32(mDirtyRect.x() * mScaling, mDirtyRect.y() * 
mScaling),
-        mDirtyRect.width() * mScaling, mDirtyRect.height() * mScaling, 8, 
pixmap.rowBytes(),
-        GetSalData()->mxRGBSpace, toCGBitmapType(image->colorType(), 
image->alphaType()));
-    if (!context)
+
+    // tdf#145843 Do not use CGBitmapContextCreate() to create a bitmap context
+    // As described in the comment in the above code, CGBitmapContextCreate()
+    // and CGBitmapContextCreateWithData() will try to access pixels up to
+    // mDirtyRect.x() + pixmap.bounds.width() for each row. When reading the
+    // last line in the SkPixmap, the buffer allocated for the SkPixmap ends at
+    // mDirtyRect.x() + mDirtyRect.width() and mDirtyRect.width() is clamped to
+    // pixmap.bounds.width() - mDirtyRect.x().
+    // This behavior looks like an optimization within CGBitmapContextCreate()
+    // to draw with a single memcpy() so fix this bug by chaining the
+    // CGDataProvider(), CGImageCreate(), and CGImageCreateWithImageInRect()
+    // functions to create the screen image.
+    CGDataProviderRef dataProvider = CGDataProviderCreateWithData(
+        nullptr, pixmap.writable_addr32(0, 0), pixmap.computeByteSize(), 
nullptr);
+    if (!dataProvider)
     {
-        SAL_WARN("vcl.skia", "flushSurfaceToScreenGC(): Failed to allocate 
bitmap context");
+        SAL_WARN("vcl.skia", "flushSurfaceToScreenGC(): Failed to allocate 
data provider");
         return;
     }
-    CGImageRef screenImage = CGBitmapContextCreateImage(context);
+
+    CGImageRef fullImage = CGImageCreate(pixmap.bounds().width(), 
pixmap.bounds().height(), 8,
+                                         8 * 
image->imageInfo().bytesPerPixel(), pixmap.rowBytes(),
+                                         GetSalData()->mxRGBSpace,
+                                         toCGBitmapType(image->colorType(), 
image->alphaType()),
+                                         dataProvider, nullptr, false, 
kCGRenderingIntentDefault);
+    if (!fullImage)
+    {
+        CGDataProviderRelease(dataProvider);
+        SAL_WARN("vcl.skia", "flushSurfaceToScreenGC(): Failed to allocate 
full image");
+        return;
+    }
+
+    CGImageRef screenImage = CGImageCreateWithImageInRect(
+        fullImage, CGRectMake(mDirtyRect.x() * mScaling, mDirtyRect.y() * 
mScaling,
+                              mDirtyRect.width() * mScaling, 
mDirtyRect.height() * mScaling));
     if (!screenImage)
     {
-        CGContextRelease(context);
+        CGImageRelease(fullImage);
+        CGDataProviderRelease(dataProvider);
         SAL_WARN("vcl.skia", "flushSurfaceToScreenGC(): Failed to allocate 
screen image");
         return;
     }
+
     mrShared.maContextHolder.saveState();
     // Drawing to the actual window has scaling active, so use unscaled 
coordinates, the scaling matrix will scale them
     // to the proper screen coordinates. Unless the scaling is fake for 
debugging, in which case scale them to draw
@@ -191,7 +218,9 @@ void AquaSkiaSalGraphicsImpl::flushSurfaceToScreenCG()
     mrShared.maContextHolder.restoreState();
 
     CGImageRelease(screenImage);
-    CGContextRelease(context);
+    CGImageRelease(fullImage);
+    CGDataProviderRelease(dataProvider);
+
     // This is also in VCL coordinates.
     mrShared.refreshRect(mDirtyRect.x(), mDirtyRect.y(), mDirtyRect.width(), 
mDirtyRect.height());
 }

Reply via email to