diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2022-01-13 15:59:49 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-01-31 10:44:29 +0100 |
commit | 60eaa424c5e213f31227008e1ed66a646491a360 (patch) | |
tree | 7f1a2ff9e73367502d63bb84a528d855ccff17a6 /vcl/qa | |
parent | b7d5ff4bbf41094b6579ae26023fbd686b060ce9 (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.cxx | 16 | ||||
-rw-r--r-- | vcl/qa/cppunit/GraphicFormatDetectorTest.cxx | 17 | ||||
-rw-r--r-- | vcl/qa/cppunit/GraphicTest.cxx | 12 | ||||
-rw-r--r-- | vcl/qa/cppunit/data/TypeDetectionExample.webp | bin | 0 -> 52 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossless.webp | bin | 0 -> 744 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossy.webp | bin | 0 -> 826 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/data/webp/fail/.gitignore | 0 | ||||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/data/webp/indeterminate/.gitignore | 0 | ||||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossless.webp | bin | 0 -> 762 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossy.webp | bin | 0 -> 782 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/data/webp/pass/.gitignore | 0 | ||||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/filters-test.cxx | 2 | ||||
-rw-r--r-- | vcl/qa/cppunit/graphicfilter/filters-webp-test.cxx | 203 |
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 Binary files differnew file mode 100644 index 000000000000..e85ae121637b --- /dev/null +++ b/vcl/qa/cppunit/data/TypeDetectionExample.webp diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossless.webp b/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossless.webp Binary files differnew file mode 100644 index 000000000000..abb67cc5f4b6 --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossless.webp diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossy.webp b/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossy.webp Binary files differnew file mode 100644 index 000000000000..c587b1bb22c0 --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/webp/alpha_lossy.webp 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 Binary files differnew file mode 100644 index 000000000000..b22ebc3b28bb --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossless.webp diff --git a/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossy.webp b/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossy.webp Binary files differnew file mode 100644 index 000000000000..c118febb0e9a --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/webp/noalpha_lossy.webp 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: */ |