vcl/Library_vcl.mk                 |   12 ++
 vcl/inc/quartz/cgutils.h           |   34 +++++++
 vcl/inc/quartz/salbmp.h            |   12 --
 vcl/inc/salbmp.hxx                 |   23 +++++
 vcl/inc/skia/quartz/cgutils.h      |   40 ++++++++
 vcl/inc/skia/salbmp.hxx            |    7 +
 vcl/quartz/AquaGraphicsBackend.cxx |  169 ++-----------------------------------
 vcl/quartz/cgutils.mm              |   73 +++++++++++++++
 vcl/quartz/salbmp.cxx              |  115 ++-----------------------
 vcl/skia/osx/gdiimpl.cxx           |   23 -----
 vcl/skia/quartz/salbmp.mm          |  107 +++++++++++++++++++++++
 11 files changed, 323 insertions(+), 292 deletions(-)

New commits:
commit b99464ef2ff58670aacee2fb5f0ea4398420ad47
Author:     Patrick Luby <plub...@neooffice.org>
AuthorDate: Sun Dec 25 10:17:01 2022 -0500
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Thu Dec 29 14:14:36 2022 +0000

    Related: tdf#146842 Eliminate temporary copies of SkiaSalBitmap when 
printing
    
    Commit 9eb732a32023e74c44ac8c3b5af9f5424273bb6c fixed crashing when
    printing SkiaSalBitmaps to a non-Skia SalGraphics. However, the fix
    almost always makes two copies of the SkiaSalBitmap's bitmap data: the
    first copy is made in SkiaSalBitmap::AcquireBuffer() and then
    QuartzSalBitmap makes a copy of the first copy.
    
    By making QuartzSalBitmap's methods that return a CGImageRef pure
    virtual, a non-Skia SalGraphics can now create a CGImageRef directly
    from a SkiaSalBitmap's Skia bitmap data without copying to any
    intermediate buffers.
    
    Change-Id: If6ab7f175889cb4839d8a2461b7be7671e575c08
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144856
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>

diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index f3a165b830ca..0c018b7bb9b4 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -684,6 +684,12 @@ ifeq ($(OS),iOS)
 $(eval $(call gb_Library_add_cxxflags,vcl,\
     $(gb_OBJCXXFLAGS) \
 ))
+$(eval $(call gb_Library_add_objcxxobjects,vcl,\
+    vcl/quartz/cgutils \
+    $(if $(filter SKIA,$(BUILD_TYPE)), \
+        vcl/skia/quartz/salbmp \
+    ) \
+))
 $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/ios/iosinst \
     vcl/ios/dummies \
@@ -709,6 +715,12 @@ endif
 
 
 ifeq ($(OS),MACOSX)
+$(eval $(call gb_Library_add_objcxxobjects,vcl,\
+    vcl/quartz/cgutils \
+    $(if $(filter SKIA,$(BUILD_TYPE)), \
+        vcl/skia/quartz/salbmp \
+    ) \
+))
 $(eval $(call gb_Library_use_system_darwin_frameworks,vcl,\
     Cocoa \
     CoreFoundation \
diff --git a/vcl/inc/quartz/cgutils.h b/vcl/inc/quartz/cgutils.h
new file mode 100644
index 000000000000..6c499448c721
--- /dev/null
+++ b/vcl/inc/quartz/cgutils.h
@@ -0,0 +1,34 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/dllapi.h>
+
+#include <premac.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <postmac.h>
+
+class SalBitmap;
+
+CGImageRef VCL_DLLPUBLIC CreateWithSalBitmapAndMask(const SalBitmap& rBitmap,
+                                                    const SalBitmap& rMask, 
int nX, int nY,
+                                                    int nWidth, int nHeight);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/quartz/salbmp.h b/vcl/inc/quartz/salbmp.h
index 459a2b528f99..8f4e6a34ee1c 100644
--- a/vcl/inc/quartz/salbmp.h
+++ b/vcl/inc/quartz/salbmp.h
@@ -30,9 +30,6 @@
 #include <salinst.hxx>
 #include <salvd.hxx>
 #include <salbmp.hxx>
-#if HAVE_FEATURE_SKIA
-#include <skia/salbmp.hxx>
-#endif
 
 #include <memory>
 
@@ -64,9 +61,6 @@ public:
     bool            Create( const SalBitmap& rSalBmp ) override;
     bool            Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics ) 
override;
     bool            Create( const SalBitmap& rSalBmp, vcl::PixelFormat 
eNewPixelFormat) override;
-#if HAVE_FEATURE_SKIA
-    bool            Create( const SkiaSalBitmap& rSkiaSalBmp, const 
SalTwoRect& rPosAry );
-#endif
     virtual bool    Create( const css::uno::Reference< 
css::rendering::XBitmapCanvas >& rBitmapCanvas,
                             Size& rSize,
                             bool bMask = false ) override;
@@ -99,9 +93,9 @@ public:
     bool            Create(CGLayerHolder const & rLayerHolder, int nBitCount, 
int nX, int nY, int nWidth, int nHeight, bool bFlipped);
 
 public:
-    CGImageRef      CreateWithMask( const QuartzSalBitmap& rMask, int nX, int 
nY, int nWidth, int nHeight ) const;
-    CGImageRef      CreateColorMask( int nX, int nY, int nWidth, int nHeight, 
Color nMaskColor ) const;
-    CGImageRef      CreateCroppedImage( int nX, int nY, int nWidth, int 
nHeight ) const;
+    virtual CGImageRef CreateWithMask( const SalBitmap& rMask, int nX, int nY, 
int nWidth, int nHeight ) const override;
+    virtual CGImageRef CreateColorMask( int nX, int nY, int nWidth, int 
nHeight, Color nMaskColor ) const override;
+    virtual CGImageRef CreateCroppedImage( int nX, int nY, int nWidth, int 
nHeight ) const override;
 
     void doDestroy();
 };
diff --git a/vcl/inc/salbmp.hxx b/vcl/inc/salbmp.hxx
index b08d66f522cb..6abaa30ea4a7 100644
--- a/vcl/inc/salbmp.hxx
+++ b/vcl/inc/salbmp.hxx
@@ -29,6 +29,12 @@
 #include <com/sun/star/rendering/XBitmapCanvas.hpp>
 #include <basegfx/utils/systemdependentdata.hxx>
 
+#if defined MACOSX || defined IOS
+#include <premac.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <postmac.h>
+#endif
+
 struct BitmapBuffer;
 class Color;
 class SalGraphics;
@@ -98,6 +104,23 @@ public:
         return false;
     }
 
+#if defined MACOSX || defined IOS
+    // Related: tdf#146842 Eliminate temporary copies of SkiaSalBitmap when
+    // printing
+    // Commit 9eb732a32023e74c44ac8c3b5af9f5424273bb6c fixed crashing when
+    // printing SkiaSalBitmaps to a non-Skia SalGraphics. However, the fix
+    // almost always makes two copies of the SkiaSalBitmap's bitmap data: the
+    // first copy is made in SkiaSalBitmap::AcquireBuffer() and then
+    // QuartzSalBitmap makes a copy of the first copy.
+    // By making QuartzSalBitmap's methods that return a CGImageRef pure
+    // virtual, a non-Skia SalGraphics can now create a CGImageRef directly
+    // from a SkiaSalBitmap's Skia bitmap data without copying to any
+    // intermediate buffers.
+    virtual CGImageRef      CreateWithMask( const SalBitmap& rMask, int nX, 
int nY, int nWidth, int nHeight ) const = 0;
+    virtual CGImageRef      CreateColorMask( int nX, int nY, int nWidth, int 
nHeight, Color nMaskColor ) const = 0;
+    virtual CGImageRef      CreateCroppedImage( int nX, int nY, int nWidth, 
int nHeight ) const = 0;
+#endif
+
     BitmapChecksum GetChecksum() const
     {
         updateChecksum();
diff --git a/vcl/inc/skia/quartz/cgutils.h b/vcl/inc/skia/quartz/cgutils.h
new file mode 100644
index 000000000000..cb43a31dcdff
--- /dev/null
+++ b/vcl/inc/skia/quartz/cgutils.h
@@ -0,0 +1,40 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+constexpr uint32_t SkiaToCGBitmapType(SkColorType color, SkAlphaType alpha)
+{
+    if (alpha == kPremul_SkAlphaType)
+    {
+        return color == kBGRA_8888_SkColorType
+                   ? (uint32_t(kCGImageAlphaPremultipliedFirst)
+                      | uint32_t(kCGBitmapByteOrder32Little))
+                   : (uint32_t(kCGImageAlphaPremultipliedLast) | 
uint32_t(kCGBitmapByteOrder32Big));
+    }
+    else
+    {
+        assert(alpha == kOpaque_SkAlphaType);
+        return color == kBGRA_8888_SkColorType
+                   ? (uint32_t(kCGImageAlphaNoneSkipFirst) | 
uint32_t(kCGBitmapByteOrder32Little))
+                   : (uint32_t(kCGImageAlphaNoneSkipLast) | 
uint32_t(kCGBitmapByteOrder32Big));
+    }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/skia/salbmp.hxx b/vcl/inc/skia/salbmp.hxx
index e79fb1cc101d..8a974f08566c 100644
--- a/vcl/inc/skia/salbmp.hxx
+++ b/vcl/inc/skia/salbmp.hxx
@@ -64,6 +64,13 @@ public:
     virtual bool ConvertToGreyscale() override;
     virtual bool Erase(const Color& color) override;
     virtual bool AlphaBlendWith(const SalBitmap& rSalBmp) override;
+#if defined MACOSX || defined IOS
+    virtual CGImageRef CreateWithMask(const SalBitmap& rMask, int nX, int nY, 
int nWidth,
+                                      int nHeight) const override;
+    virtual CGImageRef CreateColorMask(int nX, int nY, int nWidth, int nHeight,
+                                       Color nMaskColor) const override;
+    virtual CGImageRef CreateCroppedImage(int nX, int nY, int nWidth, int 
nHeight) const override;
+#endif
 
     const BitmapPalette& Palette() const { return mPalette; }
 
diff --git a/vcl/quartz/AquaGraphicsBackend.cxx 
b/vcl/quartz/AquaGraphicsBackend.cxx
index 2dd1acc8d221..700c7dbe4304 100644
--- a/vcl/quartz/AquaGraphicsBackend.cxx
+++ b/vcl/quartz/AquaGraphicsBackend.cxx
@@ -44,11 +44,6 @@
 #include <svdata.hxx>
 #endif
 
-#if HAVE_FEATURE_SKIA
-#include <vcl/skia/SkiaHelper.hxx>
-#include <skia/salbmp.hxx>
-#endif
-
 using namespace vcl;
 
 namespace
@@ -192,48 +187,6 @@ void drawPattern50(void*, CGContextRef rContext)
     CGContextAddRects(rContext, aRects, 2);
     CGContextFillPath(rContext);
 }
-
-#if HAVE_FEATURE_SKIA
-
-// Related: tdf#146842 Convert SkiaSalBitmap to QuartzSalBitmap
-// Commit de3f13e2175564316eb5a62dee65e9ff8f31b460 disabled Skia for printing.
-// However, since all SalBitmaps created are either all QuartzSalBitmaps or all
-// SkiaSalBitmaps, a crash occurs whenever a SkiaSalBitmap is passed to a
-// printer's SalGraphics instance which is now always non-Skia.
-QuartzSalBitmap* checkAndConvertToQuartzSalBitmap(const SalTwoRect& rPosAry,
-                                                  const SalBitmap& rSalBitmap,
-                                                  SalTwoRect* 
pAdjustedSrcPosAry)
-{
-    QuartzSalBitmap* pRet = nullptr;
-
-    if (SkiaHelper::isVCLSkiaEnabled() && dynamic_cast<const 
SkiaSalBitmap*>(&rSalBitmap))
-    {
-        const SkiaSalBitmap& rSkiaBitmap = static_cast<const 
SkiaSalBitmap&>(rSalBitmap);
-
-        SalTwoRect aSrcPosAry(rPosAry);
-        aSrcPosAry.mnDestX = 0;
-        aSrcPosAry.mnDestY = 0;
-
-        pRet = new QuartzSalBitmap;
-        if (pRet)
-        {
-            // Ignore any failures as returning a nullptr will lead to a crash
-            pRet->Create(rSkiaBitmap, aSrcPosAry);
-
-            if (pAdjustedSrcPosAry)
-            {
-                pAdjustedSrcPosAry->mnSrcX = 0;
-                pAdjustedSrcPosAry->mnSrcY = 0;
-                pAdjustedSrcPosAry->mnSrcWidth = aSrcPosAry.mnDestWidth;
-                pAdjustedSrcPosAry->mnSrcHeight = aSrcPosAry.mnDestHeight;
-            }
-        }
-    }
-
-    return pRet;
-}
-
-#endif
 }
 
 AquaGraphicsBackend::AquaGraphicsBackend(AquaSharedAttributes& rShared)
@@ -949,23 +902,7 @@ void AquaGraphicsBackend::drawBitmap(const SalTwoRect& 
rPosAry, const SalBitmap&
     if (!mrShared.checkContext())
         return;
 
-#if HAVE_FEATURE_SKIA
-    if (mrShared.mbPrinter)
-    {
-        SAL_INFO("vcl.print", "Printing with Skia bitmaps: 
AquaGraphicsBackend::drawBitmap");
-        SalTwoRect aDestPosAry(rPosAry);
-        std::unique_ptr<QuartzSalBitmap> pSalBitmap(
-            checkAndConvertToQuartzSalBitmap(rPosAry, rSalBitmap, 
&aDestPosAry));
-        if (pSalBitmap)
-        {
-            drawBitmap(aDestPosAry, *pSalBitmap);
-            return;
-        }
-    }
-#endif
-
-    const QuartzSalBitmap& rBitmap = static_cast<const 
QuartzSalBitmap&>(rSalBitmap);
-    CGImageRef xImage = rBitmap.CreateCroppedImage(
+    CGImageRef xImage = rSalBitmap.CreateCroppedImage(
         static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
         static_cast<int>(rPosAry.mnSrcWidth), 
static_cast<int>(rPosAry.mnSrcHeight));
     if (!xImage)
@@ -985,36 +922,9 @@ void AquaGraphicsBackend::drawBitmap(const SalTwoRect& 
rPosAry, const SalBitmap&
     if (!mrShared.checkContext())
         return;
 
-#if HAVE_FEATURE_SKIA
-    if (mrShared.mbPrinter)
-    {
-        SAL_INFO(
-            "vcl.print",
-            "Printing with Skia bitmaps: 
AquaGraphicsBackend::drawBitmapWithTranspraentBitmap");
-        SalTwoRect aDestPosAry(rPosAry);
-        std::unique_ptr<QuartzSalBitmap> pSalBitmap(
-            checkAndConvertToQuartzSalBitmap(rPosAry, rSalBitmap, 
&aDestPosAry));
-        if (pSalBitmap)
-        {
-            drawBitmap(aDestPosAry, *pSalBitmap, rTransparentBitmap);
-            return;
-        }
-
-        pSalBitmap.reset(
-            checkAndConvertToQuartzSalBitmap(rPosAry, rTransparentBitmap, 
&aDestPosAry));
-        if (pSalBitmap)
-        {
-            drawBitmap(aDestPosAry, rSalBitmap, *pSalBitmap);
-            return;
-        }
-    }
-#endif
-
-    const QuartzSalBitmap& rBitmap = static_cast<const 
QuartzSalBitmap&>(rSalBitmap);
-    const QuartzSalBitmap& rMask = static_cast<const 
QuartzSalBitmap&>(rTransparentBitmap);
-
-    CGImageRef xMaskedImage(rBitmap.CreateWithMask(rMask, rPosAry.mnSrcX, 
rPosAry.mnSrcY,
-                                                   rPosAry.mnSrcWidth, 
rPosAry.mnSrcHeight));
+    CGImageRef xMaskedImage(rSalBitmap.CreateWithMask(rTransparentBitmap, 
rPosAry.mnSrcX,
+                                                      rPosAry.mnSrcY, 
rPosAry.mnSrcWidth,
+                                                      rPosAry.mnSrcHeight));
     if (!xMaskedImage)
         return;
 
@@ -1031,24 +941,8 @@ void AquaGraphicsBackend::drawMask(const SalTwoRect& 
rPosAry, const SalBitmap& r
     if (!mrShared.checkContext())
         return;
 
-#if HAVE_FEATURE_SKIA
-    if (mrShared.mbPrinter)
-    {
-        SAL_INFO("vcl.print", "Printing with Skia bitmaps: 
AquaGraphicsBackend::drawMask");
-        SalTwoRect aDestPosAry(rPosAry);
-        std::unique_ptr<QuartzSalBitmap> pSalBitmap(
-            checkAndConvertToQuartzSalBitmap(rPosAry, rSalBitmap, 
&aDestPosAry));
-        if (pSalBitmap)
-        {
-            drawMask(aDestPosAry, *pSalBitmap, nMaskColor);
-            return;
-        }
-    }
-#endif
-
-    const QuartzSalBitmap& rBitmap = static_cast<const 
QuartzSalBitmap&>(rSalBitmap);
-    CGImageRef xImage = rBitmap.CreateColorMask(rPosAry.mnSrcX, 
rPosAry.mnSrcY, rPosAry.mnSrcWidth,
-                                                rPosAry.mnSrcHeight, 
nMaskColor);
+    CGImageRef xImage = rSalBitmap.CreateColorMask(
+        rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, 
rPosAry.mnSrcHeight, nMaskColor);
     if (!xImage)
         return;
 
@@ -1324,28 +1218,7 @@ bool AquaGraphicsBackend::drawAlphaBitmap(const 
SalTwoRect& rTR, const SalBitmap
     if (rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0)
         return false;
 
-#if HAVE_FEATURE_SKIA
-    if (!mrShared.checkContext())
-        return false;
-
-    if (mrShared.mbPrinter)
-    {
-        SAL_INFO("vcl.print", "Printing with Skia bitmaps: 
AquaGraphicsBackend::drawAlphaBitmap");
-        SalTwoRect aDestPosAry(rTR);
-        std::unique_ptr<QuartzSalBitmap> pSalBitmap(
-            checkAndConvertToQuartzSalBitmap(rTR, rSrcBitmap, &aDestPosAry));
-        if (pSalBitmap)
-            return drawAlphaBitmap(aDestPosAry, *pSalBitmap, rAlphaBmp);
-
-        pSalBitmap.reset(checkAndConvertToQuartzSalBitmap(rTR, rAlphaBmp, 
&aDestPosAry));
-        if (pSalBitmap)
-            return drawAlphaBitmap(aDestPosAry, rSrcBitmap, *pSalBitmap);
-    }
-#endif
-
-    const QuartzSalBitmap& rSrcSalBmp = static_cast<const 
QuartzSalBitmap&>(rSrcBitmap);
-    const QuartzSalBitmap& rMaskSalBmp = static_cast<const 
QuartzSalBitmap&>(rAlphaBmp);
-    CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask(rMaskSalBmp, 
rTR.mnSrcX, rTR.mnSrcY,
+    CGImageRef xMaskedImage = rSrcBitmap.CreateWithMask(rAlphaBmp, rTR.mnSrcX, 
rTR.mnSrcY,
                                                         rTR.mnSrcWidth, 
rTR.mnSrcHeight);
     if (!xMaskedImage)
         return false;
@@ -1375,35 +1248,15 @@ bool AquaGraphicsBackend::drawTransformedBitmap(const 
basegfx::B2DPoint& rNull,
     if (fAlpha != 1.0)
         return false;
 
-#if HAVE_FEATURE_SKIA
-    if (mrShared.mbPrinter)
-    {
-        SAL_INFO("vcl.print",
-                 "Printing with Skia bitmaps: 
AquaGraphicsBackend::drawTransformedBitmap");
-        const Size aSize = rSrcBitmap.GetSize();
-        SalTwoRect aTR(0, 0, aSize.Width(), aSize.Height(), 0, 0, 
aSize.Width(), aSize.Height());
-        std::unique_ptr<QuartzSalBitmap> pSalBitmap(
-            checkAndConvertToQuartzSalBitmap(aTR, rSrcBitmap, nullptr));
-        if (pSalBitmap)
-            return drawTransformedBitmap(rNull, rX, rY, *pSalBitmap, 
pAlphaBmp, fAlpha);
-
-        pSalBitmap.reset(checkAndConvertToQuartzSalBitmap(aTR, *pAlphaBmp, 
nullptr));
-        if (pSalBitmap)
-            return drawTransformedBitmap(rNull, rX, rY, rSrcBitmap, 
pSalBitmap.get(), fAlpha);
-    }
-#endif
-
     // get the Quartz image
     CGImageRef xImage = nullptr;
     const Size aSize = rSrcBitmap.GetSize();
-    const QuartzSalBitmap& rSrcSalBmp = static_cast<const 
QuartzSalBitmap&>(rSrcBitmap);
-    const QuartzSalBitmap* pMaskSalBmp = static_cast<const 
QuartzSalBitmap*>(pAlphaBmp);
 
-    if (!pMaskSalBmp)
-        xImage = rSrcSalBmp.CreateCroppedImage(0, 0, int(aSize.Width()), 
int(aSize.Height()));
+    if (!pAlphaBmp)
+        xImage = rSrcBitmap.CreateCroppedImage(0, 0, int(aSize.Width()), 
int(aSize.Height()));
     else
-        xImage = rSrcSalBmp.CreateWithMask(*pMaskSalBmp, 0, 0, 
int(aSize.Width()),
-                                           int(aSize.Height()));
+        xImage
+            = rSrcBitmap.CreateWithMask(*pAlphaBmp, 0, 0, int(aSize.Width()), 
int(aSize.Height()));
 
     if (!xImage)
         return false;
diff --git a/vcl/quartz/cgutils.mm b/vcl/quartz/cgutils.mm
new file mode 100644
index 000000000000..61935acc7fdd
--- /dev/null
+++ b/vcl/quartz/cgutils.mm
@@ -0,0 +1,73 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <quartz/cgutils.h>
+
+#include <salbmp.hxx>
+#include <osx/saldata.hxx>
+
+static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/)
+{
+    std::free( const_cast<void*>(data) );
+}
+
+CGImageRef CreateWithSalBitmapAndMask( const SalBitmap& rBitmap, const 
SalBitmap& rMask, int nX, int nY, int nWidth, int nHeight )
+{
+    CGImageRef xImage( rBitmap.CreateCroppedImage( nX, nY, nWidth, nHeight ) );
+    if( !xImage )
+        return nullptr;
+
+    CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
+    if( !xMask )
+        return xImage;
+
+    // CGImageCreateWithMask() only likes masks or greyscale images => convert 
if needed
+    // TODO: isolate in an extra method?
+    if( !CGImageIsMask(xMask) || rMask.GetBitCount() != 
8)//(CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
+    {
+        const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect 
has no offset
+
+        // create the alpha mask image fitting our image
+        // TODO: is caching the full mask or the subimage mask worth it?
+        int nMaskBytesPerRow = ((nWidth + 3) & ~3);
+        void* pMaskMem = std::malloc( nMaskBytesPerRow * nHeight );
+        CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem,
+            nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, 
kCGImageAlphaNone );
+        CGContextDrawImage( xMaskContext, xImageRect, xMask );
+        CFRelease( xMask );
+        CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( nullptr,
+        pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) );
+
+        static const CGFloat* pDecode = nullptr;
+        xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, 
xDataProvider, pDecode, false );
+        CFRelease( xDataProvider );
+        CFRelease( xMaskContext );
+    }
+
+    if( !xMask )
+        return xImage;
+
+    // combine image and alpha mask
+    CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask );
+    CFRelease( xMask );
+    CFRelease( xImage );
+    return xMaskedImage;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/quartz/salbmp.cxx b/vcl/quartz/salbmp.cxx
index 05b5093bafc1..e71c35612cb4 100644
--- a/vcl/quartz/salbmp.cxx
+++ b/vcl/quartz/salbmp.cxx
@@ -35,6 +35,7 @@
 #include <vcl/Scanline.hxx>
 
 #include <bitmap/bmpfast.hxx>
+#include <quartz/cgutils.h>
 #include <quartz/salbmp.h>
 #include <quartz/utils.h>
 #include <bitmap/ScanlineTools.hxx>
@@ -115,66 +116,6 @@ bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, 
vcl::PixelFormat eNewPix
     return false;
 }
 
-#if HAVE_FEATURE_SKIA
-
-bool QuartzSalBitmap::Create( const SkiaSalBitmap& rSalBmp, const SalTwoRect& 
rPosAry )
-{
-    bool bRet = false;
-
-    // Ugly but necessary to acquire the bitmap buffer because all of the
-    // SalBitmap instances that callers pass are already const. At least we
-    // only need to read, not write to the bitmap parameter.
-    SkiaSalBitmap& rSkiaSalBmp = const_cast<SkiaSalBitmap&>( rSalBmp );
-
-    BitmapBuffer *pSrcBuffer = rSkiaSalBmp.AcquireBuffer( 
BitmapAccessMode::Read );
-    if ( !pSrcBuffer )
-        return bRet;
-
-    if ( !pSrcBuffer->mpBits )
-    {
-        rSkiaSalBmp.ReleaseBuffer( pSrcBuffer, BitmapAccessMode::Read );
-        return bRet;
-    }
-
-    // Create only a 1 pixel buffer as it will always be discarded
-    mnBits = 32;
-    mnWidth = 1;
-    mnHeight = 1;
-    if( AllocateUserData() )
-    {
-        BitmapBuffer *pDestBuffer = AcquireBuffer( BitmapAccessMode::Read );
-        if ( pDestBuffer )
-        {
-            std::unique_ptr<BitmapBuffer> pConvertedBuffer = 
StretchAndConvert( *pSrcBuffer, rPosAry, pDestBuffer->mnFormat, 
pDestBuffer->maPalette, &pDestBuffer->maColorMask );
-            bool bUseDestBuffer = ( pConvertedBuffer &&
-                 pConvertedBuffer->mpBits &&
-                 pConvertedBuffer->mnFormat == pDestBuffer->mnFormat &&
-                 pConvertedBuffer->mnWidth == rPosAry.mnDestWidth &&
-                 pConvertedBuffer->mnHeight == rPosAry.mnDestHeight );
-
-            ReleaseBuffer( pDestBuffer, BitmapAccessMode::Read );
-
-            if ( bUseDestBuffer )
-            {
-                // Surprisingly, BitmapBuffer does not delete the bits so
-                // discard our 1 pixel buffer and take ownership of the bits
-                DestroyContext();
-                m_pUserBuffer.reset( pConvertedBuffer->mpBits );
-                mnWidth = pConvertedBuffer->mnWidth;
-                mnHeight = pConvertedBuffer->mnHeight;
-                mnBytesPerRow = pConvertedBuffer->mnScanlineSize;
-                bRet = true;
-            }
-        }
-    }
-
-    rSkiaSalBmp.ReleaseBuffer( pSrcBuffer, BitmapAccessMode::Read );
-
-    return bRet;
-}
-
-#endif
-
 bool QuartzSalBitmap::Create( const css::uno::Reference< 
css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/,
                               Size& /*rSize*/, bool /*bMask*/ )
 {
@@ -576,48 +517,10 @@ static void CFRTLFree(void* /*info*/, const void* data, 
size_t /*size*/)
     std::free( const_cast<void*>(data) );
 }
 
-CGImageRef QuartzSalBitmap::CreateWithMask( const QuartzSalBitmap& rMask,
+CGImageRef QuartzSalBitmap::CreateWithMask( const SalBitmap& rMask,
     int nX, int nY, int nWidth, int nHeight ) const
 {
-    CGImageRef xImage( CreateCroppedImage( nX, nY, nWidth, nHeight ) );
-    if( !xImage )
-        return nullptr;
-
-    CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
-    if( !xMask )
-        return xImage;
-
-    // CGImageCreateWithMask() only likes masks or greyscale images => convert 
if needed
-    // TODO: isolate in an extra method?
-    if( !CGImageIsMask(xMask) || rMask.GetBitCount() != 
8)//(CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
-    {
-        const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect 
has no offset
-
-        // create the alpha mask image fitting our image
-        // TODO: is caching the full mask or the subimage mask worth it?
-        int nMaskBytesPerRow = ((nWidth + 3) & ~3);
-        void* pMaskMem = std::malloc( nMaskBytesPerRow * nHeight );
-        CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem,
-            nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, 
kCGImageAlphaNone );
-        CGContextDrawImage( xMaskContext, xImageRect, xMask );
-        CFRelease( xMask );
-        CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( nullptr,
-        pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) );
-
-        static const CGFloat* pDecode = nullptr;
-        xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, 
xDataProvider, pDecode, false );
-        CFRelease( xDataProvider );
-        CFRelease( xMaskContext );
-    }
-
-    if( !xMask )
-        return xImage;
-
-    // combine image and alpha mask
-    CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask );
-    CFRelease( xMask );
-    CFRelease( xImage );
-    return xMaskedImage;
+    return CreateWithSalBitmapAndMask( *this, rMask, nX, nY, nWidth, nHeight );
 }
 
 /** creates an image from the given rectangle, replacing all black pixels
@@ -628,13 +531,14 @@ CGImageRef QuartzSalBitmap::CreateColorMask( int nX, int 
nY, int nWidth,
     CGImageRef xMask = nullptr;
     if (m_pUserBuffer && (nX + nWidth <= mnWidth) && (nY + nHeight <= 
mnHeight))
     {
-        const sal_uInt32 nDestBytesPerRow = nWidth << 2;
-        std::unique_ptr<sal_uInt32[]> pMaskBuffer(new (std::nothrow) 
sal_uInt32[ nHeight * nDestBytesPerRow / 4] );
-        sal_uInt32* pDest = pMaskBuffer.get();
-
         auto pSourcePixels = vcl::bitmap::getScanlineTransformer(mnBits, 
maPalette);
+        // Don't allocate destination buffer if there is no scanline 
transformer
+        if( !pSourcePixels )
+            return xMask;
 
-        if( pMaskBuffer && pSourcePixels )
+        const sal_uInt32 nDestBytesPerRow = nWidth << 2;
+        std::unique_ptr<sal_uInt32[]> pMaskBuffer(new (std::nothrow) 
sal_uInt32[ nHeight * nDestBytesPerRow / 4] );
+        if( pMaskBuffer )
         {
             sal_uInt32 nColor;
             reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff;
@@ -643,6 +547,7 @@ CGImageRef QuartzSalBitmap::CreateColorMask( int nX, int 
nY, int nWidth,
             reinterpret_cast<sal_uInt8*>(&nColor)[3] = nMaskColor.GetBlue();
 
             sal_uInt8* pSource = m_pUserBuffer.get();
+            sal_uInt32* pDest = pMaskBuffer.get();
             // First to nY on y-axis, as that is our starting point (sub-image)
             if( nY )
                 pSource += nY * mnBytesPerRow;
diff --git a/vcl/skia/osx/gdiimpl.cxx b/vcl/skia/osx/gdiimpl.cxx
index 9d3765c3cc03..1c1b7d2955c3 100644
--- a/vcl/skia/osx/gdiimpl.cxx
+++ b/vcl/skia/osx/gdiimpl.cxx
@@ -26,6 +26,7 @@
 #include <tools/sk_app/mac/WindowContextFactory_mac.h>
 
 #include <quartz/ctfonts.hxx>
+#include <skia/quartz/cgutils.h>
 
 #include <SkBitmap.h>
 #include <SkCanvas.h>
@@ -102,24 +103,6 @@ void AquaSkiaSalGraphicsImpl::flushSurfaceToWindowContext()
         SkiaSalGraphicsImpl::flushSurfaceToWindowContext();
 }
 
-constexpr static uint32_t toCGBitmapType(SkColorType color, SkAlphaType alpha)
-{
-    if (alpha == kPremul_SkAlphaType)
-    {
-        return color == kBGRA_8888_SkColorType
-                   ? (uint32_t(kCGImageAlphaPremultipliedFirst)
-                      | uint32_t(kCGBitmapByteOrder32Little))
-                   : (uint32_t(kCGImageAlphaPremultipliedLast) | 
uint32_t(kCGBitmapByteOrder32Big));
-    }
-    else
-    {
-        assert(alpha == kOpaque_SkAlphaType);
-        return color == kBGRA_8888_SkColorType
-                   ? (uint32_t(kCGImageAlphaNoneSkipFirst) | 
uint32_t(kCGBitmapByteOrder32Little))
-                   : (uint32_t(kCGImageAlphaNoneSkipLast) | 
uint32_t(kCGBitmapByteOrder32Big));
-    }
-}
-
 // For Raster we use our own screen blitting (see above).
 void AquaSkiaSalGraphicsImpl::flushSurfaceToScreenCG()
 {
@@ -174,7 +157,7 @@ void AquaSkiaSalGraphicsImpl::flushSurfaceToScreenCG()
     CGImageRef fullImage = CGImageCreate(pixmap.bounds().width(), 
pixmap.bounds().height(), 8,
                                          8 * 
image->imageInfo().bytesPerPixel(), pixmap.rowBytes(),
                                          GetSalData()->mxRGBSpace,
-                                         toCGBitmapType(image->colorType(), 
image->alphaType()),
+                                         
SkiaToCGBitmapType(image->colorType(), image->alphaType()),
                                          dataProvider, nullptr, false, 
kCGRenderingIntentDefault);
     if (!fullImage)
     {
@@ -247,7 +230,7 @@ bool AquaSkiaSalGraphicsImpl::drawNativeControl(ControlType 
nType, ControlPart n
     memset(data.get(), 0, bytes);
     CGContextRef context = CGBitmapContextCreate(
         data.get(), width, height, 8, width * 4, GetSalData()->mxRGBSpace,
-        toCGBitmapType(mSurface->imageInfo().colorType(), 
kPremul_SkAlphaType));
+        SkiaToCGBitmapType(mSurface->imageInfo().colorType(), 
kPremul_SkAlphaType));
     if (!context)
     {
         SAL_WARN("vcl.skia", "drawNativeControl(): Failed to allocate bitmap 
context");
diff --git a/vcl/skia/quartz/salbmp.mm b/vcl/skia/quartz/salbmp.mm
new file mode 100644
index 000000000000..4e9782daf1d4
--- /dev/null
+++ b/vcl/skia/quartz/salbmp.mm
@@ -0,0 +1,107 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <skia/salbmp.hxx>
+#include <skia/zone.hxx>
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+
+#include <quartz/cgutils.h>
+#ifdef MACOSX 
+#include <osx/saldata.hxx>
+#else
+#include <svdata.hxx>
+#endif
+#include <skia/quartz/cgutils.h>
+
+CGImageRef SkiaSalBitmap::CreateWithMask(const SalBitmap& rMask, int nX, int 
nY, int nWidth,
+                                         int nHeight) const
+{
+    return CreateWithSalBitmapAndMask( *this, rMask, nX, nY, nWidth, nHeight );
+}
+
+/** creates an image from the given rectangle, replacing all black pixels
+    with nMaskColor and make all other full transparent */
+CGImageRef SkiaSalBitmap::CreateColorMask(int nX, int nY, int nWidth, int 
nHeight,
+                                          Color nMaskColor) const
+{
+    SkiaZone zone;
+    SkBitmap targetBitmap;
+    if (!targetBitmap.tryAllocPixels(SkImageInfo::Make(nWidth, nHeight, 
kN32_SkColorType, kPremul_SkAlphaType)))
+        return nullptr;
+
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc); // set as is
+    SkMatrix matrix; // The image is needed upside-down.
+    matrix.preTranslate(0, targetBitmap.height());
+    matrix.setConcat(matrix, SkMatrix::Scale(1, -1));
+
+    SkCanvas canvas(targetBitmap);
+    canvas.concat(matrix);
+    canvas.drawImageRect(GetSkImage(), SkRect::MakeXYWH(nX, nY, nWidth, 
nHeight), SkRect::MakeXYWH(0, 0, nWidth, nHeight), SkSamplingOptions(), &paint, 
SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+    canvas.flush();
+
+    const SkColor maskColor = SkiaHelper::toSkColor(nMaskColor);
+    for (int y = 0; y < targetBitmap.height(); y++)
+    {
+        SkColor *pPixels = targetBitmap.getAddr32(0, y);
+        for (int x = 0; x < targetBitmap.width(); x++)
+            *pPixels++ = (*pPixels == SK_ColorBLACK ? maskColor : 
SK_ColorTRANSPARENT);
+    }
+
+    targetBitmap.setImmutable();
+
+    CGContextRef xContext = CGBitmapContextCreate(targetBitmap.getAddr32(0, 
0), targetBitmap.width(), targetBitmap.height(), 8, targetBitmap.rowBytes(), 
GetSalData()->mxRGBSpace, SkiaToCGBitmapType(targetBitmap.colorType(), 
targetBitmap.alphaType()));
+    if (!xContext)
+        return nullptr;
+
+    CGImageRef xCroppedImage = CGBitmapContextCreateImage(xContext);
+    CGContextRelease(xContext);
+    return xCroppedImage;
+}
+
+
+CGImageRef SkiaSalBitmap::CreateCroppedImage(int nX, int nY, int nWidth, int 
nHeight) const
+{
+    SkiaZone zone;
+    SkBitmap targetBitmap;
+    if (!targetBitmap.tryAllocPixels(SkImageInfo::Make(nWidth, nHeight, 
kN32_SkColorType, kPremul_SkAlphaType)))
+        return nullptr;
+
+    SkPaint paint;
+    paint.setBlendMode(SkBlendMode::kSrc); // set as is
+    SkMatrix matrix; // The image is needed upside-down.
+    matrix.preTranslate(0, targetBitmap.height());
+    matrix.setConcat(matrix, SkMatrix::Scale(1, -1));
+
+    SkCanvas canvas(targetBitmap);
+    canvas.concat(matrix);
+    canvas.drawImageRect(GetSkImage(), SkRect::MakeXYWH(nX, nY, nWidth, 
nHeight), SkRect::MakeXYWH(0, 0, nWidth, nHeight), SkSamplingOptions(), &paint, 
SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+    canvas.flush();
+    targetBitmap.setImmutable();
+
+    CGContextRef xContext = CGBitmapContextCreate(targetBitmap.getAddr32(0, 
0), targetBitmap.width(), targetBitmap.height(), 8, targetBitmap.rowBytes(), 
GetSalData()->mxRGBSpace, SkiaToCGBitmapType(targetBitmap.colorType(), 
targetBitmap.alphaType()));
+    if (!xContext)
+        return nullptr;
+
+    CGImageRef xCroppedImage = CGBitmapContextCreateImage(xContext);
+    CGContextRelease(xContext);
+    return xCroppedImage;
+}

Reply via email to