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