diff --git a/vcl/CppunitTest_vcl_filters_test.mk b/vcl/CppunitTest_vcl_filters_test.mk index f9f8e40cca98..ccaf6c7bbc6d 100644 --- a/vcl/CppunitTest_vcl_filters_test.mk +++ b/vcl/CppunitTest_vcl_filters_test.mk @@ -12,6 +12,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,vcl_filters_test)) $(eval $(call gb_CppunitTest_add_exception_objects,vcl_filters_test, \ vcl/qa/cppunit/graphicfilter/filters-dxf-test \ vcl/qa/cppunit/graphicfilter/filters-eps-test \ + vcl/qa/cppunit/graphicfilter/filters-jxl-test \ vcl/qa/cppunit/graphicfilter/filters-met-test \ vcl/qa/cppunit/graphicfilter/filters-pcd-test \ vcl/qa/cppunit/graphicfilter/filters-pcx-test \ diff --git a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx index ad4bc9eed33f..20e0288ab4ca 100644 --- a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx +++ b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx @@ -38,6 +38,7 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase void testDetectWMZ(); void testDetectPCX(); void testDetectJPG(); + void testDetectJXL(); void testDetectPNG(); void testDetectAPNG(); void testDetectGIF(); @@ -66,6 +67,7 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase CPPUNIT_TEST(testDetectWMZ); CPPUNIT_TEST(testDetectPCX); CPPUNIT_TEST(testDetectJPG); + CPPUNIT_TEST(testDetectJXL); CPPUNIT_TEST(testDetectPNG); CPPUNIT_TEST(testDetectAPNG); CPPUNIT_TEST(testDetectGIF); @@ -179,6 +181,21 @@ void GraphicFormatDetectorTest::testDetectJPG() CPPUNIT_ASSERT_EQUAL(u"JPG"_ustr, rFormatExtension); } +void GraphicFormatDetectorTest::testDetectJXL() +{ + SvFileStream aFileStream(getFullUrl(u"TypeDetectionExample.jxl"), StreamMode::READ); + vcl::GraphicFormatDetector aDetector(aFileStream, u"JXL"_ustr); + + CPPUNIT_ASSERT(aDetector.detect()); + CPPUNIT_ASSERT(aDetector.checkJXL()); + + aFileStream.Seek(aDetector.mnStreamPosition); + + OUString rFormatExtension; + CPPUNIT_ASSERT(vcl::peekGraphicFormat(aFileStream, rFormatExtension, false)); + CPPUNIT_ASSERT_EQUAL(u"JXL"_ustr, rFormatExtension); +} + void GraphicFormatDetectorTest::testDetectPNG() { SvFileStream aFileStream(getFullUrl(u"TypeDetectionExample.png"), StreamMode::READ); diff --git a/vcl/qa/cppunit/GraphicTest.cxx b/vcl/qa/cppunit/GraphicTest.cxx index a4a2f0569cce..207cb77174af 100644 --- a/vcl/qa/cppunit/GraphicTest.cxx +++ b/vcl/qa/cppunit/GraphicTest.cxx @@ -1246,6 +1246,18 @@ CPPUNIT_TEST_FIXTURE(GraphicTest, testLoadEPS) CPPUNIT_ASSERT_EQUAL(GraphicType::GdiMetafile, aGraphic.GetType()); } +CPPUNIT_TEST_FIXTURE(GraphicTest, testLoadJXL) +{ + Graphic aGraphic = loadGraphic(u"TypeDetectionExample.jxl"); + CPPUNIT_ASSERT_EQUAL(GraphicType::Bitmap, aGraphic.GetType()); + CPPUNIT_ASSERT_EQUAL(tools::Long(10), aGraphic.GetSizePixel().Width()); + CPPUNIT_ASSERT_EQUAL(tools::Long(10), aGraphic.GetSizePixel().Height()); + + // The imported Graphic is tagged with its JPEG XL origin + CPPUNIT_ASSERT_EQUAL(true, aGraphic.IsGfxLink()); + CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeJxl, aGraphic.GetGfxLink().GetType()); +} + CPPUNIT_TEST_FIXTURE(GraphicTest, testLoadWEBP) { Graphic aGraphic = loadGraphic(u"TypeDetectionExample.webp"); diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.jxl b/vcl/qa/cppunit/data/TypeDetectionExample.jxl new file mode 100644 index 000000000000..a53a088a804d Binary files /dev/null and b/vcl/qa/cppunit/data/TypeDetectionExample.jxl differ diff --git a/vcl/qa/cppunit/graphicfilter/data/jxl/alpha_lossless.jxl b/vcl/qa/cppunit/graphicfilter/data/jxl/alpha_lossless.jxl new file mode 100644 index 000000000000..db7bfb7497a0 Binary files /dev/null and b/vcl/qa/cppunit/graphicfilter/data/jxl/alpha_lossless.jxl differ diff --git a/vcl/qa/cppunit/graphicfilter/data/jxl/alpha_lossy.jxl b/vcl/qa/cppunit/graphicfilter/data/jxl/alpha_lossy.jxl new file mode 100644 index 000000000000..edc7731fc54b Binary files /dev/null and b/vcl/qa/cppunit/graphicfilter/data/jxl/alpha_lossy.jxl differ diff --git a/vcl/qa/cppunit/graphicfilter/data/jxl/fail/.gitignore b/vcl/qa/cppunit/graphicfilter/data/jxl/fail/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/vcl/qa/cppunit/graphicfilter/data/jxl/indeterminate/.gitignore b/vcl/qa/cppunit/graphicfilter/data/jxl/indeterminate/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/vcl/qa/cppunit/graphicfilter/data/jxl/noalpha_lossless.jxl b/vcl/qa/cppunit/graphicfilter/data/jxl/noalpha_lossless.jxl new file mode 100644 index 000000000000..1ed0989e0802 Binary files /dev/null and b/vcl/qa/cppunit/graphicfilter/data/jxl/noalpha_lossless.jxl differ diff --git a/vcl/qa/cppunit/graphicfilter/data/jxl/noalpha_lossy.jxl b/vcl/qa/cppunit/graphicfilter/data/jxl/noalpha_lossy.jxl new file mode 100644 index 000000000000..06313b204105 Binary files /dev/null and b/vcl/qa/cppunit/graphicfilter/data/jxl/noalpha_lossy.jxl differ diff --git a/vcl/qa/cppunit/graphicfilter/data/jxl/pass/.gitignore b/vcl/qa/cppunit/graphicfilter/data/jxl/pass/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/vcl/qa/cppunit/graphicfilter/filters-jxl-test.cxx b/vcl/qa/cppunit/graphicfilter/filters-jxl-test.cxx new file mode 100644 index 000000000000..f51d865ab4ff --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/filters-jxl-test.cxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace css; + +/* Implementation of Filters test */ + +class JxlFilterTest : public test::FiltersTest, public test::BootstrapFixture +{ +public: + JxlFilterTest() + : BootstrapFixture(true, false) + { + } + + virtual bool load(const OUString&, const OUString& rURL, const OUString&, SfxFilterFlags, + SotClipboardFormatId, unsigned int) override; + + /** + * Ensure CVEs remain unbroken + */ + void testCVEs(); + + void testReadAlphaLossless(); + void testReadAlphaLossy(); + void testReadNoAlphaLossless(); + void testReadNoAlphaLossy(); + + CPPUNIT_TEST_SUITE(JxlFilterTest); + CPPUNIT_TEST(testCVEs); + CPPUNIT_TEST(testReadAlphaLossless); + CPPUNIT_TEST(testReadAlphaLossy); + CPPUNIT_TEST(testReadNoAlphaLossless); + CPPUNIT_TEST(testReadNoAlphaLossy); + CPPUNIT_TEST_SUITE_END(); + +private: + void testRead(std::u16string_view rName, bool lossy, bool alpha); +}; + +bool JxlFilterTest::load(const OUString&, const OUString& rURL, const OUString&, SfxFilterFlags, + SotClipboardFormatId, unsigned int) +{ + SvFileStream aFileStream(rURL, StreamMode::READ); + Graphic aGraphic; + return ImportJxlGraphic(aFileStream, aGraphic); +} + +void JxlFilterTest::testCVEs() +{ +#ifndef DISABLE_CVE_TESTS + testDir(OUString(), m_directories.getURLFromSrc(u"/vcl/qa/cppunit/graphicfilter/data/jxl/")); +#endif +} + +void JxlFilterTest::testReadAlphaLossless() { testRead(u"alpha_lossless.jxl", false, true); } + +void JxlFilterTest::testReadAlphaLossy() { testRead(u"alpha_lossy.jxl", true, true); } + +void JxlFilterTest::testReadNoAlphaLossless() { testRead(u"noalpha_lossless.jxl", false, false); } + +void JxlFilterTest::testReadNoAlphaLossy() { testRead(u"noalpha_lossy.jxl", true, false); } + +void JxlFilterTest::testRead(std::u16string_view rName, bool lossy, bool alpha) +{ + // Read a file created in GIMP and check it's read correctly. + OUString file = m_directories.getURLFromSrc(u"/vcl/qa/cppunit/graphicfilter/data/jxl/") + rName; + SvFileStream aFileStream(file, StreamMode::READ); + Graphic aGraphic; + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"none", aFileStream); + CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult); + CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeJxl, aGraphic.GetGfxLink().GetType()); + Bitmap aResultBitmap = aGraphic.GetBitmap(); + CPPUNIT_ASSERT_EQUAL(Size(10, 10), aResultBitmap.GetSizePixel()); + + { + Bitmap tmpBitmap = aResultBitmap.CreateColorBitmap(); + BitmapScopedReadAccess pAccess(tmpBitmap); + // Note that x,y are swapped. + if (lossy) + CPPUNIT_ASSERT_LESS(sal_uInt16(2), pAccess->GetPixel(0, 0).GetColorError(COL_LIGHTRED)); + else + CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, Color(pAccess->GetPixel(0, 0))); + CPPUNIT_ASSERT_EQUAL(COL_LIGHTBLUE, Color(pAccess->GetPixel(9, 9))); + + // if bitmaps of non-alpha examples have an alpha channel, + // the values should all be max (255) + if (aResultBitmap.HasAlpha()) + { + AlphaMask tmpAlpha = aResultBitmap.CreateAlphaMask(); + BitmapScopedReadAccess pAccessAlpha(tmpAlpha); + CPPUNIT_ASSERT_EQUAL(sal_uInt8(255), pAccessAlpha->GetPixelIndex(0, 0)); + if (alpha) + CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), pAccessAlpha->GetPixelIndex(0, 9)); + else + CPPUNIT_ASSERT_EQUAL(sal_uInt8(255), pAccessAlpha->GetPixelIndex(0, 9)); + } + } +} + +CPPUNIT_TEST_SUITE_REGISTRATION(JxlFilterTest); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */