drawinglayer/source/processor2d/cairopixelprocessor2d.cxx  |   64 +++++++++++--
 drawinglayer/source/processor2d/processor2dtools.cxx       |   56 ++++++-----
 include/drawinglayer/processor2d/cairopixelprocessor2d.hxx |   20 +++-
 3 files changed, 106 insertions(+), 34 deletions(-)

New commits:
commit 59fc2b5ca9678855feb30345761dad739b546edc
Author:     Armin Le Grand (Collabora) <[email protected]>
AuthorDate: Thu Sep 26 21:00:50 2024 +0200
Commit:     Armin Le Grand <[email protected]>
CommitDate: Fri Sep 27 13:38:35 2024 +0200

    tdf#163125: CairoSDPR: Take virtual OutDevs into account
    
    Unfortunaletly we have 'virtual' OutDevs, that means their
    PixelSize is bigger as they claim and they use an internal
    offset. This is used to not have to create too many Windows
    inside Windows (AFAIR initial reason was that Windows has
    a fix number of Windows per process that can be incarnated).
    
    The offset was/is already supported by SDPRs using
    ViewInformation2D and adding ot to the ViewTransformation,
    but the evtl. existing PixelSize has to be clipped against.
    The fallback VclPixelProcessor2D supports that indirectly
    by doing that in the OutDev commands that get called, so
    it does not need to be done by that renderer.
    
    I have now added code to support that for the
    CairoPixelProcessor2D, see code and comments. It uses an
    existing method of cairo to do that elegantly inside the
    system-dependent code of the SDPR.
    
    Note that the Windows SDPR D2DPixelProcessor2D will
    have the same problem that will have to be solved there,
    too. Since it's currently in experimental state I made
    myself a note about that.
    
    Change-Id: I68915985102bb4a63c84299f8d022ab013633510
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/173998
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <[email protected]>

diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index 1bf2b7dd9324..a1b57ed7940a 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -866,9 +866,12 @@ basegfx::B2DRange getDiscreteViewRange(cairo_t* pRT)
 namespace drawinglayer::processor2d
 {
 CairoPixelProcessor2D::CairoPixelProcessor2D(const 
geometry::ViewInformation2D& rViewInformation,
-                                             cairo_surface_t* pTarget)
+                                             cairo_surface_t* pTarget, 
tools::Long nOffsetPixelX,
+                                             tools::Long nOffsetPixelY, 
tools::Long nWidthPixel,
+                                             tools::Long nHeightPixel)
     : BaseProcessor2D(rViewInformation)
     , maBColorModifierStack()
+    , mpCreateForRectangle(nullptr)
     , mpRT(nullptr)
     , mbRenderSimpleTextDirect(
           
officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get())
@@ -878,19 +881,64 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const 
geometry::ViewInformation2D&
 {
     if (pTarget)
     {
-        cairo_t* pRT = cairo_create(pTarget);
-        cairo_set_antialias(pRT, rViewInformation.getUseAntiAliasing() ? 
CAIRO_ANTIALIAS_DEFAULT
-                                                                       : 
CAIRO_ANTIALIAS_NONE);
-        cairo_set_fill_rule(pRT, CAIRO_FILL_RULE_EVEN_ODD);
-        cairo_set_operator(pRT, CAIRO_OPERATOR_OVER);
-        setRenderTarget(pRT);
+        bool bClipNeeded(false);
+
+        if (0 != nOffsetPixelX || 0 != nOffsetPixelY || 0 != nWidthPixel || 0 
!= nHeightPixel)
+        {
+            if (0 != nOffsetPixelX || 0 != nOffsetPixelY)
+            {
+                // if offset is used we need initial clip
+                bClipNeeded = true;
+            }
+            else
+            {
+                // no offset used, compare to real pixel size
+                const tools::Long 
nRealPixelWidth(cairo_image_surface_get_width(pTarget));
+                const tools::Long 
nRealPixelHeight(cairo_image_surface_get_height(pTarget));
+
+                if (nRealPixelWidth != nWidthPixel || nRealPixelHeight != 
nHeightPixel)
+                {
+                    // if size differs we need initial clip
+                    bClipNeeded = true;
+                }
+            }
+        }
+
+        if (bClipNeeded)
+        {
+            // optional: if the possibility to add an initial clip relative
+            // to the real pixel dimensions of the target surface is used,
+            // aplly it here using that nice existing method of cairo
+            mpCreateForRectangle = cairo_surface_create_for_rectangle(
+                pTarget, nOffsetPixelX, nOffsetPixelY, nWidthPixel, 
nHeightPixel);
+
+            if (nullptr != mpCreateForRectangle)
+                mpRT = cairo_create(mpCreateForRectangle);
+        }
+        else
+        {
+            // create RenderTarget for full target
+            mpRT = cairo_create(pTarget);
+        }
+
+        if (nullptr != mpRT)
+        {
+            // initialize some basic used values/settings
+            cairo_set_antialias(mpRT, rViewInformation.getUseAntiAliasing()
+                                          ? CAIRO_ANTIALIAS_DEFAULT
+                                          : CAIRO_ANTIALIAS_NONE);
+            cairo_set_fill_rule(mpRT, CAIRO_FILL_RULE_EVEN_ODD);
+            cairo_set_operator(mpRT, CAIRO_OPERATOR_OVER);
+        }
     }
 }
 
 CairoPixelProcessor2D::~CairoPixelProcessor2D()
 {
-    if (mpRT)
+    if (nullptr != mpRT)
         cairo_destroy(mpRT);
+    if (nullptr != mpCreateForRectangle)
+        cairo_surface_destroy(mpCreateForRectangle);
 }
 
 void CairoPixelProcessor2D::processBitmapPrimitive2D(
diff --git a/drawinglayer/source/processor2d/processor2dtools.cxx 
b/drawinglayer/source/processor2d/processor2dtools.cxx
index e11f64a11da9..7930e496664f 100644
--- a/drawinglayer/source/processor2d/processor2dtools.cxx
+++ b/drawinglayer/source/processor2d/processor2dtools.cxx
@@ -36,31 +36,18 @@ std::unique_ptr<BaseProcessor2D> 
createPixelProcessor2DFromOutputDevice(
     OutputDevice& rTargetOutDev,
     const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
 {
-    static bool bUsePrimitiveRenderer(
 #if defined(_WIN32)
-        // Windows: make dependent on TEST_SYSTEM_PRIMITIVE_RENDERER
-        nullptr != std::getenv("TEST_SYSTEM_PRIMITIVE_RENDERER")
-#elif USE_HEADLESS_CODE
-        // Linux/Cairo: activate to check tests/builds. Leave a
-        // possibility to deactivate for easy test/request testing
-        nullptr == std::getenv("DISABLE_SYSTEM_DEPENDENT_PRIMITIVE_RENDERER")
-
-        // Use this if all is stable/tested for a while
-        // true
-
-        // Also possible: make dependent on ExperimentalMode
-        // officecfg::Office::Common::Misc::ExperimentalMode::get()
-#else
-        // all others: do not use, not (yet) supported
-        false
-#endif
-    );
+    // Windows: make dependent on TEST_SYSTEM_PRIMITIVE_RENDERER
+    static bool bUsePrimitiveRenderer(nullptr != 
std::getenv("TEST_SYSTEM_PRIMITIVE_RENDERER"));
 
-    if(bUsePrimitiveRenderer)
+    if (bUsePrimitiveRenderer)
     {
         drawinglayer::geometry::ViewInformation2D 
aViewInformation2D(rViewInformation2D);
 
         // if mnOutOffX/mnOutOffY is set (a 'hack' to get a cheap additional 
offset), apply it additionally
+        // NOTE: This will also need to take extended size of target device 
into
+        //       consideration, using D2DPixelProcessor2D *will* have to clip
+        //       against that. Thus for now this is *not* sufficient (see 
tdf#163125)
         if(0 != rTargetOutDev.GetOutOffXPixel() || 0 != 
rTargetOutDev.GetOutOffYPixel())
         {
             basegfx::B2DHomMatrix 
aTransform(aViewInformation2D.getViewTransformation());
@@ -68,22 +55,45 @@ std::unique_ptr<BaseProcessor2D> 
createPixelProcessor2DFromOutputDevice(
             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
+    // Linux/Cairo: now globally activated in master. Leave a
+    // possibility to deactivate for easy test/request testing
+    static bool bUsePrimitiveRenderer(nullptr == 
std::getenv("DISABLE_SYSTEM_DEPENDENT_PRIMITIVE_RENDERER"));
+
+    if (bUsePrimitiveRenderer)
+    {
         SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData());
+        const Size aSizePixel(rTargetOutDev.GetOutputSizePixel());
+
+        // create CairoPixelProcessor2D, make use of the possibility to
+        // add an initial clip relative to the real pixel dimensions of
+        // the target surface. This is e.g. needed here due to the
+        // existance of 'virtual' target surfaces that internally use an
+        // offset and limitied pixel size, mainly used for UI elements.
+        // let the CairoPixelProcessor2D do this, it has internal,
+        // system-specific possibilities to do that in an elegant and
+        // efficient way (using cairo_surface_create_for_rectangle).
         std::unique_ptr<CairoPixelProcessor2D> aRetval(
-            std::make_unique<CairoPixelProcessor2D>(aViewInformation2D, 
static_cast<cairo_surface_t*>(aData.pSurface)));
+            std::make_unique<CairoPixelProcessor2D>(
+                rViewInformation2D, 
static_cast<cairo_surface_t*>(aData.pSurface),
+                rTargetOutDev.GetOutOffXPixel(), 
rTargetOutDev.GetOutOffYPixel(),
+                aSizePixel.getWidth(), aSizePixel.getHeight()));
+
         if (aRetval->valid())
             return aRetval;
-#endif
     }
+#endif
 
-    // default: create Pixel Vcl-Processor
+    // default: create VclPixelProcessor2D
+    // NOTE: Since this uses VCL OutputDevice in the VclPixelProcessor2D
+    //       taking care of virtual devices is not needed, OutputDevice
+    //       and VclPixelProcessor2D will traditionally take care of it
     return std::make_unique<VclPixelProcessor2D>(rViewInformation2D, 
rTargetOutDev);
 }
 
diff --git a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx 
b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
index 026c6a0c3086..cd0f8a562e50 100644
--- a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
+++ b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
@@ -11,6 +11,7 @@
 
 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
 #include <basegfx/color/bcolormodifier.hxx>
+#include <tools/long.hxx>
 #include <sal/config.h>
 
 // cairo-specific
@@ -68,6 +69,10 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) 
CairoPixelProcessor2D final : pub
     // the modifiedColorPrimitive stack
     basegfx::BColorModifierStack maBColorModifierStack;
 
+    // cairo_surface_t created when initial clip from the constructor
+    // parameters is requested
+    cairo_surface_t* mpCreateForRectangle;
+
     // cairo specific data
     cairo_t* mpRT;
 
@@ -173,13 +178,22 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) 
CairoPixelProcessor2D final : pub
 
 protected:
     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);
+    CairoPixelProcessor2D(
+        // the initial ViewInformation
+        const geometry::ViewInformation2D& rViewInformation,
+
+        // the cairo render target
+        cairo_surface_t* pTarget,
+
+        // optional: possibility to add an initial clip relative to
+        // the real pixel dimensions of the target surface
+        tools::Long nOffsetPixelX = 0, tools::Long nOffsetPixelY = 0, 
tools::Long nWidthPixel = 0,
+        tools::Long nHeightPixel = 0);
+
     virtual ~CairoPixelProcessor2D() override;
 
     // access to BColorModifierStack

Reply via email to