diff options
Diffstat (limited to 'drawinglayer/qa')
-rw-r--r-- | drawinglayer/qa/unit/border.cxx | 181 | ||||
-rw-r--r-- | drawinglayer/qa/unit/vclmetafileprocessor2d.cxx | 157 | ||||
-rw-r--r-- | drawinglayer/qa/unit/vclpixelprocessor2d.cxx | 89 |
3 files changed, 427 insertions, 0 deletions
diff --git a/drawinglayer/qa/unit/border.cxx b/drawinglayer/qa/unit/border.cxx new file mode 100644 index 000000000..53bd420ee --- /dev/null +++ b/drawinglayer/qa/unit/border.cxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <memory> +#include <cppunit/TestAssert.h> +#include <cppunit/plugin/TestPlugIn.h> + +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <rtl/ref.hxx> +#include <test/bootstrapfixture.hxx> +#include <vcl/metaact.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/virdev.hxx> +#include <editeng/borderline.hxx> +#include <svtools/borderhelper.hxx> + +using namespace com::sun::star; + +namespace +{ +class DrawinglayerBorderTest : public test::BootstrapFixture +{ +}; + +CPPUNIT_TEST_FIXTURE(DrawinglayerBorderTest, testDoubleDecompositionSolid) +{ + // Create a border line primitive that's similar to the one from the bugdoc: + // 1.47 pixels is 0.03cm at 130% zoom and 96 DPI. + basegfx::B2DPoint aStart(0, 20); + basegfx::B2DPoint aEnd(100, 20); + double const fLeftWidth = 1.47; + double const fDistance = 1.47; + double const fRightWidth = 1.47; + double const fExtendLeftStart = 0; + double const fExtendLeftEnd = 0; + double const fExtendRightStart = 0; + double const fExtendRightEnd = 0; + basegfx::BColor aColorRight; + basegfx::BColor aColorLeft; + std::vector<double> aDashing(svtools::GetLineDashing(SvxBorderLineStyle::DOUBLE, 10.0)); + const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashing)); + std::vector<drawinglayer::primitive2d::BorderLine> aBorderlines{ + + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute(aColorLeft, fLeftWidth), fExtendLeftStart, + fExtendLeftStart, fExtendLeftEnd, fExtendLeftEnd), + + drawinglayer::primitive2d::BorderLine(fDistance), + + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute(aColorRight, fRightWidth), fExtendRightStart, + fExtendRightStart, fExtendRightEnd, fExtendRightEnd) + }; + + rtl::Reference<drawinglayer::primitive2d::BorderLinePrimitive2D> aBorder( + new drawinglayer::primitive2d::BorderLinePrimitive2D(aStart, aEnd, std::move(aBorderlines), + aStrokeAttribute)); + + // Decompose it into polygons. + drawinglayer::geometry::ViewInformation2D aView; + drawinglayer::primitive2d::Primitive2DContainer aContainer; + aBorder->get2DDecomposition(aContainer, aView); + + // Make sure it results in two borders as it's a double one. + CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), aContainer.size()); + + // Get the inside line, now a PolygonStrokePrimitive2D + auto pInside = dynamic_cast<const drawinglayer::primitive2d::PolygonStrokePrimitive2D*>( + aContainer[0].get()); + CPPUNIT_ASSERT(pInside); + + // Make sure the inside line's height is fLeftWidth. + const double fLineWidthFromDecompose = pInside->getLineAttribute().getWidth(); + + // This was 2.47, i.e. the width of the inner line was 1 unit (in the bugdoc's case: 1 pixel) wider than expected. + CPPUNIT_ASSERT_DOUBLES_EQUAL(fLeftWidth, fLineWidthFromDecompose, + basegfx::fTools::getSmallValue()); +} + +CPPUNIT_TEST_FIXTURE(DrawinglayerBorderTest, testDoublePixelProcessing) +{ + // Create a pixel processor. + ScopedVclPtrInstance<VirtualDevice> pDev; + drawinglayer::geometry::ViewInformation2D aView; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( + drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(*pDev, aView)); + CPPUNIT_ASSERT(pProcessor); + GDIMetaFile aMetaFile; + // Start recording after the processor is created, so we can test the pixel processor. + aMetaFile.Record(pDev); + + // Create a border line primitive that's similar to the one from the bugdoc: + // 1.47 pixels is 0.03cm at 130% zoom and 96 DPI. + basegfx::B2DPoint aStart(0, 20); + basegfx::B2DPoint aEnd(100, 20); + double const fLeftWidth = 1.47; + double const fDistance = 1.47; + double const fRightWidth = 1.47; + double const fExtendLeftStart = 0; + double const fExtendLeftEnd = 0; + double const fExtendRightStart = 0; + double const fExtendRightEnd = 0; + basegfx::BColor aColorRight; + basegfx::BColor aColorLeft; + std::vector<double> aDashing(svtools::GetLineDashing(SvxBorderLineStyle::DOUBLE, 10.0)); + const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashing)); + std::vector<drawinglayer::primitive2d::BorderLine> aBorderlines{ + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute(aColorLeft, fLeftWidth), fExtendLeftStart, + fExtendLeftStart, fExtendLeftEnd, fExtendLeftEnd), + + drawinglayer::primitive2d::BorderLine(fDistance), + + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute(aColorRight, fRightWidth), fExtendRightStart, + fExtendRightStart, fExtendRightEnd, fExtendRightEnd) + }; + + rtl::Reference<drawinglayer::primitive2d::BorderLinePrimitive2D> aBorder( + new drawinglayer::primitive2d::BorderLinePrimitive2D(aStart, aEnd, std::move(aBorderlines), + aStrokeAttribute)); + + drawinglayer::primitive2d::Primitive2DContainer aPrimitives; + aPrimitives.push_back(drawinglayer::primitive2d::Primitive2DReference(aBorder)); + + // Process the primitives. + pProcessor->process(aPrimitives); + + // Double line now gets decomposed in Metafile to painting four lines + // with width == 0 in a cross pattern due to real line width being between + // 1.0 and 2.0. Count created lines + aMetaFile.Stop(); + aMetaFile.WindStart(); + sal_uInt32 nPolyLineActionCount = 0; + + for (std::size_t nAction = 0; nAction < aMetaFile.GetActionSize(); ++nAction) + { + MetaAction* pAction = aMetaFile.GetAction(nAction); + + if (MetaActionType::POLYLINE == pAction->GetType()) + { + auto pMPLAction = static_cast<MetaPolyLineAction*>(pAction); + + if (0 != pMPLAction->GetLineInfo().GetWidth() + && LineStyle::Solid == pMPLAction->GetLineInfo().GetStyle()) + { + nPolyLineActionCount++; + } + } + } + + // Check if all eight (2x four) simple lines with width == 0 and + // solid were created + // + // This has changed: Now, just the needed 'real' lines get created + // which have a width of 1. This are two lines. The former multiple + // lines were a combination of view-dependent force to a single-pixel + // line width (0 == lineWidth -> hairline) and vcl rendering this + // using a (insane) combination of single non-AAed lines. All the + // system-dependent part of the BorderLine stuff is now done in + // SdrFrameBorderPrimitive2D and svx. + // Adapted this test - still useful, breaking it may be a hint :-) + const sal_uInt32 nExpectedNumPolyLineActions = 2; + + CPPUNIT_ASSERT_EQUAL(nExpectedNumPolyLineActions, nPolyLineActionCount); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/qa/unit/vclmetafileprocessor2d.cxx b/drawinglayer/qa/unit/vclmetafileprocessor2d.cxx new file mode 100644 index 000000000..0467df867 --- /dev/null +++ b/drawinglayer/qa/unit/vclmetafileprocessor2d.cxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include <test/bootstrapfixture.hxx> + +#include <vcl/virdev.hxx> +#include <vcl/BitmapReadAccess.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/metaact.hxx> +#include <vcl/gdimtf.hxx> +#include <tools/stream.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> +#include <cppcanvas/vclfactory.hxx> + +#include <com/sun/star/rendering/XCanvas.hpp> + +using namespace drawinglayer; +using namespace com::sun::star; + +class VclMetaFileProcessor2DTest : public test::BootstrapFixture +{ + VclPtr<VirtualDevice> mVclDevice; + uno::Reference<rendering::XCanvas> mCanvas; + + // if enabled - check the result images with: + // "xdg-open ./workdir/CppunitTest/drawinglayer_processors.test.core/" + static constexpr const bool mbExportBitmap = false; + + void exportDevice(const OUString& filename, const VclPtr<VirtualDevice>& device) + { + if (mbExportBitmap) + { + BitmapEx aBitmapEx(device->GetBitmapEx(Point(0, 0), device->GetOutputSizePixel())); + SvFileStream aStream(filename, StreamMode::WRITE | StreamMode::TRUNC); + GraphicFilter::GetGraphicFilter().compressAsPNG(aBitmapEx, aStream); + } + } + +public: + VclMetaFileProcessor2DTest() + : BootstrapFixture(true, false) + { + } + + virtual void tearDown() override + { + mVclDevice.clear(); + mCanvas = uno::Reference<rendering::XCanvas>(); + BootstrapFixture::tearDown(); + } + + void setupCanvas(const Size& size, Color backgroundColor = COL_WHITE, bool alpha = false) + { + mVclDevice + = alpha ? VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT) + : VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT); + mVclDevice->SetOutputSizePixel(size); + mVclDevice->SetBackground(Wallpaper(backgroundColor)); + mVclDevice->Erase(); + mCanvas = mVclDevice->GetCanvas(); + CPPUNIT_ASSERT(mCanvas.is()); + } + + // Test drawing a dotted line in Impress presentation mode. + void testTdf136957() + { + // Impress presentation mode first draws the slide to a metafile. + GDIMetaFile metafile; + // I got these values by adding debug output to cppcanvas::internal::ImplRenderer::ImplRenderer(). + metafile.SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + metafile.SetPrefSize(Size(14548, 3350)); + ScopedVclPtrInstance<VirtualDevice> metadevice; + metafile.Record(metadevice); + drawinglayer::geometry::ViewInformation2D view; + std::unique_ptr<processor2d::BaseProcessor2D> processor( + processor2d::createBaseProcessor2DFromOutputDevice(*metadevice, view)); + CPPUNIT_ASSERT(processor); + // Match the values Impress uses. + basegfx::B2DPolygon polygon = { { 15601, 0 }, { 15602, 5832 } }; + attribute::LineAttribute lineAttributes( + basegfx::BColor(0.047058823529411764, 0.19607843137254902, 0.17254901960784313), 35, + basegfx::B2DLineJoin::Miter, css::drawing::LineCap_ROUND); + attribute::StrokeAttribute strokeAttributes({ 0.35, 69.65 }); + rtl::Reference<primitive2d::PolygonStrokePrimitive2D> strokePrimitive( + new primitive2d::PolygonStrokePrimitive2D(polygon, lineAttributes, strokeAttributes)); + primitive2d::Primitive2DContainer primitives; + primitives.push_back(primitive2d::Primitive2DReference(strokePrimitive)); + processor->process(primitives); + metafile.Stop(); + metafile.WindStart(); + + // Now verify that the metafile has the one PolyLine action with the right dashing. + int lineActionCount = 0; + for (std::size_t i = 0; i < metafile.GetActionSize(); ++i) + { + const MetaAction* metaAction = metafile.GetAction(i); + if (metaAction->GetType() == MetaActionType::POLYLINE) + { + const MetaPolyLineAction* action + = static_cast<const MetaPolyLineAction*>(metaAction); + + CPPUNIT_ASSERT_EQUAL(35.0, action->GetLineInfo().GetWidth()); + CPPUNIT_ASSERT_EQUAL(LineStyle::Dash, action->GetLineInfo().GetStyle()); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(1), action->GetLineInfo().GetDashCount()); + CPPUNIT_ASSERT_EQUAL(0.35, action->GetLineInfo().GetDashLen()); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), action->GetLineInfo().GetDotCount()); + CPPUNIT_ASSERT_EQUAL(0.0, action->GetLineInfo().GetDotLen()); + CPPUNIT_ASSERT_EQUAL(69.65, action->GetLineInfo().GetDistance()); + lineActionCount++; + } + } + CPPUNIT_ASSERT_EQUAL(1, lineActionCount); + + // Now draw the metafile using canvas and verify that the line is drawn. + setupCanvas(Size(1920, 1080)); + cppcanvas::CanvasSharedPtr cppCanvas = cppcanvas::VCLFactory::createCanvas(mCanvas); + // I got these matrices from a breakpoint in drawing the polyline, and walking up + // the stack to the canvas code. + cppCanvas->setTransformation( + basegfx::B2DHomMatrix(0.056662828121770453, 0, 0, 0, 0.056640419947506564, 0)); + cppcanvas::RendererSharedPtr renderer = cppcanvas::VCLFactory::createRenderer( + cppCanvas, metafile, cppcanvas::Renderer::Parameters()); + renderer->setTransformation(basegfx::B2DHomMatrix(14548, 0, -2, 0, 3350, 3431)); + CPPUNIT_ASSERT(renderer->draw()); + exportDevice("test-tdf136957", mVclDevice); + Bitmap bitmap = mVclDevice->GetBitmap(Point(), Size(1920, 1080)); + Bitmap::ScopedReadAccess access(bitmap); + // There should be a dotted line, without the fix it wouldn't be there, so check + // there's a sufficient amount of non-white pixels and that's the line. + int nonWhiteCount = 0; + for (tools::Long y = 193; y <= 524; ++y) + for (tools::Long x = 883; x <= 885; ++x) + if (access->GetColor(y, x) != COL_WHITE) + ++nonWhiteCount; + CPPUNIT_ASSERT_GREATER(100, nonWhiteCount); + } + + CPPUNIT_TEST_SUITE(VclMetaFileProcessor2DTest); + CPPUNIT_TEST(testTdf136957); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(VclMetaFileProcessor2DTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/drawinglayer/qa/unit/vclpixelprocessor2d.cxx b/drawinglayer/qa/unit/vclpixelprocessor2d.cxx new file mode 100644 index 000000000..17ef17969 --- /dev/null +++ b/drawinglayer/qa/unit/vclpixelprocessor2d.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include <test/bootstrapfixture.hxx> + +#include <vcl/virdev.hxx> +#include <vcl/BitmapReadAccess.hxx> +#include <vcl/graphicfilter.hxx> +#include <tools/stream.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processorfromoutputdevice.hxx> + +using namespace drawinglayer; + +class VclPixelProcessor2DTest : public test::BootstrapFixture +{ + // if enabled - check the result images with: + // "xdg-open ./workdir/CppunitTest/drawinglayer_processors.test.core/" + static constexpr const bool mbExportBitmap = false; + + void exportDevice(const OUString& filename, const VclPtr<VirtualDevice>& device) + { + if (mbExportBitmap) + { + BitmapEx aBitmapEx(device->GetBitmapEx(Point(0, 0), device->GetOutputSizePixel())); + SvFileStream aStream(filename, StreamMode::WRITE | StreamMode::TRUNC); + GraphicFilter::GetGraphicFilter().compressAsPNG(aBitmapEx, aStream); + } + } + +public: + VclPixelProcessor2DTest() + : BootstrapFixture(true, false) + { + } + + // Test that drawing only a part of a gradient draws the proper part of it. + void testTdf139000() + { + ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT); + device->SetOutputSizePixel(Size(100, 200)); + device->SetBackground(Wallpaper(COL_RED)); + device->Erase(); + + drawinglayer::geometry::ViewInformation2D view; + std::unique_ptr<processor2d::BaseProcessor2D> processor( + processor2d::createBaseProcessor2DFromOutputDevice(*device, view)); + CPPUNIT_ASSERT(processor); + + basegfx::B2DRange definitionRange(0, 0, 100, 200); + basegfx::B2DRange outputRange(0, 100, 100, 200); // Paint only lower half of the gradient. + attribute::FillGradientAttribute attributes(attribute::GradientStyle::Linear, 0, 0, 0, 0, + COL_WHITE.getBColor(), COL_BLACK.getBColor()); + rtl::Reference<primitive2d::FillGradientPrimitive2D> gradientPrimitive( + new primitive2d::FillGradientPrimitive2D(outputRange, definitionRange, attributes)); + primitive2d::Primitive2DContainer primitives; + primitives.push_back(primitive2d::Primitive2DReference(gradientPrimitive)); + processor->process(primitives); + + exportDevice("test-tdf139000.png", device); + Bitmap bitmap = device->GetBitmap(Point(), device->GetOutputSizePixel()); + Bitmap::ScopedReadAccess access(bitmap); + // The upper half should keep its red background color. + CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_RED), access->GetColor(Point(0, 99))); + // First line of the gradient should not be the start color, but something halfway. + CPPUNIT_ASSERT_LESS(static_cast<sal_uInt16>(16), + access->GetColor(Point(0, 100)).GetColorError(COL_GRAY)); + // Last line of the gradient should be the end color, or close. + CPPUNIT_ASSERT_LESS(static_cast<sal_uInt16>(16), + access->GetColor(Point(0, 199)).GetColorError(COL_BLACK)); + } + + CPPUNIT_TEST_SUITE(VclPixelProcessor2DTest); + CPPUNIT_TEST(testTdf139000); + CPPUNIT_TEST_SUITE_END(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(VclPixelProcessor2DTest); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |