config_host/config_vclplug.h.in                            |    1 
 configure.ac                                               |    1 
 drawinglayer/Library_drawinglayer.mk                       |    7 
 drawinglayer/source/processor2d/cairopixelprocessor2d.cxx  |  724 +++++++++++++
 drawinglayer/source/processor2d/processor2dtools.cxx       |   17 
 include/drawinglayer/processor2d/cairopixelprocessor2d.hxx |  104 +
 include/vcl/cairo.hxx                                      |   36 
 include/vcl/sysdata.hxx                                    |    7 
 vcl/headless/svpgdi.cxx                                    |    4 
 vcl/inc/headless/CairoCommon.hxx                           |   28 
 vcl/skia/SkiaHelper.cxx                                    |    2 
 11 files changed, 891 insertions(+), 40 deletions(-)

New commits:
commit ff46c8bf598481ae37022414f3495c1e9385eb7c
Author:     Caolán McNamara <caol...@redhat.com>
AuthorDate: Tue Dec 20 15:15:22 2022 +0000
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Wed Dec 21 19:14:53 2022 +0000

    initial Experimental System-Dependent PrimitiveRenderer for Cairo
    
    just some partial pieces
    
    export TEST_SYSTEM_PRIMITIVE_RENDERER=1
    
    and the simple drawing shapes work
    
    Change-Id: I3e01501a1cb21ec86d6fe8f5637a23e7358ffc86
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144632
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/config_host/config_vclplug.h.in b/config_host/config_vclplug.h.in
index b321f44e022e..eeb43a1a8608 100644
--- a/config_host/config_vclplug.h.in
+++ b/config_host/config_vclplug.h.in
@@ -26,6 +26,7 @@ Settings about which desktops have support enabled.
 /**
  * Additional settings for the plugins
  */
+#define USE_HEADLESS_CODE 0
 #define ENABLE_GSTREAMER_1_0 0
 #define QT5_HAVE_GOBJECT 0
 #define QT5_USING_X11 0
diff --git a/configure.ac b/configure.ac
index 65eb0373a285..858e2cb3fb12 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1231,6 +1231,7 @@ disable_x11_tests()
 test "$using_x11" = yes && USING_X11=TRUE
 
 if test "$using_freetype_fontconfig" = yes; then
+    AC_DEFINE(USE_HEADLESS_CODE)
     USE_HEADLESS_CODE=TRUE
     if test "$using_headless_plugin" = yes; then
         AC_DEFINE(ENABLE_HEADLESS)
diff --git a/drawinglayer/Library_drawinglayer.mk 
b/drawinglayer/Library_drawinglayer.mk
index 3197b1c1dbeb..884c32b170a8 100644
--- a/drawinglayer/Library_drawinglayer.mk
+++ b/drawinglayer/Library_drawinglayer.mk
@@ -27,6 +27,7 @@ $(eval $(call gb_Library_use_sdk_api,drawinglayer))
 $(eval $(call gb_Library_use_externals,drawinglayer,\
        boost_headers \
        libxml2 \
+       $(if $(USE_HEADLESS_CODE), cairo) \
 ))
 
 ifneq ($(ENABLE_WASM_STRIP_CANVAS),TRUE)
@@ -68,6 +69,12 @@ $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
 ))
 endif
 
+ifeq ($(USE_HEADLESS_CODE),TRUE)
+$(eval $(call gb_Library_add_exception_objects,drawinglayer,\
+    drawinglayer/source/processor2d/cairopixelprocessor2d \
+))
+endif
+
 $(eval $(call gb_Library_add_exception_objects,drawinglayer,\
     drawinglayer/source/animation/animationtiming \
     drawinglayer/source/attribute/fillgraphicattribute \
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
new file mode 100644
index 000000000000..4b79ff89592d
--- /dev/null
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -0,0 +1,724 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#include <sal/config.h>
+
+#include <drawinglayer/processor2d/cairopixelprocessor2d.hxx>
+#include <sal/log.hxx>
+#include <vcl/cairo.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/Tools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <drawinglayer/converters.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+basegfx::B2DPoint impPixelSnap(const basegfx::B2DPolygon& rPolygon,
+                               const 
drawinglayer::geometry::ViewInformation2D& rViewInformation,
+                               sal_uInt32 nIndex)
+{
+    const sal_uInt32 nCount(rPolygon.count());
+
+    // get the data
+    const basegfx::B2ITuple aPrevTuple(
+        basegfx::fround(rViewInformation.getObjectToViewTransformation()
+                        * rPolygon.getB2DPoint((nIndex + nCount - 1) % 
nCount)));
+    const basegfx::B2DPoint 
aCurrPoint(rViewInformation.getObjectToViewTransformation()
+                                       * rPolygon.getB2DPoint(nIndex));
+    const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
+    const basegfx::B2ITuple aNextTuple(
+        basegfx::fround(rViewInformation.getObjectToViewTransformation()
+                        * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
+
+    // get the states
+    const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
+    const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
+    const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
+    const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
+    const bool bSnapX(bPrevVertical || bNextVertical);
+    const bool bSnapY(bPrevHorizontal || bNextHorizontal);
+
+    if (bSnapX || bSnapY)
+    {
+        basegfx::B2DPoint aSnappedPoint(bSnapX ? aCurrTuple.getX() : 
aCurrPoint.getX(),
+                                        bSnapY ? aCurrTuple.getY() : 
aCurrPoint.getY());
+
+        aSnappedPoint *= 
rViewInformation.getInverseObjectToViewTransformation();
+
+        return aSnappedPoint;
+    }
+
+    return rPolygon.getB2DPoint(nIndex);
+}
+
+void addB2DPolygonToPathGeometry(cairo_t* cr, const basegfx::B2DPolygon& 
rPolygon,
+                                 const 
drawinglayer::geometry::ViewInformation2D* pViewInformation)
+{
+    // short circuit if there is nothing to do
+    const sal_uInt32 nPointCount(rPolygon.count());
+
+    const bool bHasCurves(rPolygon.areControlPointsUsed());
+    const bool bClosePath(rPolygon.isClosed());
+    basegfx::B2DPoint aLast;
+
+    for (sal_uInt32 nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++)
+    {
+        int nClosedIdx = nPointIdx;
+        if (nPointIdx >= nPointCount)
+        {
+            // prepare to close last curve segment if needed
+            if (bClosePath && (nPointIdx == nPointCount))
+            {
+                nClosedIdx = 0;
+            }
+            else
+            {
+                break;
+            }
+        }
+
+        const basegfx::B2DPoint aPoint(nullptr == pViewInformation
+                                           ? rPolygon.getB2DPoint(nClosedIdx)
+                                           : impPixelSnap(rPolygon, 
*pViewInformation, nClosedIdx));
+
+        if (!nPointIdx)
+        {
+            // first point => just move there
+            cairo_move_to(cr, aPoint.getX(), aPoint.getY());
+            aLast = aPoint;
+            continue;
+        }
+
+        bool bPendingCurve(false);
+
+        if (bHasCurves)
+        {
+            bPendingCurve = rPolygon.isNextControlPointUsed(nPrevIdx);
+            bPendingCurve |= rPolygon.isPrevControlPointUsed(nClosedIdx);
+        }
+
+        if (!bPendingCurve) // line segment
+        {
+            cairo_line_to(cr, aPoint.getX(), aPoint.getY());
+        }
+        else // cubic bezier segment
+        {
+            basegfx::B2DPoint aCP1 = rPolygon.getNextControlPoint(nPrevIdx);
+            basegfx::B2DPoint aCP2 = rPolygon.getPrevControlPoint(nClosedIdx);
+
+            // tdf#99165 if the control points are 'empty', create the 
mathematical
+            // correct replacement ones to avoid problems with the graphical 
sub-system
+            // tdf#101026 The 1st attempt to create a mathematically correct 
replacement control
+            // vector was wrong. Best alternative is one as close as possible 
which means short.
+            if (aCP1.equal(aLast))
+            {
+                aCP1 = aLast + ((aCP2 - aLast) * 0.0005);
+            }
+
+            if (aCP2.equal(aPoint))
+            {
+                aCP2 = aPoint + ((aCP1 - aPoint) * 0.0005);
+            }
+
+            cairo_curve_to(cr, aCP1.getX(), aCP1.getY(), aCP2.getX(), 
aCP2.getY(), aPoint.getX(),
+                           aPoint.getY());
+        }
+
+        aLast = aPoint;
+    }
+
+    if (bClosePath)
+    {
+        cairo_close_path(cr);
+    }
+}
+}
+
+namespace drawinglayer::processor2d
+{
+CairoPixelProcessor2D::CairoPixelProcessor2D(const 
geometry::ViewInformation2D& rViewInformation)
+    : BaseProcessor2D(rViewInformation)
+    , maBColorModifierStack()
+    , mpRT(nullptr)
+{
+}
+
+CairoPixelProcessor2D::CairoPixelProcessor2D(const 
geometry::ViewInformation2D& rViewInformation,
+                                             cairo_surface_t* pTarget)
+    : BaseProcessor2D(rViewInformation)
+    , maBColorModifierStack()
+    , mpRT(nullptr)
+{
+    if (pTarget)
+    {
+        cairo_t* pRT = cairo_create(pTarget);
+        cairo_set_antialias(pRT, rViewInformation.getUseAntiAliasing() ? 
CAIRO_ANTIALIAS_DEFAULT
+                                                                       : 
CAIRO_ANTIALIAS_NONE);
+        setRenderTarget(pRT);
+    }
+}
+
+CairoPixelProcessor2D::~CairoPixelProcessor2D()
+{
+    if (mpRT)
+        cairo_destroy(mpRT);
+}
+
+void CairoPixelProcessor2D::processPolygonHairlinePrimitive2D(
+    const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D)
+{
+    const basegfx::B2DPolygon& 
rPolygon(rPolygonHairlinePrimitive2D.getB2DPolygon());
+
+    if (!rPolygon.count())
+        return;
+
+    cairo_save(mpRT);
+
+    cairo_matrix_t aMatrix;
+    const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 
0.0);
+    const basegfx::B2DHomMatrix& rObjectToView(
+        getViewInformation2D().getObjectToViewTransformation());
+    cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), 
rObjectToView.c(),
+                      rObjectToView.d(), rObjectToView.e() + fAAOffset,
+                      rObjectToView.f() + fAAOffset);
+
+    // set linear transformation
+    cairo_set_matrix(mpRT, &aMatrix);
+
+    const basegfx::BColor aHairlineColor(
+        
maBColorModifierStack.getModifiedColor(rPolygonHairlinePrimitive2D.getBColor()));
+    cairo_set_source_rgb(mpRT, aHairlineColor.getRed(), 
aHairlineColor.getGreen(),
+                         aHairlineColor.getBlue());
+
+    // TODO: Unfortunately Direct2D paint of one pixel wide lines does not
+    // correctly and completely blend 100% over the background. Experimenting
+    // shows that a value around/slightly below 2.0 is needed which hints that
+    // alpha bleding the half-shifted lines (see fAAOffset above) is involved.
+    // To get correct blending I try to use just wider hairlines for now. This
+    // may need to be improved - or balanced (trying sqrt(2) now...)
+    cairo_set_line_width(mpRT, 1.44f);
+
+    addB2DPolygonToPathGeometry(mpRT, rPolygon, &getViewInformation2D());
+
+    cairo_stroke(mpRT);
+
+    cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processPolyPolygonColorPrimitive2D(
+    const primitive2d::PolyPolygonColorPrimitive2D& 
rPolyPolygonColorPrimitive2D)
+{
+    const basegfx::B2DPolyPolygon& 
rPolyPolygon(rPolyPolygonColorPrimitive2D.getB2DPolyPolygon());
+    const sal_uInt32 nCount(rPolyPolygon.count());
+
+    if (!nCount)
+        return;
+
+    cairo_save(mpRT);
+
+    cairo_matrix_t aMatrix;
+    const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 
0.0);
+    const basegfx::B2DHomMatrix& rObjectToView(
+        getViewInformation2D().getObjectToViewTransformation());
+    cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), 
rObjectToView.c(),
+                      rObjectToView.d(), rObjectToView.e() + fAAOffset,
+                      rObjectToView.f() + fAAOffset);
+
+    // set linear transformation
+    cairo_set_matrix(mpRT, &aMatrix);
+
+    const basegfx::BColor aFillColor(
+        
maBColorModifierStack.getModifiedColor(rPolyPolygonColorPrimitive2D.getBColor()));
+    cairo_set_source_rgb(mpRT, aFillColor.getRed(), aFillColor.getGreen(), 
aFillColor.getBlue());
+
+    for (const auto& rPolygon : rPolyPolygon)
+        addB2DPolygonToPathGeometry(mpRT, rPolygon, &getViewInformation2D());
+
+    cairo_fill(mpRT);
+
+    cairo_restore(mpRT);
+}
+
+#if 0
+
+void CairoPixelProcessor2D::processBitmapPrimitive2D(
+    const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
+{
+    // TODO: All the smarts to get/make a cairo_surface_t from a BitmapEx is 
internal to vcl at the moment
+}
+
+#endif
+
+namespace
+{
+// This bit-tweaking looping is unpleasant and unfortunate
+void LuminanceToAlpha(cairo_surface_t* pMask)
+{
+    cairo_surface_flush(pMask);
+
+    int nWidth = cairo_image_surface_get_width(pMask);
+    int nHeight = cairo_image_surface_get_height(pMask);
+    int nStride = cairo_image_surface_get_stride(pMask);
+    unsigned char* mask_surface_data = cairo_image_surface_get_data(pMask);
+
+    // include/basegfx/color/bcolormodifier.hxx
+    const double nRedMul = 0.2125 / 255.0;
+    const double nGreenMul = 0.7154 / 255.0;
+    const double nBlueMul = 0.0721 / 255.0;
+    for (int y = 0; y < nHeight; ++y)
+    {
+        unsigned char* pMaskPixelData = mask_surface_data + (nStride * y);
+        for (int x = 0; x < nWidth; ++x)
+        {
+            double fLuminance = pMaskPixelData[SVP_CAIRO_RED] * nRedMul
+                                + pMaskPixelData[SVP_CAIRO_GREEN] * nGreenMul
+                                + pMaskPixelData[SVP_CAIRO_BLUE] * nBlueMul;
+            // Only this alpha channel is taken into account by 
cairo_mask_surface
+            // so reuse this surface for the alpha result
+            pMaskPixelData[SVP_CAIRO_ALPHA] = 255.0 * fLuminance;
+            pMaskPixelData += 4;
+        }
+    }
+
+    cairo_surface_mark_dirty(pMask);
+}
+}
+
+void CairoPixelProcessor2D::processTransparencePrimitive2D(
+    const primitive2d::TransparencePrimitive2D& rTransCandidate)
+{
+    if (rTransCandidate.getChildren().empty())
+        return;
+
+    if (rTransCandidate.getTransparence().empty())
+        return;
+
+    cairo_surface_t* pTarget = cairo_get_target(mpRT);
+
+    double clip_x1, clip_x2, clip_y1, clip_y2;
+    cairo_clip_extents(mpRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2);
+
+    // calculate visible range, create only for that range
+    basegfx::B2DRange aDiscreteRange(
+        rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
+    
aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation());
+    const basegfx::B2DRange aViewRange(basegfx::B2DPoint(clip_x1, clip_y1),
+                                       basegfx::B2DPoint(clip_x2, clip_y2));
+    basegfx::B2DRange aVisibleRange(aDiscreteRange);
+    aVisibleRange.intersect(aViewRange);
+
+    if (aVisibleRange.isEmpty())
+    {
+        // not visible, done
+        return;
+    }
+
+    const basegfx::B2DHomMatrix 
aEmbedTransform(basegfx::utils::createTranslateB2DHomMatrix(
+        -aVisibleRange.getMinX(), -aVisibleRange.getMinY()));
+    geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+    aViewInformation2D.setViewTransformation(aEmbedTransform
+                                             * 
getViewInformation2D().getViewTransformation());
+    // draw mask to temporary surface
+    cairo_surface_t* pMask = cairo_surface_create_similar_image(pTarget, 
CAIRO_FORMAT_ARGB32,
+                                                                
ceil(aVisibleRange.getWidth()),
+                                                                
ceil(aVisibleRange.getHeight()));
+    CairoPixelProcessor2D aMaskRenderer(aViewInformation2D, pMask);
+    aMaskRenderer.process(rTransCandidate.getTransparence());
+
+    // convert mask to something cairo can use
+    LuminanceToAlpha(pMask);
+
+    // draw content to temporary surface
+    cairo_surface_t* pContent = cairo_surface_create_similar(
+        pTarget, cairo_surface_get_content(pTarget), 
ceil(aVisibleRange.getWidth()),
+        ceil(aVisibleRange.getHeight()));
+    CairoPixelProcessor2D aContent(aViewInformation2D, pContent);
+    aContent.process(rTransCandidate.getChildren());
+
+    // munge the temporary surfaces to our target surface
+    cairo_set_source_surface(mpRT, pContent, aVisibleRange.getMinX(), 
aVisibleRange.getMinY());
+    cairo_mask_surface(mpRT, pMask, aVisibleRange.getMinX(), 
aVisibleRange.getMinY());
+
+    cairo_surface_destroy(pContent);
+    cairo_surface_destroy(pMask);
+}
+
+void CairoPixelProcessor2D::processMaskPrimitive2DPixel(
+    const primitive2d::MaskPrimitive2D& rMaskCandidate)
+{
+    if (rMaskCandidate.getChildren().empty())
+        return;
+
+    basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
+
+    if (!aMask.count())
+        return;
+
+    double clip_x1, clip_x2, clip_y1, clip_y2;
+    cairo_clip_extents(mpRT, &clip_x1, &clip_y1, &clip_x2, &clip_y2);
+
+    basegfx::B2DRange aMaskRange(aMask.getB2DRange());
+    
aMaskRange.transform(getViewInformation2D().getObjectToViewTransformation());
+    const basegfx::B2DRange aViewRange(basegfx::B2DPoint(clip_x1, clip_y1),
+                                       basegfx::B2DPoint(clip_x2, clip_y2));
+
+    if (!aViewRange.overlaps(aMaskRange))
+        return;
+
+    cairo_save(mpRT);
+
+    cairo_matrix_t aMatrix;
+    const basegfx::B2DHomMatrix& rObjectToView(
+        getViewInformation2D().getObjectToViewTransformation());
+    cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), 
rObjectToView.c(),
+                      rObjectToView.d(), rObjectToView.e(), rObjectToView.f());
+
+    // set linear transformation
+    cairo_set_matrix(mpRT, &aMatrix);
+
+    // put mask as path
+    for (const auto& rPolygon : aMask)
+        addB2DPolygonToPathGeometry(mpRT, rPolygon, &getViewInformation2D());
+
+    // clip to this mask
+    cairo_clip(mpRT);
+
+    process(rMaskCandidate.getChildren());
+
+    cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processPointArrayPrimitive2D(
+    const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
+{
+    const std::vector<basegfx::B2DPoint>& 
rPositions(rPointArrayCandidate.getPositions());
+    if (rPositions.empty())
+        return;
+
+    const basegfx::BColor aPointColor(
+        
maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
+    cairo_set_source_rgb(mpRT, aPointColor.getRed(), aPointColor.getGreen(), 
aPointColor.getBlue());
+
+    // To really paint a single pixel I found nothing better than
+    // switch off AA and draw a pixel-aligned rectangle
+    const cairo_antialias_t eOldAAMode(cairo_get_antialias(mpRT));
+    cairo_set_antialias(mpRT, CAIRO_ANTIALIAS_NONE);
+
+    for (auto const& pos : rPositions)
+    {
+        const basegfx::B2DPoint 
aDiscretePos(getViewInformation2D().getObjectToViewTransformation()
+                                             * pos);
+        const double fX(ceil(aDiscretePos.getX()));
+        const double fY(ceil(aDiscretePos.getY()));
+
+        cairo_rectangle(mpRT, fX, fY, 1, 1);
+        cairo_fill(mpRT);
+    }
+
+    cairo_set_antialias(mpRT, eOldAAMode);
+}
+
+void CairoPixelProcessor2D::processModifiedColorPrimitive2D(
+    const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
+{
+    if (!rModifiedCandidate.getChildren().empty())
+    {
+        maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
+        process(rModifiedCandidate.getChildren());
+        maBColorModifierStack.pop();
+    }
+}
+
+void CairoPixelProcessor2D::processTransformPrimitive2D(
+    const primitive2d::TransformPrimitive2D& rTransformCandidate)
+{
+    // remember current transformation and ViewInformation
+    const geometry::ViewInformation2D 
aLastViewInformation2D(getViewInformation2D());
+
+    // create new transformations for local ViewInformation2D
+    geometry::ViewInformation2D aViewInformation2D(getViewInformation2D());
+    
aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation()
+                                               * 
rTransformCandidate.getTransformation());
+    updateViewInformation(aViewInformation2D);
+
+    // process content
+    process(rTransformCandidate.getChildren());
+
+    // restore transformations
+    updateViewInformation(aLastViewInformation2D);
+}
+
+void CairoPixelProcessor2D::processPolygonStrokePrimitive2D(
+    const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
+{
+    const basegfx::B2DPolygon& 
rPolygon(rPolygonStrokeCandidate.getB2DPolygon());
+    const attribute::LineAttribute& 
rLineAttribute(rPolygonStrokeCandidate.getLineAttribute());
+
+    if (!rPolygon.count() || rLineAttribute.getWidth() < 0.0)
+    {
+        // no geometry, done
+        return;
+    }
+
+    // get some values early that might be used for decisions
+    const bool bHairline(0.0 == rLineAttribute.getWidth());
+    const basegfx::B2DHomMatrix& rObjectToView(
+        getViewInformation2D().getObjectToViewTransformation());
+    const double fDiscreteLineWidth(
+        bHairline ? 1.0
+                  : ceil((rObjectToView * 
basegfx::B2DVector(rLineAttribute.getWidth(), 0.0))
+                             .getLength()));
+
+    // Here for every combination which the system-specific implementation is 
not
+    // capable of visualizing, use the (for decomposable Primitives always 
possible)
+    // fallback to the decomposition.
+    if (basegfx::B2DLineJoin::NONE == rLineAttribute.getLineJoin() && 
fDiscreteLineWidth > 1.5)
+    {
+        // basegfx::B2DLineJoin::NONE is special for our office, no other 
GraphicSystem
+        // knows that (so far), so fallback to decomposition. This is only 
needed if
+        // LineJoin will be used, so also check for discrete LineWidth before 
falling back
+        process(rPolygonStrokeCandidate);
+        return;
+    }
+
+    // This is a method every system-specific implementation of a decomposable 
Primitive
+    // can use to allow simple optical control of paint implementation:
+    // Create a copy, e.g. change color to 'red' as here and paint before the 
system
+    // paints it using the decomposition. That way you can - if active - 
directly
+    // optically compare if the system-specific solution is geometrically 
identical to
+    // the decomposition (which defines our interpretation that we need to 
visualize).
+    // Look below in the impl for bRenderDecomposeForCompareInRed to see that 
in that case
+    // we create a half-transparent paint to better support visual control
+    static bool bRenderDecomposeForCompareInRed(false);
+
+    if (bRenderDecomposeForCompareInRed)
+    {
+        const attribute::LineAttribute aRed(
+            basegfx::BColor(1.0, 0.0, 0.0), rLineAttribute.getWidth(), 
rLineAttribute.getLineJoin(),
+            rLineAttribute.getLineCap(), 
rLineAttribute.getMiterMinimumAngle());
+        rtl::Reference<primitive2d::PolygonStrokePrimitive2D> xCopy(
+            new primitive2d::PolygonStrokePrimitive2D(
+                rPolygonStrokeCandidate.getB2DPolygon(), aRed,
+                rPolygonStrokeCandidate.getStrokeAttribute()));
+        process(*xCopy);
+    }
+
+    cairo_save(mpRT);
+
+    cairo_matrix_t aMatrix;
+    const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 
0.0);
+    cairo_matrix_init(&aMatrix, rObjectToView.a(), rObjectToView.b(), 
rObjectToView.c(),
+                      rObjectToView.d(), rObjectToView.e() + fAAOffset,
+                      rObjectToView.f() + fAAOffset);
+
+    // set linear transformation
+    cairo_set_matrix(mpRT, &aMatrix);
+
+    // setup line attributes
+    cairo_line_join_t eCairoLineJoin = CAIRO_LINE_JOIN_MITER;
+    switch (rLineAttribute.getLineJoin())
+    {
+        case basegfx::B2DLineJoin::Bevel:
+            eCairoLineJoin = CAIRO_LINE_JOIN_BEVEL;
+            break;
+        case basegfx::B2DLineJoin::Round:
+            eCairoLineJoin = CAIRO_LINE_JOIN_ROUND;
+            break;
+        case basegfx::B2DLineJoin::NONE:
+        case basegfx::B2DLineJoin::Miter:
+            eCairoLineJoin = CAIRO_LINE_JOIN_MITER;
+            break;
+    }
+
+    // convert miter minimum angle to miter limit
+    double fMiterLimit
+        = 1.0 / sin(std::max(rLineAttribute.getMiterMinimumAngle(), 0.01 * 
M_PI) / 2.0);
+
+    // setup cap attribute
+    cairo_line_cap_t eCairoLineCap(CAIRO_LINE_CAP_BUTT);
+
+    switch (rLineAttribute.getLineCap())
+    {
+        default: // css::drawing::LineCap_BUTT:
+        {
+            eCairoLineCap = CAIRO_LINE_CAP_BUTT;
+            break;
+        }
+        case css::drawing::LineCap_ROUND:
+        {
+            eCairoLineCap = CAIRO_LINE_CAP_ROUND;
+            break;
+        }
+        case css::drawing::LineCap_SQUARE:
+        {
+            eCairoLineCap = CAIRO_LINE_CAP_SQUARE;
+            break;
+        }
+    }
+
+    basegfx::BColor 
aLineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor()));
+    if (bRenderDecomposeForCompareInRed)
+        aLineColor.setRed(0.5);
+
+    cairo_set_source_rgb(mpRT, aLineColor.getRed(), aLineColor.getGreen(), 
aLineColor.getBlue());
+
+    cairo_set_line_join(mpRT, eCairoLineJoin);
+    cairo_set_line_cap(mpRT, eCairoLineCap);
+
+    // TODO: Hairline LineWidth, see comment at 
processPolygonHairlinePrimitive2D
+    cairo_set_line_width(mpRT, bHairline ? 1.44 : fDiscreteLineWidth);
+    cairo_set_miter_limit(mpRT, fMiterLimit);
+
+    const attribute::StrokeAttribute& rStrokeAttribute(
+        rPolygonStrokeCandidate.getStrokeAttribute());
+    const bool bDashUsed(!rStrokeAttribute.isDefault()
+                         && !rStrokeAttribute.getDotDashArray().empty()
+                         && 0.0 < rStrokeAttribute.getFullDotDashLen());
+    if (bDashUsed)
+    {
+        const std::vector<double>& rStroke = 
rStrokeAttribute.getDotDashArray();
+        cairo_set_dash(mpRT, rStroke.data(), rStroke.size(), 0.0);
+    }
+
+    addB2DPolygonToPathGeometry(mpRT, rPolygon, &getViewInformation2D());
+
+    cairo_stroke(mpRT);
+
+    cairo_restore(mpRT);
+}
+
+void CairoPixelProcessor2D::processBasePrimitive2D(const 
primitive2d::BasePrimitive2D& rCandidate)
+{
+    switch (rCandidate.getPrimitive2DID())
+    {
+#if 0
+        // geometry that *has* to be processed
+        case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D:
+        {
+            processBitmapPrimitive2D(
+                static_cast<const 
primitive2d::BitmapPrimitive2D&>(rCandidate));
+            break;
+        }
+#endif
+        case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D:
+        {
+            processPointArrayPrimitive2D(
+                static_cast<const 
primitive2d::PointArrayPrimitive2D&>(rCandidate));
+            break;
+        }
+        case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D:
+        {
+            processPolygonHairlinePrimitive2D(
+                static_cast<const 
primitive2d::PolygonHairlinePrimitive2D&>(rCandidate));
+            break;
+        }
+        case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D:
+        {
+            processPolyPolygonColorPrimitive2D(
+                static_cast<const 
primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate));
+            break;
+        }
+        // embedding/groups that *have* to be processed
+        case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D:
+        {
+            processTransparencePrimitive2D(
+                static_cast<const 
primitive2d::TransparencePrimitive2D&>(rCandidate));
+            break;
+        }
+        case PRIMITIVE2D_ID_INVERTPRIMITIVE2D:
+        {
+            // TODO: fallback is at 
VclPixelProcessor2D::processInvertPrimitive2D, so
+            // not in reach. Ignore for now.
+            // processInvertPrimitive2D(rCandidate);
+            break;
+        }
+        case PRIMITIVE2D_ID_MASKPRIMITIVE2D:
+        {
+            processMaskPrimitive2DPixel(
+                static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
+            break;
+        }
+        case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D:
+        {
+            processModifiedColorPrimitive2D(
+                static_cast<const 
primitive2d::ModifiedColorPrimitive2D&>(rCandidate));
+            break;
+        }
+        case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+        {
+            processTransformPrimitive2D(
+                static_cast<const 
primitive2d::TransformPrimitive2D&>(rCandidate));
+            break;
+        }
+#if 0
+        // geometry that *may* be processed due to being able to do it better
+        // then using the decomposition
+        case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D:
+        {
+            processUnifiedTransparencePrimitive2D(
+                static_cast<const 
primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate));
+            break;
+        }
+        case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D:
+        {
+            processMarkerArrayPrimitive2D(
+                static_cast<const 
primitive2d::MarkerArrayPrimitive2D&>(rCandidate));
+            break;
+        }
+        case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D:
+        {
+            processBackgroundColorPrimitive2D(
+                static_cast<const 
primitive2d::BackgroundColorPrimitive2D&>(rCandidate));
+            break;
+        }
+#endif
+        case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
+        {
+            processPolygonStrokePrimitive2D(
+                static_cast<const 
primitive2d::PolygonStrokePrimitive2D&>(rCandidate));
+            break;
+        }
+
+        // continue with decompose
+        default:
+        {
+            SAL_INFO("drawinglayer", "default case for " << 
drawinglayer::primitive2d::idToString(
+                                         rCandidate.getPrimitive2DID()));
+            // process recursively
+            process(rCandidate);
+            break;
+        }
+    }
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/drawinglayer/source/processor2d/processor2dtools.cxx 
b/drawinglayer/source/processor2d/processor2dtools.cxx
index f70657d9d39f..cf823b005ed8 100644
--- a/drawinglayer/source/processor2d/processor2dtools.cxx
+++ b/drawinglayer/source/processor2d/processor2dtools.cxx
@@ -18,12 +18,15 @@
  */
 #include <drawinglayer/processor2d/processor2dtools.hxx>
 #include <vcl/gdimtf.hxx>
+#include <vcl/sysdata.hxx>
 #include "vclpixelprocessor2d.hxx"
 #include "vclmetafileprocessor2d.hxx"
+#include <config_vclplug.h>
 
 #if defined(_WIN32)
 #include <drawinglayer/processor2d/d2dpixelprocessor2d.hxx>
-#include <vcl/sysdata.hxx>
+#elif USE_HEADLESS_CODE
+#include <drawinglayer/processor2d/cairopixelprocessor2d.hxx>
 #endif
 
 using namespace com::sun::star;
@@ -34,11 +37,9 @@ std::unique_ptr<BaseProcessor2D> 
createPixelProcessor2DFromOutputDevice(
     OutputDevice& rTargetOutDev,
     const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
 {
-#if defined(_WIN32)
     static const bool bTestSystemPrimitiveRenderer(nullptr != 
std::getenv("TEST_SYSTEM_PRIMITIVE_RENDERER"));
     if(bTestSystemPrimitiveRenderer)
     {
-        SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData());
         drawinglayer::geometry::ViewInformation2D 
aViewInformation2D(rViewInformation2D);
         // if mnOutOffX/mnOutOffY is set (a 'hack' to get a cheap additional 
offset), apply it additionally
         if(0 != rTargetOutDev.GetOutOffXPixel() || 0 != 
rTargetOutDev.GetOutOffYPixel())
@@ -47,12 +48,20 @@ std::unique_ptr<BaseProcessor2D> 
createPixelProcessor2DFromOutputDevice(
             aTransform.translate(rTargetOutDev.GetOutOffXPixel(), 
rTargetOutDev.GetOutOffYPixel());
             aViewInformation2D.setViewTransformation(aTransform);
         }
+#if defined(_WIN32)
+        SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData());
         std::unique_ptr<D2DPixelProcessor2D> aRetval(
             std::make_unique<D2DPixelProcessor2D>(aViewInformation2D, 
aData.hDC));
         if (aRetval->valid())
             return aRetval;
-    }
+#elif USE_HEADLESS_CODE
+        SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData());
+        std::unique_ptr<CairoPixelProcessor2D> aRetval(
+            std::make_unique<CairoPixelProcessor2D>(aViewInformation2D, 
static_cast<cairo_surface_t*>(aData.pSurface)));
+        if (aRetval->valid())
+            return aRetval;
 #endif
+    }
 
     // create Pixel Vcl-Processor
     return std::make_unique<VclPixelProcessor2D>(rViewInformation2D, 
rTargetOutDev);
diff --git a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx 
b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
new file mode 100644
index 000000000000..326d649da330
--- /dev/null
+++ b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; 
fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#pragma once
+
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <basegfx/color/bcolormodifier.hxx>
+#include <sal/config.h>
+
+// cairo-specific
+#include <cairo.h>
+
+namespace drawinglayer::primitive2d
+{
+class PolyPolygonColorPrimitive2D;
+class PolygonHairlinePrimitive2D;
+class BitmapPrimitive2D;
+class UnifiedTransparencePrimitive2D;
+class BackgroundColorPrimitive2D;
+class TransparencePrimitive2D;
+class MaskPrimitive2D;
+class ModifiedColorPrimitive2D;
+class TransformPrimitive2D;
+class PointArrayPrimitive2D;
+class MarkerArrayPrimitive2D;
+class PolygonStrokePrimitive2D;
+}
+
+namespace drawinglayer::processor2d
+{
+class DRAWINGLAYER_DLLPUBLIC CairoPixelProcessor2D : public BaseProcessor2D
+{
+    // the modifiedColorPrimitive stack
+    basegfx::BColorModifierStack maBColorModifierStack;
+
+    // cairo specific data
+    cairo_t* mpRT;
+
+    // helpers for direct paints
+    void processPolygonHairlinePrimitive2D(
+        const primitive2d::PolygonHairlinePrimitive2D& 
rPolygonHairlinePrimitive2D);
+    void processPolyPolygonColorPrimitive2D(
+        const primitive2d::PolyPolygonColorPrimitive2D& 
rPolyPolygonColorPrimitive2D);
+#if 0
+    void processBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& 
rBitmapCandidate);
+#endif
+    void
+    processTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& 
rTransCandidate);
+#if 0
+    void processUnifiedTransparencePrimitive2D(
+        const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate);
+#endif
+    void processMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& 
rMaskCandidate);
+    void processModifiedColorPrimitive2D(
+        const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate);
+    void processTransformPrimitive2D(const primitive2d::TransformPrimitive2D& 
rTransformCandidate);
+    void
+    processPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D& 
rPointArrayCandidate);
+#if 0
+    void
+    processMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D& 
rMarkerArrayCandidate);
+    void processBackgroundColorPrimitive2D(
+        const primitive2d::BackgroundColorPrimitive2D& 
rBackgroundColorCandidate);
+#endif
+    void processPolygonStrokePrimitive2D(
+        const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate);
+#if 0
+    // common helpers
+    ID2D1Bitmap* implCreateAlpha_Direct(const 
primitive2d::TransparencePrimitive2D& rTransCandidate,
+                                        const basegfx::B2DRange& 
rVisibleRange);
+    ID2D1Bitmap*
+    implCreateAlpha_B2DBitmap(const primitive2d::TransparencePrimitive2D& 
rTransCandidate,
+                              const basegfx::B2DRange& rVisibleRange,
+                              D2D1_MATRIX_3X2_F& rMaskScale);
+#endif
+
+    /*  the local processor for BasePrimitive2D-Implementation based 
primitives,
+        called from the common process()-implementation
+     */
+    virtual void processBasePrimitive2D(const primitive2d::BasePrimitive2D& 
rCandidate) override;
+
+protected:
+    // local protected minimal constructor for usage in derivates, e.g. helpers
+    CairoPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation);
+
+    bool hasError() const { return cairo_status(mpRT) != CAIRO_STATUS_SUCCESS; 
}
+    void setRenderTarget(cairo_t* mpNewRT) { mpRT = mpNewRT; }
+    bool hasRenderTarget() const { return nullptr != mpRT; }
+
+public:
+    bool valid() const { return hasRenderTarget() && !hasError(); }
+    CairoPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation,
+                          cairo_surface_t* pTarget);
+    virtual ~CairoPixelProcessor2D() override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/include/vcl/cairo.hxx b/include/vcl/cairo.hxx
index 87d820ab81ff..c3c5e5ac5ee6 100644
--- a/include/vcl/cairo.hxx
+++ b/include/vcl/cairo.hxx
@@ -17,13 +17,43 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
-#ifndef INCLUDED_VCL_CAIRO_HXX
-#define INCLUDED_VCL_CAIRO_HXX
+#pragma once
 
 #include <sal/config.h>
+#include <osl/endian.h>
+#include <vcl/Scanline.hxx>
 #include <vcl/vclptr.hxx>
+#include <config_features.h>
 #include <memory>
 
+//Using formats that match cairo's formats. For android we patch cairo,
+//which is internal in that case, to swap the rgb components so that
+//cairo then matches the OpenGL GL_RGBA format so we can use it there
+//where we don't have GL_BGRA support.
+// SVP_24BIT_FORMAT is used to store 24-bit images in 3-byte pixels to 
conserve memory.
+#if defined(ANDROID) && !HAVE_FEATURE_ANDROID_LOK
+#define SVP_24BIT_FORMAT (ScanlineFormat::N24BitTcRgb | 
ScanlineFormat::TopDown)
+#define SVP_CAIRO_FORMAT (ScanlineFormat::N32BitTcRgba | 
ScanlineFormat::TopDown)
+#define SVP_CAIRO_BLUE 1
+#define SVP_CAIRO_GREEN 2
+#define SVP_CAIRO_RED 0
+#define SVP_CAIRO_ALPHA 3
+#elif defined OSL_BIGENDIAN
+#define SVP_24BIT_FORMAT (ScanlineFormat::N24BitTcRgb | 
ScanlineFormat::TopDown)
+#define SVP_CAIRO_FORMAT (ScanlineFormat::N32BitTcArgb | 
ScanlineFormat::TopDown)
+#define SVP_CAIRO_BLUE 3
+#define SVP_CAIRO_GREEN 2
+#define SVP_CAIRO_RED 1
+#define SVP_CAIRO_ALPHA 0
+#else
+#define SVP_24BIT_FORMAT (ScanlineFormat::N24BitTcBgr | 
ScanlineFormat::TopDown)
+#define SVP_CAIRO_FORMAT (ScanlineFormat::N32BitTcBgra | 
ScanlineFormat::TopDown)
+#define SVP_CAIRO_BLUE 0
+#define SVP_CAIRO_GREEN 1
+#define SVP_CAIRO_RED 2
+#define SVP_CAIRO_ALPHA 3
+#endif
+
 typedef struct _cairo_surface cairo_surface_t;
 typedef struct _cairo cairo_t;
 
@@ -64,6 +94,4 @@ namespace cairo {
 
 }
 
-#endif
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/vcl/sysdata.hxx b/include/vcl/sysdata.hxx
index 07ddea45dae3..f7374a06d54e 100644
--- a/include/vcl/sysdata.hxx
+++ b/include/vcl/sysdata.hxx
@@ -22,6 +22,7 @@
 
 #include <sal/types.h>
 #include <vcl/dllapi.h>
+#include <config_vclplug.h>
 
 class SalFrame;
 
@@ -156,7 +157,9 @@ struct SystemGraphicsData
     void*           pVisual;        // the visual in use
     int             nScreen;        // the current screen of the drawable
     void*           pXRenderFormat; // render format for drawable
-    void*           pSurface;       // the cairo surface when using svp-based 
backends
+#endif
+#if USE_HEADLESS_CODE
+    void*           pSurface;       // the cairo surface when using svp-based 
backends, which includes gtk[3|4]
 #endif
     SystemGraphicsData()
         : nSize( sizeof( SystemGraphicsData ) )
@@ -175,6 +178,8 @@ struct SystemGraphicsData
         , pVisual( nullptr )
         , nScreen( 0 )
         , pXRenderFormat( nullptr )
+#endif
+#if USE_HEADLESS_CODE
         , pSurface( nullptr )
 #endif
     { }
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index 322dab644f2a..a7ce0e3c769e 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -90,7 +90,9 @@ css::uno::Any 
SvpSalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& /*
 
 SystemGraphicsData SvpSalGraphics::GetGraphicsData() const
 {
-    return SystemGraphicsData();
+    SystemGraphicsData aGraphicsData;
+    aGraphicsData.pSurface = m_aCairoCommon.m_pSurface;
+    return aGraphicsData;
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/headless/CairoCommon.hxx b/vcl/inc/headless/CairoCommon.hxx
index 3fc0144ef34a..230d8988429c 100644
--- a/vcl/inc/headless/CairoCommon.hxx
+++ b/vcl/inc/headless/CairoCommon.hxx
@@ -40,34 +40,6 @@
 
 #include <unordered_map>
 
-//Using formats that match cairo's formats. For android we patch cairo,
-//which is internal in that case, to swap the rgb components so that
-//cairo then matches the OpenGL GL_RGBA format so we can use it there
-//where we don't have GL_BGRA support.
-// SVP_24BIT_FORMAT is used to store 24-bit images in 3-byte pixels to 
conserve memory.
-#if defined(ANDROID) && !HAVE_FEATURE_ANDROID_LOK
-#define SVP_24BIT_FORMAT (ScanlineFormat::N24BitTcRgb | 
ScanlineFormat::TopDown)
-#define SVP_CAIRO_FORMAT (ScanlineFormat::N32BitTcRgba | 
ScanlineFormat::TopDown)
-#define SVP_CAIRO_BLUE 1
-#define SVP_CAIRO_GREEN 2
-#define SVP_CAIRO_RED 0
-#define SVP_CAIRO_ALPHA 3
-#elif defined OSL_BIGENDIAN
-#define SVP_24BIT_FORMAT (ScanlineFormat::N24BitTcRgb | 
ScanlineFormat::TopDown)
-#define SVP_CAIRO_FORMAT (ScanlineFormat::N32BitTcArgb | 
ScanlineFormat::TopDown)
-#define SVP_CAIRO_BLUE 3
-#define SVP_CAIRO_GREEN 2
-#define SVP_CAIRO_RED 1
-#define SVP_CAIRO_ALPHA 0
-#else
-#define SVP_24BIT_FORMAT (ScanlineFormat::N24BitTcBgr | 
ScanlineFormat::TopDown)
-#define SVP_CAIRO_FORMAT (ScanlineFormat::N32BitTcBgra | 
ScanlineFormat::TopDown)
-#define SVP_CAIRO_BLUE 0
-#define SVP_CAIRO_GREEN 1
-#define SVP_CAIRO_RED 2
-#define SVP_CAIRO_ALPHA 3
-#endif
-
 typedef struct _cairo cairo_t;
 typedef struct _cairo_surface cairo_surface_t;
 typedef struct _cairo_user_data_key cairo_user_data_key_t;
diff --git a/vcl/skia/SkiaHelper.cxx b/vcl/skia/SkiaHelper.cxx
index 39e7fb093d37..02ded1c84d32 100644
--- a/vcl/skia/SkiaHelper.cxx
+++ b/vcl/skia/SkiaHelper.cxx
@@ -286,14 +286,12 @@ bool isVCLSkiaEnabled()
     static bool bEnable = false;
     static bool bForceSkia = false;
 
-#if defined(_WIN32)
     // allow global disable when testing SystemPrimitiveRenderer since current 
Skia on Win does not
     // harmonize with using Direct2D and D2DPixelProcessor2D
     static const bool bTestSystemPrimitiveRenderer(
         nullptr != std::getenv("TEST_SYSTEM_PRIMITIVE_RENDERER"));
     if (bTestSystemPrimitiveRenderer)
         return false;
-#endif
 
     // No hardware rendering, so no Skia
     // TODO SKIA

Reply via email to