Kaydet (Commit) 34a6e844 authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl Kaydeden (comit) Tomaž Vajngerl

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: 's avatarTomaž Vajngerl <quikee@gmail.com>
üst ebc6cfbb
/* -*- 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: */
# -*- 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:
...@@ -66,6 +66,7 @@ $(eval $(call gb_Library_use_custom_headers,vcl,\ ...@@ -66,6 +66,7 @@ $(eval $(call gb_Library_use_custom_headers,vcl,\
$(eval $(call gb_Library_use_externals,vcl,\ $(eval $(call gb_Library_use_externals,vcl,\
libjpeg \ libjpeg \
libeot \ libeot \
libpng \
$(if $(filter PDFIUM,$(BUILD_TYPE)),pdfium) \ $(if $(filter PDFIUM,$(BUILD_TYPE)),pdfium) \
)) ))
...@@ -407,6 +408,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ ...@@ -407,6 +408,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/filter/wmf/wmf \ vcl/source/filter/wmf/wmf \
vcl/source/filter/wmf/wmfexternal \ vcl/source/filter/wmf/wmfexternal \
vcl/source/filter/wmf/wmfwr \ vcl/source/filter/wmf/wmfwr \
vcl/source/filter/png/PngImageReader \
vcl/source/font/Feature \ vcl/source/font/Feature \
vcl/source/font/FeatureCollector \ vcl/source/font/FeatureCollector \
vcl/source/font/FeatureParser \ vcl/source/font/FeatureParser \
......
...@@ -205,6 +205,7 @@ $(eval $(call gb_Module_add_check_targets,vcl,\ ...@@ -205,6 +205,7 @@ $(eval $(call gb_Module_add_check_targets,vcl,\
CppunitTest_vcl_errorhandler \ CppunitTest_vcl_errorhandler \
CppunitTest_vcl_bitmap_render_test \ CppunitTest_vcl_bitmap_render_test \
CppunitTest_vcl_apitests \ CppunitTest_vcl_apitests \
CppunitTest_vcl_png_test \
)) ))
ifneq (,$(filter PDFIUM,$(BUILD_TYPE))) ifneq (,$(filter PDFIUM,$(BUILD_TYPE)))
......
/* -*- 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: */
/* -*- 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: */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment