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>
This commit is contained in:
Chris Sherlock
2022-01-05 05:42:32 +01:00
committed by Tomaž Vajngerl
parent 88d8c9af71
commit 22f52db0be
14 changed files with 715 additions and 618 deletions
@@ -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 );
+7 -1
View File
@@ -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, "" );
}
}
+9
View File
@@ -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
-10
View File
@@ -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();
///@}
+50
View File
@@ -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:
+1
View File
@@ -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 \
+256
View File
@@ -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: */
-218
View File
@@ -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();
+2 -1
View File
@@ -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 );
}
+2 -1
View File
@@ -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;
+2 -1
View File
@@ -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;
+382
View File
@@ -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: */
+2 -1
View File
@@ -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() );
-382
View File
@@ -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: */