Repository.mk                                 |    1 
 include/tools/gen.hxx                         |    8 
 vcl/Executable_visualbackendtest.mk           |   58 ++
 vcl/Library_vcl.mk                            |   10 
 vcl/Module_vcl.mk                             |    1 
 vcl/backendtest/VisualBackendTest.cxx         |  582 ++++++++++++++++++++++++++
 vcl/backendtest/outputdevice/bitmap.cxx       |  154 ++++++
 vcl/backendtest/outputdevice/common.cxx       |  328 ++++++++++++++
 vcl/backendtest/outputdevice/gradient.cxx     |   43 +
 vcl/backendtest/outputdevice/line.cxx         |  114 +++++
 vcl/backendtest/outputdevice/outputdevice.cxx |   75 +++
 vcl/backendtest/outputdevice/pixel.cxx        |   57 ++
 vcl/backendtest/outputdevice/polygon.cxx      |  151 ++++++
 vcl/backendtest/outputdevice/polyline.cxx     |  140 ++++++
 vcl/backendtest/outputdevice/polypolygon.cxx  |   68 +++
 vcl/backendtest/outputdevice/rectangle.cxx    |   54 ++
 vcl/inc/test/outputdevice.hxx                 |  172 +++++++
 17 files changed, 2016 insertions(+)

New commits:
commit 3e6052ce1b9a24f82358e574de32919fa2b6499f
Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
Date:   Mon Aug 22 18:10:02 2016 +0900

    vcl: Adds backend tests to check for correct rendering
    
    Collection of test that check rendering of VCL backend. This works
    by rendering primitives to the VirtualDevice, outputing the result
    to a Bitmap and checking the rendering by comparing pixels. This
    should warn when the primitive is drawn to a wrong position or if
    it is the wrong size or color. It also test some rendering tricks
    like XOR drawing.
    
    Currently this is not implemented as a CPPUnit test but separately,
    as its own executable file so we can check the results visually.
    The idea is also to add the tests into LO for the user to test or
    automatically to test the backend on first start (especially
    useful to test the OpenGL backend as some drivers could have
    unexpected behaviour).
    
    Change-Id: I2a565b9986ca68850e2752466e3cd5fc71991ba6

diff --git a/Repository.mk b/Repository.mk
index 157fdd4..03573fb 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -74,6 +74,7 @@ $(eval $(call gb_Helper_register_executables,NONE, \
        vcldemo \
        tiledrendering \
     mtfdemo \
+    visualbackendtest \
        $(if $(and $(ENABLE_GTK3), $(filter LINUX %BSD SOLARIS,$(OS))), 
gtktiledviewer) \
 ))
 
diff --git a/include/tools/gen.hxx b/include/tools/gen.hxx
index cf8383f..987dd43 100644
--- a/include/tools/gen.hxx
+++ b/include/tools/gen.hxx
@@ -379,6 +379,7 @@ public:
      * Expands the rectangle in all directions by the input value.
      */
     inline void expand(long nExpandBy);
+    inline void shrink(long nShrinkBy);
 
 private:
     long                nLeft;
@@ -620,6 +621,13 @@ inline void Rectangle::expand(long nExpandBy)
     nBottom += nExpandBy;
 }
 
+inline void Rectangle::shrink(long nShrinkBy)
+{
+    nLeft   += nShrinkBy;
+    nTop    += nShrinkBy;
+    nRight  -= nShrinkBy;
+    nBottom -= nShrinkBy;
+}
 
 template< typename charT, typename traits >
 inline std::basic_ostream<charT, traits> & operator <<(
diff --git a/vcl/Executable_visualbackendtest.mk 
b/vcl/Executable_visualbackendtest.mk
new file mode 100644
index 0000000..abf8cbe
--- /dev/null
+++ b/vcl/Executable_visualbackendtest.mk
@@ -0,0 +1,58 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+#
+# 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/.
+#
+
+$(eval $(call gb_Executable_Executable,visualbackendtest))
+
+$(eval $(call gb_Executable_use_api,visualbackendtest,\
+    offapi \
+    udkapi \
+))
+
+$(eval $(call gb_Executable_use_external,visualbackendtest,boost_headers))
+
+$(eval $(call gb_Executable_set_include,visualbackendtest,\
+    $$(INCLUDE) \
+    -I$(SRCDIR)/vcl/inc \
+))
+
+$(eval $(call gb_Executable_use_libraries,visualbackendtest,\
+       basegfx \
+    comphelper \
+    cppu \
+    cppuhelper \
+    tl \
+    sal \
+       salhelper \
+    vcl \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,visualbackendtest,\
+    vcl/backendtest/VisualBackendTest \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,visualbackendtest,\
+    vclmain \
+))
+
+ifeq ($(OS),LINUX)
+$(eval $(call gb_Executable_add_libs,visualbackendtest,\
+       -lm \
+       -ldl \
+       -lpthread \
+    -lGL \
+    -lX11 \
+))
+
+$(eval $(call gb_Executable_use_static_libraries,visualbackendtest,\
+       glxtest \
+))
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index b79087d..19a6f52 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -399,6 +399,16 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/uitest/uitest \
     vcl/source/uitest/uno/uiobject_uno \
     vcl/source/uitest/uno/uitest_uno \
+    vcl/backendtest/outputdevice/bitmap \
+    vcl/backendtest/outputdevice/common \
+    vcl/backendtest/outputdevice/gradient \
+    vcl/backendtest/outputdevice/line \
+    vcl/backendtest/outputdevice/outputdevice \
+    vcl/backendtest/outputdevice/pixel \
+    vcl/backendtest/outputdevice/polygon \
+    vcl/backendtest/outputdevice/polypolygon \
+    vcl/backendtest/outputdevice/polyline \
+    vcl/backendtest/outputdevice/rectangle \
 ))
 
 $(eval $(call gb_Library_add_cobjects,vcl,\
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index ad08e59..19f9f76 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -35,6 +35,7 @@ $(eval $(call gb_Module_add_targets,vcl,\
                        $(if $(ENABLE_HEADLESS),, \
                                Executable_vcldemo \
                                Executable_icontest \
+                               Executable_visualbackendtest \
                                Executable_mtfdemo ))) \
     $(if $(filter-out ANDROID IOS WNT,$(OS)), \
         Executable_svdemo \
diff --git a/vcl/backendtest/VisualBackendTest.cxx 
b/vcl/backendtest/VisualBackendTest.cxx
new file mode 100644
index 0000000..7b6bc02
--- /dev/null
+++ b/vcl/backendtest/VisualBackendTest.cxx
@@ -0,0 +1,582 @@
+/* -*- 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 <math.h>
+#include <rtl/math.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/random.hxx>
+#include <cppuhelper/bootstrap.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/registry/XSimpleRegistry.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+
+#include <osl/time.h>
+#include <vcl/vclmain.hxx>
+#include <vcl/layout.hxx>
+#include <salhelper/thread.hxx>
+
+#include <tools/urlobj.hxx>
+#include <tools/stream.hxx>
+#include <tools/poly.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/pngread.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/button.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/pngwrite.hxx>
+#include <vcl/floatwin.hxx>
+#include <vcl/salbtype.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <vcl/help.hxx>
+
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <vcldemo-debug.hxx>
+
+#include <rtl/math.hxx>
+
+#include <chrono>
+#include <iostream>
+
+#include "test/outputdevice.hxx"
+
+using namespace css;
+
+void drawBitmapCentered(Rectangle& rRect, Bitmap aBitmap, vcl::RenderContext& 
rRenderContext)
+{
+    long nWidth = rRect.GetWidth();
+    long nHeight = rRect.GetHeight();
+
+    Size aBitmapSize(aBitmap.GetSizePixel());
+
+    Point aPoint(rRect.TopLeft());
+
+    aPoint.X() += (nWidth  - aBitmapSize.Width())  / 2;
+    aPoint.Y() += (nHeight - aBitmapSize.Height()) / 2;
+
+    rRenderContext.DrawBitmap(aPoint, aBitmap);
+}
+
+void drawBitmapScaledAndCentered(Rectangle& rRect, Bitmap aBitmap, 
vcl::RenderContext& rRenderContext, BmpScaleFlag aFlag = BmpScaleFlag::Fast)
+{
+    long nWidth = rRect.GetWidth();
+    long nHeight = rRect.GetHeight();
+
+    Size aBitmapSize(aBitmap.GetSizePixel());
+
+    double fWidthHeight = nWidth > nHeight ? nHeight : nWidth;
+    double fScale = fWidthHeight / aBitmapSize.Width();
+    aBitmap.Scale(fScale, fScale, aFlag);
+
+    drawBitmapCentered(rRect, aBitmap, rRenderContext);
+}
+
+void drawBackgroundRect(Rectangle& rRect, Color aColor, vcl::RenderContext& 
rRenderContext)
+{
+    rRenderContext.Push(PushFlags::LINECOLOR | PushFlags::FILLCOLOR);
+    rRenderContext.SetFillColor(aColor);
+    rRenderContext.SetLineColor(aColor);
+    rRenderContext.DrawRect(rRect);
+    rRenderContext.Pop();
+}
+
+void assertAndSetBackground(vcl::test::TestResult eResult, Rectangle& rRect, 
vcl::RenderContext& rRenderContext)
+{
+    if (eResult == vcl::test::TestResult::Passed)
+        drawBackgroundRect(rRect, COL_GREEN, rRenderContext);
+    else if (eResult == vcl::test::TestResult::PassedWithQuirks)
+        drawBackgroundRect(rRect, COL_YELLOW, rRenderContext);
+    else if (eResult == vcl::test::TestResult::Failed)
+        drawBackgroundRect(rRect, COL_RED, rRenderContext);
+}
+
+class VisualBackendTestWindow : public WorkWindow
+{
+private:
+    Timer maUpdateTimer;
+    std::vector<std::chrono::high_resolution_clock::time_point> mTimePoints;
+    unsigned char mnNumberOfTests;
+    unsigned char mnTest;
+    bool mbAnimate;
+    ScopedVclPtr<VirtualDevice> mpVDev;
+
+public:
+    VisualBackendTestWindow()
+        : WorkWindow(NULL, WB_APP | WB_STDWORK)
+        , mnNumberOfTests(6)
+        , mnTest(10 * mnNumberOfTests)
+        , mbAnimate(mnTest % mnNumberOfTests == mnNumberOfTests - 1)
+        , mpVDev(VclPtr<VirtualDevice>::Create())
+    {
+        maUpdateTimer.SetTimeoutHdl(LINK(this, VisualBackendTestWindow, 
updateHdl));
+        maUpdateTimer.SetPriority(SchedulerPriority::REPAINT);
+        if (mbAnimate)
+        {
+            maUpdateTimer.SetTimeout(1000.0);
+            maUpdateTimer.Start();
+        }
+    }
+
+    virtual ~VisualBackendTestWindow()
+    {
+        disposeOnce();
+    }
+
+    DECL_LINK_TYPED(updateHdl, Timer*, void);
+
+    virtual void dispose() override
+    {
+        WorkWindow::dispose();
+    }
+
+    virtual void KeyInput(const KeyEvent& rKEvt) override
+    {
+        sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+
+        if (nCode == KEY_BACKSPACE)
+            mnTest--;
+        else if(nCode == KEY_SPACE)
+            mnTest++;
+
+        if (nCode == KEY_BACKSPACE || nCode == KEY_SPACE)
+        {
+            if (mnTest % mnNumberOfTests == mnNumberOfTests - 1)
+            {
+                mbAnimate = true;
+                maUpdateTimer.Start();
+            }
+            else
+            {
+                mbAnimate = false;
+                Invalidate();
+            }
+        }
+    }
+
+    std::vector<Rectangle> setupRegions(int nPartitionsX, int nPartitionsY, 
int nWidth, int nHeight)
+    {
+        std::vector<Rectangle> aRegions;
+
+        for (int y = 0; y < nPartitionsY; y++)
+        {
+            for (int x = 0; x < nPartitionsX; x++)
+            {
+                long x1 =  x    * (nWidth  / nPartitionsX);
+                long y1 =  y    * (nHeight / nPartitionsY);
+                long x2 = (x+1) * (nWidth  / nPartitionsX);
+                long y2 = (y+1) * (nHeight / nPartitionsY);
+
+                aRegions.push_back(Rectangle(x1 + 1, y1 + 1, x2 - 2, y2 - 2));
+            }
+        }
+        return aRegions;
+    }
+
+    void testRectangles(vcl::RenderContext& rRenderContext, int nWidth, int 
nHeight)
+    {
+        Rectangle aRectangle;
+        size_t index = 0;
+
+        std::vector<Rectangle> aRegions = setupRegions(3, 2, nWidth, nHeight);
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestRect aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupRectangle();
+            assertAndSetBackground(aOutDevTest.checkRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPixel aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupRectangle();
+            assertAndSetBackground(aOutDevTest.checkRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestLine aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupRectangle();
+            assertAndSetBackground(aOutDevTest.checkRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolygon aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupRectangle();
+            assertAndSetBackground(aOutDevTest.checkRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolyLine aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupRectangle();
+            assertAndSetBackground(aOutDevTest.checkRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolyPolygon aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupRectangle();
+            assertAndSetBackground(aOutDevTest.checkRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+    }
+
+    void testFilledRectangles(vcl::RenderContext& rRenderContext, int nWidth, 
int nHeight)
+    {
+        Rectangle aRectangle;
+        size_t index = 0;
+
+        std::vector<Rectangle> aRegions = setupRegions(3, 2, nWidth, nHeight);
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestRect aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupFilledRectangle();
+            assertAndSetBackground(aOutDevTest.checkFilledRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolygon aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupFilledRectangle();
+            assertAndSetBackground(aOutDevTest.checkFilledRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolyPolygon aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupFilledRectangle();
+            assertAndSetBackground(aOutDevTest.checkFilledRectangle(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolygon aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupDiamond();
+            assertAndSetBackground(aOutDevTest.checkDiamond(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestLine aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupDiamond();
+            assertAndSetBackground(aOutDevTest.checkDiamond(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolyLine aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupDiamond();
+            assertAndSetBackground(aOutDevTest.checkDiamond(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+    }
+
+    void testLines(vcl::RenderContext& rRenderContext, int nWidth, int nHeight)
+    {
+        Rectangle aRectangle;
+        size_t index = 0;
+
+        std::vector<Rectangle> aRegions = setupRegions(3, 2, nWidth, nHeight);
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestLine aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupLines();
+            assertAndSetBackground(aOutDevTest.checkLines(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolyLine aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupLines();
+            assertAndSetBackground(aOutDevTest.checkLines(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolygon aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupLines();
+            assertAndSetBackground(aOutDevTest.checkLines(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestLine aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupAALines();
+            assertAndSetBackground(aOutDevTest.checkAALines(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolyLine aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupAALines();
+            assertAndSetBackground(aOutDevTest.checkAALines(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestPolygon aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupAALines();
+            assertAndSetBackground(aOutDevTest.checkAALines(aBitmap), 
aRectangle, rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+    }
+
+    void testBitmaps(vcl::RenderContext& rRenderContext, int nWidth, int 
nHeight)
+    {
+        Rectangle aRectangle;
+        size_t index = 0;
+
+        std::vector<Rectangle> aRegions = setupRegions(2, 2, nWidth, nHeight);
+
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestBitmap aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupDrawBitmap();
+            
assertAndSetBackground(aOutDevTest.checkTransformedBitmap(aBitmap), aRectangle, 
rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestBitmap aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupDrawTransformedBitmap();
+            
assertAndSetBackground(aOutDevTest.checkTransformedBitmap(aBitmap), aRectangle, 
rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestBitmap aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupDrawBitmapExWithAlpha();
+            
assertAndSetBackground(aOutDevTest.checkBitmapExWithAlpha(aBitmap), aRectangle, 
rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+        aRectangle = aRegions[index++];
+        {
+            vcl::test::OutputDeviceTestBitmap aOutDevTest;
+            Bitmap aBitmap = aOutDevTest.setupDrawMask();
+            assertAndSetBackground(aOutDevTest.checkMask(aBitmap), aRectangle, 
rRenderContext);
+            drawBitmapScaledAndCentered(aRectangle, aBitmap, rRenderContext);
+        }
+    }
+
+    virtual void Paint(vcl::RenderContext& rRenderContext, const Rectangle& 
/*rRect*/) override
+    {
+        if (mnTest % mnNumberOfTests == mnNumberOfTests - 1)
+        {
+            rRenderContext.SetBackground(Wallpaper(COL_GREEN));
+
+            static size_t nTimeIndex = 0;
+            static const size_t constSamplesFPS = 120;
+            double fps = 0.0;
+
+            if (mTimePoints.size() < constSamplesFPS)
+            {
+                
mTimePoints.push_back(std::chrono::high_resolution_clock::now());
+                nTimeIndex++;
+            }
+            else
+            {
+                size_t current = nTimeIndex % constSamplesFPS;
+                mTimePoints[current] = 
std::chrono::high_resolution_clock::now();
+                size_t last = (nTimeIndex + 1) % constSamplesFPS;
+                auto ms = 
std::chrono::duration_cast<std::chrono::milliseconds>(mTimePoints[current] - 
mTimePoints[last]).count();
+                fps = constSamplesFPS * 1000.0 / ms;
+                nTimeIndex++;
+            }
+
+            double fTime = 0.5 + std::sin(nTimeIndex / 100.0) / 2.0;
+
+            Size aSizePixel = GetSizePixel();
+
+            mpVDev->SetAntialiasing(AntialiasingFlags::EnableB2dDraw | 
AntialiasingFlags::PixelSnapHairline);
+            mpVDev->SetOutputSizePixel(aSizePixel);
+            mpVDev->SetBackground(Wallpaper(COL_LIGHTGRAY));
+            mpVDev->Erase();
+            mpVDev->SetFillColor(COL_LIGHTRED);
+            mpVDev->SetLineColor(COL_LIGHTBLUE);
+
+            basegfx::B2DPolyPolygon polyPolygon;
+
+            for (int b=10; b<14; b++)
+            {
+                basegfx::B2DPolygon polygon;
+                for (double a=0.0; a<360.0; a+=0.5)
+                {
+                    double x = std::sin(a*M_PI / 180.0) * (b+1) * 20;
+                    double y = std::cos(a*M_PI / 180.0) * (b+1) * 20;
+                    polygon.append(basegfx::B2DPoint(x + 200 + 500 * fTime, y 
+ 200 + 500 * fTime));
+                }
+                polygon.setClosed(true);
+                polyPolygon.append(polygon);
+            }
+
+            mpVDev->DrawPolyPolygon(polyPolygon);
+
+            Rectangle aGradientRect(Point(200, 200), Size(200 + fTime * 300, 
200 + fTime * 300));
+            mpVDev->DrawGradient(aGradientRect, Gradient(GradientStyle_LINEAR, 
COL_YELLOW, COL_BLUE));
+
+            rRenderContext.DrawOutDev(Point(), mpVDev->GetOutputSizePixel(),
+                                      Point(), mpVDev->GetOutputSizePixel(),
+                                      *mpVDev.get());
+            rRenderContext.SetTextColor(COL_LIGHTRED);
+            rRenderContext.DrawText(Point(10, 10), OUString("FPS: ") + 
OUString::number(int(fps)));
+            return;
+        }
+
+        rRenderContext.SetBackground(Wallpaper(COL_GREEN));
+
+        Size aSize = GetOutputSizePixel();
+
+        long nWidth = aSize.Width();
+        long nHeight = aSize.Height();
+
+        Rectangle aRectangle;
+        size_t index = 0;
+
+        if (mnTest % mnNumberOfTests == 0)
+        {
+            testRectangles(rRenderContext, nWidth, nHeight);
+        }
+        else if (mnTest % mnNumberOfTests == 1)
+        {
+            testFilledRectangles(rRenderContext, nWidth, nHeight);
+        }
+        else if (mnTest % mnNumberOfTests == 2)
+        {
+            testLines(rRenderContext, nWidth, nHeight);
+        }
+        else if (mnTest % mnNumberOfTests == 3)
+        {
+            testBitmaps(rRenderContext, nWidth, nHeight);
+        }
+        else if (mnTest % mnNumberOfTests == 4)
+        {
+            std::vector<Rectangle> aRegions = setupRegions(3, 2, nWidth, 
nHeight);
+
+            aRectangle = aRegions[index++];
+            {
+                vcl::test::OutputDeviceTestAnotherOutDev aOutDevTest;
+                Bitmap aBitmap = aOutDevTest.setupDrawOutDev();
+                assertAndSetBackground(aOutDevTest.checkDrawOutDev(aBitmap), 
aRectangle, rRenderContext);
+                drawBitmapScaledAndCentered(aRectangle, aBitmap, 
rRenderContext);
+            }
+            aRectangle = aRegions[index++];
+            {
+                vcl::test::OutputDeviceTestAnotherOutDev aOutDevTest;
+                Bitmap aBitmap = aOutDevTest.setupXOR();
+                assertAndSetBackground(aOutDevTest.checkXOR(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);
+            }
+        }
+    }
+};
+
+IMPL_LINK_NOARG_TYPED(VisualBackendTestWindow, updateHdl, Timer *, void)
+{
+    if (mbAnimate)
+    {
+        maUpdateTimer.SetTimeout(1000.0 / 60.0);
+        maUpdateTimer.Start();
+        Invalidate();
+    }
+}
+
+class VisualBackendTestApp : public Application
+{
+
+public:
+    VisualBackendTestApp()
+    {}
+
+    virtual int Main() override
+    {
+        try
+        {
+            ScopedVclPtrInstance<VisualBackendTestWindow> aMainWindow;
+
+            aMainWindow->SetText("VCL Test");
+            aMainWindow->Show();
+
+            Application::Execute();
+        }
+        catch (const css::uno::Exception& rException)
+        {
+            SAL_WARN("vcl.app", "Fatal exception: " << rException.Message);
+            return 1;
+        }
+        catch (const std::exception& rException)
+        {
+            SAL_WARN("vcl.app", "Fatal exception: " << rException.what());
+            return 1;
+        }
+        return 0;
+    }
+
+protected:
+    uno::Reference<lang::XMultiServiceFactory> xMSF;
+
+    void Init() override
+    {
+        try
+        {
+            uno::Reference<uno::XComponentContext> xComponentContext = 
::cppu::defaultBootstrap_InitialComponentContext();
+            xMSF = 
uno::Reference<lang::XMultiServiceFactory>(xComponentContext->getServiceManager(),
 uno::UNO_QUERY);
+
+            if (!xMSF.is())
+                Application::Abort("Bootstrap failure - no service manager");
+
+            comphelper::setProcessServiceFactory(xMSF);
+        }
+        catch (const uno::Exception &e)
+        {
+            Application::Abort("Bootstrap exception " + e.Message);
+        }
+    }
+
+    void DeInit() override
+    {
+        uno::Reference<lang::XComponent> 
xComponent(comphelper::getProcessComponentContext(), uno::UNO_QUERY_THROW);
+        xComponent->dispose();
+        comphelper::setProcessServiceFactory(nullptr);
+    }
+};
+
+void vclmain::createApplication()
+{
+    static VisualBackendTestApp aApplication;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/bitmap.cxx 
b/vcl/backendtest/outputdevice/bitmap.cxx
new file mode 100644
index 0000000..351dfa2
--- /dev/null
+++ b/vcl/backendtest/outputdevice/bitmap.cxx
@@ -0,0 +1,154 @@
+/* -*- 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/outputdevice.hxx"
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+namespace vcl {
+namespace test {
+
+Bitmap OutputDeviceTestBitmap::setupDrawTransformedBitmap()
+{
+    Size aBitmapSize(9, 9);
+    Bitmap aBitmap(aBitmapSize, 24);
+    {
+        Bitmap::ScopedWriteAccess aWriteAccess(aBitmap);
+        aWriteAccess->Erase(constFillColor);
+        aWriteAccess->SetLineColor(COL_YELLOW);
+        aWriteAccess->DrawRect(Rectangle(0, 0,  8, 8));
+        aWriteAccess->DrawRect(Rectangle(2, 2,  6, 6));
+    }
+
+    initialSetup(13, 13, constBackgroundColor);
+
+    basegfx::B2DHomMatrix aTransform;
+    aTransform.scale(aBitmapSize.Width(), aBitmapSize.Height());
+    aTransform.translate((maVDRectangle.GetWidth()  / 2.0) - 
(aBitmapSize.Width() / 2.0),
+                         (maVDRectangle.GetHeight() / 2.0) - 
(aBitmapSize.Height() / 2.0));
+
+    mpVirtualDevice->DrawTransformedBitmapEx(aTransform, BitmapEx(aBitmap));
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+
+Bitmap OutputDeviceTestBitmap::setupDrawBitmap()
+{
+    Size aBitmapSize(9, 9);
+    Bitmap aBitmap(aBitmapSize, 24);
+    {
+        Bitmap::ScopedWriteAccess aWriteAccess(aBitmap);
+        aWriteAccess->Erase(constFillColor);
+        aWriteAccess->SetLineColor(COL_YELLOW);
+        aWriteAccess->DrawRect(Rectangle(0, 0,  8, 8));
+        aWriteAccess->DrawRect(Rectangle(2, 2,  6, 6));
+    }
+
+    initialSetup(13, 13, constBackgroundColor);
+
+    Point aPoint((maVDRectangle.GetWidth()  / 2.0) - (aBitmapSize.Width() / 
2.0),
+                 (maVDRectangle.GetHeight() / 2.0) - (aBitmapSize.Height() / 
2.0));
+
+    mpVirtualDevice->DrawBitmapEx(aPoint, BitmapEx(aBitmap));
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestBitmap::setupDrawBitmapExWithAlpha()
+{
+    Size aBitmapSize(9, 9);
+    Bitmap aBitmap(aBitmapSize, 24);
+    {
+        Bitmap::ScopedWriteAccess aWriteAccess(aBitmap);
+        aWriteAccess->Erase(COL_WHITE);
+        aWriteAccess->SetLineColor(Color(0xFF, 0xFF, 0x00));
+        aWriteAccess->DrawRect(Rectangle(0, 0, 8, 8));
+        aWriteAccess->DrawRect(Rectangle(3, 3, 5, 5));
+    }
+
+    AlphaMask aAlpha(aBitmapSize);
+    {
+        AlphaMask::ScopedWriteAccess aWriteAccess(aAlpha);
+        aWriteAccess->Erase(COL_WHITE);
+        aWriteAccess->SetLineColor(Color(0x44, 0x44, 0x44));
+        aWriteAccess->DrawRect(Rectangle(0, 0, 8, 8));
+        aWriteAccess->DrawRect(Rectangle(3, 3, 5, 5));
+    }
+
+    initialSetup(13, 13, constBackgroundColor);
+
+    Point aPoint(alignToCenter(maVDRectangle, Rectangle(Point(), 
aBitmapSize)).TopLeft());
+
+    mpVirtualDevice->DrawBitmapEx(aPoint, BitmapEx(aBitmap, aAlpha));
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestBitmap::setupDrawMask()
+{
+    Size aBitmapSize(9, 9);
+    Bitmap aBitmap(aBitmapSize, 24);
+    {
+        Bitmap::ScopedWriteAccess aWriteAccess(aBitmap);
+        aWriteAccess->Erase(COL_WHITE);
+        aWriteAccess->SetLineColor(COL_BLACK);
+        aWriteAccess->DrawRect(Rectangle(0, 0,  8, 8));
+        aWriteAccess->DrawRect(Rectangle(3, 3,  5, 5));
+    }
+
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->DrawMask(Point(2, 2), aBitmap, constFillColor);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+TestResult OutputDeviceTestBitmap::checkBitmap(Bitmap& rBitmap)
+{
+    std::vector<Color> aExpected
+    {
+        constBackgroundColor, constBackgroundColor,
+        COL_YELLOW, constFillColor, COL_YELLOW, constFillColor, constFillColor
+    };
+    return checkRectangles(rBitmap, aExpected);
+}
+
+TestResult OutputDeviceTestBitmap::checkTransformedBitmap(Bitmap& rBitmap)
+{
+    std::vector<Color> aExpected
+    {
+        constBackgroundColor, constBackgroundColor,
+        COL_YELLOW, constFillColor, COL_YELLOW, constFillColor, constFillColor
+    };
+    return checkRectangles(rBitmap, aExpected);
+}
+
+TestResult OutputDeviceTestBitmap::checkBitmapExWithAlpha(Bitmap& rBitmap)
+{
+    const Color aBlendedColor(0xEE, 0xEE, 0x33);
+
+    std::vector<Color> aExpected
+    {
+        constBackgroundColor, constBackgroundColor,
+        aBlendedColor, constBackgroundColor, constBackgroundColor,
+        aBlendedColor, constBackgroundColor
+    };
+    return checkRectangles(rBitmap, aExpected);
+}
+
+TestResult OutputDeviceTestBitmap::checkMask(Bitmap& rBitmap)
+{
+    return checkRectangle(rBitmap);
+}
+
+}} // end namespace vcl::test
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/common.cxx 
b/vcl/backendtest/outputdevice/common.cxx
new file mode 100644
index 0000000..ae3ef59
--- /dev/null
+++ b/vcl/backendtest/outputdevice/common.cxx
@@ -0,0 +1,328 @@
+/* -*- 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/outputdevice.hxx"
+
+namespace vcl {
+namespace test {
+
+namespace
+{
+
+int deltaColor(BitmapColor aColor1, BitmapColor aColor2)
+{
+    int deltaR = std::abs(aColor1.GetRed()   - aColor2.GetRed());
+    int deltaG = std::abs(aColor1.GetGreen() - aColor2.GetGreen());
+    int deltaB = std::abs(aColor1.GetBlue()  - aColor2.GetBlue());
+
+    return std::max(std::max(deltaR, deltaG), deltaB);
+}
+
+void checkValue(Bitmap::ScopedWriteAccess& pAccess, int x, int y, Color 
aExpected,
+                      int& nNumberOfQuirks, int& nNumberOfErrors, bool 
bQuirkMode, int nColorDeltaThresh = 0)
+{
+    bool bColorize = false;
+    Color aColor = pAccess->GetPixel(y, x);
+    int nColorDelta = deltaColor(aColor, aExpected);
+
+    if (nColorDelta <= nColorDeltaThresh)
+    {
+        if (bColorize)
+            pAccess->SetPixel(y, x, Color(COL_LIGHTGREEN));
+    }
+    else if (bQuirkMode)
+    {
+        nNumberOfQuirks++;
+        if (bColorize)
+            pAccess->SetPixel(y, x, Color(COL_YELLOW));
+    }
+    else
+    {
+        nNumberOfErrors++;
+        if (bColorize)
+            pAccess->SetPixel(y, x, Color(COL_LIGHTRED));
+    }
+}
+
+TestResult checkRect(Bitmap& rBitmap, int aLayerNumber, Color aExpectedColor)
+{
+    Bitmap::ScopedWriteAccess pAccess(rBitmap);
+    long nHeight = pAccess->Height();
+    long nWidth = pAccess->Width();
+
+    long firstX = 0 + aLayerNumber;
+    long firstY = 0 + aLayerNumber;
+
+    long lastX = nWidth  - aLayerNumber - 1;
+    long lastY = nHeight - aLayerNumber - 1;
+
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+
+    // check corner quirks
+    checkValue(pAccess, firstX, firstY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true);
+    checkValue(pAccess, lastX,  firstY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true);
+    checkValue(pAccess, firstX, lastY,  aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true);
+    checkValue(pAccess, lastX,  lastY,  aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true);
+
+    for (long y = firstY + 1; y <= lastY - 1; y++)
+    {
+        checkValue(pAccess, firstX, y,  aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, false);
+        checkValue(pAccess, lastX,  y,  aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, false);
+    }
+    for (long x = firstX + 1; x <= lastX - 1; x++)
+    {
+        checkValue(pAccess, x, firstY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, false);
+        checkValue(pAccess, x, lastY,  aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, false);
+    }
+    if (nNumberOfQuirks > 0)
+        aResult = TestResult::PassedWithQuirks;
+    if (nNumberOfErrors > 0)
+        aResult = TestResult::Failed;
+    return aResult;
+}
+
+TestResult checkHorizontalVerticalDiagonalLines(Bitmap& rBitmap, Color 
aExpectedColor, int nColorThresh)
+{
+    Bitmap::ScopedWriteAccess pAccess(rBitmap);
+    long nWidth  = pAccess->Width();
+    long nHeight = pAccess->Height();
+
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+
+    // check horizontal line
+    {
+        long startX = 4;
+        long endX   = nWidth - 2;
+
+        long y = 1;
+
+        checkValue(pAccess, startX, y, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true, nColorThresh);
+        checkValue(pAccess, endX,   y, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true, nColorThresh);
+
+        for (int x = startX + 1; x <= endX - 1; x++)
+        {
+            checkValue(pAccess, x, y, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, false, nColorThresh);
+        }
+    }
+
+    // check vertical line
+    {
+        long startY = 4;
+        long endY   = nHeight - 2;
+
+        long x = 1;
+
+        checkValue(pAccess, x, startY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true, nColorThresh);
+        checkValue(pAccess, x, endY,   aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true, nColorThresh);
+
+        for (int y = startY + 1; y <= endY - 1; y++)
+        {
+            checkValue(pAccess, x, y, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, false, nColorThresh);
+        }
+    }
+
+    // check diagonal line
+    {
+        long startX = 1;
+        long endX   = nWidth - 2;
+
+        long startY = 1;
+        long endY   = nHeight - 2;
+
+        long x = startX;
+        long y = startY;
+
+        checkValue(pAccess, startX, startY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true, nColorThresh);
+        checkValue(pAccess, endX,   endY,   aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true, nColorThresh);
+
+        x++; y++;
+
+        while(y <= endY - 1  && x <= endX - 1)
+        {
+            checkValue(pAccess, x, y, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, false, nColorThresh);
+            x++; y++;
+        }
+    }
+
+    if (nNumberOfQuirks > 0)
+        aResult = TestResult::PassedWithQuirks;
+    if (nNumberOfErrors > 0)
+        aResult = TestResult::Failed;
+    return aResult;
+}
+
+TestResult checkDiamondLine(Bitmap& rBitmap, int aLayerNumber, Color 
aExpectedColor)
+{
+    Bitmap::ScopedWriteAccess pAccess(rBitmap);
+    long nHeight = pAccess->Height();
+    long nWidth = pAccess->Width();
+
+    long midX = nWidth  / 2;
+    long midY = nHeight / 2;
+
+    long firstX = aLayerNumber;
+    long lastX  = nWidth - aLayerNumber - 1;
+
+    long firstY = aLayerNumber;
+    long lastY  = nHeight - aLayerNumber - 1;
+
+    long offsetFromMid = 0;
+
+    TestResult aResult = TestResult::Passed;
+    int nNumberOfQuirks = 0;
+    int nNumberOfErrors = 0;
+
+    checkValue(pAccess, firstX, midY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true);
+    checkValue(pAccess, lastX,  midY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true);
+    checkValue(pAccess, midX, firstY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true);
+    checkValue(pAccess, midX,  lastY, aExpectedColor, nNumberOfQuirks, 
nNumberOfErrors, true);
+
+    offsetFromMid = 1;
+    for (long x = firstX + 1; x <= midX - 1; x++)
+    {
+        checkValue(pAccess, x, midY - offsetFromMid, aExpectedColor, 
nNumberOfQuirks, nNumberOfErrors, false);
+        checkValue(pAccess, x, midY + offsetFromMid, aExpectedColor, 
nNumberOfQuirks, nNumberOfErrors, false);
+
+        offsetFromMid++;
+    }
+
+    offsetFromMid = midY - aLayerNumber - 1;
+
+    for (long x = midX + 1; x <= lastX - 1; x++)
+    {
+        checkValue(pAccess, x, midY - offsetFromMid, aExpectedColor, 
nNumberOfQuirks, nNumberOfErrors, false);
+        checkValue(pAccess, x, midY + offsetFromMid, aExpectedColor, 
nNumberOfQuirks, nNumberOfErrors, false);
+
+        offsetFromMid--;
+    }
+
+    if (nNumberOfQuirks > 0)
+        aResult = TestResult::PassedWithQuirks;
+    if (nNumberOfErrors > 0)
+        aResult = TestResult::Failed;
+    return aResult;
+}
+
+} // end anonymous namespace
+
+const Color OutputDeviceTestCommon::constBackgroundColor(COL_LIGHTGRAY);
+const Color OutputDeviceTestCommon::constLineColor(COL_LIGHTBLUE);
+const Color OutputDeviceTestCommon::constFillColor(COL_LIGHTBLUE);
+
+OutputDeviceTestCommon::OutputDeviceTestCommon()
+    : mpVirtualDevice(VclPtr<VirtualDevice>::Create())
+{}
+
+void OutputDeviceTestCommon::initialSetup(long nWidth, long nHeight, Color 
aColor)
+{
+    maVDRectangle = Rectangle(Point(), Size (nWidth, nHeight));
+    mpVirtualDevice->SetOutputSizePixel(maVDRectangle.GetSize());
+    mpVirtualDevice->SetBackground(Wallpaper(aColor));
+    mpVirtualDevice->Erase();
+}
+
+TestResult OutputDeviceTestCommon::checkLines(Bitmap& rBitmap)
+{
+    return checkHorizontalVerticalDiagonalLines(rBitmap, constLineColor, 0);
+}
+
+TestResult OutputDeviceTestCommon::checkAALines(Bitmap& rBitmap)
+{
+    return checkHorizontalVerticalDiagonalLines(rBitmap, constLineColor, 30); 
// 30 color values threshold delta
+}
+
+TestResult OutputDeviceTestCommon::checkRectangle(Bitmap& aBitmap)
+{
+    std::vector<Color> aExpected
+    {
+        constBackgroundColor, constBackgroundColor, constLineColor,
+        constBackgroundColor, constBackgroundColor, constLineColor, 
constBackgroundColor
+    };
+    return checkRectangles(aBitmap, aExpected);
+}
+
+TestResult OutputDeviceTestCommon::checkFilledRectangle(Bitmap& aBitmap)
+{
+    std::vector<Color> aExpected
+    {
+        constBackgroundColor, constBackgroundColor,
+        constFillColor, constFillColor, constFillColor, constFillColor, 
constFillColor
+    };
+    return checkRectangles(aBitmap, aExpected);
+}
+
+TestResult OutputDeviceTestCommon::checkRectangles(Bitmap& aBitmap, 
std::vector<Color>& aExpectedColors)
+{
+    TestResult aReturnValue = TestResult::Passed;
+    for (size_t i = 0; i < aExpectedColors.size(); i++)
+    {
+        switch(checkRect(aBitmap, i, aExpectedColors[i]))
+        {
+            case TestResult::Failed:
+                return TestResult::Failed;
+            case TestResult::PassedWithQuirks:
+                aReturnValue = TestResult::PassedWithQuirks;
+                break;
+            default:
+                break;
+        }
+
+    }
+    return aReturnValue;
+}
+
+Rectangle OutputDeviceTestCommon::alignToCenter(Rectangle aRect1, Rectangle 
aRect2)
+{
+    Point aPoint((aRect1.GetWidth()  / 2.0) - (aRect2.GetWidth()  / 2.0),
+                 (aRect1.GetHeight() / 2.0) - (aRect2.GetHeight() / 2.0));
+
+    return Rectangle(aPoint, aRect2.GetSize());
+}
+
+TestResult OutputDeviceTestCommon::checkDiamond(Bitmap& rBitmap)
+{
+    return checkDiamondLine(rBitmap, 1, constLineColor);
+}
+
+void OutputDeviceTestCommon::createDiamondPoints(Rectangle rRect, int nOffset,
+                                                 Point& rPoint1, Point& 
rPoint2,
+                                                 Point& rPoint3, Point& 
rPoint4)
+{
+    long midPointX = rRect.Left() + (rRect.Right()  - rRect.Left()) / 2.0;
+    long midPointY = rRect.Top()  + (rRect.Bottom() - rRect.Top())  / 2.0;
+
+    rPoint1 = Point(midPointX         , midPointY - nOffset);
+    rPoint2 = Point(midPointX + nOffset, midPointY         );
+    rPoint3 = Point(midPointX         , midPointY + nOffset);
+    rPoint4 = Point(midPointX - nOffset, midPointY         );
+}
+
+void 
OutputDeviceTestCommon::createHorizontalVerticalDiagonalLinePoints(Rectangle 
rRect,
+                                Point& rHorizontalLinePoint1, Point& 
rHorizontalLinePoint2,
+                                Point& rVerticalLinePoint1, Point& 
rVerticalLinePoint2,
+                                Point& rDiagonalLinePoint1, Point& 
rDiagonalLinePoint2)
+{
+    rHorizontalLinePoint1 = Point(4, 1);
+    rHorizontalLinePoint2 = Point(rRect.Right() - 1, 1);
+
+    rVerticalLinePoint1 = Point(1, 4);
+    rVerticalLinePoint2 = Point(1,rRect.Bottom() - 1);
+
+    rDiagonalLinePoint1 = Point(1, 1);
+    rDiagonalLinePoint2 = Point(rRect.Right() - 1, rRect.Bottom() - 1);
+}
+
+}} // 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
new file mode 100644
index 0000000..f686e32
--- /dev/null
+++ b/vcl/backendtest/outputdevice/gradient.cxx
@@ -0,0 +1,43 @@
+/* -*- 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/outputdevice.hxx"
+
+namespace vcl {
+namespace test {
+
+Bitmap OutputDeviceTestGradient::setupLinearGradient()
+{
+    initialSetup(12, 12, constBackgroundColor);
+
+    Gradient aGradient(GradientStyle_LINEAR, Color(0xFF, 0xFF, 0xFF), 
Color(0x00, 0x00, 0x00));
+    aGradient.SetAngle(900);
+    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);
+
+    Gradient aGradient(GradientStyle_RADIAL, Color(0xFF, 0xFF, 0xFF), 
Color(0x00, 0x00, 0x00));
+    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/backendtest/outputdevice/line.cxx 
b/vcl/backendtest/outputdevice/line.cxx
new file mode 100644
index 0000000..4cd39db
--- /dev/null
+++ b/vcl/backendtest/outputdevice/line.cxx
@@ -0,0 +1,114 @@
+/* -*- 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/outputdevice.hxx"
+
+namespace vcl {
+namespace test {
+
+namespace
+{
+
+void drawLineOffset(OutputDevice& rDevice, Rectangle& rRect, int nOffset)
+{
+    Point aLeftTop     (rRect.Left()  + nOffset, rRect.Top()    + nOffset);
+    Point aRightTop    (rRect.Right() - nOffset, rRect.Top()    + nOffset);
+    Point aLeftBottom  (rRect.Left()  + nOffset, rRect.Bottom() - nOffset);
+    Point aRightBottom (rRect.Right() - nOffset, rRect.Bottom() - nOffset);
+
+    rDevice.DrawLine(aLeftTop,     aRightTop);
+    rDevice.DrawLine(aRightTop,    aRightBottom);
+    rDevice.DrawLine(aRightBottom, aLeftBottom);
+    rDevice.DrawLine(aLeftBottom,  aLeftTop);
+}
+
+} // end anonymous namespace
+
+Bitmap OutputDeviceTestLine::setupRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    drawLineOffset(*mpVirtualDevice, maVDRectangle, 2);
+    drawLineOffset(*mpVirtualDevice, maVDRectangle, 5);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestLine::setupDiamond()
+{
+    initialSetup(11, 11, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constFillColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aPoint1, aPoint2, aPoint3, aPoint4;
+    OutputDeviceTestCommon::createDiamondPoints(maVDRectangle, 4, aPoint1, 
aPoint2, aPoint3, aPoint4);
+
+    mpVirtualDevice->DrawLine(aPoint1, aPoint2);
+    mpVirtualDevice->DrawLine(aPoint2, aPoint3);
+    mpVirtualDevice->DrawLine(aPoint3, aPoint4);
+    mpVirtualDevice->DrawLine(aPoint4, aPoint1);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestLine::setupLines()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aHorizontalLinePoint1, aHorizontalLinePoint2;
+    Point aVerticalLinePoint1, aVerticalLinePoint2;
+    Point aDiagonalLinePoint1, aDiagonalLinePoint2;
+
+    OutputDeviceTestCommon::createHorizontalVerticalDiagonalLinePoints(
+                          maVDRectangle, aHorizontalLinePoint1, 
aHorizontalLinePoint2,
+                                         aVerticalLinePoint1,   
aVerticalLinePoint2,
+                                         aDiagonalLinePoint1,   
aDiagonalLinePoint2);
+
+    mpVirtualDevice->DrawLine(aHorizontalLinePoint1, aHorizontalLinePoint2);
+    mpVirtualDevice->DrawLine(aVerticalLinePoint1,   aVerticalLinePoint2);
+    mpVirtualDevice->DrawLine(aDiagonalLinePoint1,   aDiagonalLinePoint2);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestLine::setupAALines()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetAntialiasing(AntialiasingFlags::EnableB2dDraw);
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aHorizontalLinePoint1, aHorizontalLinePoint2;
+    Point aVerticalLinePoint1, aVerticalLinePoint2;
+    Point aDiagonalLinePoint1, aDiagonalLinePoint2;
+
+    OutputDeviceTestCommon::createHorizontalVerticalDiagonalLinePoints(
+                          maVDRectangle, aHorizontalLinePoint1, 
aHorizontalLinePoint2,
+                                         aVerticalLinePoint1,   
aVerticalLinePoint2,
+                                         aDiagonalLinePoint1,   
aDiagonalLinePoint2);
+
+    mpVirtualDevice->DrawLine(aHorizontalLinePoint1, aHorizontalLinePoint2);
+    mpVirtualDevice->DrawLine(aVerticalLinePoint1,   aVerticalLinePoint2);
+    mpVirtualDevice->DrawLine(aDiagonalLinePoint1,   aDiagonalLinePoint2);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+}} // end namespace vcl::test
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/outputdevice.cxx 
b/vcl/backendtest/outputdevice/outputdevice.cxx
new file mode 100644
index 0000000..6bc4278
--- /dev/null
+++ b/vcl/backendtest/outputdevice/outputdevice.cxx
@@ -0,0 +1,75 @@
+/* -*- 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/outputdevice.hxx"
+
+namespace vcl {
+namespace test {
+
+Bitmap OutputDeviceTestAnotherOutDev::setupDrawOutDev()
+{
+    ScopedVclPtrInstance<VirtualDevice> pSourceDev;
+    Size aSourceSize(9, 9);
+    pSourceDev->SetOutputSizePixel(aSourceSize);
+    pSourceDev->SetBackground(Wallpaper(constFillColor));
+    pSourceDev->Erase();
+
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->DrawOutDev(Point(2, 2), aSourceSize, Point(), 
aSourceSize, *pSourceDev.get());
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestAnotherOutDev::setupXOR()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    Rectangle aDrawRectangle(maVDRectangle);
+    aDrawRectangle.shrink(2);
+
+    Rectangle aScissorRectangle(maVDRectangle);
+    aScissorRectangle.shrink(4);
+
+    mpVirtualDevice->SetRasterOp(ROP_XOR);
+    mpVirtualDevice->SetFillColor(constFillColor);
+    mpVirtualDevice->DrawRect(aDrawRectangle);
+
+    mpVirtualDevice->SetRasterOp(ROP_0);
+    mpVirtualDevice->SetFillColor(COL_BLACK);
+    mpVirtualDevice->DrawRect(aScissorRectangle);
+
+    mpVirtualDevice->SetRasterOp(ROP_XOR);
+    mpVirtualDevice->SetFillColor(constFillColor);
+    mpVirtualDevice->DrawRect(aDrawRectangle);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+TestResult OutputDeviceTestAnotherOutDev::checkDrawOutDev(Bitmap& rBitmap)
+{
+    return checkFilledRectangle(rBitmap);
+}
+
+TestResult OutputDeviceTestAnotherOutDev::checkXOR(Bitmap& rBitmap)
+{
+    std::vector<Color> aExpected
+    {
+        constBackgroundColor, constBackgroundColor,
+        constBackgroundColor, constBackgroundColor,
+        constFillColor, constFillColor,
+        constFillColor
+    };
+    return checkRectangles(rBitmap, aExpected);
+}
+
+}} // end namespace vcl::test
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/pixel.cxx 
b/vcl/backendtest/outputdevice/pixel.cxx
new file mode 100644
index 0000000..f13690e
--- /dev/null
+++ b/vcl/backendtest/outputdevice/pixel.cxx
@@ -0,0 +1,57 @@
+/* -*- 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/outputdevice.hxx"
+
+namespace vcl {
+namespace test {
+
+namespace
+{
+
+void drawPixelOffset(OutputDevice& rDevice, Rectangle& rRect, int nOffset)
+{
+    for (long x = 0 + nOffset; x < (rRect.GetWidth() - nOffset); ++x)
+    {
+        long y1 = nOffset;
+        long y2 = rRect.GetHeight() - nOffset - 1;
+
+        rDevice.DrawPixel(Point(x, y1));
+        rDevice.DrawPixel(Point(x, y2));
+    }
+
+    for (long y = 0 + nOffset; y < (rRect.GetHeight() - nOffset); ++y)
+    {
+        long x1 = nOffset;
+        long x2 = rRect.GetWidth() - nOffset - 1;
+
+        rDevice.DrawPixel(Point(x1, y));
+        rDevice.DrawPixel(Point(x2, y));
+    }
+}
+
+} // end anonymous namespace
+
+Bitmap OutputDeviceTestPixel::setupRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    drawPixelOffset(*mpVirtualDevice, maVDRectangle, 2);
+    drawPixelOffset(*mpVirtualDevice, maVDRectangle, 5);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+}} // end namespace vcl::test
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/polygon.cxx 
b/vcl/backendtest/outputdevice/polygon.cxx
new file mode 100644
index 0000000..de722c5
--- /dev/null
+++ b/vcl/backendtest/outputdevice/polygon.cxx
@@ -0,0 +1,151 @@
+/* -*- 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/outputdevice.hxx"
+
+namespace vcl {
+namespace test {
+
+namespace
+{
+
+void drawPolygonOffset(OutputDevice& rDevice, Rectangle& rRect, int nOffset)
+{
+    tools::Polygon aPolygon(4);
+    aPolygon.SetPoint(Point(rRect.Left()  + nOffset, rRect.Top()    + 
nOffset), 0);
+    aPolygon.SetPoint(Point(rRect.Right() - nOffset, rRect.Top()    + 
nOffset), 1);
+    aPolygon.SetPoint(Point(rRect.Right() - nOffset, rRect.Bottom() - 
nOffset), 2);
+    aPolygon.SetPoint(Point(rRect.Left()  + nOffset, rRect.Bottom() - 
nOffset), 3);
+    aPolygon.Optimize(PolyOptimizeFlags::CLOSE);
+
+    rDevice.DrawPolygon(aPolygon);
+}
+
+} // end anonymous namespace
+
+Bitmap OutputDeviceTestPolygon::setupRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    drawPolygonOffset(*mpVirtualDevice, maVDRectangle, 2);
+    drawPolygonOffset(*mpVirtualDevice, maVDRectangle, 5);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestPolygon::setupFilledRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constFillColor);
+    mpVirtualDevice->SetFillColor(constFillColor);
+    drawPolygonOffset(*mpVirtualDevice, maVDRectangle, 2);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestPolygon::setupDiamond()
+{
+    initialSetup(11, 11, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aPoint1, aPoint2, aPoint3, aPoint4;
+    OutputDeviceTestCommon::createDiamondPoints(maVDRectangle, 4, aPoint1, 
aPoint2, aPoint3, aPoint4);
+
+    tools::Polygon aPolygon(4);
+
+    aPolygon.SetPoint(aPoint1, 0);
+    aPolygon.SetPoint(aPoint2, 1);
+    aPolygon.SetPoint(aPoint3, 2);
+    aPolygon.SetPoint(aPoint4, 3);
+    aPolygon.Optimize(PolyOptimizeFlags::CLOSE);
+
+    mpVirtualDevice->DrawPolygon(aPolygon);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestPolygon::setupLines()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aHorizontalLinePoint1, aHorizontalLinePoint2;
+    Point aVerticalLinePoint1, aVerticalLinePoint2;
+    Point aDiagonalLinePoint1, aDiagonalLinePoint2;
+
+    OutputDeviceTestCommon::createHorizontalVerticalDiagonalLinePoints(
+                          maVDRectangle, aHorizontalLinePoint1, 
aHorizontalLinePoint2,
+                                         aVerticalLinePoint1,   
aVerticalLinePoint2,
+                                         aDiagonalLinePoint1,   
aDiagonalLinePoint2);
+
+    tools::Polygon aHorizontalPolygon(2);
+    aHorizontalPolygon.SetPoint(aHorizontalLinePoint1, 0);
+    aHorizontalPolygon.SetPoint(aHorizontalLinePoint2, 1);
+    mpVirtualDevice->DrawPolygon(aHorizontalPolygon);
+
+    tools::Polygon aVerticalPolygon(2);
+    aVerticalPolygon.SetPoint(aVerticalLinePoint1, 0);
+    aVerticalPolygon.SetPoint(aVerticalLinePoint2, 1);
+    mpVirtualDevice->DrawPolygon(aVerticalPolygon);
+
+    tools::Polygon aDiagonalPolygon(2);
+    aDiagonalPolygon.SetPoint(aDiagonalLinePoint1, 0);
+    aDiagonalPolygon.SetPoint(aDiagonalLinePoint2, 1);
+    mpVirtualDevice->DrawPolygon(aDiagonalPolygon);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestPolygon::setupAALines()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetAntialiasing(AntialiasingFlags::EnableB2dDraw);
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aHorizontalLinePoint1, aHorizontalLinePoint2;
+    Point aVerticalLinePoint1, aVerticalLinePoint2;
+    Point aDiagonalLinePoint1, aDiagonalLinePoint2;
+
+    OutputDeviceTestCommon::createHorizontalVerticalDiagonalLinePoints(
+                          maVDRectangle, aHorizontalLinePoint1, 
aHorizontalLinePoint2,
+                                         aVerticalLinePoint1,   
aVerticalLinePoint2,
+                                         aDiagonalLinePoint1,   
aDiagonalLinePoint2);
+
+    tools::Polygon aHorizontalPolygon(2);
+    aHorizontalPolygon.SetPoint(aHorizontalLinePoint1, 0);
+    aHorizontalPolygon.SetPoint(aHorizontalLinePoint2, 1);
+    mpVirtualDevice->DrawPolygon(aHorizontalPolygon);
+
+    tools::Polygon aVerticalPolygon(2);
+    aVerticalPolygon.SetPoint(aVerticalLinePoint1, 0);
+    aVerticalPolygon.SetPoint(aVerticalLinePoint2, 1);
+    mpVirtualDevice->DrawPolygon(aVerticalPolygon);
+
+    tools::Polygon aDiagonalPolygon(2);
+    aDiagonalPolygon.SetPoint(aDiagonalLinePoint1, 0);
+    aDiagonalPolygon.SetPoint(aDiagonalLinePoint2, 1);
+    mpVirtualDevice->DrawPolygon(aDiagonalPolygon);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+}} // end namespace vcl::test
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/polyline.cxx 
b/vcl/backendtest/outputdevice/polyline.cxx
new file mode 100644
index 0000000..808d727
--- /dev/null
+++ b/vcl/backendtest/outputdevice/polyline.cxx
@@ -0,0 +1,140 @@
+/* -*- 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/outputdevice.hxx"
+
+namespace vcl {
+namespace test {
+
+namespace
+{
+
+void drawPolyLineOffset(OutputDevice& rDevice, Rectangle& rRect, int nOffset)
+{
+    tools::Polygon aPolygon(4);
+    aPolygon.SetPoint(Point(rRect.Left()  + nOffset, rRect.Top()    + 
nOffset), 0);
+    aPolygon.SetPoint(Point(rRect.Right() - nOffset, rRect.Top()    + 
nOffset), 1);
+    aPolygon.SetPoint(Point(rRect.Right() - nOffset, rRect.Bottom() - 
nOffset), 2);
+    aPolygon.SetPoint(Point(rRect.Left()  + nOffset, rRect.Bottom() - 
nOffset), 3);
+    aPolygon.Optimize(PolyOptimizeFlags::CLOSE);
+
+    rDevice.DrawPolyLine(aPolygon);
+}
+
+} // end anonymous namespace
+
+Bitmap OutputDeviceTestPolyLine::setupRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    drawPolyLineOffset(*mpVirtualDevice, maVDRectangle, 2);
+    drawPolyLineOffset(*mpVirtualDevice, maVDRectangle, 5);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestPolyLine::setupDiamond()
+{
+    initialSetup(11, 11, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aPoint1, aPoint2, aPoint3, aPoint4;
+    OutputDeviceTestCommon::createDiamondPoints(maVDRectangle, 4, aPoint1, 
aPoint2, aPoint3, aPoint4);
+
+    tools::Polygon aPolygon(4);
+
+    aPolygon.SetPoint(aPoint1, 0);
+    aPolygon.SetPoint(aPoint2, 1);
+    aPolygon.SetPoint(aPoint3, 2);
+    aPolygon.SetPoint(aPoint4, 3);
+    aPolygon.Optimize(PolyOptimizeFlags::CLOSE);
+
+    mpVirtualDevice->DrawPolyLine(aPolygon);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestPolyLine::setupLines()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aHorizontalLinePoint1, aHorizontalLinePoint2;
+    Point aVerticalLinePoint1, aVerticalLinePoint2;
+    Point aDiagonalLinePoint1, aDiagonalLinePoint2;
+
+    OutputDeviceTestCommon::createHorizontalVerticalDiagonalLinePoints(
+                          maVDRectangle, aHorizontalLinePoint1, 
aHorizontalLinePoint2,
+                                         aVerticalLinePoint1,   
aVerticalLinePoint2,
+                                         aDiagonalLinePoint1,   
aDiagonalLinePoint2);
+
+    tools::Polygon aHorizontalPolygon(2);
+    aHorizontalPolygon.SetPoint(aHorizontalLinePoint1, 0);
+    aHorizontalPolygon.SetPoint(aHorizontalLinePoint2, 1);
+    mpVirtualDevice->DrawPolyLine(aHorizontalPolygon);
+
+    tools::Polygon aVerticalPolygon(2);
+    aVerticalPolygon.SetPoint(aVerticalLinePoint1, 0);
+    aVerticalPolygon.SetPoint(aVerticalLinePoint2, 1);
+    mpVirtualDevice->DrawPolyLine(aVerticalPolygon);
+
+    tools::Polygon aDiagonalPolygon(2);
+    aDiagonalPolygon.SetPoint(aDiagonalLinePoint1, 0);
+    aDiagonalPolygon.SetPoint(aDiagonalLinePoint2, 1);
+    mpVirtualDevice->DrawPolyLine(aDiagonalPolygon);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestPolyLine::setupAALines()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetAntialiasing(AntialiasingFlags::EnableB2dDraw);
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    Point aHorizontalLinePoint1, aHorizontalLinePoint2;
+    Point aVerticalLinePoint1, aVerticalLinePoint2;
+    Point aDiagonalLinePoint1, aDiagonalLinePoint2;
+
+    OutputDeviceTestCommon::createHorizontalVerticalDiagonalLinePoints(
+                          maVDRectangle, aHorizontalLinePoint1, 
aHorizontalLinePoint2,
+                                         aVerticalLinePoint1,   
aVerticalLinePoint2,
+                                         aDiagonalLinePoint1,   
aDiagonalLinePoint2);
+
+    tools::Polygon aHorizontalPolygon(2);
+    aHorizontalPolygon.SetPoint(aHorizontalLinePoint1, 0);
+    aHorizontalPolygon.SetPoint(aHorizontalLinePoint2, 1);
+    mpVirtualDevice->DrawPolyLine(aHorizontalPolygon);
+
+    tools::Polygon aVerticalPolygon(2);
+    aVerticalPolygon.SetPoint(aVerticalLinePoint1, 0);
+    aVerticalPolygon.SetPoint(aVerticalLinePoint2, 1);
+    mpVirtualDevice->DrawPolyLine(aVerticalPolygon);
+
+    tools::Polygon aDiagonalPolygon(2);
+    aDiagonalPolygon.SetPoint(aDiagonalLinePoint1, 0);
+    aDiagonalPolygon.SetPoint(aDiagonalLinePoint2, 1);
+    mpVirtualDevice->DrawPolyLine(aDiagonalPolygon);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+}} // end namespace vcl::test
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/polypolygon.cxx 
b/vcl/backendtest/outputdevice/polypolygon.cxx
new file mode 100644
index 0000000..a6d3a01
--- /dev/null
+++ b/vcl/backendtest/outputdevice/polypolygon.cxx
@@ -0,0 +1,68 @@
+/* -*- 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/outputdevice.hxx"
+
+
+namespace vcl {
+namespace test {
+
+namespace
+{
+
+tools::Polygon createPolygonOffset(Rectangle& rRect, int nOffset)
+{
+    tools::Polygon aPolygon(4);
+    aPolygon.SetPoint(Point(rRect.Left()  + nOffset, rRect.Top()    + 
nOffset), 0);
+    aPolygon.SetPoint(Point(rRect.Right() - nOffset, rRect.Top()    + 
nOffset), 1);
+    aPolygon.SetPoint(Point(rRect.Right() - nOffset, rRect.Bottom() - 
nOffset), 2);
+    aPolygon.SetPoint(Point(rRect.Left()  + nOffset, rRect.Bottom() - 
nOffset), 3);
+    aPolygon.Optimize(PolyOptimizeFlags::CLOSE);
+    return aPolygon;
+}
+
+} // end anonymous namespace
+
+Bitmap OutputDeviceTestPolyPolygon::setupRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    tools::PolyPolygon aPolyPolygon(2);
+    aPolyPolygon.Insert(createPolygonOffset(maVDRectangle, 2));
+    aPolyPolygon.Insert(createPolygonOffset(maVDRectangle, 5));
+
+    mpVirtualDevice->DrawPolyPolygon(aPolyPolygon);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestPolyPolygon::setupFilledRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constFillColor);
+    mpVirtualDevice->SetFillColor(constFillColor);
+
+    tools::PolyPolygon aPolyPolygon(3);
+    aPolyPolygon.Insert(createPolygonOffset(maVDRectangle, 2));
+    aPolyPolygon.Insert(createPolygonOffset(maVDRectangle, 4));
+    aPolyPolygon.Insert(createPolygonOffset(maVDRectangle, 4));
+
+    mpVirtualDevice->DrawPolyPolygon(aPolyPolygon);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+}} // end namespace vcl::test
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/backendtest/outputdevice/rectangle.cxx 
b/vcl/backendtest/outputdevice/rectangle.cxx
new file mode 100644
index 0000000..80ab3cf
--- /dev/null
+++ b/vcl/backendtest/outputdevice/rectangle.cxx
@@ -0,0 +1,54 @@
+/* -*- 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/outputdevice.hxx"
+
+namespace vcl {
+namespace test {
+
+namespace
+{
+    void drawRectOffset(OutputDevice& rDevice, Rectangle& rRect, int nOffset)
+    {
+        rDevice.DrawRect(Rectangle(rRect.Left()  + nOffset, rRect.Top()    + 
nOffset,
+                                   rRect.Right() - nOffset, rRect.Bottom() - 
nOffset));
+
+    }
+} // end anonymous namespace
+
+Bitmap OutputDeviceTestRect::setupFilledRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constFillColor);
+    mpVirtualDevice->SetFillColor(constFillColor);
+
+    drawRectOffset(*mpVirtualDevice, maVDRectangle, 2);
+    drawRectOffset(*mpVirtualDevice, maVDRectangle, 5);
+
+    return mpVirtualDevice->GetBitmap(maVDRectangle.TopLeft(), 
maVDRectangle.GetSize());
+}
+
+Bitmap OutputDeviceTestRect::setupRectangle()
+{
+    initialSetup(13, 13, constBackgroundColor);
+
+    mpVirtualDevice->SetLineColor(constLineColor);
+    mpVirtualDevice->SetFillColor();
+
+    drawRectOffset(*mpVirtualDevice, maVDRectangle, 2);
+    drawRectOffset(*mpVirtualDevice, maVDRectangle, 5);
+
+    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
new file mode 100644
index 0000000..cc90b00
--- /dev/null
+++ b/vcl/inc/test/outputdevice.hxx
@@ -0,0 +1,172 @@
+/* -*- 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/.
+ *
+ */
+
+#ifndef INCLUDED_VCL_OUTDEVTESTS_HXX
+#define INCLUDED_VCL_OUTDEVTESTS_HXX
+
+#include <vcl/virdev.hxx>
+#include <vcl/bitmapaccess.hxx>
+
+namespace vcl {
+namespace test {
+
+/** Rendering test result.
+ *
+ * Test either "Passed", "Failed" or "PassedWithQuirks" which means
+ * the test passed but at least one rendering quirk was detected.
+ */
+enum class TestResult
+{
+    Failed,
+    PassedWithQuirks,
+    Passed
+};
+
+/** Common subclass for output device rendering tests.
+ */
+class VCL_DLLPUBLIC OutputDeviceTestCommon
+{
+protected:
+
+    ScopedVclPtr<VirtualDevice> mpVirtualDevice;
+    Rectangle maVDRectangle;
+
+    static const Color constBackgroundColor;
+    static const Color constLineColor;
+    static const Color constFillColor;
+
+public:
+    OutputDeviceTestCommon();
+
+    void initialSetup(long nWidth, long nHeight, Color aColor);
+
+    static TestResult checkRectangle(Bitmap& rBitmap);
+    static TestResult checkFilledRectangle(Bitmap& rBitmap);
+    static TestResult checkLines(Bitmap& rBitmap);
+    static TestResult checkAALines(Bitmap& rBitmap);
+    static TestResult checkDiamond(Bitmap& rBitmap);
+
+    static TestResult checkRectangles(Bitmap& rBitmap, std::vector<Color>& 
aExpectedColors);
+
+    static void createDiamondPoints(Rectangle rRect, int nOffset,
+                                    Point& rPoint1, Point& rPoint2,
+                                    Point& rPoint3, Point& rPoint4);
+
+    static void createHorizontalVerticalDiagonalLinePoints(Rectangle rRect,
+                                Point& rHorizontalLinePoint1, Point& 
rHorizontalLinePoint2,
+                                Point& rVerticalLinePoint1, Point& 
rVerticalLinePoint2,
+                                Point& rDiagonalLinePoint1, Point& 
rDiagonalLinePoint2);
+    // tools
+    static Rectangle alignToCenter(Rectangle aRect1, Rectangle aRect2);
+
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestBitmap : public OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestBitmap() = default;
+
+    Bitmap setupDrawTransformedBitmap();
+    Bitmap setupDrawBitmap();
+    Bitmap setupDrawBitmapExWithAlpha();
+    Bitmap setupDrawMask();
+
+    static TestResult checkBitmap(Bitmap& rBitmap);
+    static TestResult checkTransformedBitmap(Bitmap& rBitmap);
+    static TestResult checkBitmapExWithAlpha(Bitmap& rBitmap);
+    static TestResult checkMask(Bitmap& rBitmap);
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestAnotherOutDev : public 
OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestAnotherOutDev() = default;
+
+    Bitmap setupDrawOutDev();
+    Bitmap setupXOR();
+
+    static TestResult checkDrawOutDev(Bitmap& rBitmap);
+    static TestResult checkXOR(Bitmap& rBitmap);
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestPixel : public OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestPixel() = default;
+
+    Bitmap setupRectangle();
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestLine : public OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestLine() = default;
+
+    Bitmap setupRectangle();
+    Bitmap setupDiamond();
+    Bitmap setupLines();
+    Bitmap setupAALines();
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestPolyLine : public OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestPolyLine() = default;
+
+    Bitmap setupRectangle();
+    Bitmap setupDiamond();
+    Bitmap setupLines();
+    Bitmap setupAALines();
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestRect : public OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestRect() = default;
+
+    Bitmap setupFilledRectangle();
+    Bitmap setupRectangle();
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestPolygon : public OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestPolygon() = default;
+
+    Bitmap setupFilledRectangle();
+    Bitmap setupRectangle();
+    Bitmap setupDiamond();
+    Bitmap setupLines();
+    Bitmap setupAALines();
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestPolyPolygon : public OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestPolyPolygon() = default;
+
+    Bitmap setupFilledRectangle();
+    Bitmap setupRectangle();
+};
+
+class VCL_DLLPUBLIC OutputDeviceTestGradient : public OutputDeviceTestCommon
+{
+public:
+    OutputDeviceTestGradient() = default;
+
+    Bitmap setupLinearGradient();
+    Bitmap setupRadialGradient();
+};
+
+}} // end namespace vcl::test
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to