calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / drawinglayer / source / primitive2d / fillgradientprimitive2d.cxx
blobf39daccc4320d3266626922dfe20e1beee9688be
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <utility>
27 #include <algorithm>
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(
69 getDefinitionRange(),
70 getOutputRange(),
71 getFillGradient().getSteps(),
72 getFillGradient().getColorStops(),
73 getFillGradient().getBorder(),
74 getFillGradient().getAngle());
75 aGradient.appendTransformationsAndColors(aCallback);
76 break;
78 case css::awt::GradientStyle_AXIAL:
80 texture::GeoTexSvxGradientAxial aGradient(
81 getDefinitionRange(),
82 getOutputRange(),
83 getFillGradient().getSteps(),
84 getFillGradient().getColorStops(),
85 getFillGradient().getBorder(),
86 getFillGradient().getAngle());
87 aGradient.appendTransformationsAndColors(aCallback);
88 break;
90 case css::awt::GradientStyle_RADIAL:
92 texture::GeoTexSvxGradientRadial aGradient(
93 getDefinitionRange(),
94 getFillGradient().getSteps(),
95 getFillGradient().getColorStops(),
96 getFillGradient().getBorder(),
97 getFillGradient().getOffsetX(),
98 getFillGradient().getOffsetY());
99 aGradient.appendTransformationsAndColors(aCallback);
100 break;
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);
113 break;
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);
126 break;
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);
139 break;
144 void FillGradientPrimitive2D::createFill(Primitive2DContainer& rContainer, bool bOverlapping) const
146 if (bOverlapping)
148 // OverlappingFill: create solid fill with outmost color
149 rContainer.push_back(
150 new PolyPolygonColorPrimitive2D(
151 basegfx::B2DPolyPolygon(
152 basegfx::utils::createPolygonFromRect(getOutputRange())),
153 getOuterColor()));
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);
164 // create solid fill
165 rContainer.push_back(
166 new PolyPolygonColorPrimitive2D(
167 basegfx::B2DPolyPolygon(aNewPoly),
168 rColor));
171 // call value generator to trigger callbacks
172 generateMatricesAndColors(aCallback);
174 else
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)),
185 getOuterColor()));
187 else
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(
216 aCombinedPolyPoly,
217 getOuterColor()));
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
224 aLastColor = rColor;
226 else
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(
237 aCombinedPolyPoly,
238 aLastColor));
240 // prepare re-use of inner polygon, save color
241 aCombinedPolyPoly.remove(0);
242 aLastColor = rColor;
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(
252 aCombinedPolyPoly,
253 aLastColor));
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());
303 return false;
306 basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
308 // return the geometrically visible area
309 return getOutputRange();
312 // provide unique ID
313 sal_uInt32 FillGradientPrimitive2D::getPrimitive2DID() const
315 return PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D;
318 } // end of namespace
320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */