diff options
author | Chris Sherlock <chris.sherlock79@gmail.com> | 2021-12-18 23:39:25 +1100 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2022-01-05 05:42:32 +0100 |
commit | 22f52db0be81ceec2aa7a61f7092b54f36e2d00c (patch) | |
tree | f1f2a02824321d53f949146a480db12186ca45ee | |
parent | 88d8c9af7140ec25dfbcd9323b870a2da7b6f7e0 (diff) |
vcl: migrate AddGradientActions() from OutputDevice to Gradient
Change-Id: I815fb3ce366c93b81f60d19eeed906dc7288708a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127030
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r-- | cppcanvas/source/mtfrenderer/implrenderer.cxx | 5 | ||||
-rw-r--r-- | filter/source/svg/svgwriter.cxx | 8 | ||||
-rw-r--r-- | include/vcl/gradient.hxx | 9 | ||||
-rw-r--r-- | include/vcl/outdev.hxx | 10 | ||||
-rw-r--r-- | vcl/CppunitTest_vcl_gradient.mk | 50 | ||||
-rw-r--r-- | vcl/Module_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/qa/cppunit/gradient.cxx | 256 | ||||
-rw-r--r-- | vcl/qa/cppunit/outdev.cxx | 218 | ||||
-rw-r--r-- | vcl/source/filter/eps/eps.cxx | 3 | ||||
-rw-r--r-- | vcl/source/filter/wmf/emfwr.cxx | 3 | ||||
-rw-r--r-- | vcl/source/filter/wmf/wmfwr.cxx | 3 | ||||
-rw-r--r-- | vcl/source/gdi/gradient.cxx | 382 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl2.cxx | 3 | ||||
-rw-r--r-- | vcl/source/outdev/gradient.cxx | 382 |
14 files changed, 715 insertions, 618 deletions
diff --git a/cppcanvas/source/mtfrenderer/implrenderer.cxx b/cppcanvas/source/mtfrenderer/implrenderer.cxx index 8aa1d1675fa9..f7fe8db551f0 100644 --- a/cppcanvas/source/mtfrenderer/implrenderer.cxx +++ b/cppcanvas/source/mtfrenderer/implrenderer.cxx @@ -720,9 +720,8 @@ namespace cppcanvas::internal } GDIMetaFile aTmpMtf; - rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(), - rGradient, - aTmpMtf ); + Gradient aGradient(rGradient); + aGradient.AddGradientActions( rPoly.GetBoundRect(), aTmpMtf ); createActions( aTmpMtf, rParms, bSubsettableActions ); diff --git a/filter/source/svg/svgwriter.cxx b/filter/source/svg/svgwriter.cxx index 014de0a808b0..a9dcfccbb627 100644 --- a/filter/source/svg/svgwriter.cxx +++ b/filter/source/svg/svgwriter.cxx @@ -2255,9 +2255,15 @@ void SVGActionWriter::ImplWritePattern( const tools::PolyPolygon& rPolyPoly, GDIMetaFile aTmpMtf; if( pHatch ) + { mpVDev->AddHatchActions( rPolyPoly, *pHatch, aTmpMtf ); + } else if ( pGradient ) - mpVDev->AddGradientActions( rPolyPoly.GetBoundRect(), *pGradient, aTmpMtf ); + { + Gradient aGradient(*pGradient); + aGradient.AddGradientActions( rPolyPoly.GetBoundRect(), aTmpMtf ); + } + ImplWriteActions( aTmpMtf, nWriteFlags, "" ); } } diff --git a/include/vcl/gradient.hxx b/include/vcl/gradient.hxx index 5a04616adda5..f7c2bd2ec5f9 100644 --- a/include/vcl/gradient.hxx +++ b/include/vcl/gradient.hxx @@ -33,6 +33,7 @@ namespace tools { class Rectangle; } class Point; class SvStream; +class GDIMetaFile; class VCL_DLLPUBLIC Gradient { @@ -78,11 +79,19 @@ public: void GetBoundRect( const tools::Rectangle& rRect, tools::Rectangle &rBoundRect, Point& rCenter ) const; + void AddGradientActions(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile); + Gradient& operator=( const Gradient& rGradient ); Gradient& operator=( Gradient&& rGradient ); bool operator==( const Gradient& rGradient ) const; bool operator!=( const Gradient& rGradient ) const { return !(Gradient::operator==( rGradient )); } + +private: + tools::Long GetMetafileSteps(tools::Rectangle const& rRect) const; + + void DrawComplexGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const; + void DrawLinearGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const; }; #endif // INCLUDED_VCL_GRADIENT_HXX diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx index 9c86567865ba..13c8c49064b7 100644 --- a/include/vcl/outdev.hxx +++ b/include/vcl/outdev.hxx @@ -795,11 +795,6 @@ public: void DrawGradient( const tools::Rectangle& rRect, const Gradient& rGradient ); void DrawGradient( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient ); - void AddGradientActions( - const tools::Rectangle& rRect, - const Gradient& rGradient, - GDIMetaFile& rMtf ); - protected: virtual bool UsePolyPolygonForComplexGradient() = 0; @@ -810,13 +805,8 @@ private: SAL_DLLPRIVATE void DrawLinearGradient( const tools::Rectangle& rRect, const Gradient& rGradient, const tools::PolyPolygon* pClipPolyPoly ); SAL_DLLPRIVATE void DrawComplexGradient( const tools::Rectangle& rRect, const Gradient& rGradient, const tools::PolyPolygon* pClipPolyPoly ); - SAL_DLLPRIVATE void DrawGradientToMetafile( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient ); - SAL_DLLPRIVATE void DrawLinearGradientToMetafile( const tools::Rectangle& rRect, const Gradient& rGradient ); - SAL_DLLPRIVATE void DrawComplexGradientToMetafile( const tools::Rectangle& rRect, const Gradient& rGradient ); - SAL_DLLPRIVATE tools::Long GetGradientSteps(Gradient const& rGradient, tools::Rectangle const& rRect); - SAL_DLLPRIVATE Color GetSingleColorGradientFill(); ///@} diff --git a/vcl/CppunitTest_vcl_gradient.mk b/vcl/CppunitTest_vcl_gradient.mk new file mode 100644 index 000000000000..64c7e1e2b9df --- /dev/null +++ b/vcl/CppunitTest_vcl_gradient.mk @@ -0,0 +1,50 @@ +# -*- 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_gradient)) + +$(eval $(call gb_CppunitTest_set_include,vcl_gradient,\ + $$(INCLUDE) \ + -I$(SRCDIR)/vcl/inc \ + -I$(SRCDIR)/vcl/source/window \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,vcl_gradient, \ + vcl/qa/cppunit/gradient \ +)) + +$(eval $(call gb_CppunitTest_use_externals,vcl_gradient,boost_headers)) + +$(eval $(call gb_CppunitTest_use_libraries,vcl_gradient, \ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + sal \ + svt \ + test \ + tl \ + unotest \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,vcl_gradient)) + +$(eval $(call gb_CppunitTest_use_ure,vcl_gradient)) +$(eval $(call gb_CppunitTest_use_vcl,vcl_gradient)) + +$(eval $(call gb_CppunitTest_use_components,vcl_gradient,\ + configmgr/source/configmgr \ + i18npool/util/i18npool \ + ucb/source/core/ucb1 \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,vcl_gradient)) + +# vim: set noet sw=4 ts=4: diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk index 22536d89af42..1c3c5902c7d4 100644 --- a/vcl/Module_vcl.mk +++ b/vcl/Module_vcl.mk @@ -198,6 +198,7 @@ $(eval $(call gb_Module_add_check_targets,vcl,\ CppunitTest_vcl_filters_test \ CppunitTest_vcl_mnemonic \ CppunitTest_vcl_outdev \ + CppunitTest_vcl_gradient \ CppunitTest_vcl_app_test \ CppunitTest_vcl_jpeg_read_write_test \ CppunitTest_vcl_svm_test \ diff --git a/vcl/qa/cppunit/gradient.cxx b/vcl/qa/cppunit/gradient.cxx new file mode 100644 index 000000000000..474c619bef72 --- /dev/null +++ b/vcl/qa/cppunit/gradient.cxx @@ -0,0 +1,256 @@ +/* -*- 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 <test/bootstrapfixture.hxx> +#include <test/outputdevice.hxx> + +#include <sal/log.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/vector/b2enums.hxx> + +#include <vcl/gradient.hxx> +#include <vcl/lineinfo.hxx> +#include <vcl/print.hxx> +#include <vcl/rendercontext/RasterOp.hxx> +#include <vcl/virdev.hxx> +#include <vcl/window.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/metaact.hxx> + +#include <bitmap/BitmapWriteAccess.hxx> +#include <bufferdevice.hxx> +#include <window.h> + +class VclGradientTest : public test::BootstrapFixture +{ +public: + VclGradientTest() + : BootstrapFixture(true, false) + { + } + + void testAddGradientActions_rect_linear(); + void testAddGradientActions_rect_axial(); + void testAddGradientActions_rect_complex(); + + CPPUNIT_TEST_SUITE(VclGradientTest); + CPPUNIT_TEST(testAddGradientActions_rect_linear); + CPPUNIT_TEST(testAddGradientActions_rect_axial); + CPPUNIT_TEST(testAddGradientActions_rect_complex); + CPPUNIT_TEST_SUITE_END(); +}; + +static size_t TestLinearStripes(GDIMetaFile& rMtf, size_t nTimes, size_t nIndex) +{ + nIndex++; + MetaAction* pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action (start)", MetaActionType::FILLCOLOR, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action (start)", MetaActionType::POLYGON, + pAction->GetType()); + + for (size_t i = 0; i < nTimes - 1; i++) + { + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, + pAction->GetType()); + } + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action (end)", MetaActionType::FILLCOLOR, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action (end)", MetaActionType::POLYGON, + pAction->GetType()); + + return nIndex; +} + +void VclGradientTest::testAddGradientActions_rect_linear() +{ + GDIMetaFile aMtf; + tools::Rectangle aRect(Point(10, 10), Size(40, 40)); + Gradient aGradient(GradientStyle::Linear, COL_RED, COL_WHITE); + aGradient.SetBorder(100); + + aGradient.AddGradientActions(aRect, aMtf); + + size_t nIndex = 0; + + MetaAction* pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a push action", MetaActionType::PUSH, pAction->GetType()); + + nIndex++; + pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a rectangular intersect clip action", + MetaActionType::ISECTRECTCLIPREGION, pAction->GetType()); + + nIndex++; + pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a line color action", MetaActionType::LINECOLOR, + pAction->GetType()); + + TestLinearStripes(aMtf, 3, nIndex); +} + +static size_t TestAxialStripes(GDIMetaFile& rMtf, size_t nTimes, size_t nIndex) +{ + nIndex++; + MetaAction* pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, + pAction->GetType()); + + for (size_t i = 0; i < nTimes - 1; i++) + { + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, + pAction->GetType()); + } + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, + pAction->GetType()); + + return nIndex; +} + +void VclGradientTest::testAddGradientActions_rect_axial() +{ + GDIMetaFile aMtf; + tools::Rectangle aRect(Point(10, 10), Size(40, 40)); + Gradient aGradient(GradientStyle::Axial, COL_RED, COL_WHITE); + aGradient.SetBorder(100); + + aGradient.AddGradientActions(aRect, aMtf); + + size_t nIndex = 0; + + MetaAction* pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a push action", MetaActionType::PUSH, pAction->GetType()); + + nIndex++; + pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a rectangular intersect clip action", + MetaActionType::ISECTRECTCLIPREGION, pAction->GetType()); + + nIndex++; + pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a line color action", MetaActionType::LINECOLOR, + pAction->GetType()); + + TestAxialStripes(aMtf, 3, nIndex); +} + +static size_t TestComplexStripes(GDIMetaFile& rMtf, size_t nTimes, size_t nIndex) +{ + nIndex++; + MetaAction* pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, + pAction->GetType()); + + for (size_t i = 1; i < nTimes; i++) + { + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polypolygon action", MetaActionType::POLYPOLYGON, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, + pAction->GetType()); + } + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, + pAction->GetType()); + + nIndex++; + pAction = rMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polypolygon action", MetaActionType::POLYGON, + pAction->GetType()); + + return nIndex; +} + +void VclGradientTest::testAddGradientActions_rect_complex() +{ + GDIMetaFile aMtf; + tools::Rectangle aRect(Point(10, 10), Size(40, 40)); + Gradient aGradient(GradientStyle::Square, COL_RED, COL_WHITE); + aGradient.SetBorder(10); + + aGradient.AddGradientActions(aRect, aMtf); + + size_t nIndex = 0; + + MetaAction* pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a push action", MetaActionType::PUSH, pAction->GetType()); + + nIndex++; + pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a rectangular intersect clip action", + MetaActionType::ISECTRECTCLIPREGION, pAction->GetType()); + + nIndex++; + pAction = aMtf.GetAction(nIndex); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a line color action", MetaActionType::LINECOLOR, + pAction->GetType()); + + TestComplexStripes(aMtf, 40, nIndex); +} + +CPPUNIT_TEST_SUITE_REGISTRATION(VclGradientTest); + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qa/cppunit/outdev.cxx b/vcl/qa/cppunit/outdev.cxx index dc2ea8ad2ed2..79fe71431f87 100644 --- a/vcl/qa/cppunit/outdev.cxx +++ b/vcl/qa/cppunit/outdev.cxx @@ -94,12 +94,9 @@ public: void testDrawGradient_drawmode(); void testDrawGradient_rect_linear(); void testDrawGradient_rect_axial(); - void testAddGradientActions_rect_linear(); - void testAddGradientActions_rect_axial(); void testDrawGradient_polygon_linear(); void testDrawGradient_polygon_axial(); void testDrawGradient_rect_complex(); - void testAddGradientActions_rect_complex(); CPPUNIT_TEST_SUITE(VclOutdevTest); CPPUNIT_TEST(testVirtualDevice); @@ -156,12 +153,9 @@ public: CPPUNIT_TEST(testDrawGradient_drawmode); CPPUNIT_TEST(testDrawGradient_rect_linear); CPPUNIT_TEST(testDrawGradient_rect_axial); - CPPUNIT_TEST(testAddGradientActions_rect_linear); - CPPUNIT_TEST(testAddGradientActions_rect_axial); CPPUNIT_TEST(testDrawGradient_polygon_linear); CPPUNIT_TEST(testDrawGradient_polygon_axial); CPPUNIT_TEST(testDrawGradient_rect_complex); - CPPUNIT_TEST(testAddGradientActions_rect_complex); CPPUNIT_TEST_SUITE_END(); }; @@ -2138,92 +2132,6 @@ void VclOutdevTest::testDrawGradient_drawmode() MetaActionType::POP, pAction->GetType()); } -static size_t TestLinearStripes(GDIMetaFile& rMtf, size_t nTimes, size_t nIndex) -{ - nIndex++; - MetaAction* pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action (start)", MetaActionType::FILLCOLOR, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action (start)", MetaActionType::POLYGON, - pAction->GetType()); - - for (size_t i = 0; i < nTimes - 1; i++) - { - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, - pAction->GetType()); - } - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action (end)", MetaActionType::FILLCOLOR, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action (end)", MetaActionType::POLYGON, - pAction->GetType()); - - return nIndex; -} - -static size_t TestAxialStripes(GDIMetaFile& rMtf, size_t nTimes, size_t nIndex) -{ - nIndex++; - MetaAction* pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, - pAction->GetType()); - - for (size_t i = 0; i < nTimes - 1; i++) - { - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, - pAction->GetType()); - } - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polygon action", MetaActionType::POLYGON, - pAction->GetType()); - - return nIndex; -} - void VclOutdevTest::testDrawGradient_rect_linear() { ScopedVclPtrInstance<VirtualDevice> pVDev; @@ -2245,37 +2153,6 @@ void VclOutdevTest::testDrawGradient_rect_linear() pAction->GetType()); } -void VclOutdevTest::testAddGradientActions_rect_linear() -{ - ScopedVclPtrInstance<VirtualDevice> pVDev; - GDIMetaFile aMtf; - - tools::Rectangle aRect(Point(10, 10), Size(40, 40)); - pVDev->SetOutputSizePixel(Size(100, 100)); - - Gradient aGradient(GradientStyle::Linear, COL_RED, COL_WHITE); - aGradient.SetBorder(100); - - pVDev->AddGradientActions(aRect, aGradient, aMtf); - - size_t nIndex = 0; - - MetaAction* pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a push action", MetaActionType::PUSH, pAction->GetType()); - - nIndex++; - pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a rectangular intersect clip action", - MetaActionType::ISECTRECTCLIPREGION, pAction->GetType()); - - nIndex++; - pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a line color action", MetaActionType::LINECOLOR, - pAction->GetType()); - - TestLinearStripes(aMtf, 3, nIndex); -} - void VclOutdevTest::testDrawGradient_rect_axial() { ScopedVclPtrInstance<VirtualDevice> pVDev; @@ -2297,37 +2174,6 @@ void VclOutdevTest::testDrawGradient_rect_axial() pAction->GetType()); } -void VclOutdevTest::testAddGradientActions_rect_axial() -{ - ScopedVclPtrInstance<VirtualDevice> pVDev; - GDIMetaFile aMtf; - - tools::Rectangle aRect(Point(10, 10), Size(40, 40)); - pVDev->SetOutputSizePixel(Size(100, 100)); - - Gradient aGradient(GradientStyle::Axial, COL_RED, COL_WHITE); - aGradient.SetBorder(100); - - pVDev->AddGradientActions(aRect, aGradient, aMtf); - - size_t nIndex = 0; - - MetaAction* pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a push action", MetaActionType::PUSH, pAction->GetType()); - - nIndex++; - pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a rectangular intersect clip action", - MetaActionType::ISECTRECTCLIPREGION, pAction->GetType()); - - nIndex++; - pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a line color action", MetaActionType::LINECOLOR, - pAction->GetType()); - - TestAxialStripes(aMtf, 3, nIndex); -} - void VclOutdevTest::testDrawGradient_polygon_linear() { ScopedVclPtrInstance<VirtualDevice> pVDev; @@ -2364,39 +2210,6 @@ void VclOutdevTest::testDrawGradient_polygon_axial() ClipGradientTest(aMtf, INITIAL_SETUP_ACTION_COUNT); } -static size_t TestComplexStripes(GDIMetaFile& rMtf, size_t nTimes, size_t nIndex) -{ - nIndex++; - MetaAction* pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, - pAction->GetType()); - - for (size_t i = 1; i < nTimes; i++) - { - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polypolygon action", MetaActionType::POLYPOLYGON, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, - pAction->GetType()); - } - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a fill color action", MetaActionType::FILLCOLOR, - pAction->GetType()); - - nIndex++; - pAction = rMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a polypolygon action", MetaActionType::POLYGON, - pAction->GetType()); - - return nIndex; -} - void VclOutdevTest::testDrawGradient_rect_complex() { ScopedVclPtrInstance<VirtualDevice> pVDev; @@ -2417,37 +2230,6 @@ void VclOutdevTest::testDrawGradient_rect_complex() pAction->GetType()); } -void VclOutdevTest::testAddGradientActions_rect_complex() -{ - ScopedVclPtrInstance<VirtualDevice> pVDev; - GDIMetaFile aMtf; - - tools::Rectangle aRect(Point(10, 10), Size(40, 40)); - pVDev->SetOutputSizePixel(Size(1000, 1000)); - - Gradient aGradient(GradientStyle::Square, COL_RED, COL_WHITE); - aGradient.SetBorder(10); - - pVDev->AddGradientActions(aRect, aGradient, aMtf); - - size_t nIndex = 0; - - MetaAction* pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a push action", MetaActionType::PUSH, pAction->GetType()); - - nIndex++; - pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a rectangular intersect clip action", - MetaActionType::ISECTRECTCLIPREGION, pAction->GetType()); - - nIndex++; - pAction = aMtf.GetAction(nIndex); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Not a line color action", MetaActionType::LINECOLOR, - pAction->GetType()); - - TestComplexStripes(aMtf, 40, nIndex); -} - CPPUNIT_TEST_SUITE_REGISTRATION(VclOutdevTest); CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/source/filter/eps/eps.cxx b/vcl/source/filter/eps/eps.cxx index ced4d9be1a9e..1a73c682adf1 100644 --- a/vcl/source/filter/eps/eps.cxx +++ b/vcl/source/filter/eps/eps.cxx @@ -1515,7 +1515,8 @@ void PSWriter::ImplWriteGradient( const tools::PolyPolygon& rPolyPoly, const Gra ScopedVclPtrInstance< VirtualDevice > l_pVDev; GDIMetaFile aTmpMtf; l_pVDev->SetMapMode( rVDev.GetMapMode() ); - l_pVDev->AddGradientActions( rPolyPoly.GetBoundRect(), rGradient, aTmpMtf ); + Gradient aGradient(rGradient); + aGradient.AddGradientActions( rPolyPoly.GetBoundRect(), aTmpMtf ); ImplWriteActions( aTmpMtf, rVDev ); } diff --git a/vcl/source/filter/wmf/emfwr.cxx b/vcl/source/filter/wmf/emfwr.cxx index 8f545a7b07a0..b2782847b1b1 100644 --- a/vcl/source/filter/wmf/emfwr.cxx +++ b/vcl/source/filter/wmf/emfwr.cxx @@ -1170,7 +1170,8 @@ void EMFWriter::ImplWrite( const GDIMetaFile& rMtf ) const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction); GDIMetaFile aTmpMtf; - maVDev->AddGradientActions( pA->GetRect(), pA->GetGradient(), aTmpMtf ); + Gradient aGradient = pA->GetGradient(); + aGradient.AddGradientActions( pA->GetRect(), aTmpMtf ); ImplWrite( aTmpMtf ); } break; diff --git a/vcl/source/filter/wmf/wmfwr.cxx b/vcl/source/filter/wmf/wmfwr.cxx index 6abd9f95d490..22230ba4666e 100644 --- a/vcl/source/filter/wmf/wmfwr.cxx +++ b/vcl/source/filter/wmf/wmfwr.cxx @@ -1302,7 +1302,8 @@ void WMFWriter::WriteRecords( const GDIMetaFile & rMTF ) const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pMA); GDIMetaFile aTmpMtf; - pVirDev->AddGradientActions( pA->GetRect(), pA->GetGradient(), aTmpMtf ); + Gradient aGradient = pA->GetGradient(); + aGradient.AddGradientActions( pA->GetRect(), aTmpMtf ); WriteRecords( aTmpMtf ); } break; diff --git a/vcl/source/gdi/gradient.cxx b/vcl/source/gdi/gradient.cxx index b94223a427c4..cf751abffa97 100644 --- a/vcl/source/gdi/gradient.cxx +++ b/vcl/source/gdi/gradient.cxx @@ -18,7 +18,9 @@ */ #include <tools/gen.hxx> + #include <vcl/gradient.hxx> +#include <vcl/metaact.hxx> class Gradient::Impl { @@ -295,4 +297,384 @@ bool Gradient::operator==( const Gradient& rGradient ) const return mpImplGradient == rGradient.mpImplGradient; } +const sal_uInt32 GRADIENT_DEFAULT_STEPCOUNT = 0; + +void Gradient::AddGradientActions(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) +{ + tools::Rectangle aRect(rRect); + aRect.Justify(); + + // do nothing if the rectangle is empty + if (aRect.IsEmpty()) + return; + + rMetaFile.AddAction(new MetaPushAction(vcl::PushFlags::ALL)); + rMetaFile.AddAction(new MetaISectRectClipRegionAction( aRect)); + rMetaFile.AddAction(new MetaLineColorAction(Color(), false)); + + // because we draw with no border line, we have to expand gradient + // rect to avoid missing lines on the right and bottom edge + aRect.AdjustLeft( -1 ); + aRect.AdjustTop( -1 ); + aRect.AdjustRight( 1 ); + aRect.AdjustBottom( 1 ); + + // calculate step count if necessary + if (!GetSteps()) + SetSteps(GRADIENT_DEFAULT_STEPCOUNT); + + if (GetStyle() == GradientStyle::Linear || GetStyle() == GradientStyle::Axial) + DrawLinearGradientToMetafile(aRect, rMetaFile); + else + DrawComplexGradientToMetafile(aRect, rMetaFile); + + rMetaFile.AddAction(new MetaPopAction()); +} + +tools::Long Gradient::GetMetafileSteps(tools::Rectangle const& rRect) const +{ + // calculate step count + tools::Long nStepCount = GetSteps(); + + if (nStepCount) + return nStepCount; + + if (GetStyle() == GradientStyle::Linear || GetStyle() == GradientStyle::Axial) + return rRect.GetHeight(); + else + return std::min(rRect.GetWidth(), rRect.GetHeight()); +} + + +static sal_uInt8 GetGradientColorValue(tools::Long nValue) +{ + if ( nValue < 0 ) + return 0; + else if ( nValue > 0xFF ) + return 0xFF; + else + return static_cast<sal_uInt8>(nValue); +} + +void Gradient::DrawLinearGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const +{ + // get BoundRect of rotated rectangle + tools::Rectangle aRect; + Point aCenter; + Degree10 nAngle = GetAngle() % 3600_deg10; + + GetBoundRect(rRect, aRect, aCenter); + + bool bLinear = (GetStyle() == GradientStyle::Linear); + double fBorder = GetBorder() * aRect.GetHeight() / 100.0; + if ( !bLinear ) + { + fBorder /= 2.0; + } + tools::Rectangle aMirrorRect = aRect; // used in style axial + aMirrorRect.SetTop( ( aRect.Top() + aRect.Bottom() ) / 2 ); + if ( !bLinear ) + { + aRect.SetBottom( aMirrorRect.Top() ); + } + + // colour-intensities of start- and finish; change if needed + tools::Long nFactor; + Color aStartCol = GetStartColor(); + Color aEndCol = GetEndColor(); + tools::Long nStartRed = aStartCol.GetRed(); + tools::Long nStartGreen = aStartCol.GetGreen(); + tools::Long nStartBlue = aStartCol.GetBlue(); + tools::Long nEndRed = aEndCol.GetRed(); + tools::Long nEndGreen = aEndCol.GetGreen(); + tools::Long nEndBlue = aEndCol.GetBlue(); + nFactor = GetStartIntensity(); + nStartRed = (nStartRed * nFactor) / 100; + nStartGreen = (nStartGreen * nFactor) / 100; + nStartBlue = (nStartBlue * nFactor) / 100; + nFactor = GetEndIntensity(); + nEndRed = (nEndRed * nFactor) / 100; + nEndGreen = (nEndGreen * nFactor) / 100; + nEndBlue = (nEndBlue * nFactor) / 100; + + // gradient style axial has exchanged start and end colors + if ( !bLinear) + { + tools::Long nTempColor = nStartRed; + nStartRed = nEndRed; + nEndRed = nTempColor; + nTempColor = nStartGreen; + nStartGreen = nEndGreen; + nEndGreen = nTempColor; + nTempColor = nStartBlue; + nStartBlue = nEndBlue; + nEndBlue = nTempColor; + } + + sal_uInt8 nRed; + sal_uInt8 nGreen; + sal_uInt8 nBlue; + + // Create border + tools::Rectangle aBorderRect = aRect; + tools::Polygon aPoly( 4 ); + if (fBorder > 0.0) + { + nRed = static_cast<sal_uInt8>(nStartRed); + nGreen = static_cast<sal_uInt8>(nStartGreen); + nBlue = static_cast<sal_uInt8>(nStartBlue); + + rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); + + aBorderRect.SetBottom( static_cast<tools::Long>( aBorderRect.Top() + fBorder ) ); + aRect.SetTop( aBorderRect.Bottom() ); + aPoly[0] = aBorderRect.TopLeft(); + aPoly[1] = aBorderRect.TopRight(); + aPoly[2] = aBorderRect.BottomRight(); + aPoly[3] = aBorderRect.BottomLeft(); + aPoly.Rotate( aCenter, nAngle ); + + rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); + + if ( !bLinear) + { + aBorderRect = aMirrorRect; + aBorderRect.SetTop( static_cast<tools::Long>( aBorderRect.Bottom() - fBorder ) ); + aMirrorRect.SetBottom( aBorderRect.Top() ); + aPoly[0] = aBorderRect.TopLeft(); + aPoly[1] = aBorderRect.TopRight(); + aPoly[2] = aBorderRect.BottomRight(); + aPoly[3] = aBorderRect.BottomLeft(); + aPoly.Rotate( aCenter, nAngle ); + + rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); + } + } + + tools::Long nStepCount = GetMetafileSteps(aRect); + + // minimal three steps and maximal as max color steps + tools::Long nAbsRedSteps = std::abs( nEndRed - nStartRed ); + tools::Long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen ); + tools::Long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue ); + tools::Long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps ); + nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps ); + tools::Long nSteps = std::min( nStepCount, nMaxColorSteps ); + if ( nSteps < 3) + { + nSteps = 3; + } + + double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps); + double fGradientLine = static_cast<double>(aRect.Top()); + double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom()); + + const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0; + if ( !bLinear) + { + nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap + } + for ( tools::Long i = 0; i < nSteps; i++ ) + { + // linear interpolation of color + double fAlpha = static_cast<double>(i) / fStepsMinus1; + double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha; + nRed = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); + fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha; + nGreen = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); + fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha; + nBlue = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); + + rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); + + // Polygon for this color step + aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(i) * fScanInc ) ); + aRect.SetBottom( static_cast<tools::Long>( fGradientLine + ( static_cast<double>(i) + 1.0 ) * fScanInc ) ); + aPoly[0] = aRect.TopLeft(); + aPoly[1] = aRect.TopRight(); + aPoly[2] = aRect.BottomRight(); + aPoly[3] = aRect.BottomLeft(); + aPoly.Rotate( aCenter, nAngle ); + + rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); + + if ( !bLinear ) + { + aMirrorRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) ); + aMirrorRect.SetTop( static_cast<tools::Long>( fMirrorGradientLine - (static_cast<double>(i) + 1.0)* fScanInc ) ); + aPoly[0] = aMirrorRect.TopLeft(); + aPoly[1] = aMirrorRect.TopRight(); + aPoly[2] = aMirrorRect.BottomRight(); + aPoly[3] = aMirrorRect.BottomLeft(); + aPoly.Rotate( aCenter, nAngle ); + + rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); + } + } + if ( bLinear) + return; + + // draw middle polygon with end color + nRed = GetGradientColorValue(nEndRed); + nGreen = GetGradientColorValue(nEndGreen); + nBlue = GetGradientColorValue(nEndBlue); + + rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); + + aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) ); + aRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) ); + aPoly[0] = aRect.TopLeft(); + aPoly[1] = aRect.TopRight(); + aPoly[2] = aRect.BottomRight(); + aPoly[3] = aRect.BottomLeft(); + aPoly.Rotate( aCenter, nAngle ); + + rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); + +} + +void Gradient::DrawComplexGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const +{ + // Determine if we output via Polygon or PolyPolygon + // For all rasteroperations other than Overpaint always use PolyPolygon, + // as we will get wrong results if we output multiple times on top of each other. + // Also for printers always use PolyPolygon, as not all printers + // can print polygons on top of each other. + + tools::Rectangle aRect; + Point aCenter; + GetBoundRect(rRect, aRect, aCenter); + + std::optional<tools::PolyPolygon> xPolyPoly; + xPolyPoly = tools::PolyPolygon( 2 ); + + // last parameter - true if complex gradient, false if linear + tools::Long nStepCount = GetMetafileSteps(rRect); + + // at least three steps and at most the number of colour differences + tools::Long nSteps = std::max(nStepCount, tools::Long(2)); + + Color aStartCol(GetStartColor()); + Color aEndCol(GetEndColor()); + + tools::Long nStartRed = (static_cast<tools::Long>(aStartCol.GetRed()) * GetStartIntensity()) / 100; + tools::Long nStartGreen = (static_cast<tools::Long>(aStartCol.GetGreen()) * GetStartIntensity()) / 100; + tools::Long nStartBlue = (static_cast<tools::Long>(aStartCol.GetBlue()) * GetStartIntensity()) / 100; + + tools::Long nEndRed = (static_cast<tools::Long>(aEndCol.GetRed()) * GetEndIntensity()) / 100; + tools::Long nEndGreen = (static_cast<tools::Long>(aEndCol.GetGreen()) * GetEndIntensity()) / 100; + tools::Long nEndBlue = (static_cast<tools::Long>(aEndCol.GetBlue()) * GetEndIntensity()) / 100; + + tools::Long nRedSteps = nEndRed - nStartRed; + tools::Long nGreenSteps = nEndGreen - nStartGreen; + tools::Long nBlueSteps = nEndBlue - nStartBlue; + + tools::Long nCalcSteps = std::abs(nRedSteps); + tools::Long nTempSteps = std::abs(nGreenSteps); + + if (nTempSteps > nCalcSteps) + nCalcSteps = nTempSteps; + + nTempSteps = std::abs( nBlueSteps ); + + if (nTempSteps > nCalcSteps) + nCalcSteps = nTempSteps; + + if (nCalcSteps < nSteps) + nSteps = nCalcSteps; + + if ( !nSteps ) + nSteps = 1; + + // determine output limits and stepsizes for all directions + tools::Polygon aPoly; + double fScanLeft = aRect.Left(); + double fScanTop = aRect.Top(); + double fScanRight = aRect.Right(); + double fScanBottom = aRect.Bottom(); + double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5; + double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps) * 0.5; + + // all gradients are rendered as nested rectangles which shrink + // equally in each dimension - except for 'square' gradients + // which shrink to a central vertex but are not per-se square. + if (GetStyle() != GradientStyle::Square) + { + fScanIncY = std::min( fScanIncY, fScanIncX ); + fScanIncX = fScanIncY; + } + sal_uInt8 nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue); + bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output + + rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); + + aPoly = rRect; + xPolyPoly->Insert( aPoly ); + xPolyPoly->Insert( aPoly ); + + // loop to output Polygon/PolyPolygon sequentially + for( tools::Long i = 1; i < nSteps; i++ ) + { + // calculate new Polygon + fScanLeft += fScanIncX; + aRect.SetLeft( static_cast<tools::Long>( fScanLeft ) ); + fScanTop += fScanIncY; + aRect.SetTop( static_cast<tools::Long>( fScanTop ) ); + fScanRight -= fScanIncX; + aRect.SetRight( static_cast<tools::Long>( fScanRight ) ); + fScanBottom -= fScanIncY; + aRect.SetBottom( static_cast<tools::Long>( fScanBottom ) ); + + if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) ) + break; + + if (GetStyle() == GradientStyle::Radial || GetStyle() == GradientStyle::Elliptical) + aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 ); + else + aPoly = tools::Polygon( aRect ); + + aPoly.Rotate(aCenter, GetAngle() % 3600_deg10); + + // adapt colour accordingly + const tools::Long nStepIndex = ( xPolyPoly ? i : ( i + 1 ) ); + nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) ); + nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) ); + nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) ); + + bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output + + xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 ); + xPolyPoly->Replace( aPoly, 1 ); + + rMetaFile.AddAction( new MetaPolyPolygonAction( *xPolyPoly ) ); + + // #107349# Set fill color _after_ geometry painting: + // xPolyPoly's geometry is the band from last iteration's + // aPoly to current iteration's aPoly. The window outdev + // path (see else below), on the other hand, paints the + // full aPoly. Thus, here, we're painting the band before + // the one painted in the window outdev path below. To get + // matching colors, have to delay color setting here. + rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); + } + + const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 ); + + if( rPoly.GetBoundRect().IsEmpty() ) + return; + + // #107349# Paint last polygon with end color only if loop + // has generated output. Otherwise, the current + // (i.e. start) color is taken, to generate _any_ output. + if( bPaintLastPolygon ) + { + nRed = GetGradientColorValue( nEndRed ); + nGreen = GetGradientColorValue( nEndGreen ); + nBlue = GetGradientColorValue( nEndBlue ); + } + + rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); + rMetaFile.AddAction( new MetaPolygonAction( rPoly ) ); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx b/vcl/source/gdi/pdfwriter_impl2.cxx index e472dbd52cef..69ac8708f4f1 100644 --- a/vcl/source/gdi/pdfwriter_impl2.cxx +++ b/vcl/source/gdi/pdfwriter_impl2.cxx @@ -60,8 +60,9 @@ void PDFWriterImpl::implWriteGradient( const tools::PolyPolygon& i_rPolyPoly, co VirtualDevice* i_pDummyVDev, const vcl::PDFWriter::PlayMetafileContext& i_rContext ) { GDIMetaFile aTmpMtf; + Gradient aGradient(i_rGradient); - i_pDummyVDev->AddGradientActions( i_rPolyPoly.GetBoundRect(), i_rGradient, aTmpMtf ); + aGradient.AddGradientActions( i_rPolyPoly.GetBoundRect(), aTmpMtf ); m_rOuterFace.Push(); m_rOuterFace.IntersectClipRegion( i_rPolyPoly.getB2DPolyPolygon() ); diff --git a/vcl/source/outdev/gradient.cxx b/vcl/source/outdev/gradient.cxx index 9d8e6d8b1830..eb2fd4645323 100644 --- a/vcl/source/outdev/gradient.cxx +++ b/vcl/source/outdev/gradient.cxx @@ -581,347 +581,6 @@ void OutputDevice::DrawComplexGradient( const tools::Rectangle& rRect, ImplDrawPolygon( rPoly, pClixPolyPoly ); } -static tools::Long GetGradientMetafileSteps(Gradient const& rGradient, tools::Rectangle const& rRect) -{ - // calculate step count - tools::Long nStepCount = rGradient.GetSteps(); - - if (nStepCount) - return nStepCount; - - if (rGradient.GetStyle() == GradientStyle::Linear || rGradient.GetStyle() == GradientStyle::Axial) - return rRect.GetHeight(); - else - return std::min(rRect.GetWidth(), rRect.GetHeight()); -} - -void OutputDevice::DrawLinearGradientToMetafile( const tools::Rectangle& rRect, - const Gradient& rGradient ) -{ - assert(!is_double_buffered_window()); - - // get BoundRect of rotated rectangle - tools::Rectangle aRect; - Point aCenter; - Degree10 nAngle = rGradient.GetAngle() % 3600_deg10; - - rGradient.GetBoundRect( rRect, aRect, aCenter ); - - bool bLinear = (rGradient.GetStyle() == GradientStyle::Linear); - double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0; - if ( !bLinear ) - { - fBorder /= 2.0; - } - tools::Rectangle aMirrorRect = aRect; // used in style axial - aMirrorRect.SetTop( ( aRect.Top() + aRect.Bottom() ) / 2 ); - if ( !bLinear ) - { - aRect.SetBottom( aMirrorRect.Top() ); - } - - // colour-intensities of start- and finish; change if needed - tools::Long nFactor; - Color aStartCol = rGradient.GetStartColor(); - Color aEndCol = rGradient.GetEndColor(); - tools::Long nStartRed = aStartCol.GetRed(); - tools::Long nStartGreen = aStartCol.GetGreen(); - tools::Long nStartBlue = aStartCol.GetBlue(); - tools::Long nEndRed = aEndCol.GetRed(); - tools::Long nEndGreen = aEndCol.GetGreen(); - tools::Long nEndBlue = aEndCol.GetBlue(); - nFactor = rGradient.GetStartIntensity(); - nStartRed = (nStartRed * nFactor) / 100; - nStartGreen = (nStartGreen * nFactor) / 100; - nStartBlue = (nStartBlue * nFactor) / 100; - nFactor = rGradient.GetEndIntensity(); - nEndRed = (nEndRed * nFactor) / 100; - nEndGreen = (nEndGreen * nFactor) / 100; - nEndBlue = (nEndBlue * nFactor) / 100; - - // gradient style axial has exchanged start and end colors - if ( !bLinear) - { - tools::Long nTempColor = nStartRed; - nStartRed = nEndRed; - nEndRed = nTempColor; - nTempColor = nStartGreen; - nStartGreen = nEndGreen; - nEndGreen = nTempColor; - nTempColor = nStartBlue; - nStartBlue = nEndBlue; - nEndBlue = nTempColor; - } - - sal_uInt8 nRed; - sal_uInt8 nGreen; - sal_uInt8 nBlue; - - // Create border - tools::Rectangle aBorderRect = aRect; - tools::Polygon aPoly( 4 ); - if (fBorder > 0.0) - { - nRed = static_cast<sal_uInt8>(nStartRed); - nGreen = static_cast<sal_uInt8>(nStartGreen); - nBlue = static_cast<sal_uInt8>(nStartBlue); - - mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); - - aBorderRect.SetBottom( static_cast<tools::Long>( aBorderRect.Top() + fBorder ) ); - aRect.SetTop( aBorderRect.Bottom() ); - aPoly[0] = aBorderRect.TopLeft(); - aPoly[1] = aBorderRect.TopRight(); - aPoly[2] = aBorderRect.BottomRight(); - aPoly[3] = aBorderRect.BottomLeft(); - aPoly.Rotate( aCenter, nAngle ); - - mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) ); - - if ( !bLinear) - { - aBorderRect = aMirrorRect; - aBorderRect.SetTop( static_cast<tools::Long>( aBorderRect.Bottom() - fBorder ) ); - aMirrorRect.SetBottom( aBorderRect.Top() ); - aPoly[0] = aBorderRect.TopLeft(); - aPoly[1] = aBorderRect.TopRight(); - aPoly[2] = aBorderRect.BottomRight(); - aPoly[3] = aBorderRect.BottomLeft(); - aPoly.Rotate( aCenter, nAngle ); - - mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) ); - } - } - - tools::Long nStepCount = GetGradientMetafileSteps(rGradient, aRect); - - // minimal three steps and maximal as max color steps - tools::Long nAbsRedSteps = std::abs( nEndRed - nStartRed ); - tools::Long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen ); - tools::Long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue ); - tools::Long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps ); - nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps ); - tools::Long nSteps = std::min( nStepCount, nMaxColorSteps ); - if ( nSteps < 3) - { - nSteps = 3; - } - - double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps); - double fGradientLine = static_cast<double>(aRect.Top()); - double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom()); - - const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0; - if ( !bLinear) - { - nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap - } - for ( tools::Long i = 0; i < nSteps; i++ ) - { - // linear interpolation of color - double fAlpha = static_cast<double>(i) / fStepsMinus1; - double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha; - nRed = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); - fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha; - nGreen = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); - fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha; - nBlue = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); - - mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); - - // Polygon for this color step - aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(i) * fScanInc ) ); - aRect.SetBottom( static_cast<tools::Long>( fGradientLine + ( static_cast<double>(i) + 1.0 ) * fScanInc ) ); - aPoly[0] = aRect.TopLeft(); - aPoly[1] = aRect.TopRight(); - aPoly[2] = aRect.BottomRight(); - aPoly[3] = aRect.BottomLeft(); - aPoly.Rotate( aCenter, nAngle ); - - mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) ); - - if ( !bLinear ) - { - aMirrorRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) ); - aMirrorRect.SetTop( static_cast<tools::Long>( fMirrorGradientLine - (static_cast<double>(i) + 1.0)* fScanInc ) ); - aPoly[0] = aMirrorRect.TopLeft(); - aPoly[1] = aMirrorRect.TopRight(); - aPoly[2] = aMirrorRect.BottomRight(); - aPoly[3] = aMirrorRect.BottomLeft(); - aPoly.Rotate( aCenter, nAngle ); - - mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) ); - } - } - if ( bLinear) - return; - - // draw middle polygon with end color - nRed = GetGradientColorValue(nEndRed); - nGreen = GetGradientColorValue(nEndGreen); - nBlue = GetGradientColorValue(nEndBlue); - - mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); - - aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) ); - aRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) ); - aPoly[0] = aRect.TopLeft(); - aPoly[1] = aRect.TopRight(); - aPoly[2] = aRect.BottomRight(); - aPoly[3] = aRect.BottomLeft(); - aPoly.Rotate( aCenter, nAngle ); - - mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) ); - -} - -void OutputDevice::DrawComplexGradientToMetafile( const tools::Rectangle& rRect, - const Gradient& rGradient ) -{ - assert(!is_double_buffered_window()); - - // Determine if we output via Polygon or PolyPolygon - // For all rasteroperations other than Overpaint always use PolyPolygon, - // as we will get wrong results if we output multiple times on top of each other. - // Also for printers always use PolyPolygon, as not all printers - // can print polygons on top of each other. - - tools::Rectangle aRect; - Point aCenter; - rGradient.GetBoundRect(rRect, aRect, aCenter); - - std::optional<tools::PolyPolygon> xPolyPoly; - xPolyPoly = tools::PolyPolygon( 2 ); - - // last parameter - true if complex gradient, false if linear - tools::Long nStepCount = GetGradientMetafileSteps(rGradient, rRect); - - // at least three steps and at most the number of colour differences - tools::Long nSteps = std::max(nStepCount, tools::Long(2)); - - Color aStartCol(rGradient.GetStartColor()); - Color aEndCol(rGradient.GetEndColor()); - - tools::Long nStartRed = (static_cast<tools::Long>(aStartCol.GetRed()) * rGradient.GetStartIntensity()) / 100; - tools::Long nStartGreen = (static_cast<tools::Long>(aStartCol.GetGreen()) * rGradient.GetStartIntensity()) / 100; - tools::Long nStartBlue = (static_cast<tools::Long>(aStartCol.GetBlue()) * rGradient.GetStartIntensity()) / 100; - - tools::Long nEndRed = (static_cast<tools::Long>(aEndCol.GetRed()) * rGradient.GetEndIntensity()) / 100; - tools::Long nEndGreen = (static_cast<tools::Long>(aEndCol.GetGreen()) * rGradient.GetEndIntensity()) / 100; - tools::Long nEndBlue = (static_cast<tools::Long>(aEndCol.GetBlue()) * rGradient.GetEndIntensity()) / 100; - - tools::Long nRedSteps = nEndRed - nStartRed; - tools::Long nGreenSteps = nEndGreen - nStartGreen; - tools::Long nBlueSteps = nEndBlue - nStartBlue; - - tools::Long nCalcSteps = std::abs(nRedSteps); - tools::Long nTempSteps = std::abs(nGreenSteps); - - if (nTempSteps > nCalcSteps) - nCalcSteps = nTempSteps; - - nTempSteps = std::abs( nBlueSteps ); - - if (nTempSteps > nCalcSteps) - nCalcSteps = nTempSteps; - - if (nCalcSteps < nSteps) - nSteps = nCalcSteps; - - if ( !nSteps ) - nSteps = 1; - - // determine output limits and stepsizes for all directions - tools::Polygon aPoly; - double fScanLeft = aRect.Left(); - double fScanTop = aRect.Top(); - double fScanRight = aRect.Right(); - double fScanBottom = aRect.Bottom(); - double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5; - double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps) * 0.5; - - // all gradients are rendered as nested rectangles which shrink - // equally in each dimension - except for 'square' gradients - // which shrink to a central vertex but are not per-se square. - if( rGradient.GetStyle() != GradientStyle::Square ) - { - fScanIncY = std::min( fScanIncY, fScanIncX ); - fScanIncX = fScanIncY; - } - sal_uInt8 nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue); - bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output - - mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); - - aPoly = rRect; - xPolyPoly->Insert( aPoly ); - xPolyPoly->Insert( aPoly ); - - // loop to output Polygon/PolyPolygon sequentially - for( tools::Long i = 1; i < nSteps; i++ ) - { - // calculate new Polygon - fScanLeft += fScanIncX; - aRect.SetLeft( static_cast<tools::Long>( fScanLeft ) ); - fScanTop += fScanIncY; - aRect.SetTop( static_cast<tools::Long>( fScanTop ) ); - fScanRight -= fScanIncX; - aRect.SetRight( static_cast<tools::Long>( fScanRight ) ); - fScanBottom -= fScanIncY; - aRect.SetBottom( static_cast<tools::Long>( fScanBottom ) ); - - if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) ) - break; - - if( rGradient.GetStyle() == GradientStyle::Radial || rGradient.GetStyle() == GradientStyle::Elliptical ) - aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 ); - else - aPoly = tools::Polygon( aRect ); - - aPoly.Rotate(aCenter, rGradient.GetAngle() % 3600_deg10); - - // adapt colour accordingly - const tools::Long nStepIndex = ( xPolyPoly ? i : ( i + 1 ) ); - nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) ); - nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) ); - nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) ); - - bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output - - xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 ); - xPolyPoly->Replace( aPoly, 1 ); - - mpMetaFile->AddAction( new MetaPolyPolygonAction( *xPolyPoly ) ); - - // #107349# Set fill color _after_ geometry painting: - // xPolyPoly's geometry is the band from last iteration's - // aPoly to current iteration's aPoly. The window outdev - // path (see else below), on the other hand, paints the - // full aPoly. Thus, here, we're painting the band before - // the one painted in the window outdev path below. To get - // matching colors, have to delay color setting here. - mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); - } - - const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 ); - - if( rPoly.GetBoundRect().IsEmpty() ) - return; - - // #107349# Paint last polygon with end color only if loop - // has generated output. Otherwise, the current - // (i.e. start) color is taken, to generate _any_ output. - if( bPaintLastPolygon ) - { - nRed = GetGradientColorValue( nEndRed ); - nGreen = GetGradientColorValue( nEndGreen ); - nBlue = GetGradientColorValue( nEndBlue ); - } - - mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); - mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) ); -} - tools::Long OutputDevice::GetGradientStepCount( tools::Long nMinRect ) { tools::Long nInc = (nMinRect < 50) ? 2 : 4; @@ -969,45 +628,4 @@ Color OutputDevice::GetSingleColorGradientFill() return aColor; } -void OutputDevice::AddGradientActions( const tools::Rectangle& rRect, const Gradient& rGradient, - GDIMetaFile& rMtf ) -{ - - tools::Rectangle aRect( rRect ); - - aRect.Justify(); - - // do nothing if the rectangle is empty - if ( aRect.IsEmpty() ) - return; - - Gradient aGradient( rGradient ); - GDIMetaFile* pOldMtf = mpMetaFile; - - mpMetaFile = &rMtf; - mpMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::ALL ) ); - mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) ); - mpMetaFile->AddAction( new MetaLineColorAction( Color(), false ) ); - - // because we draw with no border line, we have to expand gradient - // rect to avoid missing lines on the right and bottom edge - aRect.AdjustLeft( -1 ); - aRect.AdjustTop( -1 ); - aRect.AdjustRight( 1 ); - aRect.AdjustBottom( 1 ); - - // calculate step count if necessary - if ( !aGradient.GetSteps() ) - aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT ); - - if( aGradient.GetStyle() == GradientStyle::Linear || aGradient.GetStyle() == GradientStyle::Axial ) - DrawLinearGradientToMetafile( aRect, aGradient ); - else - DrawComplexGradientToMetafile( aRect, aGradient ); - - mpMetaFile->AddAction( new MetaPopAction() ); - mpMetaFile = pOldMtf; - -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |