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 <cppunit/TestAssert.h>
11 #include <cppunit/TestFixture.h>
12 #include <cppunit/extensions/HelperMacros.h>
14 #include <vcl/bitmap.hxx>
16 #include <tools/stream.hxx>
17 #include <vcl/graphicfilter.hxx>
19 #include <BitmapSymmetryCheck.hxx>
20 #include <vcl/BitmapWriteAccess.hxx>
24 class BitmapScaleTest
: public CppUnit::TestFixture
28 void testScaleSymmetry();
30 CPPUNIT_TEST_SUITE(BitmapScaleTest
);
31 CPPUNIT_TEST(testScale
);
32 CPPUNIT_TEST(testScale2
);
33 CPPUNIT_TEST(testScaleSymmetry
);
34 CPPUNIT_TEST_SUITE_END();
37 bool checkBitmapColor(Bitmap
const& rBitmap
, Color
const& rExpectedColor
)
40 Bitmap
aBitmap(rBitmap
);
41 BitmapScopedReadAccess
pReadAccess(aBitmap
);
42 tools::Long nHeight
= pReadAccess
->Height();
43 tools::Long nWidth
= pReadAccess
->Width();
44 for (tools::Long y
= 0; y
< nHeight
; ++y
)
46 Scanline pScanlineRead
= pReadAccess
->GetScanline(y
);
47 for (tools::Long x
= 0; x
< nWidth
; ++x
)
49 Color aColor
= pReadAccess
->GetPixelFromData(pScanlineRead
, x
);
50 if (aColor
!= rExpectedColor
)
58 void assertColorsAreSimilar(int maxDifference
, int line
, const BitmapColor
& expected
,
59 const BitmapColor
& actual
)
61 // Check that the two colors match or are reasonably similar.
62 if (expected
.GetColorError(actual
) <= maxDifference
)
65 std::stringstream stream
;
66 stream
<< "Line: " << line
;
68 CPPUNIT_ASSERT_EQUAL_MESSAGE(stream
.str(), expected
, actual
);
71 void BitmapScaleTest::testScale()
73 const bool bExportBitmap(false);
74 using tools::Rectangle
;
76 static const BmpScaleFlag scaleMethods
[]
77 = { BmpScaleFlag::Default
, BmpScaleFlag::Fast
, BmpScaleFlag::BestQuality
,
78 BmpScaleFlag::Interpolate
, BmpScaleFlag::Lanczos
, BmpScaleFlag::BiCubic
,
79 BmpScaleFlag::BiLinear
};
80 for (BmpScaleFlag scaleMethod
: scaleMethods
)
87 static const ScaleSize scaleSizes
[]
89 { Size(16, 16), Size(16, 16) },
90 // powers of 2 (OpenGL may use texture atlas)
91 { Size(16, 16), Size(14, 14) },
92 { Size(14, 14), Size(16, 16) }, // both upscaling and downscaling
94 { Size(18, 18), Size(14, 14) },
95 { Size(14, 14), Size(18, 18) },
96 // different x/y ratios
97 { Size(16, 30), Size(14, 18) },
98 { Size(14, 18), Size(16, 30) },
99 // ratio larger than 16 (triggers different paths in some OpenGL algorithms)
100 { Size(18 * 20, 18 * 20), Size(14, 14) },
101 { Size(14, 14), Size(18 * 20, 18 * 20) },
103 { Size(1, 1), Size(1, 1) },
104 { Size(16, 1), Size(12, 1) },
105 { Size(1, 16), Size(1, 12) }
107 for (const ScaleSize
& scaleSize
: scaleSizes
)
109 OString testStr
= "Testing scale (" + scaleSize
.srcSize
.toString() + ")->("
110 + scaleSize
.destSize
.toString() + "), method "
111 + OString::number(static_cast<int>(scaleMethod
));
112 fprintf(stderr
, "%s\n", testStr
.getStr());
113 Bitmap
bitmap(scaleSize
.srcSize
, vcl::PixelFormat::N24_BPP
);
115 // Fill each quarter of the source bitmap with a different color,
116 // and center with yet another color.
117 BitmapScopedWriteAccess
writeAccess(bitmap
);
118 const int halfW
= scaleSize
.srcSize
.getWidth() / 2;
119 const int halfH
= scaleSize
.srcSize
.getHeight() / 2;
120 const Size
aSize(std::max(halfW
, 1), std::max(halfH
, 1));
122 writeAccess
->SetFillColor(COL_GREEN
);
123 writeAccess
->FillRect(Rectangle(Point(0, 0), aSize
));
124 writeAccess
->SetFillColor(COL_RED
);
125 writeAccess
->FillRect(Rectangle(Point(0, halfH
), aSize
));
126 writeAccess
->SetFillColor(COL_YELLOW
);
127 writeAccess
->FillRect(Rectangle(Point(halfW
, 0), aSize
));
128 writeAccess
->SetFillColor(COL_BLACK
);
129 writeAccess
->FillRect(Rectangle(Point(halfW
, halfH
), aSize
));
130 writeAccess
->SetFillColor(COL_BLUE
);
131 writeAccess
->FillRect(Rectangle(Point(halfW
/ 2, halfH
/ 2), aSize
));
135 SvFileStream
aStream(u
"~/scale_before.png"_ustr
,
136 StreamMode::WRITE
| StreamMode::TRUNC
);
137 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
138 rFilter
.compressAsPNG(BitmapEx(bitmap
), aStream
);
140 CPPUNIT_ASSERT(bitmap
.Scale(scaleSize
.destSize
, scaleMethod
));
143 SvFileStream
aStream(u
"~/scale_after.png"_ustr
,
144 StreamMode::WRITE
| StreamMode::TRUNC
);
145 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
146 rFilter
.compressAsPNG(BitmapEx(bitmap
), aStream
);
148 CPPUNIT_ASSERT_EQUAL(scaleSize
.destSize
, bitmap
.GetSizePixel());
149 const int lastW
= scaleSize
.destSize
.getWidth() - 1;
150 const int lastH
= scaleSize
.destSize
.getHeight() - 1;
151 if (scaleSize
.srcSize
.getWidth() == 1 && scaleSize
.srcSize
.getHeight() == 1)
153 BitmapReadAccess
readAccess(bitmap
);
154 assertColorsAreSimilar(2, __LINE__
, COL_BLUE
, readAccess
.GetColor(0, 0));
155 assertColorsAreSimilar(2, __LINE__
, COL_BLUE
, readAccess
.GetColor(lastH
, 0));
156 assertColorsAreSimilar(2, __LINE__
, COL_BLUE
, readAccess
.GetColor(0, lastW
));
157 assertColorsAreSimilar(2, __LINE__
, COL_BLUE
, readAccess
.GetColor(lastH
, lastW
));
158 assertColorsAreSimilar(2, __LINE__
, COL_BLUE
,
159 readAccess
.GetColor(lastH
/ 2, lastW
/ 2));
161 else if (lastW
&& lastH
)
163 // Scaling should keep each quarter of the resulting bitmap have the same color,
164 // so check that color in each corner of the result bitmap is the same color,
165 // or reasonably close (some algorithms may alter the color very slightly).
166 BitmapReadAccess
readAccess(bitmap
);
167 assertColorsAreSimilar(2, __LINE__
, COL_GREEN
, readAccess
.GetColor(0, 0));
168 assertColorsAreSimilar(2, __LINE__
, COL_RED
, readAccess
.GetColor(lastH
, 0));
169 assertColorsAreSimilar(2, __LINE__
, COL_YELLOW
, readAccess
.GetColor(0, lastW
));
170 assertColorsAreSimilar(2, __LINE__
, COL_BLACK
, readAccess
.GetColor(lastH
, lastW
));
171 assertColorsAreSimilar(2, __LINE__
, COL_BLUE
,
172 readAccess
.GetColor(lastH
/ 2, lastW
/ 2));
176 BitmapReadAccess
readAccess(bitmap
);
177 assertColorsAreSimilar(2, __LINE__
, COL_RED
, readAccess
.GetColor(0, 0));
178 assertColorsAreSimilar(2, __LINE__
, COL_BLACK
, readAccess
.GetColor(0, lastW
));
179 assertColorsAreSimilar(2, __LINE__
, COL_BLUE
, readAccess
.GetColor(0, lastW
/ 2));
183 BitmapReadAccess
readAccess(bitmap
);
184 assertColorsAreSimilar(2, __LINE__
, COL_YELLOW
, readAccess
.GetColor(0, 0));
185 assertColorsAreSimilar(2, __LINE__
, COL_BLACK
, readAccess
.GetColor(lastH
, 0));
186 assertColorsAreSimilar(2, __LINE__
, COL_BLUE
, readAccess
.GetColor(lastH
/ 2, 0));
192 void BitmapScaleTest::testScale2()
194 const bool bExportBitmap(false);
196 Bitmap
aBitmap24Bit(Size(4096, 4096), vcl::PixelFormat::N24_BPP
);
197 CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP
, aBitmap24Bit
.getPixelFormat());
198 Color aBitmapColor
= COL_YELLOW
;
200 BitmapScopedWriteAccess
aWriteAccess(aBitmap24Bit
);
201 aWriteAccess
->Erase(aBitmapColor
);
206 SvFileStream
aStream(u
"scale_before.png"_ustr
, StreamMode::WRITE
| StreamMode::TRUNC
);
207 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
208 rFilter
.compressAsPNG(BitmapEx(aBitmap24Bit
), aStream
);
212 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(4096), aBitmap24Bit
.GetSizePixel().Width());
213 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(4096), aBitmap24Bit
.GetSizePixel().Height());
214 Bitmap aScaledBitmap
= aBitmap24Bit
;
215 aScaledBitmap
.Scale(Size(65, 65));
219 SvFileStream
aStream(u
"scale_after_65x65.png"_ustr
, StreamMode::WRITE
| StreamMode::TRUNC
);
220 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
221 rFilter
.compressAsPNG(BitmapEx(aScaledBitmap
), aStream
);
224 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(65), aScaledBitmap
.GetSizePixel().Width());
225 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(65), aScaledBitmap
.GetSizePixel().Height());
226 CPPUNIT_ASSERT(checkBitmapColor(aScaledBitmap
, aBitmapColor
));
229 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(4096), aBitmap24Bit
.GetSizePixel().Width());
230 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(4096), aBitmap24Bit
.GetSizePixel().Height());
231 aScaledBitmap
= aBitmap24Bit
;
232 aScaledBitmap
.Scale(Size(64, 64));
236 SvFileStream
aStream(u
"scale_after_64x64.png"_ustr
, StreamMode::WRITE
| StreamMode::TRUNC
);
237 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
238 rFilter
.compressAsPNG(BitmapEx(aScaledBitmap
), aStream
);
241 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(64), aScaledBitmap
.GetSizePixel().Width());
242 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(64), aScaledBitmap
.GetSizePixel().Height());
243 CPPUNIT_ASSERT(checkBitmapColor(aScaledBitmap
, aBitmapColor
));
246 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(4096), aBitmap24Bit
.GetSizePixel().Width());
247 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(4096), aBitmap24Bit
.GetSizePixel().Height());
248 aScaledBitmap
= aBitmap24Bit
;
249 aScaledBitmap
.Scale(Size(63, 63));
253 SvFileStream
aStream(u
"scale_after_63x63.png"_ustr
, StreamMode::WRITE
| StreamMode::TRUNC
);
254 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
255 rFilter
.compressAsPNG(BitmapEx(aScaledBitmap
), aStream
);
258 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(63), aScaledBitmap
.GetSizePixel().Width());
259 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(63), aScaledBitmap
.GetSizePixel().Height());
260 CPPUNIT_ASSERT(checkBitmapColor(aScaledBitmap
, aBitmapColor
));
263 void BitmapScaleTest::testScaleSymmetry()
265 const bool bExportBitmap(false);
267 Bitmap
aBitmap24Bit(Size(10, 10), vcl::PixelFormat::N24_BPP
);
268 CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP
, aBitmap24Bit
.getPixelFormat());
271 BitmapScopedWriteAccess
aWriteAccess(aBitmap24Bit
);
272 aWriteAccess
->Erase(COL_WHITE
);
273 aWriteAccess
->SetLineColor(COL_BLACK
);
274 aWriteAccess
->DrawRect(tools::Rectangle(1, 1, 8, 8));
275 aWriteAccess
->DrawRect(tools::Rectangle(3, 3, 6, 6));
278 BitmapSymmetryCheck aBitmapSymmetryCheck
;
280 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(10), aBitmap24Bit
.GetSizePixel().Width());
281 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(10), aBitmap24Bit
.GetSizePixel().Height());
283 // Check symmetry of the bitmap
284 CPPUNIT_ASSERT(BitmapSymmetryCheck::check(aBitmap24Bit
));
288 SvFileStream
aStream(u
"~/scale_before.png"_ustr
, StreamMode::WRITE
| StreamMode::TRUNC
);
289 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
290 rFilter
.compressAsPNG(BitmapEx(aBitmap24Bit
), aStream
);
293 aBitmap24Bit
.Scale(2, 2, BmpScaleFlag::Fast
);
295 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(20), aBitmap24Bit
.GetSizePixel().Width());
296 CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long
>(20), aBitmap24Bit
.GetSizePixel().Height());
298 // After scaling the bitmap should still be symmetrical. This check guarantees that
299 // scaling doesn't misalign the bitmap.
300 CPPUNIT_ASSERT(BitmapSymmetryCheck::check(aBitmap24Bit
));
304 SvFileStream
aStream(u
"~/scale_after.png"_ustr
, StreamMode::WRITE
| StreamMode::TRUNC
);
305 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
306 rFilter
.compressAsPNG(BitmapEx(aBitmap24Bit
), aStream
);
312 CPPUNIT_TEST_SUITE_REGISTRATION(BitmapScaleTest
);
314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */