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>
26 #include <drawinglayer/primitive2d/groupprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
33 using namespace com::sun::star
;
36 namespace drawinglayer::primitive2d
38 // Get the OuterColor. Take into account that for css::awt::GradientStyle_AXIAL
39 // this is the last one due to inverted gradient usage (see constructor there)
40 basegfx::BColor
FillGradientPrimitive2D::getOuterColor() const
42 if (getFillGradient().getColorStops().empty())
43 return basegfx::BColor();
45 if (css::awt::GradientStyle_AXIAL
== getFillGradient().getStyle())
46 return getFillGradient().getColorStops().back().getStopColor();
48 return getFillGradient().getColorStops().front().getStopColor();
51 // Get the needed UnitPolygon dependent on the GradientStyle
52 basegfx::B2DPolygon
FillGradientPrimitive2D::getUnitPolygon() const
54 if (css::awt::GradientStyle_RADIAL
== getFillGradient().getStyle()
55 || css::awt::GradientStyle_ELLIPTICAL
== getFillGradient().getStyle())
57 return basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0);
60 return basegfx::utils::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0));
63 void FillGradientPrimitive2D::generateMatricesAndColors(
64 std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)> aCallback
) const
66 switch(getFillGradient().getStyle())
68 default: // GradientStyle_MAKE_FIXED_SIZE
69 case css::awt::GradientStyle_LINEAR
:
71 texture::GeoTexSvxGradientLinear
aGradient(
74 getFillGradient().getSteps(),
75 getFillGradient().getColorStops(),
76 getFillGradient().getBorder(),
77 getFillGradient().getAngle());
78 aGradient
.appendTransformationsAndColors(aCallback
);
81 case css::awt::GradientStyle_AXIAL
:
83 texture::GeoTexSvxGradientAxial
aGradient(
86 getFillGradient().getSteps(),
87 getFillGradient().getColorStops(),
88 getFillGradient().getBorder(),
89 getFillGradient().getAngle());
90 aGradient
.appendTransformationsAndColors(aCallback
);
93 case css::awt::GradientStyle_RADIAL
:
95 texture::GeoTexSvxGradientRadial
aGradient(
97 getFillGradient().getSteps(),
98 getFillGradient().getColorStops(),
99 getFillGradient().getBorder(),
100 getFillGradient().getOffsetX(),
101 getFillGradient().getOffsetY());
102 aGradient
.appendTransformationsAndColors(aCallback
);
105 case css::awt::GradientStyle_ELLIPTICAL
:
107 texture::GeoTexSvxGradientElliptical
aGradient(
108 getDefinitionRange(),
109 getFillGradient().getSteps(),
110 getFillGradient().getColorStops(),
111 getFillGradient().getBorder(),
112 getFillGradient().getOffsetX(),
113 getFillGradient().getOffsetY(),
114 getFillGradient().getAngle());
115 aGradient
.appendTransformationsAndColors(aCallback
);
118 case css::awt::GradientStyle_SQUARE
:
120 texture::GeoTexSvxGradientSquare
aGradient(
121 getDefinitionRange(),
122 getFillGradient().getSteps(),
123 getFillGradient().getColorStops(),
124 getFillGradient().getBorder(),
125 getFillGradient().getOffsetX(),
126 getFillGradient().getOffsetY(),
127 getFillGradient().getAngle());
128 aGradient
.appendTransformationsAndColors(aCallback
);
131 case css::awt::GradientStyle_RECT
:
133 texture::GeoTexSvxGradientRect
aGradient(
134 getDefinitionRange(),
135 getFillGradient().getSteps(),
136 getFillGradient().getColorStops(),
137 getFillGradient().getBorder(),
138 getFillGradient().getOffsetX(),
139 getFillGradient().getOffsetY(),
140 getFillGradient().getAngle());
141 aGradient
.appendTransformationsAndColors(aCallback
);
147 Primitive2DReference
FillGradientPrimitive2D::createFill(bool bOverlapping
) const
149 Primitive2DContainer aContainer
;
152 // OverlappingFill: create solid fill with outmost color
153 aContainer
.push_back(
154 new PolyPolygonColorPrimitive2D(
155 basegfx::B2DPolyPolygon(
156 basegfx::utils::createPolygonFromRect(getOutputRange())),
159 // create solid fill steps by providing callback as lambda
160 auto aCallback([&aContainer
,this](
161 const basegfx::B2DHomMatrix
& rMatrix
,
162 const basegfx::BColor
& rColor
)
164 // create part polygon
165 basegfx::B2DPolygon
aNewPoly(getUnitPolygon());
166 aNewPoly
.transform(rMatrix
);
169 aContainer
.push_back(
170 new PolyPolygonColorPrimitive2D(
171 basegfx::B2DPolyPolygon(aNewPoly
),
175 // call value generator to trigger callbacks
176 generateMatricesAndColors(aCallback
);
180 // NonOverlappingFill
181 if (getFillGradient().getColorStops().size() < 2)
183 // not really a gradient, we need to create a start primitive
184 // entry using the single color and the covered area
185 const basegfx::B2DRange
aOutmostRange(getOutputRange());
186 aContainer
.push_back(
187 new PolyPolygonColorPrimitive2D(
188 basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aOutmostRange
)),
193 // gradient with stops, prepare CombinedPolyPoly, use callback
194 basegfx::B2DPolyPolygon aCombinedPolyPoly
;
195 basegfx::BColor aLastColor
;
197 auto aCallback([&aContainer
,&aCombinedPolyPoly
,&aLastColor
,this](
198 const basegfx::B2DHomMatrix
& rMatrix
,
199 const basegfx::BColor
& rColor
)
201 if (aContainer
.empty())
203 // 1st callback, init CombinedPolyPoly & create 1st entry
204 basegfx::B2DRange
aOutmostRange(getOutputRange());
206 // expand aOutmostRange with transformed first polygon
207 // to ensure confinement
208 basegfx::B2DPolygon
aFirstPoly(getUnitPolygon());
209 aFirstPoly
.transform(rMatrix
);
210 aOutmostRange
.expand(aFirstPoly
.getB2DRange());
212 // build 1st combined polygon; outmost range 1st, then
213 // the shaped, transformed polygon
214 aCombinedPolyPoly
.append(basegfx::utils::createPolygonFromRect(aOutmostRange
));
215 aCombinedPolyPoly
.append(aFirstPoly
);
217 // create first primitive
218 aContainer
.push_back(
219 new PolyPolygonColorPrimitive2D(
223 // save first polygon for re-use in next call, it's the second
224 // one, so remove 1st
225 aCombinedPolyPoly
.remove(0);
227 // remember color for next primitive creation
232 // regular n-th callback, create combined entry by re-using
233 // CombinedPolyPoly and aLastColor
234 basegfx::B2DPolygon
aNextPoly(getUnitPolygon());
235 aNextPoly
.transform(rMatrix
);
236 aCombinedPolyPoly
.append(aNextPoly
);
238 // create primitive with correct color
239 aContainer
.push_back(
240 new PolyPolygonColorPrimitive2D(
244 // prepare re-use of inner polygon, save color
245 aCombinedPolyPoly
.remove(0);
250 // call value generator to trigger callbacks
251 generateMatricesAndColors(aCallback
);
253 // add last inner polygon with last color
254 aContainer
.push_back(
255 new PolyPolygonColorPrimitive2D(
256 std::move(aCombinedPolyPoly
),
260 return new GroupPrimitive2D(std::move(aContainer
));
263 Primitive2DReference
FillGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& /*rViewInformation*/) const
265 // SDPR: support alpha directly now. If a primitive processor
266 // cannot deal with it, use it's decomposition. For that purpose
267 // this decomposition has two stages now: This 1st one will
268 // (if needed) separate content and alpha into a TransparencePrimitive2D
269 // and (if needed) embed that to a UnifiedTransparencePrimitive2D,
270 // so all processors can work as before
271 if (hasAlphaGradient() || hasTransparency())
273 Primitive2DReference
aRetval(
274 new FillGradientPrimitive2D(
276 getDefinitionRange(),
279 if (hasAlphaGradient())
281 Primitive2DContainer aAlpha
{ new FillGradientPrimitive2D(
283 getDefinitionRange(),
284 getAlphaGradient()) };
286 aRetval
= new TransparencePrimitive2D(Primitive2DContainer
{ aRetval
}, std::move(aAlpha
));
289 if (hasTransparency())
291 aRetval
= new UnifiedTransparencePrimitive2D(Primitive2DContainer
{ aRetval
}, getTransparency());
297 // default creates overlapping fill which works with AntiAliasing and without.
298 // The non-overlapping version does not create single filled polygons, but
299 // PolyPolygons where each one describes a 'ring' for the gradient such
300 // that the rings will not overlap. This is useful for the old XOR-paint
301 // 'trick' of VCL which is recorded in Metafiles; so this version may be
302 // used from the MetafilePrimitive2D in its decomposition.
303 if(!getFillGradient().isDefault())
305 return createFill(/*bOverlapping*/true);
311 FillGradientPrimitive2D::FillGradientPrimitive2D(
312 const basegfx::B2DRange
& rOutputRange
,
313 const attribute::FillGradientAttribute
& rFillGradient
)
314 : maOutputRange(rOutputRange
)
315 , maDefinitionRange(rOutputRange
)
316 , maFillGradient(rFillGradient
)
318 , mfTransparency(0.0)
322 FillGradientPrimitive2D::FillGradientPrimitive2D(
323 const basegfx::B2DRange
& rOutputRange
,
324 const basegfx::B2DRange
& rDefinitionRange
,
325 const attribute::FillGradientAttribute
& rFillGradient
,
326 const attribute::FillGradientAttribute
* pAlphaGradient
,
327 double fTransparency
)
328 : maOutputRange(rOutputRange
)
329 , maDefinitionRange(rDefinitionRange
)
330 , maFillGradient(rFillGradient
)
332 , mfTransparency(fTransparency
)
334 // copy alpha gradient if we got one
335 if (nullptr != pAlphaGradient
)
336 maAlphaGradient
= *pAlphaGradient
;
339 bool FillGradientPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
341 if(!BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
344 const FillGradientPrimitive2D
& rCompare(static_cast<const FillGradientPrimitive2D
&>(rPrimitive
));
346 if (getOutputRange() != rCompare
.getOutputRange())
349 if (getDefinitionRange() != rCompare
.getDefinitionRange())
352 if (getFillGradient() != rCompare
.getFillGradient())
355 if (maAlphaGradient
!= rCompare
.maAlphaGradient
)
358 if (!basegfx::fTools::equal(getTransparency(), rCompare
.getTransparency()))
364 basegfx::B2DRange
FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
366 // return the geometrically visible area
367 return getOutputRange();
371 sal_uInt32
FillGradientPrimitive2D::getPrimitive2DID() const
373 return PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D
;
376 } // end of namespace
378 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */