1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
21 #include <basegfx/polygon/b2dpolygon.hxx>
22 #include <basegfx/polygon/b2dpolygontools.hxx>
23 #include <texture/texture.hxx>
24 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
25 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
30 using namespace com::sun::star
;
33 namespace drawinglayer::primitive2d
35 // Get the OuterColor. Take into account that for css::awt::GradientStyle_AXIAL
36 // this is the last one due to inverted gradient usage (see constructor there)
37 basegfx::BColor
FillGradientPrimitive2D::getOuterColor() const
39 if (getFillGradient().getColorStops().empty())
40 return basegfx::BColor();
42 if (css::awt::GradientStyle_AXIAL
== getFillGradient().getStyle())
43 return getFillGradient().getColorStops().back().getStopColor();
45 return getFillGradient().getColorStops().front().getStopColor();
48 // Get the needed UnitPolygon dependent on the GradientStyle
49 basegfx::B2DPolygon
FillGradientPrimitive2D::getUnitPolygon() const
51 if (css::awt::GradientStyle_RADIAL
== getFillGradient().getStyle()
52 || css::awt::GradientStyle_ELLIPTICAL
== getFillGradient().getStyle())
54 return basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0);
57 return basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0));
60 void FillGradientPrimitive2D::generateMatricesAndColors(
61 std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)> aCallback
) const
63 switch(getFillGradient().getStyle())
65 default: // GradientStyle_MAKE_FIXED_SIZE
66 case css::awt::GradientStyle_LINEAR
:
68 texture::GeoTexSvxGradientLinear
aGradient(
71 getFillGradient().getSteps(),
72 getFillGradient().getColorStops(),
73 getFillGradient().getBorder(),
74 getFillGradient().getAngle());
75 aGradient
.appendTransformationsAndColors(aCallback
);
78 case css::awt::GradientStyle_AXIAL
:
80 texture::GeoTexSvxGradientAxial
aGradient(
83 getFillGradient().getSteps(),
84 getFillGradient().getColorStops(),
85 getFillGradient().getBorder(),
86 getFillGradient().getAngle());
87 aGradient
.appendTransformationsAndColors(aCallback
);
90 case css::awt::GradientStyle_RADIAL
:
92 texture::GeoTexSvxGradientRadial
aGradient(
94 getFillGradient().getSteps(),
95 getFillGradient().getColorStops(),
96 getFillGradient().getBorder(),
97 getFillGradient().getOffsetX(),
98 getFillGradient().getOffsetY());
99 aGradient
.appendTransformationsAndColors(aCallback
);
102 case css::awt::GradientStyle_ELLIPTICAL
:
104 texture::GeoTexSvxGradientElliptical
aGradient(
105 getDefinitionRange(),
106 getFillGradient().getSteps(),
107 getFillGradient().getColorStops(),
108 getFillGradient().getBorder(),
109 getFillGradient().getOffsetX(),
110 getFillGradient().getOffsetY(),
111 getFillGradient().getAngle());
112 aGradient
.appendTransformationsAndColors(aCallback
);
115 case css::awt::GradientStyle_SQUARE
:
117 texture::GeoTexSvxGradientSquare
aGradient(
118 getDefinitionRange(),
119 getFillGradient().getSteps(),
120 getFillGradient().getColorStops(),
121 getFillGradient().getBorder(),
122 getFillGradient().getOffsetX(),
123 getFillGradient().getOffsetY(),
124 getFillGradient().getAngle());
125 aGradient
.appendTransformationsAndColors(aCallback
);
128 case css::awt::GradientStyle_RECT
:
130 texture::GeoTexSvxGradientRect
aGradient(
131 getDefinitionRange(),
132 getFillGradient().getSteps(),
133 getFillGradient().getColorStops(),
134 getFillGradient().getBorder(),
135 getFillGradient().getOffsetX(),
136 getFillGradient().getOffsetY(),
137 getFillGradient().getAngle());
138 aGradient
.appendTransformationsAndColors(aCallback
);
144 void FillGradientPrimitive2D::createFill(Primitive2DContainer
& rContainer
, bool bOverlapping
) const
148 // OverlappingFill: create solid fill with outmost color
149 rContainer
.push_back(
150 new PolyPolygonColorPrimitive2D(
151 basegfx::B2DPolyPolygon(
152 basegfx::utils::createPolygonFromRect(getOutputRange())),
155 // create solid fill steps by providing callback as lambda
156 auto aCallback([&rContainer
,this](
157 const basegfx::B2DHomMatrix
& rMatrix
,
158 const basegfx::BColor
& rColor
)
160 // create part polygon
161 basegfx::B2DPolygon
aNewPoly(getUnitPolygon());
162 aNewPoly
.transform(rMatrix
);
165 rContainer
.push_back(
166 new PolyPolygonColorPrimitive2D(
167 basegfx::B2DPolyPolygon(aNewPoly
),
171 // call value generator to trigger callbacks
172 generateMatricesAndColors(aCallback
);
176 // NonOverlappingFill
177 if (getFillGradient().getColorStops().size() < 2)
179 // not really a gradient, we need to create a start primitive
180 // entry using the single color and the covered area
181 const basegfx::B2DRange
aOutmostRange(getOutputRange());
182 rContainer
.push_back(
183 new PolyPolygonColorPrimitive2D(
184 basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aOutmostRange
)),
189 // gradient with stops, prepare CombinedPolyPoly, use callback
190 basegfx::B2DPolyPolygon aCombinedPolyPoly
;
191 basegfx::BColor aLastColor
;
193 auto aCallback([&rContainer
,&aCombinedPolyPoly
,&aLastColor
,this](
194 const basegfx::B2DHomMatrix
& rMatrix
,
195 const basegfx::BColor
& rColor
)
197 if (rContainer
.empty())
199 // 1st callback, init CombinedPolyPoly & create 1st entry
200 basegfx::B2DRange
aOutmostRange(getOutputRange());
202 // expand aOutmostRange with transformed first polygon
203 // to ensure confinement
204 basegfx::B2DPolygon
aFirstPoly(getUnitPolygon());
205 aFirstPoly
.transform(rMatrix
);
206 aOutmostRange
.expand(aFirstPoly
.getB2DRange());
208 // build 1st combined polygon; outmost range 1st, then
209 // the shaped, transformed polygon
210 aCombinedPolyPoly
.append(basegfx::utils::createPolygonFromRect(aOutmostRange
));
211 aCombinedPolyPoly
.append(aFirstPoly
);
213 // create first primitive
214 rContainer
.push_back(
215 new PolyPolygonColorPrimitive2D(
219 // save first polygon for re-use in next call, it's the second
220 // one, so remove 1st
221 aCombinedPolyPoly
.remove(0);
223 // remember color for next primitive creation
228 // regular n-th callback, create combined entry by re-using
229 // CombinedPolyPoly and aLastColor
230 basegfx::B2DPolygon
aNextPoly(getUnitPolygon());
231 aNextPoly
.transform(rMatrix
);
232 aCombinedPolyPoly
.append(aNextPoly
);
234 // create primitive with correct color
235 rContainer
.push_back(
236 new PolyPolygonColorPrimitive2D(
240 // prepare re-use of inner polygon, save color
241 aCombinedPolyPoly
.remove(0);
246 // call value generator to trigger callbacks
247 generateMatricesAndColors(aCallback
);
249 // add last inner polygon with last color
250 rContainer
.push_back(
251 new PolyPolygonColorPrimitive2D(
258 void FillGradientPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
260 // default creates overlapping fill which works with AntiAliasing and without.
261 // The non-overlapping version does not create single filled polygons, but
262 // PolyPolygons where each one describes a 'ring' for the gradient such
263 // that the rings will not overlap. This is useful for the old XOR-paint
264 // 'trick' of VCL which is recorded in Metafiles; so this version may be
265 // used from the MetafilePrimitive2D in its decomposition.
267 if(!getFillGradient().isDefault())
269 createFill(rContainer
, /*bOverlapping*/true);
273 FillGradientPrimitive2D::FillGradientPrimitive2D(
274 const basegfx::B2DRange
& rOutputRange
,
275 attribute::FillGradientAttribute aFillGradient
)
276 : maOutputRange(rOutputRange
),
277 maDefinitionRange(rOutputRange
),
278 maFillGradient(std::move(aFillGradient
))
282 FillGradientPrimitive2D::FillGradientPrimitive2D(
283 const basegfx::B2DRange
& rOutputRange
,
284 const basegfx::B2DRange
& rDefinitionRange
,
285 attribute::FillGradientAttribute aFillGradient
)
286 : maOutputRange(rOutputRange
),
287 maDefinitionRange(rDefinitionRange
),
288 maFillGradient(std::move(aFillGradient
))
292 bool FillGradientPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
294 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
296 const FillGradientPrimitive2D
& rCompare
= static_cast<const FillGradientPrimitive2D
&>(rPrimitive
);
298 return (getOutputRange() == rCompare
.getOutputRange()
299 && getDefinitionRange() == rCompare
.getDefinitionRange()
300 && getFillGradient() == rCompare
.getFillGradient());
306 basegfx::B2DRange
FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
308 // return the geometrically visible area
309 return getOutputRange();
313 sal_uInt32
FillGradientPrimitive2D::getPrimitive2DID() const
315 return PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D
;
318 } // end of namespace
320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */