tdf#164932 sw inline heading: unit test for fixed text frame AutoSize
[LibreOffice.git] / vcl / qa / cppunit / png / PngFilterTest.cxx
blob8d979d393c91a7e7911f50be7e6b905a7f04929f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <string_view>
24 #include <test/bootstrapfixture.hxx>
25 #include <tools/stream.hxx>
26 #include <vcl/filter/PngImageReader.hxx>
27 #include <vcl/filter/PngImageWriter.hxx>
28 #include <vcl/bitmap/BitmapMonochromeFilter.hxx>
29 #include <vcl/BitmapWriteAccess.hxx>
30 #include <vcl/alpha.hxx>
31 #include <vcl/graphicfilter.hxx>
32 #include <unotools/tempfile.hxx>
34 using namespace css;
36 namespace
38 struct Case
40 tools::Long mnWidth;
41 tools::Long mnHeight;
42 sal_uInt16 mnBpp;
43 bool mbHasPalette;
44 bool mbIsAlpha;
46 // Checks that a pngs BitmapEx is the same after reading and
47 // after writing. Takes a vector of function pointers if there's need to test
48 // special cases
49 void checkImportExportPng(const OUString& sFilePath, const Case& aCase)
51 SvFileStream aFileStream(sFilePath, StreamMode::READ);
52 SvMemoryStream aExportStream;
53 BitmapEx aImportedBitmapEx;
54 BitmapEx aExportedImportedBitmapEx;
56 bool bOpenOk = !aFileStream.GetError() && aFileStream.GetBufferSize() > 0;
57 CPPUNIT_ASSERT_MESSAGE(OString("Failed to open file: " + sFilePath.toUtf8()).getStr(), bOpenOk);
59 // Read the png from the file
61 vcl::PngImageReader aPngReader(aFileStream);
62 bool bReadOk = aPngReader.read(aImportedBitmapEx);
63 CPPUNIT_ASSERT_MESSAGE(OString("Failed to read png from: " + sFilePath.toUtf8()).getStr(),
64 bReadOk);
65 Bitmap aImportedBitmap = aImportedBitmapEx.GetBitmap();
66 BitmapScopedInfoAccess pAccess(aImportedBitmap);
67 auto nActualWidth = aImportedBitmapEx.GetSizePixel().Width();
68 auto nActualHeight = aImportedBitmapEx.GetSizePixel().Height();
69 auto nActualBpp = vcl::pixelFormatBitCount(aImportedBitmapEx.GetBitmap().getPixelFormat());
70 auto bActualHasPalette = pAccess->HasPalette();
71 auto bActualIsAlpha = aImportedBitmapEx.IsAlpha();
72 CPPUNIT_ASSERT_EQUAL_MESSAGE(
73 OString("Width comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
74 aCase.mnWidth, nActualWidth);
75 CPPUNIT_ASSERT_EQUAL_MESSAGE(
76 OString("Height comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
77 aCase.mnHeight, nActualHeight);
78 CPPUNIT_ASSERT_EQUAL_MESSAGE(
79 OString("Bpp comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
80 aCase.mnBpp, nActualBpp);
81 CPPUNIT_ASSERT_EQUAL_MESSAGE(
82 OString("HasPalette comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
83 aCase.mbHasPalette, bActualHasPalette);
84 CPPUNIT_ASSERT_EQUAL_MESSAGE(
85 OString("IsAlpha comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
86 aCase.mbIsAlpha, bActualIsAlpha);
89 // Write the imported png to a stream
91 vcl::PngImageWriter aPngWriter(aExportStream);
92 bool bWriteOk = aPngWriter.write(aImportedBitmapEx);
93 CPPUNIT_ASSERT_MESSAGE(OString("Failed to write png: " + sFilePath.toUtf8()).getStr(),
94 bWriteOk);
95 aExportStream.Seek(0);
98 // Read the png again from the exported stream
100 vcl::PngImageReader aPngReader(aExportStream);
101 bool bReadOk = aPngReader.read(aExportedImportedBitmapEx);
102 CPPUNIT_ASSERT_MESSAGE(
103 OString("Failed to read exported png: " + sFilePath.toUtf8()).getStr(), bReadOk);
104 Bitmap aExportedImportedBitmap = aExportedImportedBitmapEx.GetBitmap();
105 BitmapScopedInfoAccess pAccess(aExportedImportedBitmap);
106 auto nActualWidth = aExportedImportedBitmapEx.GetSizePixel().Width();
107 auto nActualHeight = aExportedImportedBitmapEx.GetSizePixel().Height();
108 auto nActualBpp
109 = vcl::pixelFormatBitCount(aExportedImportedBitmapEx.GetBitmap().getPixelFormat());
110 auto bActualHasPalette = pAccess->HasPalette();
111 auto bActualIsAlpha = aExportedImportedBitmapEx.IsAlpha();
112 CPPUNIT_ASSERT_EQUAL_MESSAGE(
113 OString("Width comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
114 aCase.mnWidth, nActualWidth);
115 CPPUNIT_ASSERT_EQUAL_MESSAGE(
116 OString("Height comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
117 aCase.mnHeight, nActualHeight);
118 CPPUNIT_ASSERT_EQUAL_MESSAGE(
119 OString("Bpp comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
120 aCase.mnBpp, nActualBpp);
121 CPPUNIT_ASSERT_EQUAL_MESSAGE(
122 OString("HasPalette comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
123 aCase.mbHasPalette, bActualHasPalette);
124 CPPUNIT_ASSERT_EQUAL_MESSAGE(
125 OString("IsAlpha comparison failed for exported png:" + sFilePath.toUtf8()).getStr(),
126 aCase.mbIsAlpha, bActualIsAlpha);
129 // Compare imported and exported BitmapEx
130 // This compares size, inner bitmap and alpha mask
131 bool bIsSame = (aExportedImportedBitmapEx == aImportedBitmapEx);
132 CPPUNIT_ASSERT_MESSAGE(
133 OString("Import->Export png test failed for png: " + sFilePath.toUtf8()).getStr(), bIsSame);
136 // Checks that aPngReader.read returns false on corrupted files
137 void checkImportCorruptedPng(const OUString& sFilePath)
139 SvFileStream aFileStream(sFilePath, StreamMode::READ);
140 BitmapEx aImportedBitmapEx;
142 bool bOpenOk = !aFileStream.GetError() && aFileStream.GetBufferSize() > 0;
143 CPPUNIT_ASSERT_MESSAGE(OString("Failed to open file: " + sFilePath.toUtf8()).getStr(), bOpenOk);
144 vcl::PngImageReader aPngReader(aFileStream);
145 bool bReadOk = aPngReader.read(aImportedBitmapEx);
146 // Make sure this file was not read successfully
147 CPPUNIT_ASSERT_MESSAGE(
148 OString("Corrupted png should not be opened: " + sFilePath.toUtf8()).getStr(), !bReadOk);
152 class PngFilterTest : public test::BootstrapFixture
154 // Should keep the temp files (should be false)
155 static constexpr bool bKeepTemp = true;
157 OUString maDataUrl;
159 OUString getFullUrl(std::u16string_view sFileName)
161 return m_directories.getURLFromSrc(maDataUrl) + sFileName;
164 public:
165 PngFilterTest()
166 : BootstrapFixture(true, false)
167 , maDataUrl(u"/vcl/qa/cppunit/png/data/"_ustr)
171 void testPng();
172 void testApng();
173 void testPngSuite();
174 void testMsGifInPng();
175 void testPngRoundtrip8BitGrey();
176 void testPngRoundtrip24();
177 void testPngRoundtrip24_8();
178 void testPngRoundtrip32();
179 void testPngWrite8BitRGBPalette();
180 void testTdf153180MonochromeFilterPngExport();
181 void testDump();
183 CPPUNIT_TEST_SUITE(PngFilterTest);
184 CPPUNIT_TEST(testPng);
185 CPPUNIT_TEST(testApng);
186 CPPUNIT_TEST(testPngSuite);
187 CPPUNIT_TEST(testMsGifInPng);
188 CPPUNIT_TEST(testPngRoundtrip8BitGrey);
189 CPPUNIT_TEST(testPngRoundtrip24);
190 CPPUNIT_TEST(testPngRoundtrip24_8);
191 CPPUNIT_TEST(testPngRoundtrip32);
192 CPPUNIT_TEST(testPngWrite8BitRGBPalette);
193 CPPUNIT_TEST(testDump);
194 CPPUNIT_TEST(testTdf153180MonochromeFilterPngExport);
195 CPPUNIT_TEST_SUITE_END();
198 void PngFilterTest::testPng()
200 for (const OUString& aFileName : { u"rect-1bit-pal.png"_ustr })
202 SvFileStream aFileStream(getFullUrl(aFileName), StreamMode::READ);
204 vcl::PngImageReader aPngReader(aFileStream);
205 BitmapEx aBitmapEx;
206 aPngReader.read(aBitmapEx);
208 Bitmap aBitmap = aBitmapEx.GetBitmap();
210 BitmapScopedReadAccess pAccess(aBitmap);
211 CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAccess->Width());
212 CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAccess->Height());
214 if (pAccess->GetBitCount() == 24 || pAccess->GetBitCount() == 32)
216 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
217 pAccess->GetPixel(0, 0));
218 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
219 pAccess->GetPixel(3, 3));
220 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
221 pAccess->GetPixel(3, 0));
222 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
223 pAccess->GetPixel(0, 3));
225 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x00, 0x00),
226 pAccess->GetPixel(1, 1));
227 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x00, 0x00),
228 pAccess->GetPixel(1, 2));
229 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x00, 0x00),
230 pAccess->GetPixel(2, 1));
231 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x00, 0x00),
232 pAccess->GetPixel(2, 2));
234 else
236 CPPUNIT_ASSERT_MESSAGE("Bitmap is not 24 or 32 bit.", false);
241 OUString aFilenames[] = {
242 u"color-rect-8bit-RGB.png"_ustr,
243 u"color-rect-8bit-RGB-interlaced.png"_ustr,
244 u"color-rect-4bit-pal.png"_ustr,
247 for (const OUString& aFileName : aFilenames)
249 SvFileStream aFileStream(getFullUrl(aFileName), StreamMode::READ);
251 vcl::PngImageReader aPngReader(aFileStream);
252 BitmapEx aBitmapEx;
253 aPngReader.read(aBitmapEx);
255 Bitmap aBitmap = aBitmapEx.GetBitmap();
257 BitmapScopedReadAccess pAccess(aBitmap);
258 CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAccess->Width());
259 CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAccess->Height());
260 if (pAccess->GetBitCount() == 24 || pAccess->GetBitCount() == 32)
262 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
263 pAccess->GetPixel(0, 0));
264 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
265 pAccess->GetPixel(3, 3));
266 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
267 pAccess->GetPixel(3, 0));
268 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
269 pAccess->GetPixel(0, 3));
271 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0x00, 0x00, 0x00),
272 pAccess->GetPixel(1, 1));
273 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0xFF, 0x00, 0x00),
274 pAccess->GetPixel(1, 2));
275 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xFF, 0x00),
276 pAccess->GetPixel(2, 1));
277 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0x00, 0x00),
278 pAccess->GetPixel(2, 2));
280 else
282 CPPUNIT_ASSERT_MESSAGE("Bitmap is not 24 or 32 bit.", false);
286 for (const OUString& aFileName : { u"alpha-rect-8bit-RGBA.png"_ustr })
288 SvFileStream aFileStream(getFullUrl(aFileName), StreamMode::READ);
290 vcl::PngImageReader aPngReader(aFileStream);
291 BitmapEx aBitmapEx;
292 aPngReader.read(aBitmapEx);
294 Bitmap aBitmap = aBitmapEx.GetBitmap();
296 BitmapScopedReadAccess pAccess(aBitmap);
297 CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAccess->Width());
298 CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAccess->Height());
300 if (pAccess->GetBitCount() == 24)
302 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
303 pAccess->GetPixel(0, 0));
304 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
305 pAccess->GetPixel(3, 3));
306 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
307 pAccess->GetPixel(3, 0));
308 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x00),
309 pAccess->GetPixel(0, 3));
311 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0x00, 0x00, 0x00),
312 pAccess->GetPixel(1, 1));
313 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0xFF, 0x00, 0x00),
314 pAccess->GetPixel(1, 2));
315 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xFF, 0x00),
316 pAccess->GetPixel(2, 1));
317 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0x00, 0x00),
318 pAccess->GetPixel(2, 2));
320 AlphaMask aAlpha = aBitmapEx.GetAlphaMask();
322 BitmapScopedReadAccess pAlphaAccess(aAlpha);
323 CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), pAlphaAccess->GetBitCount());
324 CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAlphaAccess->Width());
325 CPPUNIT_ASSERT_EQUAL(tools::Long(4), pAlphaAccess->Height());
327 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x7F, 0x00),
328 pAlphaAccess->GetPixel(0, 0));
329 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x7F, 0x00),
330 pAlphaAccess->GetPixel(3, 3));
331 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x7F, 0x00),
332 pAlphaAccess->GetPixel(3, 0));
333 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x7F, 0x00),
334 pAlphaAccess->GetPixel(0, 3));
336 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xBF, 0x00),
337 pAlphaAccess->GetPixel(1, 1));
338 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x3F, 0x00),
339 pAlphaAccess->GetPixel(1, 2));
340 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0x3F, 0x00),
341 pAlphaAccess->GetPixel(2, 1));
342 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xBF, 0x00),
343 pAlphaAccess->GetPixel(2, 2));
346 else if (pAccess->GetBitCount() == 32)
348 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x80),
349 pAccess->GetPixel(0, 0));
350 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x80),
351 pAccess->GetPixel(3, 3));
352 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x80),
353 pAccess->GetPixel(3, 0));
354 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0xFF, 0x80),
355 pAccess->GetPixel(0, 3));
357 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0x00, 0x00, 0x40),
358 pAccess->GetPixel(1, 1));
359 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0xFF, 0x00, 0xC0),
360 pAccess->GetPixel(1, 2));
361 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0x00, 0x00, 0xFF, 0xC0),
362 pAccess->GetPixel(2, 1));
363 CPPUNIT_ASSERT_EQUAL(BitmapColor(ColorTransparency, 0xFF, 0xFF, 0x00, 0x40),
364 pAccess->GetPixel(2, 2));
366 else
368 CPPUNIT_ASSERT_MESSAGE("Bitmap is not 24 or 32 bit.", false);
374 void PngFilterTest::testApng()
376 SvFileStream aFileStream(getFullUrl(u"apng_simple.apng"), StreamMode::READ);
377 vcl::PngImageReader aPngReader(aFileStream);
378 Graphic aGraphic;
379 bool bSuccess = aPngReader.read(aGraphic);
380 CPPUNIT_ASSERT(bSuccess);
381 CPPUNIT_ASSERT(aGraphic.IsAnimated());
382 CPPUNIT_ASSERT_EQUAL(size_t(2), aGraphic.GetAnimation().GetAnimationFrames().size());
384 AnimationFrame aFrame1 = *aGraphic.GetAnimation().GetAnimationFrames()[0];
385 AnimationFrame aFrame2 = *aGraphic.GetAnimation().GetAnimationFrames()[1];
387 CPPUNIT_ASSERT_EQUAL(COL_WHITE, aFrame1.maBitmapEx.GetPixelColor(0, 0));
388 CPPUNIT_ASSERT_EQUAL(Color(0x72d1c8), aFrame1.maBitmapEx.GetPixelColor(2, 2));
389 CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, aFrame2.maBitmapEx.GetPixelColor(0, 0));
391 // Roundtrip the APNG
392 SvMemoryStream aOutStream;
393 vcl::PngImageWriter aPngWriter(aOutStream);
394 bSuccess = aPngWriter.write(aGraphic);
395 CPPUNIT_ASSERT(bSuccess);
397 aOutStream.Seek(STREAM_SEEK_TO_BEGIN);
398 vcl::PngImageReader aPngReader2(aOutStream);
399 Graphic aGraphic2;
400 bSuccess = aPngReader2.read(aGraphic2);
401 CPPUNIT_ASSERT(bSuccess);
402 CPPUNIT_ASSERT(aGraphic2.IsAnimated());
403 CPPUNIT_ASSERT_EQUAL(size_t(2), aGraphic2.GetAnimation().GetAnimationFrames().size());
405 AnimationFrame aFrame1Roundtripped = *aGraphic2.GetAnimation().GetAnimationFrames()[0];
406 AnimationFrame aFrame2Roundtripped = *aGraphic2.GetAnimation().GetAnimationFrames()[1];
408 CPPUNIT_ASSERT_EQUAL(COL_WHITE, aFrame1Roundtripped.maBitmapEx.GetPixelColor(0, 0));
409 CPPUNIT_ASSERT_EQUAL(Color(0x72d1c8), aFrame1Roundtripped.maBitmapEx.GetPixelColor(2, 2));
410 CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, aFrame2Roundtripped.maBitmapEx.GetPixelColor(0, 0));
412 // Make sure the two frames have the same properties
413 CPPUNIT_ASSERT_EQUAL(aFrame1.maPositionPixel, aFrame1Roundtripped.maPositionPixel);
414 CPPUNIT_ASSERT_EQUAL(aFrame1.maSizePixel, aFrame1Roundtripped.maSizePixel);
415 CPPUNIT_ASSERT_EQUAL(aFrame1.mnWait, aFrame1Roundtripped.mnWait);
416 CPPUNIT_ASSERT_EQUAL(aFrame1.meDisposal, aFrame1Roundtripped.meDisposal);
417 CPPUNIT_ASSERT_EQUAL(aFrame1.meBlend, aFrame1Roundtripped.meBlend);
419 CPPUNIT_ASSERT_EQUAL(aFrame2.maPositionPixel, aFrame2Roundtripped.maPositionPixel);
420 CPPUNIT_ASSERT_EQUAL(aFrame2.maSizePixel, aFrame2Roundtripped.maSizePixel);
421 CPPUNIT_ASSERT_EQUAL(aFrame2.mnWait, aFrame2Roundtripped.mnWait);
422 CPPUNIT_ASSERT_EQUAL(aFrame2.meDisposal, aFrame2Roundtripped.meDisposal);
423 CPPUNIT_ASSERT_EQUAL(aFrame2.meBlend, aFrame2Roundtripped.meBlend);
426 void PngFilterTest::testPngSuite()
428 // Test the PngSuite test files by Willem van Schaik
429 // filename: g04i2c08.png
430 // || ||||
431 // test feature (in this case gamma) ------+| ||||
432 // parameter of test (here gamma-value) ----+ ||||
433 // interlaced or non-interlaced --------------+|||
434 // color-type (numerical) ---------------------+||
435 // color-type (descriptive) --------------------+|
436 // bit-depth ------------------------------------+
438 // Some notes about the cases:
439 // - RGB palette PNGs get converted to a bitmap with png_set_palette_to_rgb
440 // - Grayscale PNGs with alpha also do with png_set_gray_to_rgb
441 // - Grayscale PNGs without alpha use BitmapEx palette utilities
442 // - 1, 2, 4 bit grayscale w/o alpha gets converted to 8 bit with png_set_expand_gray_1_2_4_to_8
443 // - 16 bit per channel gets converted to 8 bit per channel with png_set_scale_16
444 // - PNGs that are not size related have size 32x32
445 // - Internally BitmapEx is never 32 bpp, instead it's 24 bpp (rgb) and uses an 8 bpp alpha mask
446 std::pair<std::u16string_view, Case> aCases[] = {
447 // Basic formats, not interlaced
448 { u"basn0g01.png",
453 true,
454 false,
455 } }, // b&w
456 { u"basn0g02.png",
461 true,
462 false,
463 } }, // 2 bit grayscale
464 { u"basn0g04.png",
469 true,
470 false,
471 } }, // 4 bit grayscale
472 { u"basn0g08.png",
477 true,
478 false,
479 } }, // 8 bit grayscale
480 { u"basn0g16.png",
485 true,
486 false,
487 } }, // 16 bit grayscale
488 { u"basn2c08.png",
493 false,
494 false,
495 } }, // 8 bit rgb
496 { u"basn2c16.png",
501 false,
502 false,
503 } }, // 16 bit rgb
504 { u"basn3p01.png",
509 false,
510 false,
511 } }, // 1 bit palette
512 { u"basn3p02.png",
517 false,
518 false,
519 } }, // 2 bit palette
520 { u"basn3p04.png",
525 false,
526 false,
527 } }, // 4 bit palette
528 { u"basn3p08.png",
533 false,
534 false,
535 } }, // 8 bit palette
536 { u"basn4a08.png",
541 false,
542 true,
543 } }, // 8 bit grayscale + 8 bit alpha
544 { u"basn4a16.png",
549 false,
550 true,
551 } }, // 16 bit grayscale + 16 bit alpha
552 { u"basn6a08.png",
557 false,
558 true,
559 } }, // 8 bit rgba
560 { u"basn6a16.png",
565 false,
566 true,
567 } }, // 16 bit rgba
568 // Basic formats, interlaced
569 { u"basi0g01.png",
574 true,
575 false,
576 } }, // b&w
577 { u"basi0g02.png",
582 true,
583 false,
584 } }, // 2 bit grayscale
585 { u"basi0g04.png",
590 true,
591 false,
592 } }, // 4 bit grayscale
593 { u"basi0g08.png",
598 true,
599 false,
600 } }, // 8 bit grayscale
601 { u"basi0g16.png",
606 true,
607 false,
608 } }, // 16 bit grayscale
609 { u"basi2c08.png",
614 false,
615 false,
616 } }, // 8 bit rgb
617 { u"basi2c16.png",
622 false,
623 false,
624 } }, // 16 bit rgb
625 { u"basi3p01.png",
630 false,
631 false,
632 } }, // 1 bit palette
633 { u"basi3p02.png",
638 false,
639 false,
640 } }, // 2 bit palette
641 { u"basi3p04.png",
646 false,
647 false,
648 } }, // 4 bit palette
649 { u"basi3p08.png",
654 false,
655 false,
656 } }, // 8 bit palette
657 { u"basi4a08.png",
662 false,
663 true,
664 } }, // 8 bit grayscale + 8 bit alpha
665 { u"basi4a16.png",
670 false,
671 true,
672 } }, // 16 bit grayscale + 16 bit alpha
673 { u"basi6a08.png",
678 false,
679 true,
680 } }, // 8 bit rgba
681 { u"basi6a16.png",
686 false,
687 true,
688 } }, // 16 bit rgba
689 // // Odd sizes, not interlaced
690 { u"s01n3p01.png",
695 false,
696 false,
697 } }, // 1x1
698 { u"s02n3p01.png",
703 false,
704 false,
705 } }, // 2x2
706 { u"s03n3p01.png",
711 false,
712 false,
713 } }, // 3x3
714 { u"s04n3p01.png",
719 false,
720 false,
721 } }, // 4x4
722 { u"s05n3p02.png",
727 false,
728 false,
729 } }, // 5x5
730 { u"s06n3p02.png",
735 false,
736 false,
737 } }, // 6x6
738 { u"s07n3p02.png",
743 false,
744 false,
745 } }, // 7x7
746 { u"s08n3p02.png",
751 false,
752 false,
753 } }, // 8x8
754 { u"s09n3p02.png",
759 false,
760 false,
761 } }, // 9x9
762 { u"s32n3p04.png",
767 false,
768 false,
769 } }, // 32x32
770 { u"s33n3p04.png",
775 false,
776 false,
777 } }, // 33x33
778 { u"s34n3p04.png",
783 false,
784 false,
785 } }, // 34x34
786 { u"s35n3p04.png",
791 false,
792 false,
793 } }, // 35x35
794 { u"s36n3p04.png",
799 false,
800 false,
801 } }, // 36x36
802 { u"s37n3p04.png",
807 false,
808 false,
809 } }, // 37x37
810 { u"s38n3p04.png",
815 false,
816 false,
817 } }, // 38x38
818 { u"s39n3p04.png",
823 false,
824 false,
825 } }, // 39x39
826 { u"s40n3p04.png",
831 false,
832 false,
833 } }, // 40x40
834 // // Odd sizes, interlaced
835 { u"s01i3p01.png",
840 false,
841 false,
842 } }, // 1x1
843 { u"s02i3p01.png",
848 false,
849 false,
850 } }, // 2x2
851 { u"s03i3p01.png",
856 false,
857 false,
858 } }, // 3x3
859 { u"s04i3p01.png",
864 false,
865 false,
866 } }, // 4x4
867 { u"s05i3p02.png",
872 false,
873 false,
874 } }, // 5x5
875 { u"s06i3p02.png",
880 false,
881 false,
882 } }, // 6x6
883 { u"s07i3p02.png",
888 false,
889 false,
890 } }, // 7x7
891 { u"s08i3p02.png",
896 false,
897 false,
898 } }, // 8x8
899 { u"s09i3p02.png",
904 false,
905 false,
906 } }, // 9x9
907 { u"s32i3p04.png",
912 false,
913 false,
914 } }, // 32x32
915 { u"s33i3p04.png",
920 false,
921 false,
922 } }, // 33x33
923 { u"s34i3p04.png",
928 false,
929 false,
930 } }, // 34x34
931 { u"s35i3p04.png",
936 false,
937 false,
938 } }, // 35x35
939 { u"s36i3p04.png",
944 false,
945 false,
946 } }, // 36x36
947 { u"s37i3p04.png",
952 false,
953 false,
954 } }, // 37x37
955 { u"s38i3p04.png",
960 false,
961 false,
962 } }, // 38x38
963 { u"s39i3p04.png",
968 false,
969 false,
970 } }, // 39x39
971 { u"s40i3p04.png",
976 false,
977 false,
978 } }, // 40x40
979 // Background colors
980 { u"bgai4a08.png",
985 false,
986 true,
987 } }, // 8 bit grayscale alpha no background chunk, interlaced
988 { u"bgai4a16.png",
993 false,
994 true,
995 } }, // 16 bit grayscale alpha no background chunk, interlaced
996 { u"bgan6a08.png",
1001 false,
1002 true,
1003 } }, // 3 * 8 bits rgb color alpha, no background chunk
1004 { u"bgan6a16.png",
1009 false,
1010 true,
1011 } }, // 3 * 16 bits rgb color alpha, no background chunk
1012 { u"bgbn4a08.png",
1017 false,
1018 true,
1019 } }, // 8 bit grayscale alpha, black background chunk
1020 { u"bggn4a16.png",
1025 false,
1026 true,
1027 } }, // 16 bit grayscale alpha, gray background chunk
1028 { u"bgwn6a08.png",
1033 false,
1034 true,
1035 } }, // 3 * 8 bits rgb color alpha, white background chunk
1036 { u"bgyn6a16.png",
1041 false,
1042 true,
1043 } }, // 3 * 16 bits rgb color alpha, yellow background chunk
1044 // Transparency
1045 { u"tbbn0g04.png",
1050 false,
1051 true,
1052 } }, // transparent, black background chunk
1053 { u"tbbn2c16.png",
1058 false,
1059 true,
1060 } }, // transparent, blue background chunk
1061 { u"tbbn3p08.png",
1066 false,
1067 true,
1068 } }, // transparent, black background chunk
1069 { u"tbgn2c16.png",
1074 false,
1075 true,
1076 } }, // transparent, green background chunk
1077 { u"tbgn3p08.png",
1082 false,
1083 true,
1084 } }, // transparent, light-gray background chunk
1085 { u"tbrn2c08.png",
1090 false,
1091 true,
1092 } }, // transparent, red background chunk
1093 { u"tbwn0g16.png",
1098 false,
1099 true,
1100 } }, // transparent, white background chunk
1101 { u"tbwn3p08.png",
1106 false,
1107 true,
1108 } }, // transparent, white background chunk
1109 { u"tbyn3p08.png",
1114 false,
1115 true,
1116 } }, // transparent, yellow background chunk
1117 { u"tp1n3p08.png",
1122 false,
1123 true,
1124 } }, // transparent, but no background chunk
1125 { u"tm3n3p02.png",
1130 false,
1131 true,
1132 } }, // multiple levels of transparency, 3 entries
1133 // Gamma
1134 { u"g03n0g16.png",
1139 true,
1140 false,
1141 } }, // grayscale, file-gamma = 0.35
1142 { u"g03n2c08.png",
1147 false,
1148 false,
1149 } }, // color, file-gamma = 0.35
1150 { u"g03n3p04.png",
1155 false,
1156 false,
1157 } }, // paletted, file-gamma = 0.35
1158 { u"g04n0g16.png",
1163 true,
1164 false,
1165 } }, // grayscale, file-gamma = 0.45
1166 { u"g04n2c08.png",
1171 false,
1172 false,
1173 } }, // color, file-gamma = 0.45
1174 { u"g04n3p04.png",
1179 false,
1180 false,
1181 } }, // paletted, file-gamma = 0.45
1182 { u"g05n0g16.png",
1187 true,
1188 false,
1189 } }, // grayscale, file-gamma = 0.55
1190 { u"g05n2c08.png",
1195 false,
1196 false,
1197 } }, // color, file-gamma = 0.55
1198 { u"g05n3p04.png",
1203 false,
1204 false,
1205 } }, // paletted, file-gamma = 0.55
1206 { u"g07n0g16.png",
1211 true,
1212 false,
1213 } }, // grayscale, file-gamma = 0.70
1214 { u"g07n2c08.png",
1219 false,
1220 false,
1221 } }, // color, file-gamma = 0.70
1222 { u"g07n3p04.png",
1227 false,
1228 false,
1229 } }, // paletted, file-gamma = 0.70
1230 { u"g10n0g16.png",
1235 true,
1236 false,
1237 } }, // grayscale, file-gamma = 1.00
1238 { u"g10n2c08.png",
1243 false,
1244 false,
1245 } }, // color, file-gamma = 1.00
1246 { u"g10n3p04.png",
1251 false,
1252 false,
1253 } }, // paletted, file-gamma = 1.00
1254 { u"g25n0g16.png",
1259 true,
1260 false,
1261 } }, // grayscale, file-gamma = 2.50
1262 { u"g25n2c08.png",
1267 false,
1268 false,
1269 } }, // color, file-gamma = 2.50
1270 { u"g25n3p04.png",
1275 false,
1276 false,
1277 } }, // paletted, file-gamma = 2.50
1278 // Image filtering
1279 { u"f00n0g08.png",
1284 true,
1285 false,
1286 } }, // grayscale no interlacing, filter-type 0
1287 { u"f00n2c08.png",
1292 false,
1293 false,
1294 } }, // color no interlacing, filter-type 0
1295 { u"f01n0g08.png",
1300 true,
1301 false,
1302 } }, // grayscale no interlacing, filter-type 1
1303 { u"f01n2c08.png",
1308 false,
1309 false,
1310 } }, // color no interlacing, filter-type 1
1311 { u"f02n0g08.png",
1316 true,
1317 false,
1318 } }, // grayscale no interlacing, filter-type 2
1319 { u"f02n2c08.png",
1324 false,
1325 false,
1326 } }, // color no interlacing, filter-type 2
1327 { u"f03n0g08.png",
1332 true,
1333 false,
1334 } }, // grayscale no interlacing, filter-type 3
1335 { u"f03n2c08.png",
1340 false,
1341 false,
1342 } }, // color no interlacing, filter-type 3
1343 { u"f04n0g08.png",
1348 true,
1349 false,
1350 } }, // grayscale no interlacing, filter-type 4
1351 { u"f04n2c08.png",
1356 false,
1357 false,
1358 } }, // color no interlacing, filter-type 4
1359 { u"f99n0g04.png",
1364 true,
1365 false,
1366 } }, // grayscale bit-depth 4, filter changing per scanline
1367 // Additional palettes
1368 { u"pp0n2c16.png",
1373 false,
1374 false,
1375 } }, // six-cube palette-chunk in true-color image
1376 { u"pp0n6a08.png",
1381 false,
1382 true,
1383 } }, // six-cube palette-chunk in true-color+alpha image
1384 { u"ps1n0g08.png",
1389 true,
1390 false,
1391 } }, // six-cube suggested palette (1 byte) in grayscale image
1392 { u"ps1n2c16.png",
1397 false,
1398 false,
1399 } }, // six-cube suggested palette (1 byte) in true-color image
1400 { u"ps2n0g08.png",
1405 true,
1406 false,
1407 } }, // six-cube suggested palette (2 bytes) in grayscale image
1408 { u"ps2n2c16.png",
1413 false,
1414 false,
1415 } }, // six-cube suggested palette (2 bytes) in true-color image
1416 // Ancillary chunks
1417 { u"ccwn2c08.png",
1422 false,
1423 false,
1424 } }, // chroma chunk w:0.31270.329 r:0.640.33 g:0.300.60 b:0.15,0.06
1425 { u"ccwn3p08.png",
1430 false,
1431 false,
1432 } }, // chroma chunk w:0.31270.329 r:0.640.33 g:0.300.60 b:0.15,0.06
1433 { u"cdfn2c08.png",
1438 false,
1439 false,
1440 } }, // physical pixel dimensions, 8x32 flat pixels
1441 { u"cdhn2c08.png",
1446 false,
1447 false,
1448 } }, // physical pixel dimensions, 32x8 high pixels
1449 { u"cdsn2c08.png",
1454 false,
1455 false,
1456 } }, // physical pixel dimensions, 8x8 square pixels
1457 { u"cdun2c08.png",
1462 false,
1463 false,
1464 } }, // physical pixel dimensions, 1000 pixels per 1 meter
1465 { u"ch1n3p04.png",
1470 false,
1471 false,
1472 } }, // histogram 15 colors
1473 { u"ch2n3p08.png",
1478 false,
1479 false,
1480 } }, // histogram 256 colors
1481 { u"cm0n0g04.png",
1486 true,
1487 false,
1488 } }, // grayscale modification time, 01-jan-2000 12:34:56
1489 { u"cm7n0g04.png",
1494 true,
1495 false,
1496 } }, // grayscale modification time, 01-jan-1970 00:00:00
1497 { u"cm9n0g04.png",
1502 true,
1503 false,
1504 } }, // grayscale modification time, 31-dec-1999 23:59:59
1505 { u"cs3n2c16.png",
1510 false,
1511 false,
1512 } }, // color, 13 significant bits
1513 { u"cs3n3p08.png",
1518 false,
1519 false,
1520 } }, // paletted, 3 significant bits
1521 { u"cs5n2c08.png",
1526 false,
1527 false,
1528 } }, // color, 5 significant bits
1529 { u"cs5n3p08.png",
1534 false,
1535 false,
1536 } }, // paletted, 5 significant bits
1537 { u"cs8n2c08.png",
1542 false,
1543 false,
1544 } }, // color, 8 significant bits (reference)
1545 { u"cs8n3p08.png",
1550 false,
1551 false,
1552 } }, // paletted, 8 significant bits (reference)
1553 { u"ct0n0g04.png",
1558 true,
1559 false,
1560 } }, // grayscale no textual data
1561 { u"ct1n0g04.png",
1566 true,
1567 false,
1568 } }, // grayscale with textual data
1569 { u"ctzn0g04.png",
1574 true,
1575 false,
1576 } }, // grayscale with compressed textual data
1577 { u"cten0g04.png",
1582 true,
1583 false,
1584 } }, // grayscale international UTF-8, english
1585 { u"ctfn0g04.png",
1590 true,
1591 false,
1592 } }, // grayscale international UTF-8, finnish
1593 { u"ctgn0g04.png",
1598 true,
1599 false,
1600 } }, // grayscale international UTF-8, greek
1601 { u"cthn0g04.png",
1606 true,
1607 false,
1608 } }, // grayscale international UTF-8, hindi
1609 { u"ctjn0g04.png",
1614 true,
1615 false,
1616 } }, // grayscale international UTF-8, japanese
1617 { u"exif2c08.png",
1622 false,
1623 false,
1624 } }, // chunk with jpeg exif data
1625 // Chunk ordering
1626 { u"oi1n0g16.png",
1631 true,
1632 false,
1633 } }, // grayscale mother image with 1 idat-chunk
1634 { u"oi1n2c16.png",
1639 false,
1640 false,
1641 } }, // color mother image with 1 idat-chunk
1642 { u"oi2n0g16.png",
1647 true,
1648 false,
1649 } }, // grayscale image with 2 idat-chunks
1650 { u"oi2n2c16.png",
1655 false,
1656 false,
1657 } }, // color image with 2 idat-chunks
1658 { u"oi4n0g16.png",
1663 true,
1664 false,
1665 } }, // grayscale image with 4 unequal sized idat-chunks
1666 { u"oi4n2c16.png",
1671 false,
1672 false,
1673 } }, // color image with 4 unequal sized idat-chunks
1674 { u"oi9n0g16.png",
1679 true,
1680 false,
1681 } }, // grayscale image with all idat-chunks length one
1682 { u"oi9n2c16.png",
1687 false,
1688 false,
1689 } }, // color image with all idat-chunks length one
1690 // Zlib compression
1691 { u"z00n2c08.png",
1696 false,
1697 false,
1698 } }, // color no interlacing, compression level 0 (none)
1699 { u"z03n2c08.png",
1704 false,
1705 false,
1706 } }, // color no interlacing, compression level 3
1707 { u"z06n2c08.png",
1712 false,
1713 false,
1714 } }, // color no interlacing, compression level 6 (default)
1715 { u"z09n2c08.png",
1720 false,
1721 false,
1722 } }, // color no interlacing, compression level 9 (maximum)
1725 for (const auto & [ aCaseName, aCase ] : aCases)
1727 checkImportExportPng(getFullUrl(aCaseName), aCase);
1730 OUString aCorruptedFilenames[] = {
1731 u"xs1n0g01.png"_ustr, // signature byte 1 MSBit reset to zero
1732 u"xs2n0g01.png"_ustr, // signature byte 2 is a 'Q'
1733 u"xs4n0g01.png"_ustr, // signature byte 4 lowercase
1734 u"xs7n0g01.png"_ustr, // 7th byte a space instead of control-Z
1735 u"xcrn0g04.png"_ustr, // added cr bytes
1736 u"xlfn0g04.png"_ustr, // added lf bytes
1737 u"xhdn0g08.png"_ustr, // incorrect IHDR checksum
1738 u"xc1n0g08.png"_ustr, // color type 1
1739 u"xc9n2c08.png"_ustr, // color type 9
1740 u"xd0n2c08.png"_ustr, // bit-depth 0
1741 u"xd3n2c08.png"_ustr, // bit-depth 3
1742 u"xd9n2c08.png"_ustr, // bit-depth 99
1743 u"xdtn0g01.png"_ustr, // missing IDAT chunk
1744 u"xcsn0g01.png"_ustr, // incorrect IDAT checksum
1747 for (const auto& aFilename : aCorruptedFilenames)
1749 checkImportCorruptedPng(getFullUrl(aFilename));
1753 void PngFilterTest::testMsGifInPng()
1755 GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
1757 Graphic aGraphic;
1758 const OUString aURL(getFullUrl(u"ms-gif.png"));
1759 SvFileStream aFileStream(aURL, StreamMode::READ);
1760 ErrCode aResult = rFilter.ImportGraphic(aGraphic, aURL, aFileStream);
1761 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult);
1762 CPPUNIT_ASSERT(aGraphic.IsGfxLink());
1763 // The image is technically a PNG, but it has an animated Gif as a chunk (Microsoft extension).
1764 CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeGif, aGraphic.GetSharedGfxLink()->GetType());
1765 CPPUNIT_ASSERT(aGraphic.IsAnimated());
1768 // Tests msOG chunk export support
1769 const OUString aURL(getFullUrl(u"dummy.gif"));
1770 SvFileStream aGIFStream(aURL, StreamMode::READ);
1771 sal_uInt32 nGIFSize = aGIFStream.TellEnd();
1772 const char* const pHeader = "MSOFFICE9.0";
1773 auto nHeaderSize = strlen(pHeader);
1774 uno::Sequence<sal_Int8> aGIFSequence(nHeaderSize + nGIFSize);
1775 sal_Int8* pSequence = aGIFSequence.getArray();
1776 for (size_t i = 0; i < nHeaderSize; i++)
1777 *pSequence++ = pHeader[i];
1778 aGIFStream.Seek(STREAM_SEEK_TO_BEGIN);
1779 aGIFStream.ReadBytes(pSequence, nGIFSize);
1780 // Create msOG chunk
1781 beans::PropertyValue aChunkProperty, aFilterProperty;
1782 aChunkProperty.Name = "msOG";
1783 aChunkProperty.Value <<= aGIFSequence;
1784 uno::Sequence<beans::PropertyValue> aAdditionalChunkSequence{ aChunkProperty };
1785 aFilterProperty.Name = "AdditionalChunks";
1786 aFilterProperty.Value <<= aAdditionalChunkSequence;
1787 uno::Sequence<beans::PropertyValue> aPNGParameters{ aFilterProperty };
1788 // Export the png with the chunk
1789 utl::TempFileNamed aTempFile(u"testPngExportMsGif", true, u".png");
1790 if (!bKeepTemp)
1791 aTempFile.EnableKillingFile();
1793 SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE);
1794 BitmapEx aDummyBitmap(Size(8, 8), vcl::PixelFormat::N24_BPP);
1795 vcl::PngImageWriter aPngWriter(rStream);
1796 aPngWriter.setParameters(aPNGParameters);
1797 bool bWriteSuccess = aPngWriter.write(aDummyBitmap);
1798 CPPUNIT_ASSERT_EQUAL(true, bWriteSuccess);
1799 aTempFile.CloseStream();
1802 SvStream& rStream = *aTempFile.GetStream(StreamMode::READ);
1803 rStream.Seek(0);
1804 // Import the png and check that it is a gif
1805 Graphic aGraphic;
1806 ErrCode aResult = rFilter.ImportGraphic(aGraphic, aTempFile.GetURL(), rStream);
1807 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult);
1808 CPPUNIT_ASSERT(aGraphic.IsGfxLink());
1809 CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeGif, aGraphic.GetSharedGfxLink()->GetType());
1810 CPPUNIT_ASSERT(aGraphic.IsAnimated());
1815 void PngFilterTest::testPngRoundtrip8BitGrey()
1817 utl::TempFileNamed aTempFile(u"testPngRoundtrip8BitGrey");
1818 if (!bKeepTemp)
1819 aTempFile.EnableKillingFile();
1821 SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE);
1822 Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N8_BPP, &Bitmap::GetGreyPalette(256));
1824 BitmapScopedWriteAccess pWriteAccess(aBitmap);
1825 pWriteAccess->Erase(COL_BLACK);
1826 for (int i = 0; i < 8; ++i)
1828 for (int j = 0; j < 8; ++j)
1830 pWriteAccess->SetPixel(i, j, COL_GRAY);
1833 for (int i = 8; i < 16; ++i)
1835 for (int j = 8; j < 16; ++j)
1837 pWriteAccess->SetPixel(i, j, COL_LIGHTGRAY);
1841 BitmapEx aBitmapEx(aBitmap);
1843 vcl::PngImageWriter aPngWriter(rStream);
1844 CPPUNIT_ASSERT_EQUAL(true, aPngWriter.write(aBitmapEx));
1845 aTempFile.CloseStream();
1848 SvStream& rStream = *aTempFile.GetStream(StreamMode::READ);
1850 vcl::PngImageReader aPngReader(rStream);
1851 BitmapEx aBitmapEx;
1852 CPPUNIT_ASSERT_EQUAL(true, aPngReader.read(aBitmapEx));
1854 CPPUNIT_ASSERT_EQUAL(Size(16, 16), aBitmapEx.GetSizePixel());
1856 CPPUNIT_ASSERT_EQUAL(COL_GRAY, aBitmapEx.GetPixelColor(0, 0));
1857 CPPUNIT_ASSERT_EQUAL(COL_LIGHTGRAY, aBitmapEx.GetPixelColor(15, 15));
1858 CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmapEx.GetPixelColor(15, 0));
1859 CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmapEx.GetPixelColor(0, 15));
1863 void PngFilterTest::testPngRoundtrip24()
1865 utl::TempFileNamed aTempFile(u"testPngRoundtrip24");
1866 if (!bKeepTemp)
1867 aTempFile.EnableKillingFile();
1869 SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE);
1870 Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N24_BPP);
1872 BitmapScopedWriteAccess pWriteAccess(aBitmap);
1873 pWriteAccess->Erase(COL_BLACK);
1874 for (int i = 0; i < 8; ++i)
1876 for (int j = 0; j < 8; ++j)
1878 pWriteAccess->SetPixel(i, j, COL_LIGHTRED);
1881 for (int i = 8; i < 16; ++i)
1883 for (int j = 8; j < 16; ++j)
1885 pWriteAccess->SetPixel(i, j, COL_LIGHTBLUE);
1889 BitmapEx aBitmapEx(aBitmap);
1891 vcl::PngImageWriter aPngWriter(rStream);
1892 CPPUNIT_ASSERT_EQUAL(true, aPngWriter.write(aBitmapEx));
1895 SvStream& rStream = *aTempFile.GetStream(StreamMode::READ);
1896 rStream.Seek(0);
1898 vcl::PngImageReader aPngReader(rStream);
1899 BitmapEx aBitmapEx;
1900 CPPUNIT_ASSERT_EQUAL(true, aPngReader.read(aBitmapEx));
1902 CPPUNIT_ASSERT_EQUAL(Size(16, 16), aBitmapEx.GetSizePixel());
1904 CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, aBitmapEx.GetPixelColor(0, 0));
1905 CPPUNIT_ASSERT_EQUAL(COL_LIGHTBLUE, aBitmapEx.GetPixelColor(15, 15));
1906 CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmapEx.GetPixelColor(15, 0));
1907 CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmapEx.GetPixelColor(0, 15));
1911 void PngFilterTest::testPngRoundtrip24_8()
1913 utl::TempFileNamed aTempFile(u"testPngRoundtrip24_8");
1914 if (!bKeepTemp)
1915 aTempFile.EnableKillingFile();
1917 SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE);
1918 Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N24_BPP);
1919 AlphaMask aAlpha(Size(16, 16));
1921 BitmapScopedWriteAccess pWriteAccessBitmap(aBitmap);
1922 BitmapScopedWriteAccess pWriteAccessAlpha(aAlpha);
1923 pWriteAccessAlpha->Erase(Color(0xAA, 0xAA, 0xAA));
1924 pWriteAccessBitmap->Erase(COL_BLACK);
1925 for (int i = 0; i < 8; ++i)
1927 for (int j = 0; j < 8; ++j)
1929 pWriteAccessBitmap->SetPixel(i, j, COL_LIGHTRED);
1930 pWriteAccessAlpha->SetPixel(i, j, Color(0xBB, 0xBB, 0xBB));
1933 for (int i = 8; i < 16; ++i)
1935 for (int j = 8; j < 16; ++j)
1937 pWriteAccessBitmap->SetPixel(i, j, COL_LIGHTBLUE);
1938 pWriteAccessAlpha->SetPixel(i, j, Color(0xCC, 0xCC, 0xCC));
1942 BitmapEx aBitmapEx(aBitmap, aAlpha);
1943 vcl::PngImageWriter aPngWriter(rStream);
1944 CPPUNIT_ASSERT_EQUAL(true, aPngWriter.write(aBitmapEx));
1947 SvStream& rStream = *aTempFile.GetStream(StreamMode::READ);
1948 rStream.Seek(0);
1950 vcl::PngImageReader aPngReader(rStream);
1951 BitmapEx aBitmapEx;
1952 CPPUNIT_ASSERT_EQUAL(true, aPngReader.read(aBitmapEx));
1954 CPPUNIT_ASSERT_EQUAL(Size(16, 16), aBitmapEx.GetSizePixel());
1956 CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xBB, 0xFF, 0x00, 0x00),
1957 aBitmapEx.GetPixelColor(0, 0));
1958 CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xCC, 0x00, 0x00, 0xFF),
1959 aBitmapEx.GetPixelColor(15, 15));
1960 CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xAA, 0x00, 0x00, 0x00),
1961 aBitmapEx.GetPixelColor(15, 0));
1962 CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xAA, 0x00, 0x00, 0x00),
1963 aBitmapEx.GetPixelColor(0, 15));
1967 void PngFilterTest::testPngRoundtrip32() {}
1969 void PngFilterTest::testPngWrite8BitRGBPalette()
1971 SvMemoryStream aExportStream;
1972 BitmapPalette aRedPalette;
1973 aRedPalette.SetEntryCount(256);
1974 for (sal_uInt16 i = 0; i < 256; i++)
1976 aRedPalette[i].SetRed(i);
1977 aRedPalette[i].SetGreen(0);
1978 aRedPalette[i].SetBlue(0);
1981 Bitmap aBitmap(Size(16, 16), vcl::PixelFormat::N8_BPP, &aRedPalette);
1983 BitmapScopedWriteAccess pWriteAccessBitmap(aBitmap);
1984 for (int i = 0; i < 16; i++)
1986 for (int j = 0; j < 16; j++)
1988 pWriteAccessBitmap->SetPixelIndex(i, j, i * 16 + j);
1992 BitmapEx aBitmapEx(aBitmap);
1993 vcl::PngImageWriter aPngWriter(aExportStream);
1994 CPPUNIT_ASSERT_EQUAL(true, aPngWriter.write(aBitmapEx));
1996 aExportStream.Seek(0);
1998 vcl::PngImageReader aPngReader(aExportStream);
1999 BitmapEx aBitmapEx;
2000 CPPUNIT_ASSERT_EQUAL(true, aPngReader.read(aBitmapEx));
2002 CPPUNIT_ASSERT_EQUAL(Size(16, 16), aBitmapEx.GetSizePixel());
2004 for (int i = 0; i < 16; i++)
2006 for (int j = 0; j < 16; j++)
2008 CPPUNIT_ASSERT_EQUAL(aRedPalette[i * 16 + j].GetRGBColor(),
2009 aBitmapEx.GetPixelColor(j, i));
2015 void PngFilterTest::testTdf153180MonochromeFilterPngExport()
2017 GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
2019 Graphic aGraphicOriginal;
2021 // 3 * 16 bits rgb color alpha, no background chunk
2022 const OUString aURL(getFullUrl(u"bgan6a16.png"));
2023 SvFileStream aFileStream(aURL, StreamMode::READ);
2024 ErrCode aResult = rFilter.ImportGraphic(aGraphicOriginal, aURL, aFileStream);
2025 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult);
2026 CPPUNIT_ASSERT(aGraphicOriginal.IsAlpha());
2029 // Apply the monochrome filter to the graphic but keep the alpha.
2030 BitmapEx aBitmapEx(aGraphicOriginal.GetBitmapEx());
2031 AlphaMask aAlphaMask(aBitmapEx.GetAlphaMask());
2033 BitmapEx aTmpBmpEx(aBitmapEx.GetBitmap());
2034 BitmapFilter::Filter(aTmpBmpEx, BitmapMonochromeFilter{ sal_uInt8{ 127 } });
2036 Graphic aGraphicAfterFilter{ BitmapEx(aTmpBmpEx.GetBitmap(), aAlphaMask) };
2037 CPPUNIT_ASSERT(aGraphicAfterFilter.IsAlpha());
2039 // export the resulting graphic
2040 utl::TempFileNamed aTempFile(u"testPngExportTdf153180", true, u".png");
2041 if (!bKeepTemp)
2042 aTempFile.EnableKillingFile();
2044 SvStream& rStream = *aTempFile.GetStream(StreamMode::WRITE);
2045 vcl::PngImageWriter aPngWriter(rStream);
2046 bool bWriteSuccess = aPngWriter.write(aGraphicAfterFilter.GetBitmapEx());
2047 CPPUNIT_ASSERT_EQUAL(true, bWriteSuccess);
2048 aTempFile.CloseStream();
2051 SvStream& rStream = *aTempFile.GetStream(StreamMode::READ);
2052 rStream.Seek(0);
2053 // Import the png and check that it still has alpha
2054 Graphic aGraphic;
2055 ErrCode aResult = rFilter.ImportGraphic(aGraphic, aTempFile.GetURL(), rStream);
2056 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aResult);
2058 // Without the accompanying patch would fail with:
2059 // assertion failed
2060 // -Expression : aGraphic.IsAlpha()
2061 CPPUNIT_ASSERT(aGraphic.IsAlpha());
2065 void PngFilterTest::testDump()
2067 utl::TempFileNamed aTempFile;
2068 Bitmap aBitmap(Size(1, 1), vcl::PixelFormat::N24_BPP);
2070 BitmapScopedWriteAccess pWriteAccessBitmap(aBitmap);
2071 pWriteAccessBitmap->SetPixel(0, 0, BitmapColor());
2073 BitmapEx aBitmapEx(aBitmap);
2074 aBitmapEx.DumpAsPng(aTempFile.GetURL().toUtf8().getStr());
2075 SvStream* pStream = aTempFile.GetStream(StreamMode::READ);
2076 CPPUNIT_ASSERT_GREATER(static_cast<sal_uInt64>(0), pStream->remainingSize());
2079 CPPUNIT_TEST_SUITE_REGISTRATION(PngFilterTest);
2081 CPPUNIT_PLUGIN_IMPLEMENT();
2083 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */