1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_drawinglayer.hxx"
30 
31 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
32 #include <basegfx/polygon/b2dpolygon.hxx>
33 #include <basegfx/polygon/b2dpolygontools.hxx>
34 #include <drawinglayer/texture/texture.hxx>
35 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
36 #include <basegfx/tools/canvastools.hxx>
37 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
38 
39 //////////////////////////////////////////////////////////////////////////////
40 
41 using namespace com::sun::star;
42 
43 //////////////////////////////////////////////////////////////////////////////
44 
45 namespace drawinglayer
46 {
47 	namespace primitive2d
48 	{
49         void FillGradientPrimitive2D::generateMatricesAndColors(
50             std::vector< basegfx::B2DHomMatrix >& rMatrices,
51             std::vector< basegfx::BColor >& rColors) const
52         {
53             rMatrices.clear();
54             rColors.clear();
55 
56 			// make sure steps is not too high/low
57 			const basegfx::BColor aStart(getFillGradient().getStartColor());
58 			const basegfx::BColor aEnd(getFillGradient().getEndColor());
59 			const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5));
60 			sal_uInt32 nSteps(getFillGradient().getSteps());
61 
62 			if(nSteps == 0)
63 			{
64 				nSteps = nMaxSteps;
65 			}
66 
67 			if(nSteps < 2)
68 			{
69 				nSteps = 2;
70 			}
71 
72 			if(nSteps > nMaxSteps)
73 			{
74 				nSteps = nMaxSteps;
75 			}
76 
77 			switch(getFillGradient().getStyle())
78 			{
79 				case attribute::GRADIENTSTYLE_LINEAR:
80 				{
81 					texture::GeoTexSvxGradientLinear aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getAngle());
82 					aGradient.appendTransformations(rMatrices);
83 					aGradient.appendColors(rColors);
84 					break;
85 				}
86 				case attribute::GRADIENTSTYLE_AXIAL:
87 				{
88 					texture::GeoTexSvxGradientAxial aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getAngle());
89 					aGradient.appendTransformations(rMatrices);
90 					aGradient.appendColors(rColors);
91 					break;
92 				}
93 				case attribute::GRADIENTSTYLE_RADIAL:
94 				{
95 					texture::GeoTexSvxGradientRadial aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY());
96 					aGradient.appendTransformations(rMatrices);
97 					aGradient.appendColors(rColors);
98 					break;
99 				}
100 				case attribute::GRADIENTSTYLE_ELLIPTICAL:
101 				{
102 					texture::GeoTexSvxGradientElliptical aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle());
103 					aGradient.appendTransformations(rMatrices);
104 					aGradient.appendColors(rColors);
105 					break;
106 				}
107 				case attribute::GRADIENTSTYLE_SQUARE:
108 				{
109 					texture::GeoTexSvxGradientSquare aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle());
110 					aGradient.appendTransformations(rMatrices);
111 					aGradient.appendColors(rColors);
112 					break;
113 				}
114 				case attribute::GRADIENTSTYLE_RECT:
115 				{
116 					texture::GeoTexSvxGradientRect aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle());
117 					aGradient.appendTransformations(rMatrices);
118 					aGradient.appendColors(rColors);
119 					break;
120 				}
121 			}
122         }
123 
124         Primitive2DSequence FillGradientPrimitive2D::createOverlappingFill(
125             const std::vector< basegfx::B2DHomMatrix >& rMatrices,
126             const std::vector< basegfx::BColor >& rColors,
127             const basegfx::B2DPolygon& rUnitPolygon) const
128         {
129 			// prepare return value
130 			Primitive2DSequence aRetval(rColors.size() ? rMatrices.size() + 1 : rMatrices.size());
131 
132             // create solid fill with start color
133 		    if(rColors.size())
134 		    {
135 			    // create primitive
136 			    const Primitive2DReference xRef(
137                     new PolyPolygonColorPrimitive2D(
138                         basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(getObjectRange())),
139                         rColors[0]));
140 			    aRetval[0] = xRef;
141 		    }
142 
143 		    // create solid fill steps
144 		    for(sal_uInt32 a(0); a < rMatrices.size(); a++)
145 		    {
146 			    // create part polygon
147 			    basegfx::B2DPolygon aNewPoly(rUnitPolygon);
148 			    aNewPoly.transform(rMatrices[a]);
149 
150 			    // create solid fill
151 			    const Primitive2DReference xRef(
152                     new PolyPolygonColorPrimitive2D(
153                         basegfx::B2DPolyPolygon(aNewPoly),
154                         rColors[a + 1]));
155 			    aRetval[a + 1] = xRef;
156 		    }
157 
158             return aRetval;
159         }
160 
161         Primitive2DSequence FillGradientPrimitive2D::createNonOverlappingFill(
162             const std::vector< basegfx::B2DHomMatrix >& rMatrices,
163             const std::vector< basegfx::BColor >& rColors,
164             const basegfx::B2DPolygon& rUnitPolygon) const
165         {
166 			// prepare return value
167 			Primitive2DSequence aRetval;
168             const sal_uInt32 nMatricesSize(rMatrices.size());
169 
170             if(nMatricesSize)
171             {
172 			    basegfx::B2DPolygon aOuterPoly(rUnitPolygon);
173 			    aOuterPoly.transform(rMatrices[0]);
174                 basegfx::B2DPolyPolygon aCombinedPolyPoly(aOuterPoly);
175     			const sal_uInt32 nEntryCount(rColors.size() ? rMatrices.size() + 1 : rMatrices.size());
176                 sal_uInt32 nIndex(0);
177 
178                 aRetval.realloc(nEntryCount);
179 
180 		        if(rColors.size())
181 		        {
182                     basegfx::B2DRange aOuterPolyRange(aOuterPoly.getB2DRange());
183                     aOuterPolyRange.expand(getObjectRange());
184                     aCombinedPolyPoly.append(basegfx::tools::createPolygonFromRect(aOuterPolyRange));
185                     aRetval[nIndex++] = Primitive2DReference(new PolyPolygonColorPrimitive2D(aCombinedPolyPoly, rColors[0]));
186                     aCombinedPolyPoly = basegfx::B2DPolyPolygon(aOuterPoly);
187                 }
188 
189                 for(sal_uInt32 a(1); a < nMatricesSize - 1; a++)
190 		        {
191                     basegfx::B2DPolygon aInnerPoly(rUnitPolygon);
192 			        aInnerPoly.transform(rMatrices[a]);
193                     aCombinedPolyPoly.append(aInnerPoly);
194 			        aRetval[nIndex++] = Primitive2DReference(new PolyPolygonColorPrimitive2D(aCombinedPolyPoly, rColors[a]));
195                     aCombinedPolyPoly = basegfx::B2DPolyPolygon(aInnerPoly);
196                 }
197 
198 		        if(rColors.size())
199 		        {
200                     aRetval[nIndex] = Primitive2DReference(new PolyPolygonColorPrimitive2D(
201                         aCombinedPolyPoly, rColors[rColors.size() - 1]));
202                 }
203             }
204 
205             return aRetval;
206         }
207 
208 		Primitive2DSequence FillGradientPrimitive2D::createFill(bool bOverlapping) const
209 		{
210             // prepare shape of the Unit Polygon
211 			basegfx::B2DPolygon aUnitPolygon;
212 
213 			if(attribute::GRADIENTSTYLE_RADIAL == getFillGradient().getStyle()
214                 || attribute::GRADIENTSTYLE_ELLIPTICAL == getFillGradient().getStyle())
215 			{
216 				aUnitPolygon = basegfx::tools::createPolygonFromCircle(
217                     basegfx::B2DPoint(0,0), 1);
218 			}
219 			else if(attribute::GRADIENTSTYLE_LINEAR == maFillGradient.getStyle())
220 			{
221 				aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(0, 0, 1, 1));
222 			}
223 			else
224 			{
225 				aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(-1, -1, 1, 1));
226 			}
227 
228             // get the transform matrices and colors (where colors
229             // will have one more entry that matrices)
230 			std::vector< basegfx::B2DHomMatrix > aMatrices;
231 			std::vector< basegfx::BColor > aColors;
232             generateMatricesAndColors(aMatrices, aColors);
233 
234             if(bOverlapping)
235             {
236                 return createOverlappingFill(aMatrices, aColors, aUnitPolygon);
237             }
238             else
239             {
240                 return createNonOverlappingFill(aMatrices, aColors, aUnitPolygon);
241             }
242         }
243 
244 		Primitive2DSequence FillGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
245 		{
246             // default creates overlapping fill which works with AntiAliasing and without.
247             // The non-overlapping version does not create single filled polygons, but
248             // PolyPolygons where each one describes a 'ring' for the gradient such
249             // that the rings will not overlap. This is useful fir the old XOR-paint
250             // 'trick' of VCL which is recorded in Metafiles; so this version may be
251             // used from the MetafilePrimitive2D in it's decomposition.
252 
253             if(!getFillGradient().isDefault())
254             {
255         		return createFill(true);
256             }
257             else
258             {
259                 return Primitive2DSequence();
260             }
261 		}
262 
263 		FillGradientPrimitive2D::FillGradientPrimitive2D(
264 			const basegfx::B2DRange& rObjectRange,
265 			const attribute::FillGradientAttribute& rFillGradient)
266 		:	BufferedDecompositionPrimitive2D(),
267 			maObjectRange(rObjectRange),
268 			maFillGradient(rFillGradient)
269 		{
270 		}
271 
272 		bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
273 		{
274 			if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
275 			{
276 				const FillGradientPrimitive2D& rCompare = (FillGradientPrimitive2D&)rPrimitive;
277 
278 				return (getObjectRange() == rCompare.getObjectRange()
279 					&& getFillGradient() == rCompare.getFillGradient());
280 			}
281 
282 			return false;
283 		}
284 
285 		basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
286 		{
287 			// return ObjectRange
288 			return getObjectRange();
289 		}
290 
291 		// provide unique ID
292 		ImplPrimitrive2DIDBlock(FillGradientPrimitive2D, PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D)
293 
294 	} // end of namespace primitive2d
295 } // end of namespace drawinglayer
296 
297 //////////////////////////////////////////////////////////////////////////////
298 // eof
299