1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include <test/bootstrapfixture.hxx>
12 #include <vcl/bitmap.hxx>
13 #include <vcl/bitmapaccess.hxx>
14 #include <bitmapwriteaccess.hxx>
16 #include <tools/stream.hxx>
17 #include <vcl/graphicfilter.hxx>
19 #include <vcl/BitmapBasicMorphologyFilter.hxx>
20 #include <vcl/BitmapFilterStackBlur.hxx>
21 #include <BitmapSymmetryCheck.hxx>
27 constexpr bool constWriteResultBitmap(false);
28 constexpr bool constEnablePerformanceTest(false);
30 class BitmapFilterTest
: public test::BootstrapFixture
34 : test::BootstrapFixture(true, false)
38 void testBlurCorrectness();
39 void testBasicMorphology();
40 void testPerformance();
41 void testGenerateStripRanges();
43 CPPUNIT_TEST_SUITE(BitmapFilterTest
);
44 CPPUNIT_TEST(testBlurCorrectness
);
45 CPPUNIT_TEST(testBasicMorphology
);
46 CPPUNIT_TEST(testPerformance
);
47 CPPUNIT_TEST(testGenerateStripRanges
);
48 CPPUNIT_TEST_SUITE_END();
51 OUString
getFullUrl(const OUString
& sFileName
)
53 return m_directories
.getURLFromSrc("vcl/qa/cppunit/data/") + sFileName
;
56 BitmapEx
loadBitmap(const OUString
& sFileName
)
59 const OUString
aURL(getFullUrl(sFileName
));
60 SvFileStream
aFileStream(aURL
, StreamMode::READ
);
61 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
62 ErrCode aResult
= rFilter
.ImportGraphic(aGraphic
, aURL
, aFileStream
);
63 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE
, aResult
);
64 return aGraphic
.GetBitmapEx();
67 template <class BitmapT
> // handle both Bitmap and BitmapEx
68 void savePNG(const OUString
& sWhere
, const BitmapT
& rBmp
)
70 SvFileStream
aStream(sWhere
, StreamMode::WRITE
| StreamMode::TRUNC
);
71 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
72 rFilter
.compressAsPNG(BitmapEx(rBmp
), aStream
);
76 void BitmapFilterTest::testBlurCorrectness()
80 Bitmap
aBitmap24Bit(aSize
, 24);
82 ScanlineFormat scanlineFormat
= ScanlineFormat::NONE
;
83 sal_uInt16 nBPP
= aBitmap24Bit
.GetBitCount();
86 tools::Long aMargin1
= 1;
87 tools::Long aMargin2
= 3;
88 BitmapScopedWriteAccess
aWriteAccess(aBitmap24Bit
);
89 scanlineFormat
= aWriteAccess
->GetScanlineFormat();
90 aWriteAccess
->Erase(COL_WHITE
);
91 aWriteAccess
->SetLineColor(COL_BLACK
);
93 tools::Rectangle
aRectangle1(aMargin1
, aMargin1
, aSize
.Width() - 1 - aMargin1
,
94 aSize
.Height() - 1 - aMargin1
);
96 tools::Rectangle
aRectangle2(aMargin2
, aMargin2
, aSize
.Width() - 1 - aMargin2
,
97 aSize
.Height() - 1 - aMargin2
);
99 tools::Rectangle
aRectangle3(aSize
.Width() / 2, aSize
.Height() / 2, aSize
.Width() / 2,
102 aWriteAccess
->DrawRect(aRectangle1
);
103 aWriteAccess
->DrawRect(aRectangle2
);
104 aWriteAccess
->DrawRect(aRectangle3
);
107 if (constWriteResultBitmap
)
109 savePNG("~/blurBefore.png", aBitmap24Bit
);
113 BitmapFilterStackBlur
aBlurFilter(2);
114 aBitmap24Bit
= aBlurFilter
.filter(aBitmap24Bit
);
118 if (constWriteResultBitmap
)
120 savePNG("~/blurAfter.png", aBitmap24Bit
);
123 // Check blurred bitmap parameters
124 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(41), aBitmap24Bit
.GetSizePixel().Width());
125 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(31), aBitmap24Bit
.GetSizePixel().Height());
127 CPPUNIT_ASSERT_EQUAL(nBPP
, aBitmap24Bit
.GetBitCount());
129 // Check that the bitmap is horizontally and vertically symmetrical
130 CPPUNIT_ASSERT(BitmapSymmetryCheck::check(aBitmap24Bit
));
133 Bitmap::ScopedReadAccess
aReadAccess(aBitmap24Bit
);
134 CPPUNIT_ASSERT_EQUAL(scanlineFormat
, aReadAccess
->GetScanlineFormat());
138 void BitmapFilterTest::testBasicMorphology()
140 const BitmapEx aOrigBitmap
= loadBitmap("testBasicMorphology.png");
141 const BitmapEx aRefBitmapDilated1
= loadBitmap("testBasicMorphologyDilated1.png");
142 const BitmapEx aRefBitmapDilated1Eroded1
= loadBitmap("testBasicMorphologyDilated1Eroded1.png");
143 const BitmapEx aRefBitmapDilated2
= loadBitmap("testBasicMorphologyDilated2.png");
144 const BitmapEx aRefBitmapDilated2Eroded1
= loadBitmap("testBasicMorphologyDilated2Eroded1.png");
146 BitmapEx aTransformBitmap
= aOrigBitmap
;
147 BitmapFilter::Filter(aTransformBitmap
, BitmapDilateFilter(1));
148 if (constWriteResultBitmap
)
149 savePNG("~/Dilated1.png", aTransformBitmap
);
150 CPPUNIT_ASSERT_EQUAL(aRefBitmapDilated1
.GetChecksum(), aTransformBitmap
.GetChecksum());
151 BitmapFilter::Filter(aTransformBitmap
, BitmapErodeFilter(1));
152 if (constWriteResultBitmap
)
153 savePNG("~/Dilated1Eroded1.png", aTransformBitmap
);
154 CPPUNIT_ASSERT_EQUAL(aRefBitmapDilated1Eroded1
.GetChecksum(), aTransformBitmap
.GetChecksum());
156 aTransformBitmap
= aOrigBitmap
;
157 BitmapFilter::Filter(aTransformBitmap
, BitmapDilateFilter(2));
158 if (constWriteResultBitmap
)
159 savePNG("~/Dilated2.png", aTransformBitmap
);
160 CPPUNIT_ASSERT_EQUAL(aRefBitmapDilated2
.GetChecksum(), aTransformBitmap
.GetChecksum());
161 BitmapFilter::Filter(aTransformBitmap
, BitmapErodeFilter(1));
162 if (constWriteResultBitmap
)
163 savePNG("~/Dilated2Eroded1.png", aTransformBitmap
);
164 CPPUNIT_ASSERT_EQUAL(aRefBitmapDilated2Eroded1
.GetChecksum(), aTransformBitmap
.GetChecksum());
167 void BitmapFilterTest::testPerformance()
169 if (!constEnablePerformanceTest
)
172 Size
aSize(4000, 3000); // A rather common picture size
175 Bitmap
aBigBitmap(aSize
, 24);
177 tools::Long aMargin
= 500;
178 BitmapScopedWriteAccess
aWriteAccess(aBigBitmap
);
179 aWriteAccess
->Erase(COL_WHITE
);
180 aWriteAccess
->SetLineColor(COL_BLACK
);
181 aWriteAccess
->SetFillColor(COL_BLACK
);
182 tools::Rectangle
aRectangle(aMargin
, aMargin
, aSize
.Width() - 1 - aMargin
,
183 aSize
.Height() - 1 - aMargin
);
185 aWriteAccess
->DrawRect(aRectangle
);
188 int nIterations
= 10;
189 auto start
= std::chrono::high_resolution_clock::now();
191 for (int i
= 0; i
< nIterations
; i
++)
193 BitmapFilterStackBlur
aBlurFilter(250);
194 aResult
= aBlurFilter
.filter(aBigBitmap
);
196 auto end
= std::chrono::high_resolution_clock::now();
197 auto elapsed
= (end
- start
) / nIterations
;
199 if (constWriteResultBitmap
)
201 std::unique_ptr
<SvFileStream
> pStream(
202 new SvFileStream("~/BlurBigPerformance.png", StreamMode::WRITE
| StreamMode::TRUNC
));
203 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
204 rFilter
.compressAsPNG(BitmapEx(aResult
), *pStream
);
206 pStream
.reset(new SvFileStream("~/BlurBigPerformance.txt", StreamMode::WRITE
));
207 pStream
->WriteOString("Blur average time: ");
208 pStream
->WriteOString(OString::number(
209 std::chrono::duration_cast
<std::chrono::milliseconds
>(elapsed
).count()));
210 pStream
->WriteOString("\n");
214 void BitmapFilterTest::testGenerateStripRanges()
217 constexpr tools::Long nFirstIndex
= 0;
218 constexpr tools::Long nLastIndex
= 100;
219 constexpr tools::Long nStripSize
= 32;
221 std::vector
<std::tuple
<tools::Long
, tools::Long
, bool>> aRanges
;
223 vcl::bitmap::generateStripRanges
<nStripSize
>(
224 nFirstIndex
, nLastIndex
,
225 [&](tools::Long
const nStart
, tools::Long
const nEnd
, bool const bLast
) {
226 aRanges
.emplace_back(nStart
, nEnd
, bLast
);
229 CPPUNIT_ASSERT_EQUAL(size_t(4), aRanges
.size());
231 CPPUNIT_ASSERT_EQUAL(tools::Long(0), std::get
<0>(aRanges
[0]));
232 CPPUNIT_ASSERT_EQUAL(tools::Long(31), std::get
<1>(aRanges
[0]));
233 CPPUNIT_ASSERT_EQUAL(false, std::get
<2>(aRanges
[0]));
235 CPPUNIT_ASSERT_EQUAL(tools::Long(32), std::get
<0>(aRanges
[1]));
236 CPPUNIT_ASSERT_EQUAL(tools::Long(63), std::get
<1>(aRanges
[1]));
237 CPPUNIT_ASSERT_EQUAL(false, std::get
<2>(aRanges
[1]));
239 CPPUNIT_ASSERT_EQUAL(tools::Long(64), std::get
<0>(aRanges
[2]));
240 CPPUNIT_ASSERT_EQUAL(tools::Long(95), std::get
<1>(aRanges
[2]));
241 CPPUNIT_ASSERT_EQUAL(false, std::get
<2>(aRanges
[2]));
243 CPPUNIT_ASSERT_EQUAL(tools::Long(96), std::get
<0>(aRanges
[3]));
244 CPPUNIT_ASSERT_EQUAL(tools::Long(100), std::get
<1>(aRanges
[3]));
245 CPPUNIT_ASSERT_EQUAL(true, std::get
<2>(aRanges
[3]));
249 constexpr tools::Long nFirstIndex
= 0;
250 constexpr tools::Long nLastIndex
= 95;
251 constexpr tools::Long nStripSize
= 32;
253 std::vector
<std::tuple
<tools::Long
, tools::Long
, bool>> aRanges
;
255 vcl::bitmap::generateStripRanges
<nStripSize
>(
256 nFirstIndex
, nLastIndex
,
257 [&](tools::Long
const nStart
, tools::Long
const nEnd
, bool const bLast
) {
258 aRanges
.emplace_back(nStart
, nEnd
, bLast
);
261 CPPUNIT_ASSERT_EQUAL(size_t(3), aRanges
.size());
263 CPPUNIT_ASSERT_EQUAL(tools::Long(0), std::get
<0>(aRanges
[0]));
264 CPPUNIT_ASSERT_EQUAL(tools::Long(31), std::get
<1>(aRanges
[0]));
265 CPPUNIT_ASSERT_EQUAL(false, std::get
<2>(aRanges
[0]));
267 CPPUNIT_ASSERT_EQUAL(tools::Long(32), std::get
<0>(aRanges
[1]));
268 CPPUNIT_ASSERT_EQUAL(tools::Long(63), std::get
<1>(aRanges
[1]));
269 CPPUNIT_ASSERT_EQUAL(false, std::get
<2>(aRanges
[1]));
271 CPPUNIT_ASSERT_EQUAL(tools::Long(64), std::get
<0>(aRanges
[2]));
272 CPPUNIT_ASSERT_EQUAL(tools::Long(95), std::get
<1>(aRanges
[2]));
273 CPPUNIT_ASSERT_EQUAL(true, std::get
<2>(aRanges
[2]));
279 CPPUNIT_TEST_SUITE_REGISTRATION(BitmapFilterTest
);
281 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */