use Skia to do dashed lines, no need to do it manually (tdf#130431)
Change-Id: Id5efe7227f3c2bcb5ef6f1b990327e72014e8c47
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94857
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
diff --git a/vcl/backendtest/VisualBackendTest.cxx b/vcl/backendtest/VisualBackendTest.cxx
index 5daa9a6..08efc1d 100644
--- a/vcl/backendtest/VisualBackendTest.cxx
+++ b/vcl/backendtest/VisualBackendTest.cxx
@@ -576,7 +576,7 @@ public:
}
else if (mnTest % gnNumberOfTests == 7)
{
std::vector<tools::Rectangle> aRegions = setupRegions(3, 1, nWidth, nHeight);
std::vector<tools::Rectangle> aRegions = setupRegions(2, 2, nWidth, nHeight);
aRectangle = aRegions[index++];
{
@@ -587,6 +587,13 @@ public:
}
aRectangle = aRegions[index++];
{
vcl::test::OutputDeviceTestLine aOutDevTest;
Bitmap aBitmap = aOutDevTest.setupDashedLine();
assertAndSetBackground(vcl::test::OutputDeviceTestLine::checkDashedLine(aBitmap), aRectangle, rRenderContext);
drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
}
aRectangle = aRegions[index++];
{
vcl::test::OutputDeviceTestGradient aOutDevTest;
Bitmap aBitmap = aOutDevTest.setupLinearGradient();
drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
diff --git a/vcl/backendtest/outputdevice/common.cxx b/vcl/backendtest/outputdevice/common.cxx
index f9052fb..a5d0323 100644
--- a/vcl/backendtest/outputdevice/common.cxx
+++ b/vcl/backendtest/outputdevice/common.cxx
@@ -416,6 +416,11 @@ TestResult OutputDeviceTestCommon::checkRectangles(Bitmap& aBitmap, std::vector<
return aReturnValue;
}
TestResult OutputDeviceTestCommon::checkRectangle(Bitmap& rBitmap, int aLayerNumber, Color aExpectedColor)
{
return checkRect(rBitmap, aLayerNumber, aExpectedColor);
}
tools::Rectangle OutputDeviceTestCommon::alignToCenter(tools::Rectangle aRect1, tools::Rectangle aRect2)
{
Point aPoint((aRect1.GetWidth() / 2.0) - (aRect2.GetWidth() / 2.0),
diff --git a/vcl/backendtest/outputdevice/line.cxx b/vcl/backendtest/outputdevice/line.cxx
index b9236dc..5b5d732 100644
--- a/vcl/backendtest/outputdevice/line.cxx
+++ b/vcl/backendtest/outputdevice/line.cxx
@@ -10,6 +10,11 @@
#include <test/outputdevice.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <vcl/bitmapaccess.hxx>
#include <list>
namespace vcl::test {
namespace
@@ -108,6 +113,88 @@ Bitmap OutputDeviceTestLine::setupAALines()
return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
}
Bitmap OutputDeviceTestLine::setupDashedLine()
{
initialSetup(13, 13, constBackgroundColor);
mpVirtualDevice->SetLineColor(constLineColor);
mpVirtualDevice->SetFillColor();
tools::Rectangle rectangle = maVDRectangle;
rectangle.shrink(2);
std::vector stroke({ 2.0, 1.0 });
mpVirtualDevice->DrawPolyLineDirect( basegfx::B2DHomMatrix(),
basegfx::B2DPolygon{
basegfx::B2DPoint(rectangle.getX(), rectangle.getY()),
basegfx::B2DPoint(rectangle.getX(), rectangle.getY() + rectangle.getHeight()),
basegfx::B2DPoint(rectangle.getX() + rectangle.getWidth(),
rectangle.getY() + rectangle.getHeight()),
basegfx::B2DPoint(rectangle.getX() + rectangle.getWidth(), rectangle.getY()),
basegfx::B2DPoint(rectangle.getX(), rectangle.getY())},
1, 0, &stroke, basegfx::B2DLineJoin::NONE, css::drawing::LineCap_BUTT, basegfx::deg2rad(15.0), true );
return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
}
TestResult OutputDeviceTestLine::checkDashedLine(Bitmap& rBitmap)
{
TestResult returnValue = TestResult::Passed;
for (int i = 0; i < 7; i++)
{
TestResult eResult = TestResult::Passed;
if( i == 2 )
{
// Build a sequence of pixels for the drawn rectangle border,
// check that they alternate appropriately (there should be
// normally 2 line, 1 background).
std::list< bool > dash; // true - line color, false - background
const int width = rBitmap.GetSizePixel().Width();
const int height = rBitmap.GetSizePixel().Height();
BitmapReadAccess access(rBitmap);
for( int x = 2; x < width - 2; ++x )
dash.push_back( access.GetPixel( 2, x ) == constLineColor );
for( int y = 3; y < height - 3; ++y )
dash.push_back( access.GetPixel( y, width - 3 ) == constLineColor );
for( int x = width - 3; x >= 2; --x )
dash.push_back( access.GetPixel( height - 3, x ) == constLineColor );
for( int y = height - 4; y >= 3; --y )
dash.push_back( access.GetPixel( y, 2 ) == constLineColor );
for( int x = 2; x < width - 2; ++x ) // repeat, to check also the corner
dash.push_back( access.GetPixel( 2, x ) == constLineColor );
bool last = false;
int lastCount = 0;
while( !dash.empty())
{
if( dash.front() == last )
{
++lastCount;
if( lastCount > ( last ? 4 : 3 ))
eResult = TestResult::Failed;
else if( lastCount > ( last ? 3 : 2 ) && eResult != TestResult::Failed)
eResult = TestResult::PassedWithQuirks;
}
else
{
last = dash.front();
lastCount = 1;
}
dash.pop_front();
}
}
else
{
eResult = OutputDeviceTestCommon::checkRectangle(rBitmap, i, constBackgroundColor);
}
if (eResult == TestResult::Failed)
returnValue = TestResult::Failed;
if (eResult == TestResult::PassedWithQuirks && returnValue != TestResult::Failed)
returnValue = TestResult::PassedWithQuirks;
}
return returnValue;
}
} // end namespace vcl::test
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx
index c78845f..eb5fbdb 100644
--- a/vcl/inc/skia/gdiimpl.hxx
+++ b/vcl/inc/skia/gdiimpl.hxx
@@ -104,9 +104,9 @@ public:
virtual bool drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon&, double fTransparency, double fLineWidth,
const std::vector<double>* pStroke, // MM01
basegfx::B2DLineJoin, css::drawing::LineCap,
double fMiterMinimumAngle, bool bPixelSnapHairline) override;
const std::vector<double>* pStroke, basegfx::B2DLineJoin,
css::drawing::LineCap, double fMiterMinimumAngle,
bool bPixelSnapHairline) override;
virtual bool drawPolyLineBezier(sal_uInt32 nPoints, const SalPoint* pPtAry,
const PolyFlags* pFlgAry) override;
diff --git a/vcl/inc/test/outputdevice.hxx b/vcl/inc/test/outputdevice.hxx
index b6cf70c..b8ad0b6 100644
--- a/vcl/inc/test/outputdevice.hxx
+++ b/vcl/inc/test/outputdevice.hxx
@@ -60,6 +60,7 @@ public:
static TestResult checkInvertTrackFrameRectangle(Bitmap& aBitmap);
static TestResult checkRectangles(Bitmap& rBitmap, std::vector<Color>& aExpectedColors);
static TestResult checkRectangle(Bitmap& rBitmap, int aLayerNumber, Color aExpectedColor);
static TestResult checkFilled(Bitmap& rBitmap, tools::Rectangle aRectangle, Color aExpectedColor);
static TestResult checkChecker(Bitmap& rBitmap, sal_Int32 nStartX, sal_Int32 nEndX,
@@ -125,6 +126,9 @@ public:
Bitmap setupDiamond();
Bitmap setupLines();
Bitmap setupAALines();
Bitmap setupDashedLine();
static TestResult checkDashedLine(Bitmap& rBitmap);
};
class VCL_DLLPUBLIC OutputDeviceTestPolyLine : public OutputDeviceTestCommon
diff --git a/vcl/qa/cppunit/BackendTest.cxx b/vcl/qa/cppunit/BackendTest.cxx
index b34dda5..ff4ed0d 100644
--- a/vcl/qa/cppunit/BackendTest.cxx
+++ b/vcl/qa/cppunit/BackendTest.cxx
@@ -495,6 +495,16 @@ public:
CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
}
void testDashedLine()
{
vcl::test::OutputDeviceTestLine aOutDevTest;
Bitmap aBitmap = aOutDevTest.setupDashedLine();
auto eResult = vcl::test::OutputDeviceTestLine::checkDashedLine(aBitmap);
exportImage("10-01_dashed_line_test.png", aBitmap);
if (SHOULD_ASSERT)
CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
}
void testTdf124848()
{
ScopedVclPtr<VirtualDevice> device = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
@@ -575,6 +585,8 @@ public:
CPPUNIT_TEST(testClipPolyPolygon);
CPPUNIT_TEST(testClipB2DPolyPolygon);
CPPUNIT_TEST(testDashedLine);
CPPUNIT_TEST(testTdf124848);
CPPUNIT_TEST_SUITE_END();
diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx
index a2ccb12d..b5b9aad 100644
--- a/vcl/skia/gdiimpl.cxx
+++ b/vcl/skia/gdiimpl.cxx
@@ -644,10 +644,8 @@ void SkiaSalGraphicsImpl::drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAr
aPolygon.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
aPolygon.setClosed(false);
drawPolyLine(basegfx::B2DHomMatrix(), aPolygon, 0.0, 1.0,
nullptr, // MM01
basegfx::B2DLineJoin::Miter, css::drawing::LineCap_BUTT,
basegfx::deg2rad(15.0) /*default*/, false);
drawPolyLine(basegfx::B2DHomMatrix(), aPolygon, 0.0, 1.0, nullptr, basegfx::B2DLineJoin::Miter,
css::drawing::LineCap_BUTT, basegfx::deg2rad(15.0) /*default*/, false);
}
void SkiaSalGraphicsImpl::drawPolygon(sal_uInt32 nPoints, const SalPoint* pPtAry)
@@ -736,13 +734,11 @@ bool SkiaSalGraphicsImpl::drawPolyPolygon(const basegfx::B2DHomMatrix& rObjectTo
bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDevice,
const basegfx::B2DPolygon& rPolyLine, double fTransparency,
double fLineWidth,
const std::vector<double>* pStroke, // MM01
double fLineWidth, const std::vector<double>* pStroke,
basegfx::B2DLineJoin eLineJoin,
css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
bool bPixelSnapHairline)
{
// MM01 check done for simple reasons
if (!rPolyLine.count() || fTransparency < 0.0 || fTransparency > 1.0
|| mLineColor == SALCOLOR_NONE)
{
@@ -758,29 +754,9 @@ bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDev
else // Adjust line width for object-to-device scale.
fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();
// MM01 need to do line dashing as fallback stuff here now
const double fDotDashLength(
nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
const bool bStrokeUsed(0.0 != fDotDashLength);
assert(!bStrokeUsed || (bStrokeUsed && pStroke));
basegfx::B2DPolyPolygon aPolyPolygonLine;
if (bStrokeUsed)
{
// apply LineStyle
basegfx::utils::applyLineDashing(rPolyLine, // source
*pStroke, // pattern
&aPolyPolygonLine, // target for lines
nullptr, // target for gaps
fDotDashLength); // full length if available
}
else
{
// no line dashing, just copy
aPolyPolygonLine.append(rPolyLine);
}
// Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
basegfx::B2DPolyPolygon aPolyPolygonLine;
aPolyPolygonLine.append(rPolyLine);
aPolyPolygonLine.transform(rObjectToDevice);
if (bPixelSnapHairline)
{
@@ -831,6 +807,13 @@ bool SkiaSalGraphicsImpl::drawPolyLine(const basegfx::B2DHomMatrix& rObjectToDev
aPaint.setStrokeWidth(fLineWidth);
aPaint.setAntiAlias(mParent.getAntiAliasB2DDraw());
if (pStroke && std::accumulate(pStroke->begin(), pStroke->end(), 0.0) != 0)
{
std::vector<SkScalar> intervals;
intervals.assign(pStroke->begin(), pStroke->end());
aPaint.setPathEffect(SkDashPathEffect::Make(intervals.data(), intervals.size(), 0));
}
// Skia does not support basegfx::B2DLineJoin::NONE, so in that case batch only if lines
// are not wider than a pixel.
if (eLineJoin != basegfx::B2DLineJoin::NONE || fLineWidth <= 1.0)