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