diff options
-rw-r--r-- | include/vcl/filter/PngImageReader.hxx | 39 | ||||
-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 |
10 files changed, 463 insertions, 0 deletions
diff --git a/include/vcl/filter/PngImageReader.hxx b/include/vcl/filter/PngImageReader.hxx new file mode 100644 index 000000000000..f555939a4836 --- /dev/null +++ b/include/vcl/filter/PngImageReader.hxx @@ -0,0 +1,39 @@ +/* -*- 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/graph.hxx> +#include <vcl/dllapi.h> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <tools/stream.hxx> +#include <vcl/bitmapex.hxx> + +#ifndef INCLUDED_VCL_SOURCE_FILTER_PNG_PNGIMAGEREADER_HXX +#define INCLUDED_VCL_SOURCE_FILTER_PNG_PNGIMAGEREADER_HXX + +namespace vcl +{ +class VCL_DLLPUBLIC PngImageReader +{ + SvStream& mrStream; + css::uno::Reference<css::task::XStatusIndicator> mxStatusIndicator; + +public: + PngImageReader(SvStream& rStream); + + virtual ~PngImageReader() {} + + bool read(BitmapEx& rBitmap); +}; + +} // namespace vcl + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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: */ |