tdf#159660: Add support for lighten mode in feBlend
Change-Id: I17471a9c70a38d05de5ad476f817285fb2438e5a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166429 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
/* -*- 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/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INCLUDED_VCL_BITMAPLIGHTENBLENDFILTER_HXX
|
||||
#define INCLUDED_VCL_BITMAPLIGHTENBLENDFILTER_HXX
|
||||
|
||||
#include <vcl/bitmapex.hxx>
|
||||
|
||||
class VCL_DLLPUBLIC BitmapLightenBlendFilter
|
||||
{
|
||||
private:
|
||||
BitmapEx maBitmapEx;
|
||||
BitmapEx maBitmapEx2;
|
||||
|
||||
public:
|
||||
BitmapLightenBlendFilter(BitmapEx const& rBmpEx, BitmapEx const& rBmpEx2);
|
||||
|
||||
virtual ~BitmapLightenBlendFilter();
|
||||
BitmapEx execute();
|
||||
};
|
||||
#endif
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
@@ -27,6 +27,7 @@ namespace svgio::svgreader
|
||||
enum class Mode
|
||||
{
|
||||
Darken,
|
||||
Lighten,
|
||||
Multiply,
|
||||
Normal,
|
||||
Screen
|
||||
|
||||
@@ -1909,6 +1909,50 @@ CPPUNIT_TEST_FIXTURE(Test, testDarkenBlend)
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("008000"), aPixels[125]);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testLightenBlend)
|
||||
{
|
||||
xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/lightenBlend.svg");
|
||||
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "height"_ostr, "150");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "width"_ostr, "150");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap/data"_ostr, 150);
|
||||
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy11"_ostr, "150");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy12"_ostr, "0");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy13"_ostr, "20");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy21"_ostr, "0");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy22"_ostr, "150");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy23"_ostr, "20");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy31"_ostr, "0");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy32"_ostr, "0");
|
||||
assertXPath(pDocument,
|
||||
"/primitive2D/transform/transform/bitmap"_ostr, "xy33"_ostr, "1");
|
||||
|
||||
// Check the colors in the diagonal
|
||||
OUString sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[25]"_ostr, "row"_ostr);
|
||||
std::vector<OUString> aPixels = comphelper::string::split(sDataRow, ',');
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("ff0000"), aPixels[25]);
|
||||
|
||||
sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[75]"_ostr, "row"_ostr);
|
||||
aPixels = comphelper::string::split(sDataRow, ',');
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("ff8000"), aPixels[75]);
|
||||
|
||||
sDataRow = getXPath(pDocument, "/primitive2D/transform/transform/bitmap/data[125]"_ostr, "row"_ostr);
|
||||
aPixels = comphelper::string::split(sDataRow, ',');
|
||||
CPPUNIT_ASSERT_EQUAL(OUString("008000"), aPixels[125]);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testTdf149880)
|
||||
{
|
||||
xmlDocUniquePtr pDocument = dumpAndParseSvg(u"/svgio/qa/cppunit/data/tdf149880.svg");
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg width="100" height="140" viewBox="0 0 200 280" xmlns="http://www.w3.org/2000/svg">
|
||||
<filter id="filter" filterUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
|
||||
<feFlood x="20" y="20" width="100" height="100" flood-color="red" flood-opacity="1" result="img1"></feFlood>
|
||||
<feFlood x="50" y="50" width="100" height="100" flood-color="green" flood-opacity="1" result="img2"></feFlood>
|
||||
<feBlend in="img1" in2="img2" mode="lighten"></feBlend>
|
||||
</filter>
|
||||
<use style="filter: url(#filter)"></use>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 529 B |
@@ -25,6 +25,7 @@
|
||||
#include <drawinglayer/converters.hxx>
|
||||
#include <basegfx/matrix/b2dhommatrixtools.hxx>
|
||||
#include <vcl/BitmapDarkenBlendFilter.hxx>
|
||||
#include <vcl/BitmapLightenBlendFilter.hxx>
|
||||
#include <vcl/BitmapMultiplyBlendFilter.hxx>
|
||||
#include <vcl/BitmapScreenBlendFilter.hxx>
|
||||
#include <vcl/BitmapTools.hxx>
|
||||
@@ -84,6 +85,10 @@ void SvgFeBlendNode::parseAttribute(SVGToken aSVGToken, const OUString& aContent
|
||||
{
|
||||
maMode = Mode::Darken;
|
||||
}
|
||||
else if (o3tl::equalsIgnoreAsciiCase(o3tl::trim(aContent), u"lighten"))
|
||||
{
|
||||
maMode = Mode::Lighten;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -182,6 +187,11 @@ void SvgFeBlendNode::apply(drawinglayer::primitive2d::Primitive2DContainer& rTar
|
||||
BitmapDarkenBlendFilter aDarkenBlendFilter(aBmpEx, aBmpEx2);
|
||||
aResBmpEx = aDarkenBlendFilter.execute();
|
||||
}
|
||||
else if (maMode == Mode::Lighten)
|
||||
{
|
||||
BitmapLightenBlendFilter aLightenBlendFilter(aBmpEx, aBmpEx2);
|
||||
aResBmpEx = aLightenBlendFilter.execute();
|
||||
}
|
||||
|
||||
const drawinglayer::primitive2d::Primitive2DReference xRef(
|
||||
new drawinglayer::primitive2d::BitmapPrimitive2D(
|
||||
|
||||
@@ -346,6 +346,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
|
||||
vcl/source/bitmap/BitmapAlphaClampFilter \
|
||||
vcl/source/bitmap/BitmapBasicMorphologyFilter \
|
||||
vcl/source/bitmap/BitmapDarkenBlendFilter \
|
||||
vcl/source/bitmap/BitmapLightenBlendFilter \
|
||||
vcl/source/bitmap/BitmapMaskToAlphaFilter \
|
||||
vcl/source/bitmap/BitmapMonochromeFilter \
|
||||
vcl/source/bitmap/BitmapMultiplyBlendFilter \
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <vcl/BitmapArithmeticBlendFilter.hxx>
|
||||
#include <vcl/BitmapDarkenBlendFilter.hxx>
|
||||
#include <vcl/BitmapLightenBlendFilter.hxx>
|
||||
#include <vcl/BitmapMultiplyBlendFilter.hxx>
|
||||
#include <vcl/BitmapScreenBlendFilter.hxx>
|
||||
#include <vcl/BitmapBasicMorphologyFilter.hxx>
|
||||
@@ -45,6 +46,7 @@ public:
|
||||
void testGenerateStripRanges();
|
||||
void testMultiplyBlendFilter();
|
||||
void testDarkenBlendFilter();
|
||||
void testLightenBlendFilter();
|
||||
void testScreenBlendFilter();
|
||||
void testArithmeticBlendFilter();
|
||||
|
||||
@@ -55,6 +57,7 @@ public:
|
||||
CPPUNIT_TEST(testGenerateStripRanges);
|
||||
CPPUNIT_TEST(testMultiplyBlendFilter);
|
||||
CPPUNIT_TEST(testDarkenBlendFilter);
|
||||
CPPUNIT_TEST(testLightenBlendFilter);
|
||||
CPPUNIT_TEST(testScreenBlendFilter);
|
||||
CPPUNIT_TEST(testArithmeticBlendFilter);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
@@ -396,6 +399,60 @@ void BitmapFilterTest::testDarkenBlendFilter()
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapFilterTest::testLightenBlendFilter()
|
||||
{
|
||||
Bitmap aRedBitmap(Size(4, 4), vcl::PixelFormat::N24_BPP);
|
||||
CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, aRedBitmap.getPixelFormat());
|
||||
{
|
||||
BitmapScopedWriteAccess aWriteAccess(aRedBitmap);
|
||||
aWriteAccess->Erase(COL_LIGHTRED);
|
||||
}
|
||||
|
||||
Bitmap aGreenBitmap(Size(4, 4), vcl::PixelFormat::N24_BPP);
|
||||
CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, aGreenBitmap.getPixelFormat());
|
||||
{
|
||||
BitmapScopedWriteAccess aWriteAccess(aGreenBitmap);
|
||||
aWriteAccess->Erase(COL_GREEN);
|
||||
}
|
||||
|
||||
Bitmap aTransparentBitmap(Size(4, 4), vcl::PixelFormat::N24_BPP);
|
||||
CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, aTransparentBitmap.getPixelFormat());
|
||||
{
|
||||
BitmapScopedWriteAccess aWriteAccess(aTransparentBitmap);
|
||||
aWriteAccess->Erase(COL_AUTO);
|
||||
}
|
||||
|
||||
BitmapEx aRedBitmapEx(aRedBitmap);
|
||||
BitmapEx aGreenBitmapEx(aGreenBitmap);
|
||||
BitmapEx aTransparentBitmapEx(aTransparentBitmap);
|
||||
|
||||
// same color
|
||||
{
|
||||
BitmapLightenBlendFilter* pArithmeticFilter
|
||||
= new BitmapLightenBlendFilter(aRedBitmapEx, aRedBitmapEx);
|
||||
BitmapEx aResBitmapEx = pArithmeticFilter->execute();
|
||||
CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, aResBitmapEx.GetPixelColor(2, 2));
|
||||
}
|
||||
|
||||
// different color
|
||||
{
|
||||
BitmapLightenBlendFilter* pArithmeticFilter
|
||||
= new BitmapLightenBlendFilter(aRedBitmapEx, aGreenBitmapEx);
|
||||
BitmapEx aResBitmapEx = pArithmeticFilter->execute();
|
||||
CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xFF, 0xFF, 0x80, 0x00),
|
||||
aResBitmapEx.GetPixelColor(2, 2));
|
||||
}
|
||||
|
||||
// transparent
|
||||
{
|
||||
BitmapLightenBlendFilter* pArithmeticFilter
|
||||
= new BitmapLightenBlendFilter(aRedBitmapEx, aTransparentBitmapEx);
|
||||
BitmapEx aResBitmapEx = pArithmeticFilter->execute();
|
||||
CPPUNIT_ASSERT_EQUAL(Color(ColorAlpha, 0xFF, 0xFF, 0xFF, 0xFF),
|
||||
aResBitmapEx.GetPixelColor(2, 2));
|
||||
}
|
||||
}
|
||||
|
||||
void BitmapFilterTest::testScreenBlendFilter()
|
||||
{
|
||||
Bitmap aRedBitmap(Size(4, 4), vcl::PixelFormat::N24_BPP);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/* -*- 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 <comphelper/diagnose_ex.hxx>
|
||||
#include <vcl/BitmapLightenBlendFilter.hxx>
|
||||
#include <vcl/BitmapWriteAccess.hxx>
|
||||
#include <vcl/BitmapTools.hxx>
|
||||
|
||||
BitmapLightenBlendFilter::BitmapLightenBlendFilter(BitmapEx const& rBitmapEx,
|
||||
BitmapEx const& rBitmapEx2)
|
||||
: maBitmapEx(rBitmapEx)
|
||||
, maBitmapEx2(rBitmapEx2)
|
||||
{
|
||||
}
|
||||
|
||||
BitmapLightenBlendFilter::~BitmapLightenBlendFilter() {}
|
||||
|
||||
static sal_uInt8 lcl_calculate(const sal_uInt8 aColor, const sal_uInt8 aAlpha,
|
||||
const sal_uInt8 aColor2, const sal_uInt8 aAlpha2)
|
||||
{
|
||||
const double c1 = aColor / 255.0;
|
||||
const double c2 = aColor2 / 255.0;
|
||||
const double a1 = aAlpha / 255.0;
|
||||
const double a2 = aAlpha2 / 255.0;
|
||||
const double result = std::max((1.0 - a1) * c2 + c1, (1.0 - a2) * c1 + c2);
|
||||
return result * 255.0;
|
||||
}
|
||||
|
||||
static BitmapColor premultiply(const BitmapColor c)
|
||||
{
|
||||
return BitmapColor(ColorAlpha, vcl::bitmap::premultiply(c.GetRed(), c.GetAlpha()),
|
||||
vcl::bitmap::premultiply(c.GetGreen(), c.GetAlpha()),
|
||||
vcl::bitmap::premultiply(c.GetBlue(), c.GetAlpha()), c.GetAlpha());
|
||||
}
|
||||
|
||||
static BitmapColor unpremultiply(const BitmapColor c)
|
||||
{
|
||||
return BitmapColor(ColorAlpha, vcl::bitmap::unpremultiply(c.GetRed(), c.GetAlpha()),
|
||||
vcl::bitmap::unpremultiply(c.GetGreen(), c.GetAlpha()),
|
||||
vcl::bitmap::unpremultiply(c.GetBlue(), c.GetAlpha()), c.GetAlpha());
|
||||
}
|
||||
|
||||
BitmapEx BitmapLightenBlendFilter::execute()
|
||||
{
|
||||
if (maBitmapEx.IsEmpty() || maBitmapEx2.IsEmpty())
|
||||
return BitmapEx();
|
||||
|
||||
Size aSize = maBitmapEx.GetBitmap().GetSizePixel();
|
||||
Size aSize2 = maBitmapEx2.GetBitmap().GetSizePixel();
|
||||
sal_Int32 nHeight = std::min(aSize.getHeight(), aSize2.getHeight());
|
||||
sal_Int32 nWidth = std::min(aSize.getWidth(), aSize2.getWidth());
|
||||
|
||||
BitmapScopedReadAccess pReadAccess(maBitmapEx.GetBitmap());
|
||||
Bitmap aDstBitmap(Size(nWidth, nHeight), maBitmapEx.GetBitmap().getPixelFormat(),
|
||||
&pReadAccess->GetPalette());
|
||||
Bitmap aDstAlpha(AlphaMask(Size(nWidth, nHeight)).GetBitmap());
|
||||
|
||||
{
|
||||
// just to be on the safe side: let the
|
||||
// ScopedAccessors get destructed before
|
||||
// copy-constructing the resulting bitmap. This will
|
||||
// rule out the possibility that cached accessor data
|
||||
// is not yet written back.
|
||||
|
||||
BitmapScopedWriteAccess pWriteAccess(aDstBitmap);
|
||||
BitmapScopedWriteAccess pAlphaWriteAccess(aDstAlpha);
|
||||
|
||||
if (pWriteAccess.get() != nullptr && pAlphaWriteAccess.get() != nullptr)
|
||||
{
|
||||
for (tools::Long y(0); y < nHeight; ++y)
|
||||
{
|
||||
Scanline pScanline = pWriteAccess->GetScanline(y);
|
||||
Scanline pScanAlpha = pAlphaWriteAccess->GetScanline(y);
|
||||
for (tools::Long x(0); x < nWidth; ++x)
|
||||
{
|
||||
BitmapColor i1 = premultiply(maBitmapEx.GetPixelColor(x, y));
|
||||
BitmapColor i2 = premultiply(maBitmapEx2.GetPixelColor(x, y));
|
||||
sal_uInt8 r(
|
||||
lcl_calculate(i1.GetRed(), i1.GetAlpha(), i2.GetRed(), i2.GetAlpha()));
|
||||
sal_uInt8 g(
|
||||
lcl_calculate(i1.GetGreen(), i1.GetAlpha(), i2.GetGreen(), i2.GetAlpha()));
|
||||
sal_uInt8 b(
|
||||
lcl_calculate(i1.GetBlue(), i1.GetAlpha(), i2.GetBlue(), i2.GetAlpha()));
|
||||
sal_uInt8 a(
|
||||
lcl_calculate(i1.GetAlpha(), i1.GetAlpha(), i2.GetAlpha(), i2.GetAlpha()));
|
||||
|
||||
pWriteAccess->SetPixelOnData(
|
||||
pScanline, x, unpremultiply(BitmapColor(ColorAlpha, r, g, b, a)));
|
||||
pAlphaWriteAccess->SetPixelOnData(pScanAlpha, x, BitmapColor(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(E2): Error handling!
|
||||
ENSURE_OR_THROW(false, "BitmapLightenBlendFilter: could not access bitmap");
|
||||
}
|
||||
}
|
||||
|
||||
return BitmapEx(aDstBitmap, AlphaMask(aDstAlpha));
|
||||
}
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
Reference in New Issue
Block a user