drawinglayer/source/processor2d/vclpixelprocessor2d.cxx | 1557 ++++++++-------- solenv/clang-format/blacklist | 1 vcl/qa/cppunit/pdfexport/data/SimpleMultiPagePDF.pdf |binary vcl/qa/cppunit/pdfexport/pdfexport.cxx | 134 + 4 files changed, 945 insertions(+), 747 deletions(-)
New commits: commit f6fb4a0b2127b12ebb904659259de216c6a9b2d2 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Thu Apr 30 07:54:46 2020 +0200 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Thu Apr 30 08:50:29 2020 +0200 test import and export of multi-page PDF with PDFium filter The test checks that the exported PDFs contain embedded PDF for different pages. Change-Id: I4e5cd108d8597851d86aa774efbde0d4f2b9d2ac diff --git a/vcl/qa/cppunit/pdfexport/data/SimpleMultiPagePDF.pdf b/vcl/qa/cppunit/pdfexport/data/SimpleMultiPagePDF.pdf new file mode 100644 index 000000000000..af665fcba8a0 Binary files /dev/null and b/vcl/qa/cppunit/pdfexport/data/SimpleMultiPagePDF.pdf differ diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index 313dbdd278b4..c228cc7945a6 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -26,6 +26,7 @@ #include <com/sun/star/document/XExporter.hpp> #include <com/sun/star/io/XOutputStream.hpp> +#include <comphelper/scopeguard.hxx> #include <comphelper/processfactory.hxx> #include <comphelper/propertysequence.hxx> #include <test/bootstrapfixture.hxx> @@ -146,6 +147,8 @@ public: void testLargePage(); void testVersion15(); void testDefaultVersion(); + void testMultiPagePDF(); + CPPUNIT_TEST_SUITE(PdfExportTest); CPPUNIT_TEST(testTdf106059); @@ -189,6 +192,7 @@ public: CPPUNIT_TEST(testLargePage); CPPUNIT_TEST(testVersion15); CPPUNIT_TEST(testDefaultVersion); + CPPUNIT_TEST(testMultiPagePDF); CPPUNIT_TEST_SUITE_END(); }; @@ -2154,6 +2158,136 @@ void PdfExportTest::testVersion15() CPPUNIT_ASSERT_EQUAL(15, nFileVersion); } +// Check round-trip of importing and exporting the PDF with PDFium filter, +// which imports the PDF document as multiple PDFs as graphic object. +// Each page in the document has one PDF graphic object which content is +// the correcponding page in the PDF. When such a document is exported, +// the PDF graphic gets embedded into the exported PDF document (as a +// Form XObject). +void PdfExportTest::testMultiPagePDF() +{ + // We need to enable PDFium import (and make sure to disable after the test) + bool bResetEnvVar = false; + if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr) + { + bResetEnvVar = true; + setenv("LO_IMPORT_USE_PDFIUM", "1", false); + } + comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() { + if (bResetEnvVar) + unsetenv("LO_IMPORT_USE_PDFIUM"); + }); + + // Load the PDF and save as PDF + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "SimpleMultiPagePDF.pdf"; + mxComponent = loadFromDesktop(aURL); + CPPUNIT_ASSERT(mxComponent.is()); + + uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); + + // Parse the export result. + vcl::filter::PDFDocument aDocument; + SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ); + CPPUNIT_ASSERT(aDocument.Read(aStream)); + + std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages(); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPages.size()); + + vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources"); + CPPUNIT_ASSERT(pResources); + + auto pXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject")); + CPPUNIT_ASSERT(pXObjects); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), pXObjects->GetItems().size()); // 3 PDFs as Form XObjects + + std::vector<OString> rIDs; + for (auto const & rPair : pXObjects->GetItems()) { + rIDs.push_back(rPair.first); + } + + // Let's check the embedded PDF pages - just make sure the size differs, + // which should indicate we don't have 3 times the same page. + + { // embedded PDF page 1 + vcl::filter::PDFObjectElement* pXObject1 = pXObjects->LookupObject(rIDs[0]); + CPPUNIT_ASSERT(pXObject1); + CPPUNIT_ASSERT_EQUAL(OString("Im19"), rIDs[0]); + + auto pSubtype1 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject1->Lookup("Subtype")); + CPPUNIT_ASSERT(pSubtype1); + CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype1->GetValue()); + + auto pXObjectResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject1->Lookup("Resources")); + CPPUNIT_ASSERT(pXObjectResources); + auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObjectResources->LookupElement("XObject")); + CPPUNIT_ASSERT(pXObjectForms); + vcl::filter::PDFObjectElement* pForm = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first); + CPPUNIT_ASSERT(pForm); + + vcl::filter::PDFStreamElement* pStream = pForm->GetStream(); + CPPUNIT_ASSERT(pStream); + SvMemoryStream& rObjectStream = pStream->GetMemory(); + rObjectStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Just check that the size of the page stream is what is expected. + CPPUNIT_ASSERT_EQUAL(sal_uInt64(230), rObjectStream.remainingSize()); + } + + { // embedded PDF page 2 + vcl::filter::PDFObjectElement* pXObject2 = pXObjects->LookupObject(rIDs[1]); + CPPUNIT_ASSERT(pXObject2); + CPPUNIT_ASSERT_EQUAL(OString("Im34"), rIDs[1]); + + auto pSubtype2 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject2->Lookup("Subtype")); + CPPUNIT_ASSERT(pSubtype2); + CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype2->GetValue()); + + auto pXObjectResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject2->Lookup("Resources")); + CPPUNIT_ASSERT(pXObjectResources); + auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObjectResources->LookupElement("XObject")); + CPPUNIT_ASSERT(pXObjectForms); + vcl::filter::PDFObjectElement* pForm = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first); + CPPUNIT_ASSERT(pForm); + + vcl::filter::PDFStreamElement* pStream = pForm->GetStream(); + CPPUNIT_ASSERT(pStream); + SvMemoryStream& rObjectStream = pStream->GetMemory(); + rObjectStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Just check that the size of the page stream is what is expected + CPPUNIT_ASSERT_EQUAL(sal_uInt64(309), rObjectStream.remainingSize()); + } + + { // embedded PDF page 3 + vcl::filter::PDFObjectElement* pXObject3 = pXObjects->LookupObject(rIDs[2]); + CPPUNIT_ASSERT(pXObject3); + CPPUNIT_ASSERT_EQUAL(OString("Im4"), rIDs[2]); + + auto pSubtype3 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject3->Lookup("Subtype")); + CPPUNIT_ASSERT(pSubtype3); + CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype3->GetValue()); + + auto pXObjectResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject3->Lookup("Resources")); + CPPUNIT_ASSERT(pXObjectResources); + auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObjectResources->LookupElement("XObject")); + CPPUNIT_ASSERT(pXObjectForms); + vcl::filter::PDFObjectElement* pForm = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first); + CPPUNIT_ASSERT(pForm); + + vcl::filter::PDFStreamElement* pStream = pForm->GetStream(); + CPPUNIT_ASSERT(pStream); + SvMemoryStream& rObjectStream = pStream->GetMemory(); + rObjectStream.Seek(STREAM_SEEK_TO_BEGIN); + + // Just check that the size of the page stream is what is expected + CPPUNIT_ASSERT_EQUAL(sal_uInt64(193), rObjectStream.remainingSize()); + } +} + CPPUNIT_TEST_SUITE_REGISTRATION(PdfExportTest); } commit 38b0be5834a306f808eee980bf0f22131ed72580 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Wed Apr 29 17:50:06 2020 +0200 Commit: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> CommitDate: Wed Apr 29 17:50:06 2020 +0200 remove vclpixelprocessor2d.cxx from clang-format blacklist Change-Id: I03ad72d0754cb8c180f4eab0e8040c3dab533ef9 diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx index 654fa6e6168f..f09c6ad0fd76 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -65,826 +65,891 @@ using namespace com::sun::star; namespace drawinglayer::processor2d { - struct VclPixelProcessor2D::Impl - { - AntialiasingFlags m_nOrigAntiAliasing; +struct VclPixelProcessor2D::Impl +{ + AntialiasingFlags m_nOrigAntiAliasing; + + explicit Impl(OutputDevice const& rOutDev) + : m_nOrigAntiAliasing(rOutDev.GetAntialiasing()) + { + } +}; + +VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, + OutputDevice& rOutDev) + : VclProcessor2D(rViewInformation, rOutDev) + , m_pImpl(new Impl(rOutDev)) +{ + // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels + maCurrentTransformation = rViewInformation.getObjectToViewTransformation(); + + // prepare output directly to pixels + mpOutputDevice->Push(PushFlags::MAPMODE); + mpOutputDevice->SetMapMode(); + + // react on AntiAliasing settings + if (getOptionsDrawinglayer().IsAntiAliasing()) + { + mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing + | AntialiasingFlags::EnableB2dDraw); + } + else + { + mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing + & ~AntialiasingFlags::EnableB2dDraw); + } +} + +VclPixelProcessor2D::~VclPixelProcessor2D() +{ + // restore MapMode + mpOutputDevice->Pop(); - explicit Impl(OutputDevice const& rOutDev) - : m_nOrigAntiAliasing(rOutDev.GetAntialiasing()) - { } - }; + // restore AntiAliasing + mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing); +} - VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev) - : VclProcessor2D(rViewInformation, rOutDev) - , m_pImpl(new Impl(rOutDev)) - { - // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels - maCurrentTransformation = rViewInformation.getObjectToViewTransformation(); +void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect( + const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D& rSource, double fTransparency) +{ + if (!rSource.getB2DPolyPolygon().count() || fTransparency < 0.0 || fTransparency >= 1.0) + { + // no geometry, done + return; + } + + const basegfx::BColor aPolygonColor( + maBColorModifierStack.getModifiedColor(rSource.getBColor())); + + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawTransparent(maCurrentTransformation, rSource.getB2DPolyPolygon(), + fTransparency); +} + +bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect( + const drawinglayer::primitive2d::PolygonHairlinePrimitive2D& rSource, double fTransparency) +{ + const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon()); - // prepare output directly to pixels - mpOutputDevice->Push(PushFlags::MAPMODE); - mpOutputDevice->SetMapMode(); + if (!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) + { + // no geometry, done + return true; + } - // react on AntiAliasing settings - if(getOptionsDrawinglayer().IsAntiAliasing()) - { - mpOutputDevice->SetAntialiasing( - m_pImpl->m_nOrigAntiAliasing | AntialiasingFlags::EnableB2dDraw); - } - else - { - mpOutputDevice->SetAntialiasing( - m_pImpl->m_nOrigAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); - } - } + const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rSource.getBColor())); - VclPixelProcessor2D::~VclPixelProcessor2D() - { - // restore MapMode - mpOutputDevice->Pop(); + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(Color(aLineColor)); + //aLocalPolygon.transform(maCurrentTransformation); - // restore AntiAliasing - mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing); - } + // try drawing; if it did not work, use standard fallback + return mpOutputDevice->DrawPolyLineDirect(maCurrentTransformation, rLocalPolygon, 0.0, + fTransparency); +} - void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect(const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D& rSource, double fTransparency) +bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect( + const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency) +{ + const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon()); + + if (!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) + { + // no geometry, done + return true; + } + + if (basegfx::B2DLineJoin::NONE == rSource.getLineAttribute().getLineJoin() + && css::drawing::LineCap_BUTT != rSource.getLineAttribute().getLineCap()) + { + // better use decompose to get that combination done for now, see discussion + // at https://bugs.documentfoundation.org/show_bug.cgi?id=130478#c17 and ff + return false; + } + + // MM01: Radically change here - no dismantle/applyLineDashing, + // let that happen low-level at DrawPolyLineDirect implementations + // to open up for buffering and evtl. direct draw with sys-dep + // graphic systems. Check for stroke is in use + const bool bStrokeAttributeNotUsed(rSource.getStrokeAttribute().isDefault() + || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen()); + + const basegfx::BColor aLineColor( + maBColorModifierStack.getModifiedColor(rSource.getLineAttribute().getColor())); + + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(Color(aLineColor)); + + // MM01 draw direct, hand over dash data if available + return mpOutputDevice->DrawPolyLineDirect( + maCurrentTransformation, rLocalPolygon, + // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline) + rSource.getLineAttribute().getWidth(), fTransparency, + bStrokeAttributeNotUsed ? nullptr : &rSource.getStrokeAttribute().getDotDashArray(), + rSource.getLineAttribute().getLineJoin(), rSource.getLineAttribute().getLineCap(), + rSource.getLineAttribute().getMiterMinimumAngle() + /* false bBypassAACheck, default*/); +} + +void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) +{ + switch (rCandidate.getPrimitive2DID()) + { + case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D: { - if(!rSource.getB2DPolyPolygon().count() || fTransparency < 0.0 || fTransparency >= 1.0) - { - // no geometry, done - return; - } - - const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rSource.getBColor())); - - mpOutputDevice->SetFillColor(Color(aPolygonColor)); - mpOutputDevice->SetLineColor(); - mpOutputDevice->DrawTransparent( - maCurrentTransformation, - rSource.getB2DPolyPolygon(), - fTransparency); + processWrongSpellPrimitive2D( + static_cast<const primitive2d::WrongSpellPrimitive2D&>(rCandidate)); + break; } - - bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect(const drawinglayer::primitive2d::PolygonHairlinePrimitive2D& rSource, double fTransparency) + case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: { - const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon()); - - if(!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) - { - // no geometry, done - return true; - } - - const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rSource.getBColor())); - - mpOutputDevice->SetFillColor(); - mpOutputDevice->SetLineColor(Color(aLineColor)); - //aLocalPolygon.transform(maCurrentTransformation); - - // try drawing; if it did not work, use standard fallback - return mpOutputDevice->DrawPolyLineDirect( - maCurrentTransformation, - rLocalPolygon, - 0.0, - fTransparency); + processTextSimplePortionPrimitive2D( + static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); + break; } - - bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency) + case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: { - const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon()); - - if(!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) - { - // no geometry, done - return true; - } - - if (basegfx::B2DLineJoin::NONE == rSource.getLineAttribute().getLineJoin() - && css::drawing::LineCap_BUTT != rSource.getLineAttribute().getLineCap()) - { - // better use decompose to get that combination done for now, see discussion - // at https://bugs.documentfoundation.org/show_bug.cgi?id=130478#c17 and ff - return false; - } - - // MM01: Radically change here - no dismantle/applyLineDashing, - // let that happen low-level at DrawPolyLineDirect implementations - // to open up for buffering and evtl. direct draw with sys-dep - // graphic systems. Check for stroke is in use - const bool bStrokeAttributeNotUsed(rSource.getStrokeAttribute().isDefault() - || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen()); - - const basegfx::BColor aLineColor( - maBColorModifierStack.getModifiedColor( - rSource.getLineAttribute().getColor())); - - mpOutputDevice->SetFillColor(); - mpOutputDevice->SetLineColor(Color(aLineColor)); - - // MM01 draw direct, hand over dash data if available - return mpOutputDevice->DrawPolyLineDirect( - maCurrentTransformation, - rLocalPolygon, - // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline) - rSource.getLineAttribute().getWidth(), - fTransparency, - bStrokeAttributeNotUsed ? nullptr : &rSource.getStrokeAttribute().getDotDashArray(), - rSource.getLineAttribute().getLineJoin(), - rSource.getLineAttribute().getLineCap(), - rSource.getLineAttribute().getMiterMinimumAngle() - /* false bBypassAACheck, default*/); - } - - void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) - { - switch(rCandidate.getPrimitive2DID()) - { - case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D : - { - processWrongSpellPrimitive2D(static_cast<const primitive2d::WrongSpellPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D : - { - processTextSimplePortionPrimitive2D(static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D : - { - processTextDecoratedPortionPrimitive2D(static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D : - { - processPolygonHairlinePrimitive2D(static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D : - { - // direct draw of transformed BitmapEx primitive - processBitmapPrimitive2D(static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D : - { - // direct draw of fillBitmapPrimitive - RenderFillGraphicPrimitive2D(static_cast< const primitive2d::FillGraphicPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D : - { - processPolyPolygonGradientPrimitive2D(static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D : - { - // direct draw of bitmap - RenderPolyPolygonGraphicPrimitive2D(static_cast< const primitive2d::PolyPolygonGraphicPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D : - { - processPolyPolygonColorPrimitive2D(static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D : - { - processMetaFilePrimitive2D(rCandidate); - break; - } - case PRIMITIVE2D_ID_MASKPRIMITIVE2D : - { - // mask group. - RenderMaskPrimitive2DPixel(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D : - { - // modified color group. Force output to unified color. - RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D : - { - processUnifiedTransparencePrimitive2D(static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D : - { - // sub-transparence group. Draw to VDev first. - RenderTransparencePrimitive2D(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D : - { - // transform group. - RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D : - { - // new XDrawPage for ViewInformation2D - RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D : - { - // marker array - RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D : - { - // point array - RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D : - { - processControlPrimitive2D(static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: - { - processPolygonStrokePrimitive2D(static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D : - { - processFillHatchPrimitive2D(static_cast<const primitive2d::FillHatchPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D : - { - processBackgroundColorPrimitive2D(static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D : - { - // #i97628# - // This primitive means that the content is derived from an active text edit, - // not from model data itself. Some renderers need to suppress this content, e.g. - // the pixel renderer used for displaying the edit view (like this one). It's - // not to be suppressed by the MetaFile renderers, so that the edited text is - // part of the MetaFile, e.g. needed for presentation previews. - // Action: Ignore here, do nothing. - break; - } - case PRIMITIVE2D_ID_INVERTPRIMITIVE2D : - { - processInvertPrimitive2D(rCandidate); - break; - } - case PRIMITIVE2D_ID_EPSPRIMITIVE2D : - { - RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D: - { - RenderSvgLinearAtomPrimitive2D(static_cast< const primitive2d::SvgLinearAtomPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D: - { - RenderSvgRadialAtomPrimitive2D(static_cast< const primitive2d::SvgRadialAtomPrimitive2D& >(rCandidate)); - break; - } - case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D: - { - processBorderLinePrimitive2D(static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate)); - break; - } - case PRIMITIVE2D_ID_GLOWPRIMITIVE2D: - { - processGlowPrimitive2D(static_cast<const drawinglayer::primitive2d::GlowPrimitive2D&>(rCandidate)); - break; - } - default : - { - SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(rCandidate.getPrimitive2DID())); - // process recursively - process(rCandidate); - break; - } - } + processTextDecoratedPortionPrimitive2D( + static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processWrongSpellPrimitive2D(const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive) + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: { - if(!renderWrongSpellPrimitive2D( - rWrongSpellPrimitive, - *mpOutputDevice, - maCurrentTransformation, - maBColorModifierStack)) - { - // fallback to decomposition (MetaFile) - process(rWrongSpellPrimitive); - } + processPolygonHairlinePrimitive2D( + static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processTextSimplePortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rCandidate) + case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: { - // Adapt evtl. used special DrawMode - const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); - adaptTextToFillDrawMode(); - - if(getOptionsDrawinglayer().IsRenderSimpleTextDirect()) - { - RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); - } - else - { - process(rCandidate); - } - - // restore DrawMode - mpOutputDevice->SetDrawMode(nOriginalDrawMode); + // direct draw of transformed BitmapEx primitive + processBitmapPrimitive2D( + static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rCandidate) + case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D: { - // Adapt evtl. used special DrawMode - const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); - adaptTextToFillDrawMode(); - - if(getOptionsDrawinglayer().IsRenderDecoratedTextDirect()) - { - RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); - } - else - { - process(rCandidate); - } - - // restore DrawMode - mpOutputDevice->SetDrawMode(nOriginalDrawMode); + // direct draw of fillBitmapPrimitive + RenderFillGraphicPrimitive2D( + static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D) + case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D: { - if(tryDrawPolygonHairlinePrimitive2DDirect(rPolygonHairlinePrimitive2D, 0.0)) - { - return; - } - - // direct draw of hairline - RenderPolygonHairlinePrimitive2D(rPolygonHairlinePrimitive2D, true); + processPolyPolygonGradientPrimitive2D( + static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate) + case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D: { - // check if graphic content is inside discrete local ViewPort - const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport()); - const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rBitmapCandidate.getTransform()); - - if(!rDiscreteViewPort.isEmpty()) - { - basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); - - aUnitRange.transform(aLocalTransform); - - if(!aUnitRange.overlaps(rDiscreteViewPort)) - { - // content is outside discrete local ViewPort - return; - } - } - - RenderBitmapPrimitive2D(rBitmapCandidate); + // direct draw of bitmap + RenderPolyPolygonGraphicPrimitive2D( + static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D(const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate) + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: { - // direct draw of gradient - const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); - basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor())); - basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor())); - basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); - - if(!aLocalPolyPolygon.count()) - return; - - aLocalPolyPolygon.transform(maCurrentTransformation); - - if(aStartColor == aEndColor) - { - // no gradient at all, draw as polygon in AA and non-AA case - mpOutputDevice->SetLineColor(); - mpOutputDevice->SetFillColor(Color(aStartColor)); - mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); - } - else - { - // use the primitive decomposition of the metafile - process(rPolygonCandidate); - } + processPolyPolygonColorPrimitive2D( + static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D) + case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D: { - // try to use directly - basegfx::B2DPolyPolygon aLocalPolyPolygon; - - tryDrawPolyPolygonColorPrimitive2DDirect(rPolyPolygonColorPrimitive2D, 0.0); - // okay, done. In this case no gaps should have to be repaired, too - - // when AA is on and this filled polygons are the result of stroked line geometry, - // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons - // Caution: This is needed in both cases (!) - if(!(mnPolygonStrokePrimitive2D - && getOptionsDrawinglayer().IsAntiAliasing() - && (mpOutputDevice->GetAntialiasing() & AntialiasingFlags::EnableB2dDraw))) - return; - - const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolyPolygonColorPrimitive2D.getBColor())); - sal_uInt32 nCount(aLocalPolyPolygon.count()); - - if(!nCount) - { - aLocalPolyPolygon = rPolyPolygonColorPrimitive2D.getB2DPolyPolygon(); - aLocalPolyPolygon.transform(maCurrentTransformation); - nCount = aLocalPolyPolygon.count(); - } - - mpOutputDevice->SetFillColor(); - mpOutputDevice->SetLineColor(Color(aPolygonColor)); - - for(sal_uInt32 a(0); a < nCount; a++) - { - mpOutputDevice->DrawPolyLine(aLocalPolyPolygon.getB2DPolygon(a), 0.0); - } + processMetaFilePrimitive2D(rCandidate); + break; } - - void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate) + case PRIMITIVE2D_ID_MASKPRIMITIVE2D: { - // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case, - // use the faster OutputDevice::DrawTransparent method - const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren(); - - if(rContent.empty()) - return; - - if(0.0 == rUniTransparenceCandidate.getTransparence()) - { - // not transparent at all, use content - process(rUniTransparenceCandidate.getChildren()); - } - else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0) - { - bool bDrawTransparentUsed(false); - - if(1 == rContent.size()) - { - const primitive2d::Primitive2DReference xReference(rContent[0]); - const primitive2d::BasePrimitive2D* pBasePrimitive = dynamic_cast< const primitive2d::BasePrimitive2D* >(xReference.get()); - - if(pBasePrimitive) - { - switch(pBasePrimitive->getPrimitive2DID()) - { - case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: - { - // single transparent tools::PolyPolygon identified, use directly - const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = static_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(pBasePrimitive); - SAL_WARN_IF(!pPoPoColor, "drawinglayer", "OOps, PrimitiveID and PrimitiveType do not match (!)"); - bDrawTransparentUsed = true; - tryDrawPolyPolygonColorPrimitive2DDirect(*pPoPoColor, rUniTransparenceCandidate.getTransparence()); - break; - } - case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: - { - // single transparent PolygonHairlinePrimitive2D identified, use directly - const primitive2d::PolygonHairlinePrimitive2D* pPoHair = static_cast< const primitive2d::PolygonHairlinePrimitive2D* >(pBasePrimitive); - SAL_WARN_IF(!pPoHair, "drawinglayer", "OOps, PrimitiveID and PrimitiveType do not match (!)"); - - // do no tallow by default - problem is that self-overlapping parts of this geometry will - // not be in an all-same transparency but will already alpha-cover themselves with blending. - // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its - // content to be uniformly transparent. - // For hairline the effect is pretty minimal, but still not correct. - bDrawTransparentUsed = false; - break; - } - case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: - { - // single transparent PolygonStrokePrimitive2D identified, use directly - const primitive2d::PolygonStrokePrimitive2D* pPoStroke = static_cast< const primitive2d::PolygonStrokePrimitive2D* >(pBasePrimitive); - SAL_WARN_IF(!pPoStroke, "drawinglayer", "OOps, PrimitiveID and PrimitiveType do not match (!)"); - - // do no tallow by default - problem is that self-overlapping parts of this geometry will - // not be in an all-same transparency but will already alpha-cover themselves with blending. - // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its - // content to be uniformly transparent. - // To check, activate and draw a wide transparent self-crossing line/curve - bDrawTransparentUsed = false; - break; - } - default: - SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(rUniTransparenceCandidate.getPrimitive2DID())); - break; - } - } - } - - if(!bDrawTransparentUsed) - { - // unified sub-transparence. Draw to VDev first. - RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate); - } - } + // mask group. + RenderMaskPrimitive2DPixel( + static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processControlPrimitive2D(const primitive2d::ControlPrimitive2D& rControlPrimitive) + case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: { - // control primitive - const uno::Reference< awt::XControl >& rXControl(rControlPrimitive.getXControl()); - - try - { - // remember old graphics and create new - uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY_THROW); - const uno::Reference< awt::XGraphics > xOriginalGraphics(xControlView->getGraphics()); - const uno::Reference< awt::XGraphics > xNewGraphics(mpOutputDevice->CreateUnoGraphics()); - - if(xNewGraphics.is()) - { - // link graphics and view - xControlView->setGraphics(xNewGraphics); - - // get position - const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation * rControlPrimitive.getTransform()); - const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); - - // find out if the control is already visualized as a VCL-ChildWindow. If yes, - // it does not need to be painted at all. - uno::Reference< awt::XWindow2 > xControlWindow(rXControl, uno::UNO_QUERY_THROW); - const bool bControlIsVisibleAsChildWindow(rXControl->getPeer().is() && xControlWindow->isVisible()); - - if(!bControlIsVisibleAsChildWindow) - { - // draw it. Do not forget to use the evtl. offsetted origin of the target device, - // e.g. when used with mask/transparence buffer device - const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); - xControlView->draw( - aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), - aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); - } - - // restore original graphics - xControlView->setGraphics(xOriginalGraphics); - } - } - catch(const uno::Exception&) - { - // #i116763# removing since there is a good alternative when the xControlView - // is not found and it is allowed to happen - // DBG_UNHANDLED_EXCEPTION(); - - // process recursively and use the decomposition as Bitmap - process(rControlPrimitive); - } + // modified color group. Force output to unified color. + RenderModifiedColorPrimitive2D( + static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate)); + break; } - - void VclPixelProcessor2D::processPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D) + case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D: { - // try to use directly - if(tryDrawPolygonStrokePrimitive2DDirect(rPolygonStrokePrimitive2D, 0.0)) - { - return; - } + processUnifiedTransparencePrimitive2D( + static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D: + { + // sub-transparence group. Draw to VDev first. + RenderTransparencePrimitive2D( + static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: + { + // transform group. + RenderTransformPrimitive2D( + static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D: + { + // new XDrawPage for ViewInformation2D + RenderPagePreviewPrimitive2D( + static_cast<const primitive2d::PagePreviewPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D: + { + // marker array + RenderMarkerArrayPrimitive2D( + static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: + { + // point array + RenderPointArrayPrimitive2D( + static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D: + { + processControlPrimitive2D( + static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: + { + processPolygonStrokePrimitive2D( + static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D: + { + processFillHatchPrimitive2D( + static_cast<const primitive2d::FillHatchPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D: + { + processBackgroundColorPrimitive2D( + static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D: + { + // #i97628# + // This primitive means that the content is derived from an active text edit, + // not from model data itself. Some renderers need to suppress this content, e.g. + // the pixel renderer used for displaying the edit view (like this one). It's + // not to be suppressed by the MetaFile renderers, so that the edited text is + // part of the MetaFile, e.g. needed for presentation previews. + // Action: Ignore here, do nothing. + break; + } + case PRIMITIVE2D_ID_INVERTPRIMITIVE2D: + { + processInvertPrimitive2D(rCandidate); + break; + } + case PRIMITIVE2D_ID_EPSPRIMITIVE2D: + { + RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D: + { + RenderSvgLinearAtomPrimitive2D( + static_cast<const primitive2d::SvgLinearAtomPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D: + { + RenderSvgRadialAtomPrimitive2D( + static_cast<const primitive2d::SvgRadialAtomPrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D: + { + processBorderLinePrimitive2D( + static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate)); + break; + } + case PRIMITIVE2D_ID_GLOWPRIMITIVE2D: + { + processGlowPrimitive2D( + static_cast<const drawinglayer::primitive2d::GlowPrimitive2D&>(rCandidate)); + break; + } + default: + { + SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString( + rCandidate.getPrimitive2DID())); + // process recursively + process(rCandidate); + break; + } + } +} - // the stroke primitive may be decomposed to filled polygons. To keep - // evtl. set DrawModes aka DrawModeFlags::BlackLine, DrawModeFlags::GrayLine, - // DrawModeFlags::GhostedLine, DrawModeFlags::WhiteLine or DrawModeFlags::SettingsLine - // working, these need to be copied to the corresponding fill modes - const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); - adaptLineToFillDrawMode(); +void VclPixelProcessor2D::processWrongSpellPrimitive2D( + const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive) +{ + if (!renderWrongSpellPrimitive2D(rWrongSpellPrimitive, *mpOutputDevice, maCurrentTransformation, + maBColorModifierStack)) + { + // fallback to decomposition (MetaFile) + process(rWrongSpellPrimitive); + } +} + +void VclPixelProcessor2D::processTextSimplePortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rCandidate) +{ + // Adapt evtl. used special DrawMode + const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptTextToFillDrawMode(); + + if (getOptionsDrawinglayer().IsRenderSimpleTextDirect()) + { + RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); + } + else + { + process(rCandidate); + } + + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); +} + +void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D( + const primitive2d::TextSimplePortionPrimitive2D& rCandidate) +{ + // Adapt evtl. used special DrawMode + const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptTextToFillDrawMode(); + + if (getOptionsDrawinglayer().IsRenderDecoratedTextDirect()) + { + RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); + } + else + { + process(rCandidate); + } + + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); +} + +void VclPixelProcessor2D::processPolygonHairlinePrimitive2D( + const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D) +{ + if (tryDrawPolygonHairlinePrimitive2DDirect(rPolygonHairlinePrimitive2D, 0.0)) + { + return; + } - // polygon stroke primitive + // direct draw of hairline + RenderPolygonHairlinePrimitive2D(rPolygonHairlinePrimitive2D, true); +} - // Lines with 1 and 2 pixel width without AA need special treatment since their visualization - // as filled polygons is geometrically correct but looks wrong since polygon filling avoids - // the right and bottom pixels. The used method evaluates that and takes the correct action, - // including calling recursively with decomposition if line is wide enough - RenderPolygonStrokePrimitive2D(rPolygonStrokePrimitive2D); +void VclPixelProcessor2D::processBitmapPrimitive2D( + const primitive2d::BitmapPrimitive2D& rBitmapCandidate) +{ + // check if graphic content is inside discrete local ViewPort + const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport()); + const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation + * rBitmapCandidate.getTransform()); - // restore DrawMode - mpOutputDevice->SetDrawMode(nOriginalDrawMode); - } + if (!rDiscreteViewPort.isEmpty()) + { + basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); - void VclPixelProcessor2D::processFillHatchPrimitive2D(const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive) - { - if(getOptionsDrawinglayer().IsAntiAliasing()) - { - // if AA is used (or ignore smoothing is on), there is no need to smooth - // hatch painting, use decomposition - process(rFillHatchPrimitive); - } - else - { - // without AA, use VCL to draw the hatch. It snaps hatch distances to the next pixel - // and forces hatch distance to be >= 3 pixels to make the hatch display look smoother. - // This is wrong in principle, but looks nicer. This could also be done here directly - // without VCL usage if needed - const attribute::FillHatchAttribute& rFillHatchAttributes = rFillHatchPrimitive.getFillHatch(); - - // create hatch polygon in range size and discrete coordinates - basegfx::B2DRange aHatchRange(rFillHatchPrimitive.getOutputRange()); - aHatchRange.transform(maCurrentTransformation); - const basegfx::B2DPolygon aHatchPolygon(basegfx::utils::createPolygonFromRect(aHatchRange)); - - if(rFillHatchAttributes.isFillBackground()) - { - // #i111846# background fill is active; draw fill polygon - const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); + aUnitRange.transform(aLocalTransform); - mpOutputDevice->SetFillColor(Color(aPolygonColor)); - mpOutputDevice->SetLineColor(); - mpOutputDevice->DrawPolygon(aHatchPolygon); - } + if (!aUnitRange.overlaps(rDiscreteViewPort)) + { + // content is outside discrete local ViewPort + return; + } + } - // set hatch line color - const basegfx::BColor aHatchColor(maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); - mpOutputDevice->SetFillColor(); - mpOutputDevice->SetLineColor(Color(aHatchColor)); + RenderBitmapPrimitive2D(rBitmapCandidate); +} - // get hatch style - HatchStyle eHatchStyle(HatchStyle::Single); +void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D( + const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate) +{ + // direct draw of gradient + const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); + basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor())); + basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor())); + basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); + + if (!aLocalPolyPolygon.count()) + return; + + aLocalPolyPolygon.transform(maCurrentTransformation); + + if (aStartColor == aEndColor) + { + // no gradient at all, draw as polygon in AA and non-AA case + mpOutputDevice->SetLineColor(); + mpOutputDevice->SetFillColor(Color(aStartColor)); + mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); + } + else + { + // use the primitive decomposition of the metafile + process(rPolygonCandidate); + } +} + +void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D( + const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D) +{ + // try to use directly + basegfx::B2DPolyPolygon aLocalPolyPolygon; + + tryDrawPolyPolygonColorPrimitive2DDirect(rPolyPolygonColorPrimitive2D, 0.0); + // okay, done. In this case no gaps should have to be repaired, too + + // when AA is on and this filled polygons are the result of stroked line geometry, + // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons + // Caution: This is needed in both cases (!) + if (!(mnPolygonStrokePrimitive2D && getOptionsDrawinglayer().IsAntiAliasing() + && (mpOutputDevice->GetAntialiasing() & AntialiasingFlags::EnableB2dDraw))) + return; + + const basegfx::BColor aPolygonColor( + maBColorModifierStack.getModifiedColor(rPolyPolygonColorPrimitive2D.getBColor())); + sal_uInt32 nCount(aLocalPolyPolygon.count()); + + if (!nCount) + { + aLocalPolyPolygon = rPolyPolygonColorPrimitive2D.getB2DPolyPolygon(); + aLocalPolyPolygon.transform(maCurrentTransformation); + nCount = aLocalPolyPolygon.count(); + } + + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(Color(aPolygonColor)); + + for (sal_uInt32 a(0); a < nCount; a++) + { + mpOutputDevice->DrawPolyLine(aLocalPolyPolygon.getB2DPolygon(a), 0.0); + } +} + +void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D( + const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate) +{ + // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case, + // use the faster OutputDevice::DrawTransparent method + const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren(); + + if (rContent.empty()) + return; + + if (0.0 == rUniTransparenceCandidate.getTransparence()) + { + // not transparent at all, use content + process(rUniTransparenceCandidate.getChildren()); + } + else if (rUniTransparenceCandidate.getTransparence() > 0.0 + && rUniTransparenceCandidate.getTransparence() < 1.0) + { + bool bDrawTransparentUsed(false); + + if (1 == rContent.size()) + { + const primitive2d::Primitive2DReference xReference(rContent[0]); + const primitive2d::BasePrimitive2D* pBasePrimitive + = dynamic_cast<const primitive2d::BasePrimitive2D*>(xReference.get()); - switch(rFillHatchAttributes.getStyle()) + if (pBasePrimitive) + { + switch (pBasePrimitive->getPrimitive2DID()) { - default : // HatchStyle::Single + case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: { + // single transparent tools::PolyPolygon identified, use directly + const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor + = static_cast<const primitive2d::PolyPolygonColorPrimitive2D*>( + pBasePrimitive); + SAL_WARN_IF(!pPoPoColor, "drawinglayer", + "OOps, PrimitiveID and PrimitiveType do not match (!)"); + bDrawTransparentUsed = true; + tryDrawPolyPolygonColorPrimitive2DDirect( + *pPoPoColor, rUniTransparenceCandidate.getTransparence()); break; } - case attribute::HatchStyle::Double : + case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: { - eHatchStyle = HatchStyle::Double; + // single transparent PolygonHairlinePrimitive2D identified, use directly + const primitive2d::PolygonHairlinePrimitive2D* pPoHair + = static_cast<const primitive2d::PolygonHairlinePrimitive2D*>( + pBasePrimitive); + SAL_WARN_IF(!pPoHair, "drawinglayer", + "OOps, PrimitiveID and PrimitiveType do not match (!)"); + + // do no tallow by default - problem is that self-overlapping parts of this geometry will + // not be in an all-same transparency but will already alpha-cover themselves with blending. + // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its + // content to be uniformly transparent. + // For hairline the effect is pretty minimal, but still not correct. + bDrawTransparentUsed = false; break; } - case attribute::HatchStyle::Triple : + case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: { - eHatchStyle = HatchStyle::Triple; + // single transparent PolygonStrokePrimitive2D identified, use directly + const primitive2d::PolygonStrokePrimitive2D* pPoStroke + = static_cast<const primitive2d::PolygonStrokePrimitive2D*>( + pBasePrimitive); + SAL_WARN_IF(!pPoStroke, "drawinglayer", + "OOps, PrimitiveID and PrimitiveType do not match (!)"); + + // do no tallow by default - problem is that self-overlapping parts of this geometry will + // not be in an all-same transparency but will already alpha-cover themselves with blending. + // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its + // content to be uniformly transparent. + // To check, activate and draw a wide transparent self-crossing line/curve + bDrawTransparentUsed = false; break; } + default: + SAL_INFO("drawinglayer", + "default case for " << drawinglayer::primitive2d::idToString( + rUniTransparenceCandidate.getPrimitive2DID())); + break; } - - // create hatch - const basegfx::B2DVector aDiscreteDistance(maCurrentTransformation * basegfx::B2DVector(rFillHatchAttributes.getDistance(), 0.0)); - const sal_uInt32 nDistance(basegfx::fround(aDiscreteDistance.getLength())); - const sal_uInt16 nAngle10(static_cast<sal_uInt16>(basegfx::fround(rFillHatchAttributes.getAngle() / F_PI1800))); - ::Hatch aVCLHatch(eHatchStyle, Color(rFillHatchAttributes.getColor()), nDistance, nAngle10); - - // draw hatch using VCL - mpOutputDevice->DrawHatch(::tools::PolyPolygon(::tools::Polygon(aHatchPolygon)), aVCLHatch); } } - void VclPixelProcessor2D::processBackgroundColorPrimitive2D(const primitive2d::BackgroundColorPrimitive2D& rPrimitive) + if (!bDrawTransparentUsed) { - // #i98404# Handle directly, especially when AA is active - const AntialiasingFlags nOriginalAA(mpOutputDevice->GetAntialiasing()); + // unified sub-transparence. Draw to VDev first. + RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate); + } + } +} - // switch AA off in all cases - mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~AntialiasingFlags::EnableB2dDraw); +void VclPixelProcessor2D::processControlPrimitive2D( + const primitive2d::ControlPrimitive2D& rControlPrimitive) +{ + // control primitive + const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); - // create color for fill - const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPrimitive.getBColor())); - Color aFillColor(aPolygonColor); - aFillColor.SetTransparency(sal_uInt8((rPrimitive.getTransparency() * 255.0) + 0.5)); - mpOutputDevice->SetFillColor(aFillColor); - mpOutputDevice->SetLineColor(); + try + { + // remember old graphics and create new + uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW); + const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics()); + const uno::Reference<awt::XGraphics> xNewGraphics(mpOutputDevice->CreateUnoGraphics()); - // create rectangle for fill - const basegfx::B2DRange& aViewport(getViewInformation2D().getDiscreteViewport()); - const ::tools::Rectangle aRectangle( - static_cast<sal_Int32>(floor(aViewport.getMinX())), static_cast<sal_Int32>(floor(aViewport.getMinY())), - static_cast<sal_Int32>(ceil(aViewport.getMaxX())), static_cast<sal_Int32>(ceil(aViewport.getMaxY()))); - mpOutputDevice->DrawRect(aRectangle); - - // restore AA setting - mpOutputDevice->SetAntialiasing(nOriginalAA); - } - - void VclPixelProcessor2D::processBorderLinePrimitive2D(const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder) - { - // Process recursively, but switch off AntiAliasing for - // horizontal/vertical lines (*not* diagonal lines). - // Checked using AntialiasingFlags::PixelSnapHairline instead, - // but with AntiAliasing on the display really is too 'ghosty' when - // using fine stroking. Correct, but 'ghosty'. - - // It has shown that there are quite some problems here: - // - vcl OutDev renderer methods still use fallbacks to paint - // multiple single lines between discrete sizes of < 3.5 what - // looks bad and does not match - // - mix of filled Polygons and Lines is bad when AA switched off - // - Alignment of AA with non-AA may be bad in diverse different - // renderers - // - // Due to these reasons I change the strategy: Always draw AAed, but - // allow fallback to test/check and if needed. The normal case - // where BorderLines will be system-dependently snapped to have at - // least a single discrete width per partial line (there may be up to - // three) works well nowadays due to most renderers moving the AA stuff - // by 0.5 pixels (discrete units) to match well with the non-AAed parts. - // - // Env-Switch for steering this, default is off. - // Enable by setting at all (and to something) - static const char* pSwitchOffAntiAliasingForHorVerBorderlines(getenv("SAL_SWITCH_OFF_ANTIALIASING_FOR_HOR_VER_BORTDERLINES")); - static bool bSwitchOffAntiAliasingForHorVerBorderlines(nullptr != pSwitchOffAntiAliasingForHorVerBorderlines); - - if (bSwitchOffAntiAliasingForHorVerBorderlines && rBorder.isHorizontalOrVertical(getViewInformation2D())) - { - AntialiasingFlags nAntiAliasing = mpOutputDevice->GetAntialiasing(); - mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); - process(rBorder); - mpOutputDevice->SetAntialiasing(nAntiAliasing); - } - else + if (xNewGraphics.is()) + { + // link graphics and view + xControlView->setGraphics(xNewGraphics); + + // get position + const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation + * rControlPrimitive.getTransform()); + const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); + + // find out if the control is already visualized as a VCL-ChildWindow. If yes, + // it does not need to be painted at all. + uno::Reference<awt::XWindow2> xControlWindow(rXControl, uno::UNO_QUERY_THROW); + const bool bControlIsVisibleAsChildWindow(rXControl->getPeer().is() + && xControlWindow->isVisible()); + + if (!bControlIsVisibleAsChildWindow) { - process(rBorder); + // draw it. Do not forget to use the evtl. offsetted origin of the target device, + // e.g. when used with mask/transparence buffer device + const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); + xControlView->draw(aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), + aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); } - } - void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) + // restore original graphics + xControlView->setGraphics(xOriginalGraphics); + } + } + catch (const uno::Exception&) + { + // #i116763# removing since there is a good alternative when the xControlView + // is not found and it is allowed to happen + // DBG_UNHANDLED_EXCEPTION(); + + // process recursively and use the decomposition as Bitmap + process(rControlPrimitive); + } +} + +void VclPixelProcessor2D::processPolygonStrokePrimitive2D( + const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D) +{ + // try to use directly + if (tryDrawPolygonStrokePrimitive2DDirect(rPolygonStrokePrimitive2D, 0.0)) + { + return; + } + + // the stroke primitive may be decomposed to filled polygons. To keep + // evtl. set DrawModes aka DrawModeFlags::BlackLine, DrawModeFlags::GrayLine, + // DrawModeFlags::GhostedLine, DrawModeFlags::WhiteLine or DrawModeFlags::SettingsLine + // working, these need to be copied to the corresponding fill modes + const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); + adaptLineToFillDrawMode(); + + // polygon stroke primitive + + // Lines with 1 and 2 pixel width without AA need special treatment since their visualization + // as filled polygons is geometrically correct but looks wrong since polygon filling avoids + // the right and bottom pixels. The used method evaluates that and takes the correct action, + // including calling recursively with decomposition if line is wide enough + RenderPolygonStrokePrimitive2D(rPolygonStrokePrimitive2D); + + // restore DrawMode + mpOutputDevice->SetDrawMode(nOriginalDrawMode); +} + +void VclPixelProcessor2D::processFillHatchPrimitive2D( + const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive) +{ + if (getOptionsDrawinglayer().IsAntiAliasing()) + { + // if AA is used (or ignore smoothing is on), there is no need to smooth + // hatch painting, use decomposition + process(rFillHatchPrimitive); + } + else + { + // without AA, use VCL to draw the hatch. It snaps hatch distances to the next pixel + // and forces hatch distance to be >= 3 pixels to make the hatch display look smoother. + // This is wrong in principle, but looks nicer. This could also be done here directly + // without VCL usage if needed + const attribute::FillHatchAttribute& rFillHatchAttributes + = rFillHatchPrimitive.getFillHatch(); + + // create hatch polygon in range size and discrete coordinates + basegfx::B2DRange aHatchRange(rFillHatchPrimitive.getOutputRange()); + aHatchRange.transform(maCurrentTransformation); + const basegfx::B2DPolygon aHatchPolygon(basegfx::utils::createPolygonFromRect(aHatchRange)); + + if (rFillHatchAttributes.isFillBackground()) { - // invert primitive (currently only used for HighContrast fallback for selection in SW and SC). - // (Not true, also used at least for the drawing of dragged column and row boundaries in SC.) - // Set OutDev to XOR and switch AA off (XOR does not work with AA) - mpOutputDevice->Push(); - mpOutputDevice->SetRasterOp( RasterOp::Xor ); - const AntialiasingFlags nAntiAliasing(mpOutputDevice->GetAntialiasing()); - mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); - - // process content recursively - process(rCandidate); + // #i111846# background fill is active; draw fill polygon + const basegfx::BColor aPolygonColor( + maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); - // restore OutDev - mpOutputDevice->Pop(); - mpOutputDevice->SetAntialiasing(nAntiAliasing); + mpOutputDevice->SetFillColor(Color(aPolygonColor)); + mpOutputDevice->SetLineColor(); + mpOutputDevice->DrawPolygon(aHatchPolygon); } - void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) - { - // #i98289# - const bool bForceLineSnap(getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()); - const AntialiasingFlags nOldAntiAliase(mpOutputDevice->GetAntialiasing()); + // set hatch line color + const basegfx::BColor aHatchColor( + maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetLineColor(Color(aHatchColor)); + + // get hatch style + HatchStyle eHatchStyle(HatchStyle::Single); - if(bForceLineSnap) + switch (rFillHatchAttributes.getStyle()) + { + default: // HatchStyle::Single { - mpOutputDevice->SetAntialiasing(nOldAntiAliase | AntialiasingFlags::PixelSnapHairline); + break; } - - process(rCandidate); - - if(bForceLineSnap) + case attribute::HatchStyle::Double: { - mpOutputDevice->SetAntialiasing(nOldAntiAliase); + eHatchStyle = HatchStyle::Double; + break; } - } - - void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate) - { - basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); - aRange.transform(maCurrentTransformation); - aRange.grow(10.0); - impBufferDevice aBufferDevice(*mpOutputDevice, aRange); - if (aBufferDevice.isVisible()) + case attribute::HatchStyle::Triple: { - // remember last OutDev and set to content - OutputDevice* pLastOutputDevice = mpOutputDevice; - mpOutputDevice = &aBufferDevice.getTransparence(); - // paint content to virtual device - mpOutputDevice->Erase(); - process(rCandidate); - - // obtain result as a bitmap - auto bitmap = mpOutputDevice->GetBitmapEx(Point(aRange.getMinX(), aRange.getMinY()), Size(aRange.getWidth(), aRange.getHeight())); - constexpr double nRadius = 5.0; - bitmap.Scale(Size(aRange.getWidth()-nRadius, aRange.getHeight()-nRadius)); - // use bitmap later as mask - auto mask = bitmap.GetBitmap(); - - mpOutputDevice = &aBufferDevice.getContent(); - process(rCandidate); - bitmap = mpOutputDevice->GetBitmapEx(Point(aRange.getMinX(), aRange.getMinY()), Size(aRange.getWidth(), aRange.getHeight())); - bitmap.Scale(Size(aRange.getWidth()-nRadius, aRange.getHeight()-nRadius)); - - // calculate blurry effect - BitmapFilterStackBlur glowFilter(nRadius); - BitmapFilter::Filter(bitmap, glowFilter); - // back to old OutDev - mpOutputDevice = pLastOutputDevice; - mpOutputDevice->DrawBitmapEx(Point(aRange.getMinX()-nRadius/2, aRange.getMinY()-nRadius/2), BitmapEx(bitmap.GetBitmap(), mask)); - - // paint result - //aBufferDevice.paint(); + eHatchStyle = HatchStyle::Triple; + break; } - else - SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); } + + // create hatch + const basegfx::B2DVector aDiscreteDistance( + maCurrentTransformation * basegfx::B2DVector(rFillHatchAttributes.getDistance(), 0.0)); + const sal_uInt32 nDistance(basegfx::fround(aDiscreteDistance.getLength())); + const sal_uInt16 nAngle10( + static_cast<sal_uInt16>(basegfx::fround(rFillHatchAttributes.getAngle() / F_PI1800))); + ::Hatch aVCLHatch(eHatchStyle, Color(rFillHatchAttributes.getColor()), nDistance, nAngle10); + + // draw hatch using VCL + mpOutputDevice->DrawHatch(::tools::PolyPolygon(::tools::Polygon(aHatchPolygon)), aVCLHatch); + } +} + +void VclPixelProcessor2D::processBackgroundColorPrimitive2D( + const primitive2d::BackgroundColorPrimitive2D& rPrimitive) +{ + // #i98404# Handle directly, especially when AA is active + const AntialiasingFlags nOriginalAA(mpOutputDevice->GetAntialiasing()); + + // switch AA off in all cases + mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() + & ~AntialiasingFlags::EnableB2dDraw); + + // create color for fill + const basegfx::BColor aPolygonColor( + maBColorModifierStack.getModifiedColor(rPrimitive.getBColor())); + Color aFillColor(aPolygonColor); + aFillColor.SetTransparency(sal_uInt8((rPrimitive.getTransparency() * 255.0) + 0.5)); + mpOutputDevice->SetFillColor(aFillColor); + mpOutputDevice->SetLineColor(); + + // create rectangle for fill + const basegfx::B2DRange& aViewport(getViewInformation2D().getDiscreteViewport()); + const ::tools::Rectangle aRectangle(static_cast<sal_Int32>(floor(aViewport.getMinX())), + static_cast<sal_Int32>(floor(aViewport.getMinY())), + static_cast<sal_Int32>(ceil(aViewport.getMaxX())), + static_cast<sal_Int32>(ceil(aViewport.getMaxY()))); + mpOutputDevice->DrawRect(aRectangle); + + // restore AA setting + mpOutputDevice->SetAntialiasing(nOriginalAA); +} + +void VclPixelProcessor2D::processBorderLinePrimitive2D( + const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder) +{ + // Process recursively, but switch off AntiAliasing for + // horizontal/vertical lines (*not* diagonal lines). + // Checked using AntialiasingFlags::PixelSnapHairline instead, + // but with AntiAliasing on the display really is too 'ghosty' when + // using fine stroking. Correct, but 'ghosty'. + + // It has shown that there are quite some problems here: + // - vcl OutDev renderer methods still use fallbacks to paint + // multiple single lines between discrete sizes of < 3.5 what + // looks bad and does not match + // - mix of filled Polygons and Lines is bad when AA switched off + // - Alignment of AA with non-AA may be bad in diverse different + // renderers + // + // Due to these reasons I change the strategy: Always draw AAed, but + // allow fallback to test/check and if needed. The normal case + // where BorderLines will be system-dependently snapped to have at + // least a single discrete width per partial line (there may be up to + // three) works well nowadays due to most renderers moving the AA stuff + // by 0.5 pixels (discrete units) to match well with the non-AAed parts. + // + // Env-Switch for steering this, default is off. + // Enable by setting at all (and to something) + static const char* pSwitchOffAntiAliasingForHorVerBorderlines( + getenv("SAL_SWITCH_OFF_ANTIALIASING_FOR_HOR_VER_BORTDERLINES")); + static bool bSwitchOffAntiAliasingForHorVerBorderlines( + nullptr != pSwitchOffAntiAliasingForHorVerBorderlines); + + if (bSwitchOffAntiAliasingForHorVerBorderlines + && rBorder.isHorizontalOrVertical(getViewInformation2D())) + { + AntialiasingFlags nAntiAliasing = mpOutputDevice->GetAntialiasing(); + mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); + process(rBorder); + mpOutputDevice->SetAntialiasing(nAntiAliasing); + } + else + { + process(rBorder); + } +} + +void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) +{ + // invert primitive (currently only used for HighContrast fallback for selection in SW and SC). + // (Not true, also used at least for the drawing of dragged column and row boundaries in SC.) + // Set OutDev to XOR and switch AA off (XOR does not work with AA) + mpOutputDevice->Push(); + mpOutputDevice->SetRasterOp(RasterOp::Xor); + const AntialiasingFlags nAntiAliasing(mpOutputDevice->GetAntialiasing()); + mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); + + // process content recursively + process(rCandidate); + + // restore OutDev + mpOutputDevice->Pop(); + mpOutputDevice->SetAntialiasing(nAntiAliasing); +} + +void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) +{ + // #i98289# + const bool bForceLineSnap(getOptionsDrawinglayer().IsAntiAliasing() + && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()); + const AntialiasingFlags nOldAntiAliase(mpOutputDevice->GetAntialiasing()); + + if (bForceLineSnap) + { + mpOutputDevice->SetAntialiasing(nOldAntiAliase | AntialiasingFlags::PixelSnapHairline); + } + + process(rCandidate); + + if (bForceLineSnap) + { + mpOutputDevice->SetAntialiasing(nOldAntiAliase); + } +} + +void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate) +{ + basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); + aRange.transform(maCurrentTransformation); + aRange.grow(10.0); + impBufferDevice aBufferDevice(*mpOutputDevice, aRange); + if (aBufferDevice.isVisible()) + { + // remember last OutDev and set to content + OutputDevice* pLastOutputDevice = mpOutputDevice; + mpOutputDevice = &aBufferDevice.getTransparence(); + // paint content to virtual device + mpOutputDevice->Erase(); + process(rCandidate); + + // obtain result as a bitmap + auto bitmap = mpOutputDevice->GetBitmapEx(Point(aRange.getMinX(), aRange.getMinY()), + Size(aRange.getWidth(), aRange.getHeight())); + constexpr double nRadius = 5.0; + bitmap.Scale(Size(aRange.getWidth() - nRadius, aRange.getHeight() - nRadius)); + // use bitmap later as mask + auto mask = bitmap.GetBitmap(); + + mpOutputDevice = &aBufferDevice.getContent(); + process(rCandidate); + bitmap = mpOutputDevice->GetBitmapEx(Point(aRange.getMinX(), aRange.getMinY()), + Size(aRange.getWidth(), aRange.getHeight())); + bitmap.Scale(Size(aRange.getWidth() - nRadius, aRange.getHeight() - nRadius)); + + // calculate blurry effect + BitmapFilterStackBlur glowFilter(nRadius); + BitmapFilter::Filter(bitmap, glowFilter); + // back to old OutDev + mpOutputDevice = pLastOutputDevice; + mpOutputDevice->DrawBitmapEx( + Point(aRange.getMinX() - nRadius / 2, aRange.getMinY() - nRadius / 2), + BitmapEx(bitmap.GetBitmap(), mask)); + + // paint result + //aBufferDevice.paint(); + } + else + SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); +} } // end of namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/solenv/clang-format/blacklist b/solenv/clang-format/blacklist index 298369ee7da6..375ed4dd1647 100644 --- a/solenv/clang-format/blacklist +++ b/solenv/clang-format/blacklist @@ -3756,7 +3756,6 @@ drawinglayer/source/processor2d/vclhelperbufferdevice.cxx drawinglayer/source/processor2d/vclhelperbufferdevice.hxx drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx drawinglayer/source/processor2d/vclmetafileprocessor2d.hxx -drawinglayer/source/processor2d/vclpixelprocessor2d.cxx drawinglayer/source/processor3d/baseprocessor3d.cxx drawinglayer/source/processor3d/cutfindprocessor3d.cxx drawinglayer/source/processor3d/defaultprocessor3d.cxx _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits