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 3 : void FillGradientPrimitive2D::generateMatricesAndColors(
39 : std::vector< basegfx::B2DHomMatrix >& rMatrices,
40 : std::vector< basegfx::BColor >& rColors) const
41 : {
42 3 : rMatrices.clear();
43 3 : rColors.clear();
44 :
45 : // make sure steps is not too high/low
46 3 : const basegfx::BColor aStart(getFillGradient().getStartColor());
47 3 : const basegfx::BColor aEnd(getFillGradient().getEndColor());
48 3 : const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5));
49 3 : sal_uInt32 nSteps(getFillGradient().getSteps());
50 :
51 3 : if(nSteps == 0)
52 : {
53 0 : nSteps = nMaxSteps;
54 : }
55 :
56 3 : if(nSteps < 2)
57 : {
58 0 : nSteps = 2;
59 : }
60 :
61 3 : if(nSteps > nMaxSteps)
62 : {
63 0 : nSteps = nMaxSteps;
64 : }
65 :
66 3 : nSteps = std::max(sal_uInt32(1), nSteps);
67 :
68 3 : switch(getFillGradient().getStyle())
69 : {
70 : case attribute::GRADIENTSTYLE_LINEAR:
71 : {
72 3 : texture::GeoTexSvxGradientLinear aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getAngle());
73 3 : aGradient.appendTransformations(rMatrices);
74 3 : aGradient.appendColors(rColors);
75 3 : break;
76 : }
77 : case attribute::GRADIENTSTYLE_AXIAL:
78 : {
79 0 : texture::GeoTexSvxGradientAxial aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getAngle());
80 0 : aGradient.appendTransformations(rMatrices);
81 0 : aGradient.appendColors(rColors);
82 0 : break;
83 : }
84 : case attribute::GRADIENTSTYLE_RADIAL:
85 : {
86 0 : texture::GeoTexSvxGradientRadial aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY());
87 0 : aGradient.appendTransformations(rMatrices);
88 0 : aGradient.appendColors(rColors);
89 0 : break;
90 : }
91 : case attribute::GRADIENTSTYLE_ELLIPTICAL:
92 : {
93 0 : texture::GeoTexSvxGradientElliptical aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle());
94 0 : aGradient.appendTransformations(rMatrices);
95 0 : aGradient.appendColors(rColors);
96 0 : break;
97 : }
98 : case attribute::GRADIENTSTYLE_SQUARE:
99 : {
100 0 : texture::GeoTexSvxGradientSquare aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle());
101 0 : aGradient.appendTransformations(rMatrices);
102 0 : aGradient.appendColors(rColors);
103 0 : break;
104 : }
105 : case attribute::GRADIENTSTYLE_RECT:
106 : {
107 0 : texture::GeoTexSvxGradientRect aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle());
108 0 : aGradient.appendTransformations(rMatrices);
109 0 : aGradient.appendColors(rColors);
110 0 : break;
111 : }
112 3 : }
113 3 : }
114 :
115 3 : Primitive2DSequence FillGradientPrimitive2D::createOverlappingFill(
116 : const std::vector< basegfx::B2DHomMatrix >& rMatrices,
117 : const std::vector< basegfx::BColor >& rColors,
118 : const basegfx::B2DPolygon& rUnitPolygon) const
119 : {
120 : // prepare return value
121 3 : Primitive2DSequence aRetval(rColors.size() ? rMatrices.size() + 1 : rMatrices.size());
122 :
123 : // create solid fill with start color
124 3 : if(!rColors.empty())
125 : {
126 : // create primitive
127 : const Primitive2DReference xRef(
128 : new PolyPolygonColorPrimitive2D(
129 3 : basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(getObjectRange())),
130 3 : rColors[0]));
131 3 : aRetval[0] = xRef;
132 : }
133 :
134 : // create solid fill steps
135 6 : for(sal_uInt32 a(0); a < rMatrices.size(); a++)
136 : {
137 : // create part polygon
138 3 : basegfx::B2DPolygon aNewPoly(rUnitPolygon);
139 3 : aNewPoly.transform(rMatrices[a]);
140 :
141 : // create solid fill
142 : const Primitive2DReference xRef(
143 : new PolyPolygonColorPrimitive2D(
144 : basegfx::B2DPolyPolygon(aNewPoly),
145 3 : rColors[a + 1]));
146 3 : aRetval[a + 1] = xRef;
147 3 : }
148 :
149 3 : return aRetval;
150 : }
151 :
152 0 : Primitive2DSequence FillGradientPrimitive2D::createNonOverlappingFill(
153 : const std::vector< basegfx::B2DHomMatrix >& rMatrices,
154 : const std::vector< basegfx::BColor >& rColors,
155 : const basegfx::B2DPolygon& rUnitPolygon) const
156 : {
157 : // prepare return value
158 0 : Primitive2DSequence aRetval;
159 0 : const sal_uInt32 nMatricesSize(rMatrices.size());
160 :
161 0 : if(nMatricesSize)
162 : {
163 0 : basegfx::B2DPolygon aOuterPoly(rUnitPolygon);
164 0 : aOuterPoly.transform(rMatrices[0]);
165 0 : basegfx::B2DPolyPolygon aCombinedPolyPoly(aOuterPoly);
166 0 : const sal_uInt32 nEntryCount(rColors.size() ? rMatrices.size() + 1 : rMatrices.size());
167 0 : sal_uInt32 nIndex(0);
168 :
169 0 : aRetval.realloc(nEntryCount);
170 :
171 0 : if(!rColors.empty())
172 : {
173 0 : basegfx::B2DRange aOuterPolyRange(aOuterPoly.getB2DRange());
174 0 : aOuterPolyRange.expand(getObjectRange());
175 0 : aCombinedPolyPoly.append(basegfx::tools::createPolygonFromRect(aOuterPolyRange));
176 0 : aRetval[nIndex++] = Primitive2DReference(new PolyPolygonColorPrimitive2D(aCombinedPolyPoly, rColors[0]));
177 0 : aCombinedPolyPoly = basegfx::B2DPolyPolygon(aOuterPoly);
178 : }
179 :
180 0 : for(sal_uInt32 a(1); a < nMatricesSize - 1; a++)
181 : {
182 0 : basegfx::B2DPolygon aInnerPoly(rUnitPolygon);
183 0 : aInnerPoly.transform(rMatrices[a]);
184 0 : aCombinedPolyPoly.append(aInnerPoly);
185 0 : aRetval[nIndex++] = Primitive2DReference(new PolyPolygonColorPrimitive2D(aCombinedPolyPoly, rColors[a]));
186 0 : aCombinedPolyPoly = basegfx::B2DPolyPolygon(aInnerPoly);
187 0 : }
188 :
189 0 : if(!rColors.empty())
190 : {
191 0 : aRetval[nIndex] = Primitive2DReference(new PolyPolygonColorPrimitive2D(
192 0 : aCombinedPolyPoly, rColors[rColors.size() - 1]));
193 0 : }
194 : }
195 :
196 0 : return aRetval;
197 : }
198 :
199 3 : Primitive2DSequence FillGradientPrimitive2D::createFill(bool bOverlapping) const
200 : {
201 : // prepare shape of the Unit Polygon
202 3 : basegfx::B2DPolygon aUnitPolygon;
203 :
204 6 : if(attribute::GRADIENTSTYLE_RADIAL == getFillGradient().getStyle()
205 3 : || attribute::GRADIENTSTYLE_ELLIPTICAL == getFillGradient().getStyle())
206 : {
207 : aUnitPolygon = basegfx::tools::createPolygonFromCircle(
208 0 : basegfx::B2DPoint(0,0), 1);
209 : }
210 3 : else if(attribute::GRADIENTSTYLE_LINEAR == maFillGradient.getStyle())
211 : {
212 3 : aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(0, 0, 1, 1));
213 : }
214 : else
215 : {
216 0 : aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(-1, -1, 1, 1));
217 : }
218 :
219 : // get the transform matrices and colors (where colors
220 : // will have one more entry that matrices)
221 3 : std::vector< basegfx::B2DHomMatrix > aMatrices;
222 3 : std::vector< basegfx::BColor > aColors;
223 3 : generateMatricesAndColors(aMatrices, aColors);
224 :
225 3 : if(bOverlapping)
226 : {
227 3 : return createOverlappingFill(aMatrices, aColors, aUnitPolygon);
228 : }
229 : else
230 : {
231 0 : return createNonOverlappingFill(aMatrices, aColors, aUnitPolygon);
232 3 : }
233 : }
234 :
235 3 : Primitive2DSequence FillGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
236 : {
237 : // default creates overlapping fill which works with AntiAliasing and without.
238 : // The non-overlapping version does not create single filled polygons, but
239 : // PolyPolygons where each one describes a 'ring' for the gradient such
240 : // that the rings will not overlap. This is useful fir the old XOR-paint
241 : // 'trick' of VCL which is recorded in Metafiles; so this version may be
242 : // used from the MetafilePrimitive2D in it's decomposition.
243 :
244 3 : if(!getFillGradient().isDefault())
245 : {
246 3 : return createFill(true);
247 : }
248 : else
249 : {
250 0 : return Primitive2DSequence();
251 : }
252 : }
253 :
254 30 : FillGradientPrimitive2D::FillGradientPrimitive2D(
255 : const basegfx::B2DRange& rObjectRange,
256 : const attribute::FillGradientAttribute& rFillGradient)
257 : : BufferedDecompositionPrimitive2D(),
258 : maObjectRange(rObjectRange),
259 30 : maFillGradient(rFillGradient)
260 : {
261 30 : }
262 :
263 0 : bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
264 : {
265 0 : if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
266 : {
267 0 : const FillGradientPrimitive2D& rCompare = (FillGradientPrimitive2D&)rPrimitive;
268 :
269 0 : return (getObjectRange() == rCompare.getObjectRange()
270 0 : && getFillGradient() == rCompare.getFillGradient());
271 : }
272 :
273 0 : return false;
274 : }
275 :
276 30 : basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
277 : {
278 : // return ObjectRange
279 30 : return getObjectRange();
280 : }
281 :
282 : // provide unique ID
283 3 : ImplPrimitrive2DIDBlock(FillGradientPrimitive2D, PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D)
284 :
285 : } // end of namespace primitive2d
286 : } // end of namespace drawinglayer
287 :
288 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|