drawinglayer/source/processor2d/cairopixelprocessor2d.cxx | 140 +++++++++---- drawinglayer/source/processor2d/processor2dtools.cxx | 24 -- include/drawinglayer/processor2d/cairopixelprocessor2d.hxx | 54 ++--- 3 files changed, 131 insertions(+), 87 deletions(-)
New commits: commit 7a1de78ec2e58d2cceded3bf03c0c3c3cccc675a Author: Armin Le Grand (Collabora) <armin.le.gr...@me.com> AuthorDate: Fri Mar 21 15:34:16 2025 +0100 Commit: Armin Le Grand <armin.le.gr...@me.com> CommitDate: Mon Mar 24 10:55:52 2025 +0100 tdf#165706 additional taking care in CairoSDPR The task is fixed with the previous commit for this tdf number, but convinced me to do also some more safe stuff in the CairoSDPR implementation. The associated OutputDevice *is* now handed over to CairoSDPR and some stuff being done in the helper to create the SDPR is now done directly in the CairoSDPR constructors. Also the FormControl rendering is now closer to what the VCLPrimitiveRenderer does, to be on the safe side. It is still just the FormControl rendering that indirectly uses the associated OutputDevice, but more convenient. The CairoSDPR now also (as VCLRenderer) resets the MapMode in the associated OutputDevice for the time using it - just in case there might be other *indirect* usages like the FormControl one. Change-Id: I5029788655ff81bf360d98312d417b7886208e1f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183204 Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> Tested-by: Jenkins diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx index e7156bdb3238..137d25931e9f 100644 --- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx @@ -17,6 +17,7 @@ #include <vcl/alpha.hxx> #include <vcl/cairo.hxx> #include <vcl/outdev.hxx> +#include <vcl/sysdata.hxx> #include <vcl/svapp.hxx> #include <comphelper/lok.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> @@ -948,10 +949,46 @@ bool checkCoordinateLimitWorkaroundNeededForUsedCairo() namespace drawinglayer::processor2d { +CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, + cairo_surface_t* pTarget) + : BaseProcessor2D(rViewInformation) + , mpTargetOutputDevice(nullptr) + , maBColorModifierStack() + , mpOwnedSurface(nullptr) + , mpRT(nullptr) + , mbRenderSimpleTextDirect( + officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get()) + , mbRenderDecoratedTextDirect( + officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) + , mnClipRecursionCount(0) + , mbCairoCoordinateLimitWorkaroundActive(false) +{ + // no target, nothing to initialize + if (nullptr == pTarget) + return; + + // create RenderTarget for full target + mpRT = cairo_create(pTarget); + + if (nullptr == mpRT) + // error, invalid + return; + + // 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); + + // evaluate if CairoCoordinateLimitWorkaround is needed + evaluateCairoCoordinateLimitWorkaround(); +} + CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, tools::Long nWidthPixel, tools::Long nHeightPixel, bool bUseRGBA) : BaseProcessor2D(rViewInformation) + , mpTargetOutputDevice(nullptr) , maBColorModifierStack() , mpOwnedSurface(nullptr) , mpRT(nullptr) @@ -961,7 +998,6 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) , mnClipRecursionCount(0) , mbCairoCoordinateLimitWorkaroundActive(false) - , maXGraphics() { if (nWidthPixel <= 0 || nHeightPixel <= 0) // no size, invalid @@ -991,11 +1027,10 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& evaluateCairoCoordinateLimitWorkaround(); } -CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, - cairo_surface_t* pTarget, tools::Long nOffsetPixelX, - tools::Long nOffsetPixelY, tools::Long nWidthPixel, - tools::Long nHeightPixel) +CairoPixelProcessor2D::CairoPixelProcessor2D(OutputDevice& rOutputDevice, + const geometry::ViewInformation2D& rViewInformation) : BaseProcessor2D(rViewInformation) + , mpTargetOutputDevice(&rOutputDevice) , maBColorModifierStack() , mpOwnedSurface(nullptr) , mpRT(nullptr) @@ -1005,12 +1040,20 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get()) , mnClipRecursionCount(0) , mbCairoCoordinateLimitWorkaroundActive(false) - , maXGraphics() { + SystemGraphicsData aData(mpTargetOutputDevice->GetSystemGfxData()); + cairo_surface_t* pTarget(static_cast<cairo_surface_t*>(aData.pSurface)); + // no target, nothing to initialize if (nullptr == pTarget) return; + // get evtl. offsets if OutputDevice is e.g. a OUTDEV_WINDOW + // to evaluate if initial clip is needed + const tools::Long nOffsetPixelX(mpTargetOutputDevice->GetOutOffXPixel()); + const tools::Long nOffsetPixelY(mpTargetOutputDevice->GetOutOffYPixel()); + const tools::Long nWidthPixel(mpTargetOutputDevice->GetOutputWidthPixel()); + const tools::Long nHeightPixel(mpTargetOutputDevice->GetOutputHeightPixel()); bool bClipNeeded(false); if (0 != nOffsetPixelX || 0 != nOffsetPixelY || 0 != nWidthPixel || 0 != nHeightPixel) @@ -1036,14 +1079,22 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& if (bClipNeeded) { - // optional: if the possibility to add an initial clip relative - // to the real pixel dimensions of the target surface is used, - // apply it here using that nice existing method of cairo + // 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 existence of 'virtual' target surfaces that + // internally use an offset and limited 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). mpOwnedSurface = cairo_surface_create_for_rectangle(pTarget, nOffsetPixelX, nOffsetPixelY, nWidthPixel, nHeightPixel); - if (nullptr != mpOwnedSurface) - mpRT = cairo_create(mpOwnedSurface); + if (nullptr == mpOwnedSurface) + // error, invalid + return; + + mpRT = cairo_create(mpOwnedSurface); } else { @@ -1051,14 +1102,19 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& 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); - } + if (nullptr == mpRT) + // error, invalid + return; + + // 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); + + // prepare output directly to pixels + mpTargetOutputDevice->Push(vcl::PushFlags::MAPMODE); + mpTargetOutputDevice->SetMapMode(); // evaluate if CairoCoordinateLimitWorkaround is needed evaluateCairoCoordinateLimitWorkaround(); @@ -1066,6 +1122,8 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const geometry::ViewInformation2D& CairoPixelProcessor2D::~CairoPixelProcessor2D() { + if (nullptr != mpTargetOutputDevice) // restore MapMode + mpTargetOutputDevice->Pop(); if (nullptr != mpRT) cairo_destroy(mpRT); if (nullptr != mpOwnedSurface) @@ -3889,26 +3947,32 @@ void CairoPixelProcessor2D::processControlPrimitive2D( try { - if (getXGraphics().is()) + if (nullptr != mpTargetOutputDevice) { - // Needs to be drawn. Link new graphics and view - const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); - uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW); - const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics()); - xControlView->setGraphics(getXGraphics()); - - // get position - const basegfx::B2DHomMatrix aObjectToPixel( - getViewInformation2D().getObjectToViewTransformation() - * rControlPrimitive.getTransform()); - const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); - - xControlView->draw(basegfx::fround(aTopLeftPixel.getX()), - basegfx::fround(aTopLeftPixel.getY())); - - // restore original graphics - xControlView->setGraphics(xOriginalGraphics); - bDone = true; + const uno::Reference<awt::XGraphics> xTargetGraphics( + mpTargetOutputDevice->CreateUnoGraphics()); + + if (xTargetGraphics.is()) + { + // Needs to be drawn. Link new graphics and view + const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); + uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW); + const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics()); + xControlView->setGraphics(xTargetGraphics); + + // get position + const basegfx::B2DHomMatrix aObjectToPixel( + getViewInformation2D().getObjectToViewTransformation() + * rControlPrimitive.getTransform()); + const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); + + xControlView->draw(basegfx::fround(aTopLeftPixel.getX()), + basegfx::fround(aTopLeftPixel.getY())); + + // restore original graphics + xControlView->setGraphics(xOriginalGraphics); + bDone = true; + } } } catch (const uno::Exception&) diff --git a/drawinglayer/source/processor2d/processor2dtools.cxx b/drawinglayer/source/processor2d/processor2dtools.cxx index f04d121467c5..6c38abc4039d 100644 --- a/drawinglayer/source/processor2d/processor2dtools.cxx +++ b/drawinglayer/source/processor2d/processor2dtools.cxx @@ -18,13 +18,13 @@ */ #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> #include <officecfg/Office/Common.hxx> @@ -118,29 +118,15 @@ std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromOutputDevice( if (!bMirrored) { - SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData()); - - // 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 - // existence of 'virtual' target surfaces that internally use an - // offset and limited 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). + // create CairoPixelProcessor2D associated with the given + // OutputDevice std::unique_ptr<CairoPixelProcessor2D> aRetval( std::make_unique<CairoPixelProcessor2D>( - rViewInformation2D, static_cast<cairo_surface_t*>(aData.pSurface), - rTargetOutDev.GetOutOffXPixel(), rTargetOutDev.GetOutOffYPixel(), - rTargetOutDev.GetOutputWidthPixel(), rTargetOutDev.GetOutputHeightPixel())); + rTargetOutDev, + rViewInformation2D)); if (aRetval->valid()) { - // if we construct a CairoPixelProcessor2D from OutputDevice, - // additionally set the XGraphics that can be obtained from - // there. It may be used e.g. to render FormControls directly - aRetval->setXGraphics(rTargetOutDev.CreateUnoGraphics()); - return aRetval; } } diff --git a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx index 2e9c2ad2ce45..eb7515c0b77c 100644 --- a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx +++ b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx @@ -13,7 +13,7 @@ #include <basegfx/color/bcolormodifier.hxx> #include <tools/long.hxx> #include <sal/config.h> -#include <com/sun/star/awt/XGraphics.hpp> +#include <vcl/vclptr.hxx> // cairo-specific #include <cairo.h> @@ -62,12 +62,16 @@ class B2DHomMatrixBufferedOnDemandDecompose; } class BitmapEx; +class OutputDevice; class SalLayout; namespace drawinglayer::processor2d { class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : public BaseProcessor2D { + // the OutputDevice if this renderer is associated with one, else nullptr + VclPtr<OutputDevice> mpTargetOutputDevice; + // the modifiedColorPrimitive stack basegfx::BColorModifierStack maBColorModifierStack; @@ -90,9 +94,6 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : pub // calculated result of if we are in outsideCairoCoordinateLimits mode bool mbCairoCoordinateLimitWorkaroundActive; - // the XGraphics which may be set using setXGraphics() - com::sun::star::uno::Reference<com::sun::star::awt::XGraphics> maXGraphics; - // helpers for direct paints void paintPolyPolygonRGBA(const basegfx::B2DPolyPolygon& rPolyPolygon, const basegfx::BColor& rColor, double fTransparency = 0.0); @@ -192,21 +193,22 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : pub protected: bool hasError() const { return cairo_status(mpRT) != CAIRO_STATUS_SUCCESS; } bool hasRenderTarget() const { return nullptr != mpRT; } - const com::sun::star::uno::Reference<com::sun::star::awt::XGraphics>& getXGraphics() const - { - return maXGraphics; - } + + // constructor to create a CairoPixelProcessor2D for + // the given cairo_surface_t pTarget. pTarget will not + // be owned and not destroyed, but be used as render + // target. You should check the result using valid() + CairoPixelProcessor2D( + + // the ViewInformation + const geometry::ViewInformation2D& rViewInformation, + + // the cairo render target + cairo_surface_t* pTarget); public: bool valid() const { return hasRenderTarget() && !hasError(); } - // set a XGraphics for this CairoPixelProcessor2D when it is available - void - setXGraphics(const com::sun::star::uno::Reference<com::sun::star::awt::XGraphics>& rXGraphics) - { - maXGraphics = rXGraphics; - } - // read access to CairoCoordinateLimitWorkaround mechanism bool isCairoCoordinateLimitWorkaroundActive() const { @@ -227,24 +229,16 @@ public: // define RGBA (true) or RGB (false) bool bUseRGBA); - // constructor to create a CairoPixelProcessor2D for - // the given cairo_surface_t pTarget. pTarget will not - // be owned and not destroyed, but be used as render - // target. If needed you can define a sub-rectangle - // to which the rendering will be limited (clipped). - // You should check the result using valid() + // constructor to create a CairoPixelProcessor2D + // associated with an OutputDevice. You should + // check the result using valid() CairoPixelProcessor2D( - // the initial ViewInformation - const geometry::ViewInformation2D& rViewInformation, - - // the cairo render target - cairo_surface_t* pTarget, + // the OutputDevice this processor shall be associated with + OutputDevice& rOutputDevice, - // 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); + // the initial ViewInformation + const geometry::ViewInformation2D& rViewInformation); virtual ~CairoPixelProcessor2D() override;