Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
21 : #include <basegfx/polygon/b2dpolygon.hxx>
22 : #include <basegfx/polygon/b2dpolygontools.hxx>
23 : #include <drawinglayer/texture/texture.hxx>
24 : #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
25 : #include <basegfx/tools/canvastools.hxx>
26 : #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
27 :
28 :
29 :
30 : using namespace com::sun::star;
31 :
32 :
33 :
34 : namespace drawinglayer
35 : {
36 : namespace primitive2d
37 : {
38 1765 : void FillGradientPrimitive2D::generateMatricesAndColors(
39 : std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
40 : basegfx::BColor& rOuterColor) const
41 : {
42 1765 : rEntries.clear();
43 :
44 : // make sure steps is not too high/low
45 1765 : const basegfx::BColor aStart(getFillGradient().getStartColor());
46 3530 : const basegfx::BColor aEnd(getFillGradient().getEndColor());
47 1765 : const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5));
48 1765 : sal_uInt32 nSteps(getFillGradient().getSteps());
49 :
50 1765 : if(nSteps == 0)
51 : {
52 1763 : nSteps = nMaxSteps;
53 : }
54 :
55 1765 : if(nSteps < 2)
56 : {
57 0 : nSteps = 2;
58 : }
59 :
60 1765 : if(nSteps > nMaxSteps)
61 : {
62 0 : nSteps = nMaxSteps;
63 : }
64 :
65 1765 : nSteps = std::max(sal_uInt32(1), nSteps);
66 :
67 1765 : switch(getFillGradient().getStyle())
68 : {
69 : case attribute::GRADIENTSTYLE_LINEAR:
70 : {
71 : texture::GeoTexSvxGradientLinear aGradient(
72 1404 : getDefinitionRange(),
73 1404 : getOutputRange(),
74 : aStart,
75 : aEnd,
76 : nSteps,
77 1404 : getFillGradient().getBorder(),
78 4212 : getFillGradient().getAngle());
79 1404 : aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
80 1404 : break;
81 : }
82 : case attribute::GRADIENTSTYLE_AXIAL:
83 : {
84 : texture::GeoTexSvxGradientAxial aGradient(
85 361 : getDefinitionRange(),
86 361 : getOutputRange(),
87 : aStart,
88 : aEnd,
89 : nSteps,
90 361 : getFillGradient().getBorder(),
91 1083 : getFillGradient().getAngle());
92 361 : aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
93 361 : break;
94 : }
95 : case attribute::GRADIENTSTYLE_RADIAL:
96 : {
97 : texture::GeoTexSvxGradientRadial aGradient(
98 0 : getDefinitionRange(),
99 : aStart,
100 : aEnd,
101 : nSteps,
102 0 : getFillGradient().getBorder(),
103 0 : getFillGradient().getOffsetX(),
104 0 : getFillGradient().getOffsetY());
105 0 : aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
106 0 : break;
107 : }
108 : case attribute::GRADIENTSTYLE_ELLIPTICAL:
109 : {
110 : texture::GeoTexSvxGradientElliptical aGradient(
111 0 : getDefinitionRange(),
112 : aStart,
113 : aEnd,
114 : nSteps,
115 0 : getFillGradient().getBorder(),
116 0 : getFillGradient().getOffsetX(),
117 0 : getFillGradient().getOffsetY(),
118 0 : getFillGradient().getAngle());
119 0 : aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
120 0 : break;
121 : }
122 : case attribute::GRADIENTSTYLE_SQUARE:
123 : {
124 : texture::GeoTexSvxGradientSquare aGradient(
125 0 : getDefinitionRange(),
126 : aStart,
127 : aEnd,
128 : nSteps,
129 0 : getFillGradient().getBorder(),
130 0 : getFillGradient().getOffsetX(),
131 0 : getFillGradient().getOffsetY(),
132 0 : getFillGradient().getAngle());
133 0 : aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
134 0 : break;
135 : }
136 : case attribute::GRADIENTSTYLE_RECT:
137 : {
138 : texture::GeoTexSvxGradientRect aGradient(
139 0 : getDefinitionRange(),
140 : aStart,
141 : aEnd,
142 : nSteps,
143 0 : getFillGradient().getBorder(),
144 0 : getFillGradient().getOffsetX(),
145 0 : getFillGradient().getOffsetY(),
146 0 : getFillGradient().getAngle());
147 0 : aGradient.appendTransformationsAndColors(rEntries, rOuterColor);
148 0 : break;
149 : }
150 1765 : }
151 1765 : }
152 :
153 1765 : Primitive2DSequence FillGradientPrimitive2D::createOverlappingFill(
154 : const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
155 : const basegfx::BColor& rOuterColor,
156 : const basegfx::B2DPolygon& rUnitPolygon) const
157 : {
158 : // prepare return value
159 1765 : Primitive2DSequence aRetval(rEntries.size() + 1);
160 :
161 : // create solid fill with outmost color
162 3530 : aRetval[0] = Primitive2DReference(
163 : new PolyPolygonColorPrimitive2D(
164 : basegfx::B2DPolyPolygon(
165 1765 : basegfx::tools::createPolygonFromRect(getOutputRange())),
166 5295 : rOuterColor));
167 :
168 : // create solid fill steps
169 220712 : for(size_t a(0); a < rEntries.size(); a++)
170 : {
171 : // create part polygon
172 218947 : basegfx::B2DPolygon aNewPoly(rUnitPolygon);
173 :
174 218947 : aNewPoly.transform(rEntries[a].maB2DHomMatrix);
175 :
176 : // create solid fill
177 437894 : aRetval[a + 1] = Primitive2DReference(
178 : new PolyPolygonColorPrimitive2D(
179 : basegfx::B2DPolyPolygon(aNewPoly),
180 656841 : rEntries[a].maBColor));
181 218947 : }
182 :
183 1765 : return aRetval;
184 : }
185 :
186 0 : Primitive2DSequence FillGradientPrimitive2D::createNonOverlappingFill(
187 : const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries,
188 : const basegfx::BColor& rOuterColor,
189 : const basegfx::B2DPolygon& rUnitPolygon) const
190 : {
191 : // prepare return value
192 0 : Primitive2DSequence aRetval(rEntries.size() + 1);
193 :
194 : // get outmost viusible range from object
195 0 : basegfx::B2DRange aOutmostRange(getOutputRange());
196 0 : basegfx::B2DPolyPolygon aCombinedPolyPoly;
197 :
198 0 : if(rEntries.size())
199 : {
200 : // extend aOutmostRange with first polygon
201 0 : basegfx::B2DPolygon aFirstPoly(rUnitPolygon);
202 :
203 0 : aFirstPoly.transform(rEntries[0].maB2DHomMatrix);
204 0 : aCombinedPolyPoly.append(aFirstPoly);
205 0 : aOutmostRange.expand(aFirstPoly.getB2DRange());
206 : }
207 :
208 : // add outmost range to combined polypolygon (in 1st place), create first primitive
209 0 : aCombinedPolyPoly.insert(0, basegfx::tools::createPolygonFromRect(aOutmostRange));
210 0 : aRetval[0] = Primitive2DReference(
211 : new PolyPolygonColorPrimitive2D(
212 : aCombinedPolyPoly,
213 0 : rOuterColor));
214 :
215 0 : if(rEntries.size())
216 : {
217 : // reuse first polygon, it's the second one
218 0 : aCombinedPolyPoly.remove(0);
219 :
220 0 : for(sal_uInt32 a(0); a < rEntries.size() - 1; a++)
221 : {
222 : // create next inner polygon, combinbe with last one
223 0 : basegfx::B2DPolygon aNextPoly(rUnitPolygon);
224 :
225 0 : aNextPoly.transform(rEntries[a + 1].maB2DHomMatrix);
226 0 : aCombinedPolyPoly.append(aNextPoly);
227 :
228 : // create primitive with correct color
229 0 : aRetval[a + 1] = Primitive2DReference(
230 : new PolyPolygonColorPrimitive2D(
231 : aCombinedPolyPoly,
232 0 : rEntries[a].maBColor));
233 :
234 : // reuse inner polygon, it's the 2nd one
235 0 : aCombinedPolyPoly.remove(0);
236 0 : }
237 :
238 : // add last inner polygon with last color
239 0 : aRetval[rEntries.size()] = Primitive2DReference(
240 : new PolyPolygonColorPrimitive2D(
241 : aCombinedPolyPoly,
242 0 : rEntries[rEntries.size() - 1].maBColor));
243 : }
244 :
245 0 : return aRetval;
246 : }
247 :
248 1765 : Primitive2DSequence FillGradientPrimitive2D::createFill(bool bOverlapping) const
249 : {
250 : // prepare shape of the Unit Polygon
251 1765 : basegfx::B2DPolygon aUnitPolygon;
252 :
253 1765 : switch(getFillGradient().getStyle())
254 : {
255 : case attribute::GRADIENTSTYLE_RADIAL:
256 : case attribute::GRADIENTSTYLE_ELLIPTICAL:
257 : {
258 0 : aUnitPolygon = basegfx::tools::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0);
259 0 : break;
260 : }
261 : default: // GRADIENTSTYLE_LINEAR, attribute::GRADIENTSTYLE_AXIAL, attribute::GRADIENTSTYLE_SQUARE, attribute::GRADIENTSTYLE_RECT
262 : {
263 1765 : aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0));
264 1765 : break;
265 : }
266 : }
267 :
268 : // get the transform matrices and colors (where colors
269 : // will have one more entry that matrices)
270 1765 : std::vector< drawinglayer::texture::B2DHomMatrixAndBColor > aEntries;
271 3530 : basegfx::BColor aOuterColor;
272 :
273 1765 : generateMatricesAndColors(aEntries, aOuterColor);
274 :
275 1765 : if(bOverlapping)
276 : {
277 1765 : return createOverlappingFill(aEntries, aOuterColor, aUnitPolygon);
278 : }
279 : else
280 : {
281 0 : return createNonOverlappingFill(aEntries, aOuterColor, aUnitPolygon);
282 1765 : }
283 : }
284 :
285 1765 : Primitive2DSequence FillGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
286 : {
287 : // default creates overlapping fill which works with AntiAliasing and without.
288 : // The non-overlapping version does not create single filled polygons, but
289 : // PolyPolygons where each one describes a 'ring' for the gradient such
290 : // that the rings will not overlap. This is useful fir the old XOR-paint
291 : // 'trick' of VCL which is recorded in Metafiles; so this version may be
292 : // used from the MetafilePrimitive2D in it's decomposition.
293 :
294 1765 : if(!getFillGradient().isDefault())
295 : {
296 : static bool bOverlapping(true); // allow to test non-overlapping in the debugger
297 :
298 1765 : return createFill(bOverlapping);
299 : }
300 : else
301 : {
302 0 : return Primitive2DSequence();
303 : }
304 : }
305 :
306 504 : FillGradientPrimitive2D::FillGradientPrimitive2D(
307 : const basegfx::B2DRange& rOutputRange,
308 : const attribute::FillGradientAttribute& rFillGradient)
309 : : BufferedDecompositionPrimitive2D(),
310 : maOutputRange(rOutputRange),
311 : maDefinitionRange(rOutputRange),
312 504 : maFillGradient(rFillGradient)
313 : {
314 504 : }
315 :
316 3303 : FillGradientPrimitive2D::FillGradientPrimitive2D(
317 : const basegfx::B2DRange& rOutputRange,
318 : const basegfx::B2DRange& rDefinitionRange,
319 : const attribute::FillGradientAttribute& rFillGradient)
320 : : BufferedDecompositionPrimitive2D(),
321 : maOutputRange(rOutputRange),
322 : maDefinitionRange(rDefinitionRange),
323 3303 : maFillGradient(rFillGradient)
324 : {
325 3303 : }
326 :
327 0 : bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
328 : {
329 0 : if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
330 : {
331 0 : const FillGradientPrimitive2D& rCompare = static_cast<const FillGradientPrimitive2D&>(rPrimitive);
332 :
333 0 : return (getOutputRange() == rCompare.getOutputRange()
334 0 : && getDefinitionRange() == rCompare.getDefinitionRange()
335 0 : && getFillGradient() == rCompare.getFillGradient());
336 : }
337 :
338 0 : return false;
339 : }
340 :
341 504 : basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
342 : {
343 : // return the geometrically visible area
344 504 : return getOutputRange();
345 : }
346 :
347 : // provide unique ID
348 1775 : ImplPrimitive2DIDBlock(FillGradientPrimitive2D, PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D)
349 :
350 : } // end of namespace primitive2d
351 : } // end of namespace drawinglayer
352 :
353 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|