[Libreoffice-commits] core.git: vcl/backendtest vcl/inc vcl/qa

Luboš Luňák (via logerrit) logerrit at kemper.freedesktop.org
Fri Sep 25 10:04:22 UTC 2020


 vcl/backendtest/VisualBackendTest.cxx     |   85 ++++++++--
 vcl/backendtest/outputdevice/common.cxx   |  241 ++++++++++++++++++++++++++++++
 vcl/backendtest/outputdevice/gradient.cxx |   80 +++++++++
 vcl/inc/test/outputdevice.hxx             |   15 +
 vcl/qa/cppunit/BackendTest.cxx            |   91 +++++++++++
 5 files changed, 496 insertions(+), 16 deletions(-)

New commits:
commit eed2f45806fdc14e1dcc8a2820496b1d66379a75
Author:     Luboš Luňák <l.lunak at collabora.com>
AuthorDate: Wed Sep 23 22:02:32 2020 +0200
Commit:     Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Fri Sep 25 12:03:45 2020 +0200

    make vclbackendtest test gradients
    
    And tweak the tests so that the default VCL algorithm actually passes.
    Also allow a slight inaccuracy for the starting and ending white and
    black colors, as the upcoming Cairo and Skia implementations are not
    precise there.
    
    Change-Id: I93bae57c0e168027a52ced0d757ee6925cb5335a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103281
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>

diff --git a/vcl/backendtest/VisualBackendTest.cxx b/vcl/backendtest/VisualBackendTest.cxx
index 858ae7b996f1..afc4147ce287 100644
--- a/vcl/backendtest/VisualBackendTest.cxx
+++ b/vcl/backendtest/VisualBackendTest.cxx
@@ -90,7 +90,7 @@ class VisualBackendTestWindow : public WorkWindow
 private:
     Timer maUpdateTimer;
     std::vector<std::chrono::high_resolution_clock::time_point> mTimePoints;
-    static constexpr unsigned char gnNumberOfTests = 10;
+    static constexpr unsigned char gnNumberOfTests = 11;
     unsigned char mnTest;
     bool mbAnimate;
     ScopedVclPtr<VirtualDevice> mpVDev;
@@ -526,6 +526,71 @@ public:
         }
     }
 
+    static void testGradients(vcl::RenderContext& rRenderContext, int nWidth, int nHeight)
+    {
+        tools::Rectangle aRectangle;
+        size_t index = 0;
+
+        std::vector<tools::Rectangle> aRegions = setupRegions(4, 2, nWidth, nHeight);
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestGradient aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupLinearGradient();
+            assertAndSetBackground(vcl::test::OutputDeviceTestGradient::checkLinearGradient(aBitmap), aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestGradient aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupLinearGradientAngled();
+            assertAndSetBackground(vcl::test::OutputDeviceTestGradient::checkLinearGradientAngled(aBitmap), aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestGradient aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupLinearGradientBorder();
+            assertAndSetBackground(vcl::test::OutputDeviceTestGradient::checkLinearGradientBorder(aBitmap), aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestGradient aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupLinearGradientIntensity();
+            assertAndSetBackground(vcl::test::OutputDeviceTestGradient::checkLinearGradientIntensity(aBitmap), aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestGradient aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupLinearGradientSteps();
+            assertAndSetBackground(vcl::test::OutputDeviceTestGradient::checkLinearGradientSteps(aBitmap), aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestGradient aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupAxialGradient();
+            assertAndSetBackground(vcl::test::OutputDeviceTestGradient::checkAxialGradient(aBitmap), aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestGradient aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupRadialGradient();
+            assertAndSetBackground(vcl::test::OutputDeviceTestGradient::checkRadialGradient(aBitmap), aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestGradient aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupRadialGradientOfs();
+            assertAndSetBackground(vcl::test::OutputDeviceTestGradient::checkRadialGradientOfs(aBitmap), aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+    }
+
     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) override
     {
         if (mnTest % gnNumberOfTests == gnNumberOfTests - 1)
@@ -634,7 +699,11 @@ public:
         }
         else if (mnTest % gnNumberOfTests == 8)
         {
-            std::vector<tools::Rectangle> aRegions = setupRegions(2, 2, nWidth, nHeight);
+            testGradients(rRenderContext, nWidth, nHeight);
+        }
+        else if (mnTest % gnNumberOfTests == 9)
+        {
+            std::vector<tools::Rectangle> aRegions = setupRegions(2, 1, nWidth, nHeight);
 
             aRectangle = aRegions[index++];
             {
@@ -650,18 +719,6 @@ public:
                 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);
-            }
-            aRectangle = aRegions[index++];
-            {
-                vcl::test::OutputDeviceTestGradient aOutDevTest;
-                Bitmap aBitmap = aOutDevTest.setupRadialGradient();
-                drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
-            }
         }
     }
 };
diff --git a/vcl/backendtest/outputdevice/common.cxx b/vcl/backendtest/outputdevice/common.cxx
index a5d032315474..41b7419c29bf 100644
--- a/vcl/backendtest/outputdevice/common.cxx
+++ b/vcl/backendtest/outputdevice/common.cxx
@@ -52,6 +52,44 @@ void checkValue(BitmapScopedWriteAccess& pAccess, int x, int y, Color aExpected,
     }
 }
 
+void checkValue(BitmapScopedWriteAccess& pAccess, int x, int y, Color aExpected,
+                      int& nNumberOfQuirks, int& nNumberOfErrors, int nColorDeltaThresh, int nColorDeltaThreshQuirk = 0)
+{
+    const bool bColorize = false;
+    Color aColor = pAccess->GetPixel(y, x);
+    int nColorDelta = deltaColor(aColor, aExpected);
+    nColorDeltaThreshQuirk = std::max( nColorDeltaThresh, nColorDeltaThreshQuirk);
+
+    if (nColorDelta <= nColorDeltaThresh)
+    {
+        if (bColorize)
+            pAccess->SetPixel(y, x, COL_LIGHTGREEN);
+    }
+    else if (nColorDelta <= nColorDeltaThreshQuirk)
+    {
+        nNumberOfQuirks++;
+        if (bColorize)
+            pAccess->SetPixel(y, x, COL_YELLOW);
+    }
+    else
+    {
+        nNumberOfErrors++;
+        if (bColorize)
+            pAccess->SetPixel(y, x, COL_LIGHTRED);
+    }
+}
+
+// Return all colors in the rectangle and their count.
+std::map<Color, int> collectColors(Bitmap& bitmap, const tools::Rectangle& rectangle)
+{
+    std::map<Color, int> colors;
+    BitmapScopedWriteAccess pAccess(bitmap);
+    for( long y = rectangle.getY(); y < rectangle.GetHeight(); ++y)
+        for( long x = rectangle.getX(); x < rectangle.GetWidth(); ++x)
+          ++colors[pAccess->GetPixel(y, x)]; // operator[] initializes to 0 (default ctor) if creating
+    return colors;
+}
+
 TestResult checkRect(Bitmap& rBitmap, int aLayerNumber, Color aExpectedColor)
 {
     BitmapScopedWriteAccess pAccess(rBitmap);
@@ -473,6 +511,209 @@ TestResult OutputDeviceTestCommon::checkBezier(Bitmap& rBitmap)
     return checkRectangles(rBitmap, aExpected);
 }
 
+// Check 'count' pixels from (x,y) in (addX,addY) direction, the color values must not decrease.
+static bool checkGradient(BitmapScopedWriteAccess& pAccess, int x, int y, int count, int addX, int addY)
+{
+    const bool bColorize = false;
+    Color maxColor = COL_BLACK;
+    for( int i = 0; i < count; ++i )
+    {
+        Color color = pAccess->GetPixel(y, x);
+        if( color.GetRed() < maxColor.GetRed() || color.GetGreen() < maxColor.GetGreen() || color.GetBlue() < maxColor.GetBlue())
+        {
+            if (bColorize)
+                pAccess->SetPixel(y, x, COL_RED);
+            return false;
+        }
+        maxColor = color;
+        if (bColorize)
+            pAccess->SetPixel(y, x, COL_LIGHTGREEN);
+        x += addX;
+        y += addY;
+    }
+    return true;
+}
+
+TestResult OutputDeviceTestCommon::checkLinearGradient(Bitmap& bitmap)
+{
+    BitmapScopedWriteAccess pAccess(bitmap);
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+
+    // The lowest line is missing in the default VCL implementation => quirk.
+    checkValue(pAccess, 1, 10, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, true, 255 / 10);
+    checkValue(pAccess, 10, 10, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, true, 255 / 10);
+    for(int y = 1; y < 10; ++y)
+    {
+        checkValue(pAccess, 1, y, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 255 / 10);
+        checkValue(pAccess, 10, y, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 255 / 10);
+    }
+    for(int y = 1; y < 10; ++y)
+        if( !checkGradient( pAccess, 10, y, 10, -1, 0 ))
+            return TestResult::Failed;
+    if (nNumberOfQuirks > 0)
+        checkResult(TestResult::PassedWithQuirks, aResult);
+    if (nNumberOfErrors > 0)
+        checkResult(TestResult::Failed, aResult);
+    return aResult;
+}
+
+TestResult OutputDeviceTestCommon::checkLinearGradientAngled(Bitmap& bitmap)
+{
+    BitmapScopedWriteAccess pAccess(bitmap);
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+
+    // The top-left pixel is not white but gray in the default VCL implementation => quirk.
+    checkValue(pAccess, 1, 1, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 50);
+    checkValue(pAccess, 10, 10, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 0, 255 / 10); // Bottom-right.
+    // Main diagonal.
+    if( !checkGradient( pAccess, 10, 10, 10, -1, -1 ))
+        return TestResult::Failed;
+    if (nNumberOfQuirks > 0)
+        checkResult(TestResult::PassedWithQuirks, aResult);
+    if (nNumberOfErrors > 0)
+        checkResult(TestResult::Failed, aResult);
+    return TestResult::Passed;
+}
+
+TestResult OutputDeviceTestCommon::checkLinearGradientBorder(Bitmap& bitmap)
+{
+    TestResult aResult = TestResult::Passed;
+    // Top half is border.
+    checkResult(checkFilled(bitmap, tools::Rectangle(Point(1, 1), Size(10, 5)), COL_WHITE), aResult);
+    BitmapScopedWriteAccess pAccess(bitmap);
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+    for(int x = 1; x <= 10; ++x)
+    {
+        checkValue(pAccess, x, 10, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+        if( !checkGradient( pAccess, x, 10, 5, 0, -1 ))
+            return TestResult::Failed;
+    }
+    if (nNumberOfQuirks > 0)
+        checkResult(TestResult::PassedWithQuirks, aResult);
+    if (nNumberOfErrors > 0)
+        checkResult(TestResult::Failed, aResult);
+    return aResult;
+}
+
+TestResult OutputDeviceTestCommon::checkLinearGradientIntensity(Bitmap& bitmap)
+{
+    BitmapScopedWriteAccess pAccess(bitmap);
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+
+    for(int x = 1; x <= 10; ++x)
+    {
+        // The gradient starts at half intensity, i.e. white's 255's are halved.
+        checkValue(pAccess, x, 1, Color(128,128,128), nNumberOfQuirks, nNumberOfErrors, false, 10);
+        checkValue(pAccess, x, 10, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 255 / 10);
+        if( !checkGradient( pAccess, x, 10, 10, 0, -1 ))
+            return TestResult::Failed;
+    }
+    if (nNumberOfQuirks > 0)
+        checkResult(TestResult::PassedWithQuirks, aResult);
+    if (nNumberOfErrors > 0)
+        checkResult(TestResult::Failed, aResult);
+    return aResult;
+}
+
+TestResult OutputDeviceTestCommon::checkLinearGradientSteps(Bitmap& bitmap)
+{
+    // Reuse the basic linear gradient check.
+    TestResult aResult = checkLinearGradient(bitmap);
+    // Only 4 steps in the gradient, there should be only 4 colors.
+    if( collectColors( bitmap, tools::Rectangle( Point( 1, 1 ), Size( 10, 10 ))).size() != 4 )
+        return TestResult::Failed;
+    return aResult;
+}
+
+TestResult OutputDeviceTestCommon::checkAxialGradient(Bitmap& bitmap)
+{
+    BitmapScopedWriteAccess pAccess(bitmap);
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+
+    for(int y = 1; y <= 11; ++y)
+    {
+        // Middle horizontal line is white, gradients to the sides.
+        checkValue(pAccess, 6, y, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, false);
+        checkValue(pAccess, 1, y, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, false);
+        checkValue(pAccess, 11, y, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, false);
+        if( !checkGradient( pAccess, 1, y, 6, 1, 0 ))
+            return TestResult::Failed;
+        if( !checkGradient( pAccess, 11, y, 6, -1, 0 ))
+            return TestResult::Failed;
+    }
+    if (nNumberOfQuirks > 0)
+        checkResult(TestResult::PassedWithQuirks, aResult);
+    if (nNumberOfErrors > 0)
+        checkResult(TestResult::Failed, aResult);
+    return aResult;
+}
+
+TestResult OutputDeviceTestCommon::checkRadialGradient(Bitmap& bitmap)
+{
+    BitmapScopedWriteAccess pAccess(bitmap);
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+    // The default VCL implementation is off-center in the direction to the top-left.
+    // This means not all corners will be pure white => quirks.
+    checkValue(pAccess, 1, 1, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 3);
+    checkValue(pAccess, 1, 10, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+    checkValue(pAccess, 10, 1, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+    checkValue(pAccess, 10, 10, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+    // And not all centers will be pure black => quirks.
+    checkValue(pAccess, 5, 5, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+    checkValue(pAccess, 5, 6, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 3);
+    checkValue(pAccess, 6, 5, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 3);
+    checkValue(pAccess, 6, 6, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 2);
+    // Check diagonals, from the offset center.
+    if(!checkGradient(pAccess, 5, 5, 5, -1, -1))
+        return TestResult::Failed;
+    if(!checkGradient(pAccess, 5, 5, 6, 1, 1))
+        return TestResult::Failed;
+    if(!checkGradient(pAccess, 5, 5, 5, 1, -1))
+        return TestResult::Failed;
+    if(!checkGradient(pAccess, 5, 5, 5, -1, 1))
+        return TestResult::Failed;
+    if (nNumberOfQuirks > 0)
+        checkResult(TestResult::PassedWithQuirks, aResult);
+    if (nNumberOfErrors > 0)
+        checkResult(TestResult::Failed, aResult);
+    return aResult;
+}
+
+TestResult OutputDeviceTestCommon::checkRadialGradientOfs(Bitmap& bitmap)
+{
+    BitmapScopedWriteAccess pAccess(bitmap);
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+    checkValue(pAccess, 1, 1, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+    checkValue(pAccess, 10, 1, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+    checkValue(pAccess, 1, 10, COL_WHITE, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+    checkValue(pAccess, 10, 10, COL_BLACK, nNumberOfQuirks, nNumberOfErrors, 255 / 10, 255 / 5);
+    // Check gradients from the center (=bottom-right corner).
+    if(!checkGradient(pAccess, 10, 10, 10, -1, -1))
+        return TestResult::Failed;
+    if(!checkGradient(pAccess, 10, 10, 10, -1, 0))
+        return TestResult::Failed;
+    if(!checkGradient(pAccess, 10, 10, 10, 0, -1))
+        return TestResult::Failed;
+    if (nNumberOfQuirks > 0)
+        checkResult(TestResult::PassedWithQuirks, aResult);
+    if (nNumberOfErrors > 0)
+        checkResult(TestResult::Failed, aResult);
+    return aResult;
+}
+
 } // end namespace vcl::test
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/gradient.cxx b/vcl/backendtest/outputdevice/gradient.cxx
index 9880fedd1e4c..c0ed3fc1b717 100644
--- a/vcl/backendtest/outputdevice/gradient.cxx
+++ b/vcl/backendtest/outputdevice/gradient.cxx
@@ -26,6 +26,72 @@ Bitmap OutputDeviceTestGradient::setupLinearGradient()
     return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
 }
 
+Bitmap OutputDeviceTestGradient::setupLinearGradientAngled()
+{
+    initialSetup(12, 12, constBackgroundColor);
+
+    Gradient aGradient(GradientStyle::Linear, Color(0xFF, 0xFF, 0xFF), Color(0x00, 0x00, 0x00));
+    aGradient.SetAngle(450);
+    tools::Rectangle aDrawRect(maVDRectangle.Left() + 1,  maVDRectangle.Top() + 1,
+                        maVDRectangle.Right() - 1, maVDRectangle.Bottom() - 1);
+    mpVirtualDevice->DrawGradient(aDrawRect, aGradient);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestGradient::setupLinearGradientBorder()
+{
+    initialSetup(12, 12, constBackgroundColor);
+
+    Gradient aGradient(GradientStyle::Linear, Color(0xFF, 0xFF, 0xFF), Color(0x00, 0x00, 0x00));
+    aGradient.SetBorder(50);
+    tools::Rectangle aDrawRect(maVDRectangle.Left() + 1,  maVDRectangle.Top() + 1,
+                        maVDRectangle.Right() - 1, maVDRectangle.Bottom() - 1);
+    mpVirtualDevice->DrawGradient(aDrawRect, aGradient);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestGradient::setupLinearGradientIntensity()
+{
+    initialSetup(12, 12, constBackgroundColor);
+
+    Gradient aGradient(GradientStyle::Linear, Color(0xFF, 0xFF, 0xFF), Color(0x00, 0x00, 0x00));
+    aGradient.SetStartIntensity(50);
+    tools::Rectangle aDrawRect(maVDRectangle.Left() + 1,  maVDRectangle.Top() + 1,
+                        maVDRectangle.Right() - 1, maVDRectangle.Bottom() - 1);
+    mpVirtualDevice->DrawGradient(aDrawRect, aGradient);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestGradient::setupLinearGradientSteps()
+{
+    initialSetup(12, 12, constBackgroundColor);
+
+    Gradient aGradient(GradientStyle::Linear, Color(0xFF, 0xFF, 0xFF), Color(0x00, 0x00, 0x00));
+    aGradient.SetAngle(900);
+    aGradient.SetSteps(4);
+    tools::Rectangle aDrawRect(maVDRectangle.Left() + 1,  maVDRectangle.Top() + 1,
+                        maVDRectangle.Right() - 1, maVDRectangle.Bottom() - 1);
+    mpVirtualDevice->DrawGradient(aDrawRect, aGradient);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestGradient::setupAxialGradient()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    Gradient aGradient(GradientStyle::Axial, Color(0xFF, 0xFF, 0xFF), Color(0x00, 0x00, 0x00));
+    aGradient.SetAngle(900);
+    tools::Rectangle aDrawRect(maVDRectangle.Left() + 1,  maVDRectangle.Top() + 1,
+                        maVDRectangle.Right() - 1, maVDRectangle.Bottom() - 1);
+    mpVirtualDevice->DrawGradient(aDrawRect, aGradient);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
+}
+
 Bitmap OutputDeviceTestGradient::setupRadialGradient()
 {
     initialSetup(12, 12, constBackgroundColor);
@@ -38,6 +104,20 @@ Bitmap OutputDeviceTestGradient::setupRadialGradient()
     return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
 }
 
+Bitmap OutputDeviceTestGradient::setupRadialGradientOfs()
+{
+    initialSetup(12, 12, constBackgroundColor);
+
+    Gradient aGradient(GradientStyle::Radial, Color(0xFF, 0xFF, 0xFF), Color(0x00, 0x00, 0x00));
+    aGradient.SetOfsX(100); // Move center to the bottom-right corner.
+    aGradient.SetOfsY(100);
+    tools::Rectangle aDrawRect(maVDRectangle.Left() + 1,  maVDRectangle.Top() + 1,
+                        maVDRectangle.Right() - 1, maVDRectangle.Bottom() - 1);
+    mpVirtualDevice->DrawGradient(aDrawRect, aGradient);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), maVDRectangle.GetSize());
+}
+
 } // end namespace vcl::test
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/test/outputdevice.hxx b/vcl/inc/test/outputdevice.hxx
index f45185682814..293e817d35e9 100644
--- a/vcl/inc/test/outputdevice.hxx
+++ b/vcl/inc/test/outputdevice.hxx
@@ -65,6 +65,15 @@ public:
     static TestResult checkChecker(Bitmap& rBitmap, sal_Int32 nStartX, sal_Int32 nEndX,
                                    sal_Int32 nStartY, sal_Int32 nEndY, std::vector<Color> const & rExpected);
 
+    static TestResult checkLinearGradient(Bitmap& bitmap);
+    static TestResult checkLinearGradientAngled(Bitmap& bitmap);
+    static TestResult checkLinearGradientBorder(Bitmap& bitmap);
+    static TestResult checkLinearGradientIntensity(Bitmap& bitmap);
+    static TestResult checkLinearGradientSteps(Bitmap& bitmap);
+    static TestResult checkAxialGradient(Bitmap& bitmap);
+    static TestResult checkRadialGradient(Bitmap& bitmap);
+    static TestResult checkRadialGradientOfs(Bitmap& bitmap);
+
     static void createDiamondPoints(tools::Rectangle rRect, int nOffset,
                                     Point& rPoint1, Point& rPoint2,
                                     Point& rPoint3, Point& rPoint4);
@@ -200,7 +209,13 @@ public:
     OutputDeviceTestGradient() = default;
 
     Bitmap setupLinearGradient();
+    Bitmap setupLinearGradientAngled();
+    Bitmap setupLinearGradientBorder();
+    Bitmap setupLinearGradientIntensity();
+    Bitmap setupLinearGradientSteps();
+    Bitmap setupAxialGradient();
     Bitmap setupRadialGradient();
+    Bitmap setupRadialGradientOfs();
 };
 
 class VCL_DLLPUBLIC OutputDeviceTestClip : public OutputDeviceTestCommon
diff --git a/vcl/qa/cppunit/BackendTest.cxx b/vcl/qa/cppunit/BackendTest.cxx
index ba82e9d2ba9d..631ef0989b76 100644
--- a/vcl/qa/cppunit/BackendTest.cxx
+++ b/vcl/qa/cppunit/BackendTest.cxx
@@ -516,8 +516,6 @@ public:
             CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
     }
 
-    // vcl::test::OutputDeviceTestGradient does not verify anything, cannot test here
-
     void testErase()
     {
         {
@@ -583,6 +581,86 @@ public:
         }
     }
 
+    void testLinearGradient()
+    {
+        vcl::test::OutputDeviceTestGradient aOutDevTest;
+        Bitmap aBitmap = aOutDevTest.setupLinearGradient();
+        auto eResult = vcl::test::OutputDeviceTestGradient::checkLinearGradient(aBitmap);
+        exportImage("13-01_linear_gradient_test.png", aBitmap);
+        if (SHOULD_ASSERT)
+            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
+    }
+
+    void testLinearGradientAngled()
+    {
+        vcl::test::OutputDeviceTestGradient aOutDevTest;
+        Bitmap aBitmap = aOutDevTest.setupLinearGradientAngled();
+        auto eResult = vcl::test::OutputDeviceTestGradient::checkLinearGradientAngled(aBitmap);
+        exportImage("13-02_linear_gradient_angled_test.png", aBitmap);
+        if (SHOULD_ASSERT)
+            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
+    }
+
+    void testLinearGradientBorder()
+    {
+        vcl::test::OutputDeviceTestGradient aOutDevTest;
+        Bitmap aBitmap = aOutDevTest.setupLinearGradientBorder();
+        auto eResult = vcl::test::OutputDeviceTestGradient::checkLinearGradientBorder(aBitmap);
+        exportImage("13-03_linear_gradient_border_test.png", aBitmap);
+        if (SHOULD_ASSERT)
+            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
+    }
+
+    void testLinearGradientIntensity()
+    {
+        vcl::test::OutputDeviceTestGradient aOutDevTest;
+        Bitmap aBitmap = aOutDevTest.setupLinearGradientIntensity();
+        auto eResult = vcl::test::OutputDeviceTestGradient::checkLinearGradientIntensity(aBitmap);
+        exportImage("13-04_linear_gradient_intensity_test.png", aBitmap);
+        if (SHOULD_ASSERT)
+            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
+    }
+
+    void testLinearGradientSteps()
+    {
+        vcl::test::OutputDeviceTestGradient aOutDevTest;
+        Bitmap aBitmap = aOutDevTest.setupLinearGradientSteps();
+        auto eResult = vcl::test::OutputDeviceTestGradient::checkLinearGradientSteps(aBitmap);
+        exportImage("13-05_linear_gradient_steps_test.png", aBitmap);
+        if (SHOULD_ASSERT)
+            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
+    }
+
+    void testAxialGradient()
+    {
+        vcl::test::OutputDeviceTestGradient aOutDevTest;
+        Bitmap aBitmap = aOutDevTest.setupAxialGradient();
+        auto eResult = vcl::test::OutputDeviceTestGradient::checkAxialGradient(aBitmap);
+        exportImage("13-06_axial_gradient_test.png", aBitmap);
+        if (SHOULD_ASSERT)
+            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
+    }
+
+    void testRadialGradient()
+    {
+        vcl::test::OutputDeviceTestGradient aOutDevTest;
+        Bitmap aBitmap = aOutDevTest.setupRadialGradient();
+        auto eResult = vcl::test::OutputDeviceTestGradient::checkRadialGradient(aBitmap);
+        exportImage("13-07_radial_gradient_test.png", aBitmap);
+        if (SHOULD_ASSERT)
+            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
+    }
+
+    void testRadialGradientOfs()
+    {
+        vcl::test::OutputDeviceTestGradient aOutDevTest;
+        Bitmap aBitmap = aOutDevTest.setupRadialGradientOfs();
+        auto eResult = vcl::test::OutputDeviceTestGradient::checkRadialGradientOfs(aBitmap);
+        exportImage("13-08_radial_gradient_ofs_test.png", aBitmap);
+        if (SHOULD_ASSERT)
+            CPPUNIT_ASSERT(eResult != vcl::test::TestResult::Failed);
+    }
+
     // Test SalGraphics::blendBitmap() and blendAlphaBitmap() calls.
     void testDrawBlendExtended()
     {
@@ -744,6 +822,15 @@ public:
 
     CPPUNIT_TEST(testErase);
 
+    CPPUNIT_TEST(testLinearGradient);
+    CPPUNIT_TEST(testLinearGradientAngled);
+    CPPUNIT_TEST(testLinearGradientBorder);
+    CPPUNIT_TEST(testLinearGradientIntensity);
+    CPPUNIT_TEST(testLinearGradientSteps);
+    CPPUNIT_TEST(testAxialGradient);
+    CPPUNIT_TEST(testRadialGradient);
+    CPPUNIT_TEST(testRadialGradientOfs);
+
     CPPUNIT_TEST(testDrawBlendExtended);
 
     CPPUNIT_TEST(testTdf124848);


More information about the Libreoffice-commits mailing list