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 #include <test/bootstrapfixture.hxx>
11
12 #include <vcl/print.hxx>
13 #include <vcl/virdev.hxx>
14 #include <vcl/window.hxx>
15 #include <vcl/gdimtf.hxx>
16 #include <vcl/metaact.hxx>
17
18 #include <bitmap/BitmapWriteAccess.hxx>
19 #include <bufferdevice.hxx>
20 #include <window.h>
21
22 #include <basegfx/matrix/b2dhommatrix.hxx>
23
24 class VclOutdevTest : public test::BootstrapFixture
25 {
26 public:
VclOutdevTest()27 VclOutdevTest()
28 : BootstrapFixture(true, false)
29 {
30 }
31
32 void testVirtualDevice();
33 void testUseAfterDispose();
34 void testPrinterBackgroundColor();
35 void testWindowBackgroundColor();
36 void testGetReadableFontColorPrinter();
37 void testGetReadableFontColorWindow();
38 void testDrawTransformedBitmapEx();
39 void testDrawTransformedBitmapExFlip();
40 void testRTL();
41 void testRTLGuard();
42
43 CPPUNIT_TEST_SUITE(VclOutdevTest);
44 CPPUNIT_TEST(testVirtualDevice);
45 CPPUNIT_TEST(testUseAfterDispose);
46 CPPUNIT_TEST(testPrinterBackgroundColor);
47 CPPUNIT_TEST(testWindowBackgroundColor);
48 CPPUNIT_TEST(testGetReadableFontColorPrinter);
49 CPPUNIT_TEST(testGetReadableFontColorWindow);
50 CPPUNIT_TEST(testDrawTransformedBitmapEx);
51 CPPUNIT_TEST(testDrawTransformedBitmapExFlip);
52 CPPUNIT_TEST(testRTL);
53 CPPUNIT_TEST(testRTLGuard);
54 CPPUNIT_TEST_SUITE_END();
55 };
56
testGetReadableFontColorPrinter()57 void VclOutdevTest::testGetReadableFontColorPrinter()
58 {
59 ScopedVclPtrInstance<Printer> pPrinter;
60 CPPUNIT_ASSERT_EQUAL(pPrinter->GetReadableFontColor(COL_WHITE, COL_WHITE), COL_BLACK);
61 }
62
testGetReadableFontColorWindow()63 void VclOutdevTest::testGetReadableFontColorWindow()
64 {
65 ScopedVclPtrInstance<vcl::Window> pWindow(nullptr, WB_APP | WB_STDWORK);
66 CPPUNIT_ASSERT_EQUAL(pWindow->GetOutDev()->GetReadableFontColor(COL_WHITE, COL_BLACK),
67 COL_WHITE);
68 CPPUNIT_ASSERT_EQUAL(pWindow->GetOutDev()->GetReadableFontColor(COL_WHITE, COL_WHITE),
69 COL_BLACK);
70 CPPUNIT_ASSERT_EQUAL(pWindow->GetOutDev()->GetReadableFontColor(COL_BLACK, COL_BLACK),
71 COL_WHITE);
72 }
73
testPrinterBackgroundColor()74 void VclOutdevTest::testPrinterBackgroundColor()
75 {
76 ScopedVclPtrInstance<Printer> pPrinter;
77 CPPUNIT_ASSERT_EQUAL(pPrinter->GetBackgroundColor(), COL_WHITE);
78 }
79
testWindowBackgroundColor()80 void VclOutdevTest::testWindowBackgroundColor()
81 {
82 ScopedVclPtrInstance<vcl::Window> pWindow(nullptr, WB_APP | WB_STDWORK);
83 pWindow->SetBackground(Wallpaper(COL_WHITE));
84 CPPUNIT_ASSERT_EQUAL(pWindow->GetBackgroundColor(), COL_WHITE);
85 }
86
testVirtualDevice()87 void VclOutdevTest::testVirtualDevice()
88 {
89 // TODO: This unit test is not executed for macOS unless bitmap scaling is implemented
90 #ifndef MACOSX
91 ScopedVclPtrInstance<VirtualDevice> pVDev;
92 pVDev->SetOutputSizePixel(Size(32, 32));
93 pVDev->SetBackground(Wallpaper(COL_WHITE));
94
95 CPPUNIT_ASSERT_EQUAL(pVDev->GetBackgroundColor(), COL_WHITE);
96
97 pVDev->Erase();
98 pVDev->DrawPixel(Point(1, 2), COL_BLUE);
99 pVDev->DrawPixel(Point(31, 30), COL_RED);
100
101 Size aSize = pVDev->GetOutputSizePixel();
102 CPPUNIT_ASSERT_EQUAL(Size(32, 32), aSize);
103
104 Bitmap aBmp = pVDev->GetBitmap(Point(), aSize);
105
106 #if 0
107 OUString rFileName("/tmp/foo-unx.png");
108 try {
109 vcl::PNGWriter aWriter( aBmp );
110 SvFileStream sOutput( rFileName, StreamMode::WRITE );
111 aWriter.Write( sOutput );
112 sOutput.Close();
113 } catch (...) {
114 SAL_WARN("vcl", "Error writing png to " << rFileName);
115 }
116 #endif
117
118 CPPUNIT_ASSERT_EQUAL(COL_WHITE, pVDev->GetPixel(Point(0, 0)));
119 #if !defined _WIN32 //TODO: various failures on Windows tinderboxes
120 CPPUNIT_ASSERT_EQUAL(COL_BLUE, pVDev->GetPixel(Point(1, 2)));
121 CPPUNIT_ASSERT_EQUAL(COL_RED, pVDev->GetPixel(Point(31, 30)));
122 #endif
123 CPPUNIT_ASSERT_EQUAL(COL_WHITE, pVDev->GetPixel(Point(30, 31)));
124
125 // Gotcha: y and x swap for BitmapReadAccess: deep joy.
126 Bitmap::ScopedReadAccess pAcc(aBmp);
127 CPPUNIT_ASSERT_EQUAL(COL_WHITE, static_cast<Color>(pAcc->GetPixel(0, 0)));
128 #if !defined _WIN32 //TODO: various failures on Windows tinderboxes
129 CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<Color>(pAcc->GetPixel(2, 1)));
130 CPPUNIT_ASSERT_EQUAL(COL_RED, static_cast<Color>(pAcc->GetPixel(30, 31)));
131 #endif
132 CPPUNIT_ASSERT_EQUAL(COL_WHITE, static_cast<Color>(pAcc->GetPixel(31, 30)));
133
134 #if 0
135 VclPtr<vcl::Window> pWin = VclPtr<WorkWindow>::Create( (vcl::Window *)nullptr );
136 CPPUNIT_ASSERT( pWin );
137 OutputDevice *pOutDev = pWin.get();
138 #endif
139 #endif
140 }
141
testUseAfterDispose()142 void VclOutdevTest::testUseAfterDispose()
143 {
144 // Create a virtual device, enable map mode then dispose it.
145 ScopedVclPtrInstance<VirtualDevice> pVDev;
146
147 pVDev->EnableMapMode();
148
149 pVDev->disposeOnce();
150
151 // Make sure that these don't crash after dispose.
152 pVDev->GetInverseViewTransformation();
153
154 pVDev->GetViewTransformation();
155 }
156
testDrawTransformedBitmapEx()157 void VclOutdevTest::testDrawTransformedBitmapEx()
158 {
159 // Create a virtual device, and connect a metafile to it.
160 // Also create a 16x16 bitmap.
161 ScopedVclPtrInstance<VirtualDevice> pVDev;
162 Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N24_BPP);
163 {
164 // Fill the top left quarter with black.
165 BitmapScopedWriteAccess pWriteAccess(aBitmap);
166 pWriteAccess->Erase(COL_WHITE);
167 for (int i = 0; i < 8; ++i)
168 {
169 for (int j = 0; j < 8; ++j)
170 {
171 pWriteAccess->SetPixel(j, i, COL_BLACK);
172 }
173 }
174 }
175 BitmapEx aBitmapEx(aBitmap);
176 basegfx::B2DHomMatrix aMatrix;
177 aMatrix.scale(8, 8);
178 // Rotate 90 degrees clockwise, so the black part goes to the top right.
179 aMatrix.rotate(M_PI / 2);
180 GDIMetaFile aMtf;
181 aMtf.Record(pVDev.get());
182
183 // Draw the rotated bitmap on the vdev.
184 pVDev->DrawTransformedBitmapEx(aMatrix, aBitmapEx);
185 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aMtf.GetActionSize());
186 MetaAction* pAction = aMtf.GetAction(0);
187 CPPUNIT_ASSERT_EQUAL(MetaActionType::BMPEXSCALE, pAction->GetType());
188 auto pBitmapAction = static_cast<MetaBmpExScaleAction*>(pAction);
189 const BitmapEx& rBitmapEx = pBitmapAction->GetBitmapEx();
190 Size aTransformedSize = rBitmapEx.GetSizePixel();
191 // Without the accompanying fix in place, this test would have failed with:
192 // - Expected: 16x16
193 // - Actual : 8x8
194 // I.e. the bitmap before scaling was already scaled down, just because it was rotated.
195 CPPUNIT_ASSERT_EQUAL(Size(16, 16), aTransformedSize);
196
197 aBitmap = rBitmapEx.GetBitmap();
198 Bitmap::ScopedReadAccess pAccess(aBitmap);
199 for (int i = 0; i < 16; ++i)
200 {
201 for (int j = 0; j < 16; ++j)
202 {
203 BitmapColor aColor = pAccess->GetPixel(j, i);
204 Color aExpected = i >= 8 && j < 8 ? COL_BLACK : COL_WHITE;
205 std::stringstream ss;
206 ss << "Color is expected to be ";
207 ss << ((aExpected == COL_WHITE) ? "white" : "black");
208 ss << ", is " << aColor.AsRGBHexString();
209 ss << " (row " << j << ", col " << i << ")";
210 // Without the accompanying fix in place, this test would have failed with:
211 // - Expected: c[00000000]
212 // - Actual : c[ffffff00]
213 // - Color is expected to be black, is ffffff (row 0, col 8)
214 // i.e. the top right quarter of the image was not fully black, there was a white first
215 // row.
216 CPPUNIT_ASSERT_EQUAL_MESSAGE(ss.str(), aExpected, Color(aColor));
217 }
218 }
219 }
220
testDrawTransformedBitmapExFlip()221 void VclOutdevTest::testDrawTransformedBitmapExFlip()
222 {
223 // Create a virtual device, and connect a metafile to it.
224 // Also create a 16x16 bitmap.
225 ScopedVclPtrInstance<VirtualDevice> pVDev;
226 Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N24_BPP);
227 {
228 // Fill the top left quarter with black.
229 BitmapScopedWriteAccess pWriteAccess(aBitmap);
230 pWriteAccess->Erase(COL_WHITE);
231 for (int i = 0; i < 8; ++i)
232 {
233 for (int j = 0; j < 8; ++j)
234 {
235 pWriteAccess->SetPixel(j, i, COL_BLACK);
236 }
237 }
238 }
239 BitmapEx aBitmapEx(aBitmap);
240 basegfx::B2DHomMatrix aMatrix;
241 // Negative y scale: bitmap should be upside down, so the black part goes to the bottom left.
242 aMatrix.scale(8, -8);
243 // Rotate 90 degrees clockwise, so the black part goes back to the top left.
244 aMatrix.rotate(M_PI / 2);
245 GDIMetaFile aMtf;
246 aMtf.Record(pVDev.get());
247
248 // Draw the scaled and rotated bitmap on the vdev.
249 pVDev->DrawTransformedBitmapEx(aMatrix, aBitmapEx);
250 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aMtf.GetActionSize());
251 MetaAction* pAction = aMtf.GetAction(0);
252 CPPUNIT_ASSERT_EQUAL(MetaActionType::BMPEXSCALE, pAction->GetType());
253 auto pBitmapAction = static_cast<MetaBmpExScaleAction*>(pAction);
254 const BitmapEx& rBitmapEx = pBitmapAction->GetBitmapEx();
255
256 aBitmap = rBitmapEx.GetBitmap();
257 Bitmap::ScopedReadAccess pAccess(aBitmap);
258 int nX = 8 * 0.25;
259 int nY = 8 * 0.25;
260 BitmapColor aColor = pAccess->GetPixel(nY, nX);
261 std::stringstream ss;
262 ss << "Color is expected to be black, is " << aColor.AsRGBHexString();
263 ss << " (row " << nY << ", col " << nX << ")";
264 // Without the accompanying fix in place, this test would have failed with:
265 // - Expected: c[00000000]
266 // - Actual : c[ffffff00]
267 // - Color is expected to be black, is ffffff (row 2, col 2)
268 // i.e. the top left quarter of the image was not black, due to a missing flip.
269 CPPUNIT_ASSERT_EQUAL_MESSAGE(ss.str(), COL_BLACK, Color(aColor));
270 }
271
testRTL()272 void VclOutdevTest::testRTL()
273 {
274 ScopedVclPtrInstance<vcl::Window> pWindow(nullptr, WB_APP | WB_STDWORK);
275 pWindow->EnableRTL();
276 vcl::RenderContext& rRenderContext = *pWindow->GetOutDev();
277 vcl::BufferDevice pBuffer(pWindow, rRenderContext);
278
279 // Without the accompanying fix in place, this test would have failed, because the RTL status
280 // from pWindow was not propagated to pBuffer.
281 CPPUNIT_ASSERT(pBuffer->IsRTLEnabled());
282 }
283
testRTLGuard()284 void VclOutdevTest::testRTLGuard()
285 {
286 ScopedVclPtrInstance<vcl::Window> pWindow(nullptr, WB_APP | WB_STDWORK);
287 pWindow->EnableRTL();
288 pWindow->RequestDoubleBuffering(true);
289 ImplFrameData* pFrameData = pWindow->ImplGetWindowImpl()->mpFrameData;
290 vcl::PaintBufferGuard aGuard(pFrameData, pWindow);
291 // Without the accompanying fix in place, this test would have failed, because the RTL status
292 // from pWindow was not propagated to aGuard.
293 CPPUNIT_ASSERT(aGuard.GetRenderContext()->IsRTLEnabled());
294 }
295
296 CPPUNIT_TEST_SUITE_REGISTRATION(VclOutdevTest);
297
298 CPPUNIT_PLUGIN_IMPLEMENT();
299
300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
301