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
