1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  */
10 
11 #include <test/bootstrapfixture.hxx>
12 
13 #include <vcl/virdev.hxx>
14 #include <vcl/BitmapReadAccess.hxx>
15 #include <vcl/canvastools.hxx>
16 #include <vcl/graphicfilter.hxx>
17 #include <tools/stream.hxx>
18 
19 #include <com/sun/star/rendering/XBitmap.hpp>
20 #include <com/sun/star/rendering/XCanvas.hpp>
21 #include <com/sun/star/rendering/XBitmapCanvas.hpp>
22 #include <com/sun/star/rendering/CompositeOperation.hpp>
23 #include <com/sun/star/rendering/PathCapType.hpp>
24 #include <com/sun/star/rendering/PathJoinType.hpp>
25 
26 using namespace ::com::sun::star;
27 
28 class CanvasTest : public test::BootstrapFixture
29 {
30     VclPtr<VirtualDevice> mVclDevice;
31     uno::Reference<rendering::XCanvas> mCanvas;
32     uno::Reference<rendering::XGraphicDevice> mDevice;
33     rendering::ViewState mViewState;
34     rendering::RenderState mRenderState;
35     uno::Sequence<double> mColorBlack;
36     uno::Sequence<double> mColorBlue;
37 
38     // if enabled - check the result images with:
39     // "xdg-open ./workdir/CppunitTest/canvas_test.test.core/"
40     static constexpr const bool mbExportBitmap = false;
41 
exportDevice(const OUString & filename,const VclPtr<VirtualDevice> & device)42     void exportDevice(const OUString& filename, const VclPtr<VirtualDevice>& device)
43     {
44         if (mbExportBitmap)
45         {
46             BitmapEx aBitmapEx(device->GetBitmapEx(Point(0, 0), device->GetOutputSizePixel()));
47             SvFileStream aStream(filename, StreamMode::WRITE | StreamMode::TRUNC);
48             GraphicFilter::GetGraphicFilter().compressAsPNG(aBitmapEx, aStream);
49         }
50     }
51 
52 public:
CanvasTest()53     CanvasTest()
54         : BootstrapFixture(true, false)
55     {
56     }
57 
setUp()58     virtual void setUp() override
59     {
60         BootstrapFixture::setUp();
61         mColorBlack = vcl::unotools::colorToStdColorSpaceSequence(COL_BLACK);
62         mColorBlue = vcl::unotools::colorToStdColorSpaceSequence(COL_BLUE);
63         // Geometry init
64         geometry::AffineMatrix2D aUnit(1, 0, 0, 0, 1, 0);
65         mViewState.AffineTransform = aUnit;
66         mRenderState.AffineTransform = aUnit;
67         mRenderState.DeviceColor = mColorBlack;
68         mRenderState.CompositeOperation = rendering::CompositeOperation::OVER;
69     }
70 
tearDown()71     virtual void tearDown() override
72     {
73         mVclDevice.disposeAndClear();
74         mCanvas = uno::Reference<rendering::XCanvas>();
75         mDevice = uno::Reference<rendering::XGraphicDevice>();
76         BootstrapFixture::tearDown();
77     }
78 
setupCanvas(const Size & size,Color backgroundColor=COL_WHITE,bool alpha=false)79     void setupCanvas(const Size& size, Color backgroundColor = COL_WHITE, bool alpha = false)
80     {
81         mVclDevice
82             = alpha ? VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT, DeviceFormat::DEFAULT)
83                     : VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
84         mVclDevice->SetOutputSizePixel(size);
85         mVclDevice->SetBackground(Wallpaper(backgroundColor));
86         mVclDevice->Erase();
87         mCanvas = mVclDevice->GetCanvas();
88         CPPUNIT_ASSERT(mCanvas.is());
89         mDevice
90             = uno::Reference<rendering::XGraphicDevice>(mCanvas->getDevice(), uno::UNO_SET_THROW);
91         CPPUNIT_ASSERT(mDevice.is());
92     }
93 
testDrawLine()94     void testDrawLine()
95     {
96         setupCanvas(Size(10, 10));
97         mCanvas->drawLine(geometry::RealPoint2D(1, 1), geometry::RealPoint2D(9, 1), mViewState,
98                           mRenderState);
99         exportDevice("test-draw-line.png", mVclDevice);
100         Bitmap bitmap = mVclDevice->GetBitmap(Point(), mVclDevice->GetOutputSizePixel());
101         Bitmap::ScopedReadAccess access(bitmap);
102         // Canvas uses AA, which blurs the line, and it cannot be turned off,
103         // so do not check the end points.
104         CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_WHITE), access->GetPixel(0, 0));
105         CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLACK), access->GetPixel(1, 2));
106         CPPUNIT_ASSERT_EQUAL(BitmapColor(COL_BLACK), access->GetPixel(1, 8));
107     }
108 
109     // Draw a dashed line scaled, make sure the dashing is scaled properly.
testTdf134053()110     void testTdf134053()
111     {
112         setupCanvas(Size(1000, 100));
113         // Scale everything up by 10 (2 in render state, 5 in view state).
114         mRenderState.AffineTransform = geometry::AffineMatrix2D(2, 0, 0, 0, 2, 0);
115         mViewState.AffineTransform = geometry::AffineMatrix2D(5, 0, 0, 0, 5, 0);
116 
117         uno::Sequence<uno::Sequence<geometry::RealPoint2D>> polygonPoints{ { { 10, 5 },
118                                                                              { 88, 5 } } };
119         uno::Reference<rendering::XLinePolyPolygon2D> polygon
120             = mDevice->createCompatibleLinePolyPolygon(polygonPoints);
121         polygon->setClosed(0, false);
122 
123         mRenderState.DeviceColor = mColorBlue;
124         rendering::StrokeAttributes strokeAttributes;
125         strokeAttributes.StrokeWidth = 2.0;
126         strokeAttributes.MiterLimit = 2.0; // ?
127         strokeAttributes.StartCapType = rendering::PathCapType::ROUND;
128         strokeAttributes.EndCapType = rendering::PathCapType::ROUND;
129         strokeAttributes.JoinType = rendering::PathJoinType::MITER;
130         strokeAttributes.DashArray = { 10, 5, 0.1, 5 };
131 
132         mCanvas->strokePolyPolygon(polygon, mViewState, mRenderState, strokeAttributes);
133 
134         exportDevice("test-tdf134053.png", mVclDevice);
135         Bitmap bitmap = mVclDevice->GetBitmap(Point(), mVclDevice->GetOutputSizePixel());
136         Bitmap::ScopedReadAccess access(bitmap);
137         struct Check
138         {
139             tools::Long start;
140             tools::Long end;
141             Color color;
142         };
143         // There should be a long dash at X 100-200, a dot at 250, long one at 300-400, a dot at 450, etc.
144         // until a dot at 850. Add -5/+5 to account for round caps.
145         const Check checks[] = { { 0, 85, COL_WHITE }, // empty start
146                                  // dash, space, dot, space
147                                  { 95, 205, COL_BLUE },
148                                  { 215, 235, COL_WHITE },
149                                  { 245, 255, COL_BLUE },
150                                  { 265, 285, COL_WHITE },
151                                  { 295, 405, COL_BLUE },
152                                  { 415, 435, COL_WHITE },
153                                  { 445, 455, COL_BLUE },
154                                  { 465, 485, COL_WHITE },
155                                  { 495, 605, COL_BLUE },
156                                  { 615, 635, COL_WHITE },
157                                  { 645, 655, COL_BLUE },
158                                  { 665, 685, COL_WHITE },
159                                  { 695, 805, COL_BLUE },
160                                  { 815, 835, COL_WHITE },
161                                  { 845, 855, COL_BLUE },
162                                  { 865, 999, COL_WHITE } }; // empty end
163         for (const Check& check : checks)
164         {
165             for (tools::Long x = check.start; x <= check.end; ++x)
166             {
167                 if (access->GetColor(50, x) != check.color)
168                 {
169                     std::ostringstream str;
170                     str << "X: " << x;
171                     CPPUNIT_ASSERT_EQUAL_MESSAGE(str.str(), BitmapColor(check.color),
172                                                  access->GetColor(50, x));
173                 }
174             }
175         }
176     }
177 
178     CPPUNIT_TEST_SUITE(CanvasTest);
179     CPPUNIT_TEST(testDrawLine);
180     CPPUNIT_TEST(testTdf134053);
181     CPPUNIT_TEST_SUITE_END();
182 };
183 
184 CPPUNIT_TEST_SUITE_REGISTRATION(CanvasTest);
185 
186 CPPUNIT_PLUGIN_IMPLEMENT();
187 
188 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
189