diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2019-02-08 21:30:39 +0100 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2019-02-09 11:57:48 +0100 |
commit | 34a6e8443312c5f9aa0a1bb055fac50d722a90ff (patch) | |
tree | 41c1c25c6c639d6c6ce4d29d117138bad61e5358 /vcl | |
parent | ebc6cfbb323d2f933336f7e2f4a775098c4f4341 (diff) |
add PngImageFilter that uses libpng for PNG loading
This adds loading of PNG images that uses libpng instead of our
own solution. It always loaded the image as either RGB or RGBA
image and if the source PNG is using something else, libpng
converts to either RGB or RGBA.
In addition this adds tests for loading of various PNG files to
make sure the resulting bitmaps are using pixel data as expected.
(especially needed to check the RGBA bitmaps)
Change-Id: I194321caf76c2ec2365bb6075c79c5e84983658a
Reviewed-on: https://gerrit.libreoffice.org/67571
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/CppunitTest_vcl_png_test.mk | 52 | ||||
-rw-r--r-- | vcl/Library_vcl.mk | 2 | ||||
-rw-r--r-- | vcl/Module_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/qa/cppunit/png/PngFilterTest.cxx | 155 | ||||
-rw-r--r-- | vcl/qa/cppunit/png/data/alpha-rect-8bit-RGBA.png | bin | 0 -> 158 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/png/data/color-rect-4bit-pal.png | bin | 0 -> 104 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/png/data/color-rect-8bit-RGB.png | bin | 0 -> 90 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/png/data/rect-1bit-pal.png | bin | 0 -> 89 bytes | |||
-rw-r--r-- | vcl/source/filter/png/PngImageReader.cxx | 214 |
9 files changed, 424 insertions, 0 deletions
diff --git a/vcl/CppunitTest_vcl_png_test.mk b/vcl/CppunitTest_vcl_png_test.mk new file mode 100644 index 000000000000..1f88209313a0 --- /dev/null +++ b/vcl/CppunitTest_vcl_png_test.mk @@ -0,0 +1,52 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_CppunitTest_CppunitTest,vcl_png_test)) + +$(eval $(call gb_CppunitTest_add_exception_objects,vcl_png_test, \ + vcl/qa/cppunit/png/PngFilterTest \ +)) + +$(eval $(call gb_CppunitTest_set_include,vcl_png_test,\ + $$(INCLUDE) \ + -I$(SRCDIR)/vcl/inc \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,vcl_png_test, \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + svt \ + test \ + tl \ + unotest \ + vcl \ + utl \ + $(gb_UWINAPI) \ +)) + +$(eval $(call gb_CppunitTest_use_api,vcl_png_test,\ + udkapi \ + offapi \ +)) + +$(eval $(call gb_CppunitTest_use_ure,vcl_png_test)) +$(eval $(call gb_CppunitTest_use_vcl,vcl_png_test)) + +$(eval $(call gb_CppunitTest_use_components,vcl_png_test,\ + configmgr/source/configmgr \ + i18npool/util/i18npool \ + ucb/source/core/ucb1 \ + unotools/util/utl \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,vcl_png_test)) + +# vim: set noet sw=4 ts=4: diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 31e677b9e37d..0178ffc809df 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -66,6 +66,7 @@ $(eval $(call gb_Library_use_custom_headers,vcl,\ $(eval $(call gb_Library_use_externals,vcl,\ libjpeg \ libeot \ + libpng \ $(if $(filter PDFIUM,$(BUILD_TYPE)),pdfium) \ )) @@ -407,6 +408,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/filter/wmf/wmf \ vcl/source/filter/wmf/wmfexternal \ vcl/source/filter/wmf/wmfwr \ + vcl/source/filter/png/PngImageReader \ vcl/source/font/Feature \ vcl/source/font/FeatureCollector \ vcl/source/font/FeatureParser \ diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk index 95dd579c38de..14e3eacf1bf5 100644 --- a/vcl/Module_vcl.mk +++ b/vcl/Module_vcl.mk @@ -205,6 +205,7 @@ $(eval $(call gb_Module_add_check_targets,vcl,\ CppunitTest_vcl_errorhandler \ CppunitTest_vcl_bitmap_render_test \ CppunitTest_vcl_apitests \ + CppunitTest_vcl_png_test \ )) ifneq (,$(filter PDFIUM,$(BUILD_TYPE))) diff --git a/vcl/qa/cppunit/png/PngFilterTest.cxx b/vcl/qa/cppunit/png/PngFilterTest.cxx new file mode 100644 index 000000000000..1a9dd0a4cead --- /dev/null +++ b/vcl/qa/cppunit/png/PngFilterTest.cxx @@ -0,0 +1,155 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <test/bootstrapfixture.hxx> +#include <vcl/filter/PngImageReader.hxx> +#include <vcl/bitmapaccess.hxx> +#include <vcl/alpha.hxx> + +using namespace css; + +class PngFilterTest : public test::BootstrapFixture +{ + OUString maDataUrl; + + OUString getFullUrl(const OUString& sFileName) + { + return m_directories.getURLFromSrc(maDataUrl) + sFileName; + } + +public: + PngFilterTest() + : BootstrapFixture(true, false) + , maDataUrl("/vcl/qa/cppunit/png/data/") + { + } + + void testPng(); + + CPPUNIT_TEST_SUITE(PngFilterTest); + CPPUNIT_TEST(testPng); + CPPUNIT_TEST_SUITE_END(); +}; + +void PngFilterTest::testPng() +{ + for (const OUString& aFileName : { OUString("rect-1bit-pal.png") }) + { + SvFileStream aFileStream(getFullUrl(aFileName), StreamMode::READ); + + vcl::PngImageReader aPngReader(aFileStream); + BitmapEx aBitmapEx; + aPngReader.read(aBitmapEx); + + Bitmap aBitmap = aBitmapEx.GetBitmap(); + { + Bitmap::ScopedReadAccess pAccess(aBitmap); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(24), pAccess->GetBitCount()); + CPPUNIT_ASSERT_EQUAL(4L, pAccess->Width()); + CPPUNIT_ASSERT_EQUAL(4L, pAccess->Height()); + + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(0, 0)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(3, 3)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(3, 0)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(0, 3)); + + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x00, 0x00), pAccess->GetPixel(1, 1)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x00, 0x00), pAccess->GetPixel(1, 2)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x00, 0x00), pAccess->GetPixel(2, 1)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x00, 0x00), pAccess->GetPixel(2, 2)); + } + } + for (const OUString& aFileName : + { OUString("color-rect-8bit-RGB.png"), OUString("color-rect-4bit-pal.png") }) + { + SvFileStream aFileStream(getFullUrl(aFileName), StreamMode::READ); + + vcl::PngImageReader aPngReader(aFileStream); + BitmapEx aBitmapEx; + aPngReader.read(aBitmapEx); + + Bitmap aBitmap = aBitmapEx.GetBitmap(); + { + Bitmap::ScopedReadAccess pAccess(aBitmap); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(24), pAccess->GetBitCount()); + CPPUNIT_ASSERT_EQUAL(4L, pAccess->Width()); + CPPUNIT_ASSERT_EQUAL(4L, pAccess->Height()); + + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(0, 0)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(3, 3)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(3, 0)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(0, 3)); + + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0x00, 0x00, 0x00), pAccess->GetPixel(1, 1)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0xFF, 0x00, 0x00), pAccess->GetPixel(1, 2)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0xFF, 0x00), pAccess->GetPixel(2, 1)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0x00, 0x00), pAccess->GetPixel(2, 2)); + } + } + for (const OUString& aFileName : { OUString("alpha-rect-8bit-RGBA.png") }) + { + SvFileStream aFileStream(getFullUrl(aFileName), StreamMode::READ); + + vcl::PngImageReader aPngReader(aFileStream); + BitmapEx aBitmapEx; + aPngReader.read(aBitmapEx); + + Bitmap aBitmap = aBitmapEx.GetBitmap(); + { + Bitmap::ScopedReadAccess pAccess(aBitmap); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(24), pAccess->GetBitCount()); + CPPUNIT_ASSERT_EQUAL(4L, pAccess->Width()); + CPPUNIT_ASSERT_EQUAL(4L, pAccess->Height()); + + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(0, 0)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(3, 3)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(3, 0)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0xFF, 0x00), pAccess->GetPixel(0, 3)); + + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0x00, 0x00, 0x00), pAccess->GetPixel(1, 1)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0xFF, 0x00, 0x00), pAccess->GetPixel(1, 2)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0xFF, 0x00), pAccess->GetPixel(2, 1)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0xFF, 0xFF, 0x00, 0x00), pAccess->GetPixel(2, 2)); + } + AlphaMask aAlpha = aBitmapEx.GetAlpha(); + { + AlphaMask::ScopedReadAccess pAccess(aAlpha); + CPPUNIT_ASSERT_EQUAL(sal_uInt16(8), pAccess->GetBitCount()); + CPPUNIT_ASSERT_EQUAL(4L, pAccess->Width()); + CPPUNIT_ASSERT_EQUAL(4L, pAccess->Height()); + + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x80, 0x00), pAccess->GetPixel(0, 0)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x80, 0x00), pAccess->GetPixel(3, 3)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x80, 0x00), pAccess->GetPixel(3, 0)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x80, 0x00), pAccess->GetPixel(0, 3)); + + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x40, 0x00), pAccess->GetPixel(1, 1)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0xC0, 0x00), pAccess->GetPixel(1, 2)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0xC0, 0x00), pAccess->GetPixel(2, 1)); + CPPUNIT_ASSERT_EQUAL(BitmapColor(0x00, 0x00, 0x40, 0x00), pAccess->GetPixel(2, 2)); + } + } + // CPPUNIT_ASSERT(false); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(PngFilterTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qa/cppunit/png/data/alpha-rect-8bit-RGBA.png b/vcl/qa/cppunit/png/data/alpha-rect-8bit-RGBA.png Binary files differnew file mode 100644 index 000000000000..1e90e1a6cfed --- /dev/null +++ b/vcl/qa/cppunit/png/data/alpha-rect-8bit-RGBA.png diff --git a/vcl/qa/cppunit/png/data/color-rect-4bit-pal.png b/vcl/qa/cppunit/png/data/color-rect-4bit-pal.png Binary files differnew file mode 100644 index 000000000000..740eede512d1 --- /dev/null +++ b/vcl/qa/cppunit/png/data/color-rect-4bit-pal.png diff --git a/vcl/qa/cppunit/png/data/color-rect-8bit-RGB.png b/vcl/qa/cppunit/png/data/color-rect-8bit-RGB.png Binary files differnew file mode 100644 index 000000000000..727859d8a7c1 --- /dev/null +++ b/vcl/qa/cppunit/png/data/color-rect-8bit-RGB.png diff --git a/vcl/qa/cppunit/png/data/rect-1bit-pal.png b/vcl/qa/cppunit/png/data/rect-1bit-pal.png Binary files differnew file mode 100644 index 000000000000..cf7ac3e7c31b --- /dev/null +++ b/vcl/qa/cppunit/png/data/rect-1bit-pal.png diff --git a/vcl/source/filter/png/PngImageReader.cxx b/vcl/source/filter/png/PngImageReader.cxx new file mode 100644 index 000000000000..790336654714 --- /dev/null +++ b/vcl/source/filter/png/PngImageReader.cxx @@ -0,0 +1,214 @@ +/* -*- 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 <vcl/filter/PngImageReader.hxx> +#include <png.h> +#include <vcl/bitmapaccess.hxx> +#include <bitmapwriteaccess.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/alpha.hxx> + +namespace +{ +void lclReadStream(png_structp pPng, png_bytep pOutBytes, png_size_t nBytesToRead) +{ + png_voidp pIO = png_get_io_ptr(pPng); + + if (pIO == nullptr) + return; + + SvStream* pStream = static_cast<SvStream*>(pIO); + + sal_Size nBytesRead = pStream->ReadBytes(pOutBytes, nBytesToRead); + + if (nBytesRead != nBytesToRead) + png_error(pPng, "Error reading"); +} + +bool reader(SvStream& rStream, BitmapEx& rBitmapEx) +{ + enum + { + PNG_SIGNATURE_SIZE = 8 + }; + + sal_uInt8 aHeader[PNG_SIGNATURE_SIZE]; + rStream.ReadBytes(aHeader, PNG_SIGNATURE_SIZE); + + if (png_sig_cmp(aHeader, 0, PNG_SIGNATURE_SIZE)) + return false; + + png_structp pPng = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!pPng) + return false; + + png_infop pInfo = png_create_info_struct(pPng); + if (!pInfo) + { + png_destroy_read_struct(&pPng, nullptr, nullptr); + return false; + } + + if (setjmp(png_jmpbuf(pPng))) + { + png_destroy_read_struct(&pPng, &pInfo, nullptr); + return false; + } + + png_set_read_fn(pPng, &rStream, lclReadStream); + + png_set_crc_action(pPng, PNG_CRC_WARN_USE, PNG_CRC_WARN_DISCARD); + + png_set_sig_bytes(pPng, PNG_SIGNATURE_SIZE); + + png_read_info(pPng, pInfo); + + png_uint_32 width = 0; + png_uint_32 height = 0; + int bitDepth = 0; + int colorType = -1; + int interlace = -1; + + png_uint_32 returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, &colorType, + &interlace, nullptr, nullptr); + + if (returnValue != 1) + { + png_destroy_read_struct(&pPng, &pInfo, nullptr); + return false; + } + + Bitmap aBitmap(Size(width, height), 24); + AlphaMask aBitmapAlpha(Size(width, height), nullptr); + + png_set_bgr(pPng); + + if (colorType == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(pPng); + + if (colorType == PNG_COLOR_TYPE_GRAY) + png_set_expand_gray_1_2_4_to_8(pPng); + + if (png_get_valid(pPng, pInfo, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(pPng); + + if (bitDepth == 16) + png_set_scale_16(pPng); + + if (bitDepth < 8) + png_set_packing(pPng); + + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb(pPng); + } + + // Sets the filler byte - if RGB it converts to RGBA + // png_set_filler(pPng, 0xFF, PNG_FILLER_AFTER); + + int nNumberOfPasses = png_set_interlace_handling(pPng); + + png_read_update_info(pPng, pInfo); + returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, &colorType, nullptr, + nullptr, nullptr); + + if (returnValue != 1) + { + png_destroy_read_struct(&pPng, &pInfo, nullptr); + return false; + } + + if (bitDepth != 8 + || !(colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA)) + { + png_destroy_read_struct(&pPng, &pInfo, nullptr); + return false; + } + + { + if (colorType == PNG_COLOR_TYPE_RGB) + { + size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo); + + BitmapScopedWriteAccess pWriteAccess(aBitmap); + + std::vector<png_byte> aRow(aRowSizeBytes, 0); + + for (int pass = 0; pass < nNumberOfPasses; pass++) + { + for (png_uint_32 y = 0; y < height; y++) + { + Scanline pScanline = pWriteAccess->GetScanline(y); + png_bytep pRow = aRow.data(); + png_read_rows(pPng, &pRow, nullptr, 1); + size_t iColor = 0; + for (size_t i = 0; i < aRowSizeBytes; i += 3) + { + pScanline[iColor++] = pRow[i + 0]; + pScanline[iColor++] = pRow[i + 1]; + pScanline[iColor++] = pRow[i + 2]; + } + } + } + } + else if (colorType == PNG_COLOR_TYPE_RGB_ALPHA) + { + size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo); + + BitmapScopedWriteAccess pWriteAccess(aBitmap); + AlphaScopedWriteAccess pWriteAccessAlpha(aBitmapAlpha); + + std::vector<png_byte> aRow(aRowSizeBytes, 0); + + for (int pass = 0; pass < nNumberOfPasses; pass++) + { + for (png_uint_32 y = 0; y < height; y++) + { + Scanline pScanAlpha = pWriteAccessAlpha->GetScanline(y); + Scanline pScanline = pWriteAccess->GetScanline(y); + png_bytep pRow = aRow.data(); + png_read_rows(pPng, &pRow, nullptr, 1); + size_t iAlpha = 0; + size_t iColor = 0; + for (size_t i = 0; i < aRowSizeBytes; i += 4) + { + pScanline[iColor++] = pRow[i + 0]; + pScanline[iColor++] = pRow[i + 1]; + pScanline[iColor++] = pRow[i + 2]; + pScanAlpha[iAlpha++] = 0xFF - pRow[i + 3]; + } + } + } + } + } + + png_read_end(pPng, pInfo); + + png_destroy_read_struct(&pPng, &pInfo, nullptr); + + rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha); + + return true; +} + +} // anonymous namespace + +namespace vcl +{ +PngImageReader::PngImageReader(SvStream& rStream) + : mrStream(rStream) +{ +} + +bool PngImageReader::read(BitmapEx& rBitmapEx) { return reader(mrStream, rBitmapEx); } + +} // namespace vcl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |