summaryrefslogtreecommitdiff
path: root/vcl/qa
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2022-01-13 15:59:49 +0100
committerMiklos Vajna <vmiklos@collabora.com>2022-01-31 10:44:29 +0100
commit60eaa424c5e213f31227008e1ed66a646491a360 (patch)
tree7f1a2ff9e73367502d63bb84a528d855ccff17a6 /vcl/qa
parentb7d5ff4bbf41094b6579ae26023fbd686b060ce9 (diff)
support for the WebP image format (tdf#114532)
This commit implements a WebP reader and writer for both lossless and lossy WebP, export dialog options for selecting lossless/lossy and quality for lossy, and various internal support for the format. Since writing WebP to e.g. ODT documents would make those images unreadable by previous versions with no WebP support, support for that is explicitly disabled in GraphicFilter, to be enabled somewhen later. Change-Id: I9b10f6da6faa78a0bb74415a92e9f163c14685f7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128920 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'vcl/qa')
-rw-r--r--vcl/qa/cppunit/GraphicDescriptorTest.cxx16
-rw-r--r--vcl/qa/cppunit/GraphicFormatDetectorTest.cxx17
-rw-r--r--vcl/qa/cppunit/GraphicTest.cxx12
-rw-r--r--vcl/qa/cppunit/data/TypeDetectionExample.webpbin0 -> 52 bytes
-rw-r--r--vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossless.webpbin0 -> 744 bytes
-rw-r--r--vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossy.webpbin0 -> 826 bytes
-rw-r--r--vcl/qa/cppunit/graphicfilter/data/webp/fail/.gitignore0
-rw-r--r--vcl/qa/cppunit/graphicfilter/data/webp/indeterminate/.gitignore0
-rw-r--r--vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossless.webpbin0 -> 762 bytes
-rw-r--r--vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossy.webpbin0 -> 782 bytes
-rw-r--r--vcl/qa/cppunit/graphicfilter/data/webp/pass/.gitignore0
-rw-r--r--vcl/qa/cppunit/graphicfilter/filters-test.cxx2
-rw-r--r--vcl/qa/cppunit/graphicfilter/filters-webp-test.cxx203
13 files changed, 249 insertions, 1 deletions
diff --git a/vcl/qa/cppunit/GraphicDescriptorTest.cxx b/vcl/qa/cppunit/GraphicDescriptorTest.cxx
index 652393ae9b63..865202cedbf5 100644
--- a/vcl/qa/cppunit/GraphicDescriptorTest.cxx
+++ b/vcl/qa/cppunit/GraphicDescriptorTest.cxx
@@ -34,6 +34,7 @@ class GraphicDescriptorTest : public test::BootstrapFixtureBase
void testDetectGIF();
void testDetectTIF();
void testDetectBMP();
+ void testDetectWEBP();
CPPUNIT_TEST_SUITE(GraphicDescriptorTest);
CPPUNIT_TEST(testDetectPNG);
@@ -41,6 +42,7 @@ class GraphicDescriptorTest : public test::BootstrapFixtureBase
CPPUNIT_TEST(testDetectGIF);
CPPUNIT_TEST(testDetectTIF);
CPPUNIT_TEST(testDetectBMP);
+ CPPUNIT_TEST(testDetectWEBP);
CPPUNIT_TEST_SUITE_END();
};
@@ -138,6 +140,20 @@ void GraphicDescriptorTest::testDetectBMP()
CPPUNIT_ASSERT_EQUAL(MapUnit::MapMM, aGraphic.GetPrefMapMode().GetMapUnit());
}
+void GraphicDescriptorTest::testDetectWEBP()
+{
+ SvMemoryStream aStream;
+ createBitmapAndExportForType(aStream, u"webp");
+
+ GraphicDescriptor aDescriptor(aStream, nullptr);
+ aDescriptor.Detect(true);
+
+ CPPUNIT_ASSERT_EQUAL(GraphicFileFormat::WEBP, aDescriptor.GetFileFormat());
+
+ CPPUNIT_ASSERT_EQUAL(tools::Long(100), aDescriptor.GetSizePixel().Width());
+ CPPUNIT_ASSERT_EQUAL(tools::Long(100), aDescriptor.GetSizePixel().Height());
+}
+
} // namespace
CPPUNIT_TEST_SUITE_REGISTRATION(GraphicDescriptorTest);
diff --git a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx
index 264a0e8cd48d..4c16bfc82e1b 100644
--- a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx
+++ b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx
@@ -47,6 +47,7 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase
void testDetectSVGZ();
void testDetectPDF();
void testDetectEPS();
+ void testDetectWEBP();
void testMatchArray();
void testCheckArrayForMatchingStrings();
@@ -67,6 +68,7 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase
CPPUNIT_TEST(testDetectSVGZ);
CPPUNIT_TEST(testDetectPDF);
CPPUNIT_TEST(testDetectEPS);
+ CPPUNIT_TEST(testDetectWEBP);
CPPUNIT_TEST(testMatchArray);
CPPUNIT_TEST(testCheckArrayForMatchingStrings);
CPPUNIT_TEST_SUITE_END();
@@ -312,6 +314,21 @@ void GraphicFormatDetectorTest::testDetectEPS()
CPPUNIT_ASSERT_EQUAL(OUString("EPS"), rFormatExtension);
}
+void GraphicFormatDetectorTest::testDetectWEBP()
+{
+ SvFileStream aFileStream(getFullUrl(u"TypeDetectionExample.webp"), StreamMode::READ);
+ vcl::GraphicFormatDetector aDetector(aFileStream, "WEBP");
+
+ CPPUNIT_ASSERT(aDetector.detect());
+ CPPUNIT_ASSERT(aDetector.checkWEBP());
+
+ aFileStream.Seek(aDetector.mnStreamPosition);
+
+ OUString rFormatExtension;
+ CPPUNIT_ASSERT(vcl::peekGraphicFormat(aFileStream, rFormatExtension, false));
+ CPPUNIT_ASSERT_EQUAL(OUString("WEBP"), rFormatExtension);
+}
+
void GraphicFormatDetectorTest::testMatchArray()
{
std::string aString("<?xml version=\"1.0\" standalone=\"no\"?>\n"
diff --git a/vcl/qa/cppunit/GraphicTest.cxx b/vcl/qa/cppunit/GraphicTest.cxx
index 44ad1df12829..2e398c544c79 100644
--- a/vcl/qa/cppunit/GraphicTest.cxx
+++ b/vcl/qa/cppunit/GraphicTest.cxx
@@ -81,6 +81,7 @@ private:
void testLoadXPM();
void testLoadPCX();
void testLoadEPS();
+ void testLoadWEBP();
void testAvailableThreaded();
@@ -118,6 +119,7 @@ private:
CPPUNIT_TEST(testLoadXPM);
CPPUNIT_TEST(testLoadPCX);
CPPUNIT_TEST(testLoadEPS);
+ CPPUNIT_TEST(testLoadWEBP);
CPPUNIT_TEST(testAvailableThreaded);
@@ -305,7 +307,7 @@ void GraphicTest::testUnloadedGraphic()
void GraphicTest::testUnloadedGraphicLoading()
{
- const OUString aFormats[] = { "png", "gif", "jpg", "tif" };
+ const OUString aFormats[] = { "png", "gif", "jpg", "tif", "webp" };
for (OUString const& sFormat : aFormats)
{
@@ -1310,6 +1312,14 @@ void GraphicTest::testLoadEPS()
CPPUNIT_ASSERT_EQUAL(GraphicType::GdiMetafile, aGraphic.GetType());
}
+void GraphicTest::testLoadWEBP()
+{
+ Graphic aGraphic = loadGraphic(u"TypeDetectionExample.webp");
+ 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());
+}
+
void GraphicTest::testAvailableThreaded()
{
Graphic jpgGraphic1 = importUnloadedGraphic(u"TypeDetectionExample.jpg");
diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.webp b/vcl/qa/cppunit/data/TypeDetectionExample.webp
new file mode 100644
index 000000000000..e85ae121637b
--- /dev/null
+++ b/vcl/qa/cppunit/data/TypeDetectionExample.webp
Binary files differ
diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossless.webp b/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossless.webp
new file mode 100644
index 000000000000..abb67cc5f4b6
--- /dev/null
+++ b/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossless.webp
Binary files differ
diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossy.webp b/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossy.webp
new file mode 100644
index 000000000000..c587b1bb22c0
--- /dev/null
+++ b/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossy.webp
Binary files differ
diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/fail/.gitignore b/vcl/qa/cppunit/graphicfilter/data/webp/fail/.gitignore
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/vcl/qa/cppunit/graphicfilter/data/webp/fail/.gitignore
diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/indeterminate/.gitignore b/vcl/qa/cppunit/graphicfilter/data/webp/indeterminate/.gitignore
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/vcl/qa/cppunit/graphicfilter/data/webp/indeterminate/.gitignore
diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossless.webp b/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossless.webp
new file mode 100644
index 000000000000..b22ebc3b28bb
--- /dev/null
+++ b/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossless.webp
Binary files differ
diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossy.webp b/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossy.webp
new file mode 100644
index 000000000000..c118febb0e9a
--- /dev/null
+++ b/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossy.webp
Binary files differ
diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/pass/.gitignore b/vcl/qa/cppunit/graphicfilter/data/webp/pass/.gitignore
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/vcl/qa/cppunit/graphicfilter/data/webp/pass/.gitignore
diff --git a/vcl/qa/cppunit/graphicfilter/filters-test.cxx b/vcl/qa/cppunit/graphicfilter/filters-test.cxx
index 22078cf9de65..f10de06fc837 100644
--- a/vcl/qa/cppunit/graphicfilter/filters-test.cxx
+++ b/vcl/qa/cppunit/graphicfilter/filters-test.cxx
@@ -145,6 +145,8 @@ void VclFiltersTest::testExportImport()
checkExportImport(u"bmp");
fprintf(stderr, "Check ExportImport TIF\n");
checkExportImport(u"tif");
+ fprintf(stderr, "Check ExportImport WEBP\n");
+ checkExportImport(u"webp");
}
void VclFiltersTest::testCVEs()
diff --git a/vcl/qa/cppunit/graphicfilter/filters-webp-test.cxx b/vcl/qa/cppunit/graphicfilter/filters-webp-test.cxx
new file mode 100644
index 000000000000..90528199c0ba
--- /dev/null
+++ b/vcl/qa/cppunit/graphicfilter/filters-webp-test.cxx
@@ -0,0 +1,203 @@
+/* -*- 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 <unotest/filters-test.hxx>
+#include <test/bootstrapfixture.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <tools/stream.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <graphic/GraphicFormatDetector.hxx>
+#include <filter/WebpReader.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace css;
+
+/* Implementation of Filters test */
+
+class WebpFilterTest : public test::FiltersTest, public test::BootstrapFixture
+{
+public:
+ WebpFilterTest()
+ : 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 testRoundtripLossless();
+ void testRoundtripLossy();
+ void testReadAlphaLossless();
+ void testReadAlphaLossy();
+ void testReadNoAlphaLossless();
+ void testReadNoAlphaLossy();
+
+ CPPUNIT_TEST_SUITE(WebpFilterTest);
+ CPPUNIT_TEST(testCVEs);
+ CPPUNIT_TEST(testRoundtripLossless);
+ CPPUNIT_TEST(testRoundtripLossy);
+ CPPUNIT_TEST(testReadAlphaLossless);
+ CPPUNIT_TEST(testReadAlphaLossy);
+ CPPUNIT_TEST(testReadNoAlphaLossless);
+ CPPUNIT_TEST(testReadNoAlphaLossy);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+ void testRoundtrip(bool lossy);
+ void testRead(bool lossy, bool alpha);
+};
+
+bool WebpFilterTest::load(const OUString&, const OUString& rURL, const OUString&, SfxFilterFlags,
+ SotClipboardFormatId, unsigned int)
+{
+ SvFileStream aFileStream(rURL, StreamMode::READ);
+ Graphic aGraphic;
+ return ImportWebpGraphic(aFileStream, aGraphic);
+}
+
+void WebpFilterTest::testCVEs()
+{
+#ifndef DISABLE_CVE_TESTS
+ testDir(OUString(), m_directories.getURLFromSrc(u"/vcl/qa/cppunit/graphicfilter/data/webp/"));
+#endif
+}
+
+void WebpFilterTest::testRoundtripLossless() { testRoundtrip(false); }
+
+void WebpFilterTest::testRoundtripLossy() { testRoundtrip(true); }
+
+void WebpFilterTest::testRoundtrip(bool lossy)
+{
+ // Do not use just 2x2, lossy saving would change colors.
+ Bitmap aBitmap(Size(20, 20), vcl::PixelFormat::N24_BPP);
+ AlphaMask aAlpha(Size(20, 20));
+ {
+ BitmapScopedWriteAccess pAccess(aBitmap);
+ pAccess->SetFillColor(COL_WHITE);
+ pAccess->FillRect(tools::Rectangle(Point(0, 0), Size(10, 10)));
+ pAccess->SetFillColor(COL_BLACK);
+ pAccess->FillRect(tools::Rectangle(Point(10, 0), Size(10, 10)));
+ pAccess->SetFillColor(COL_LIGHTRED);
+ pAccess->FillRect(tools::Rectangle(Point(0, 10), Size(10, 10)));
+ pAccess->SetFillColor(COL_BLUE);
+ pAccess->FillRect(tools::Rectangle(Point(10, 10), Size(10, 10)));
+ AlphaScopedWriteAccess pAccessAlpha(aAlpha);
+ pAccessAlpha->SetFillColor(BitmapColor(0)); // opaque
+ pAccessAlpha->FillRect(tools::Rectangle(Point(0, 0), Size(10, 10)));
+ pAccessAlpha->FillRect(tools::Rectangle(Point(10, 0), Size(10, 10)));
+ pAccessAlpha->FillRect(tools::Rectangle(Point(0, 10), Size(10, 10)));
+ pAccessAlpha->SetFillColor(BitmapColor(64, 64, 64));
+ pAccessAlpha->FillRect(tools::Rectangle(Point(10, 10), Size(10, 10)));
+ }
+ BitmapEx aBitmapEx(aBitmap, aAlpha);
+
+ SvMemoryStream aStream;
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ sal_uInt16 nFilterFormat = rFilter.GetExportFormatNumberForShortName(u"webp");
+ css::uno::Sequence<css::beans::PropertyValue> aFilterData{
+ comphelper::makePropertyValue("Lossless", !lossy),
+ comphelper::makePropertyValue("Quality", sal_Int32(100))
+ };
+ rFilter.ExportGraphic(Graphic(aBitmapEx), "none", aStream, nFilterFormat, &aFilterData);
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ Graphic aGraphic;
+ ErrCode bResult = rFilter.ImportGraphic(aGraphic, "none", aStream);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
+ CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeWebp, aGraphic.GetGfxLink().GetType());
+ BitmapEx aResultBitmap = aGraphic.GetBitmapEx();
+ CPPUNIT_ASSERT_EQUAL(Size(20, 20), aResultBitmap.GetSizePixel());
+ CPPUNIT_ASSERT(aResultBitmap.IsAlpha());
+
+ {
+ Bitmap tmpBitmap = aResultBitmap.GetBitmap();
+ Bitmap::ScopedReadAccess pAccess(tmpBitmap);
+ // Note that x,y are swapped.
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, Color(pAccess->GetPixel(0, 0)));
+ CPPUNIT_ASSERT_EQUAL(COL_BLACK, Color(pAccess->GetPixel(0, 19)));
+ if (lossy)
+ {
+ CPPUNIT_ASSERT_LESS(sal_uInt16(3),
+ pAccess->GetPixel(19, 0).GetColorError(COL_LIGHTRED));
+ CPPUNIT_ASSERT_LESS(sal_uInt16(3), pAccess->GetPixel(19, 19).GetColorError(COL_BLUE));
+ }
+ else
+ {
+ CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, Color(pAccess->GetPixel(19, 0)));
+ CPPUNIT_ASSERT_EQUAL(COL_BLUE, Color(pAccess->GetPixel(19, 19)));
+ }
+ AlphaMask tmpAlpha = aResultBitmap.GetAlpha();
+ AlphaMask::ScopedReadAccess pAccessAlpha(tmpAlpha);
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), pAccessAlpha->GetPixelIndex(0, 0));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), pAccessAlpha->GetPixelIndex(0, 19));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), pAccessAlpha->GetPixelIndex(19, 0));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(64), pAccessAlpha->GetPixelIndex(19, 19));
+ }
+
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+ vcl::GraphicFormatDetector aDetector(aStream, "");
+
+ CPPUNIT_ASSERT_EQUAL(true, aDetector.detect());
+ CPPUNIT_ASSERT_EQUAL(true, aDetector.checkWEBP());
+ CPPUNIT_ASSERT_EQUAL(OUString(u"WEBP"), aDetector.msDetectedFormat);
+}
+
+void WebpFilterTest::testReadAlphaLossless() { testRead(false, true); }
+
+void WebpFilterTest::testReadAlphaLossy() { testRead(true, true); }
+
+void WebpFilterTest::testReadNoAlphaLossless() { testRead(false, false); }
+
+void WebpFilterTest::testReadNoAlphaLossy() { testRead(true, false); }
+
+void WebpFilterTest::testRead(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/webp/")
+ + (alpha ? u"alpha" : u"noalpha") + "_" + (lossy ? u"lossy" : u"lossless")
+ + ".webp";
+ SvFileStream aFileStream(file, StreamMode::READ);
+ Graphic aGraphic;
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ ErrCode bResult = rFilter.ImportGraphic(aGraphic, "none", aFileStream);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
+ CPPUNIT_ASSERT_EQUAL(GfxLinkType::NativeWebp, aGraphic.GetGfxLink().GetType());
+ BitmapEx aResultBitmap = aGraphic.GetBitmapEx();
+ CPPUNIT_ASSERT_EQUAL(Size(10, 10), aResultBitmap.GetSizePixel());
+ CPPUNIT_ASSERT_EQUAL(alpha, aResultBitmap.IsAlpha());
+
+ {
+ Bitmap tmpBitmap = aResultBitmap.GetBitmap();
+ Bitmap::ScopedReadAccess 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 (alpha)
+ {
+ AlphaMask tmpAlpha = aResultBitmap.GetAlpha();
+ AlphaMask::ScopedReadAccess pAccessAlpha(tmpAlpha);
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(0), pAccessAlpha->GetPixelIndex(0, 0));
+ CPPUNIT_ASSERT_EQUAL(sal_uInt8(255), pAccessAlpha->GetPixelIndex(0, 9));
+ }
+ }
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(WebpFilterTest);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */