1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_drawinglayer.hxx" 26 27 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> 28 #include <basegfx/polygon/b2dpolygon.hxx> 29 #include <basegfx/polygon/b2dpolygontools.hxx> 30 #include <drawinglayer/texture/texture.hxx> 31 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> 32 #include <basegfx/tools/canvastools.hxx> 33 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> 34 35 ////////////////////////////////////////////////////////////////////////////// 36 37 using namespace com::sun::star; 38 39 ////////////////////////////////////////////////////////////////////////////// 40 41 namespace drawinglayer 42 { 43 namespace primitive2d 44 { generateMatricesAndColors(std::vector<drawinglayer::texture::B2DHomMatrixAndBColor> & rEntries,basegfx::BColor & rOutmostColor) const45 void FillGradientPrimitive2D::generateMatricesAndColors( 46 std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, 47 basegfx::BColor& rOutmostColor) const 48 { 49 rEntries.clear(); 50 51 // make sure steps is not too high/low 52 const basegfx::BColor aStart(getFillGradient().getStartColor()); 53 const basegfx::BColor aEnd(getFillGradient().getEndColor()); 54 const sal_uInt32 nMaxSteps(sal_uInt32((aStart.getMaximumDistance(aEnd) * 127.5) + 0.5)); 55 sal_uInt32 nSteps(getFillGradient().getSteps()); 56 57 if(nSteps == 0) 58 { 59 nSteps = nMaxSteps; 60 } 61 62 if(nSteps < 2) 63 { 64 nSteps = 2; 65 } 66 67 if(nSteps > nMaxSteps) 68 { 69 nSteps = nMaxSteps; 70 } 71 72 switch(getFillGradient().getStyle()) 73 { 74 case attribute::GRADIENTSTYLE_LINEAR: 75 { 76 texture::GeoTexSvxGradientLinear aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getAngle()); 77 aGradient.appendTransformationsAndColors(rEntries, rOutmostColor); 78 break; 79 } 80 case attribute::GRADIENTSTYLE_AXIAL: 81 { 82 texture::GeoTexSvxGradientAxial aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getAngle()); 83 aGradient.appendTransformationsAndColors(rEntries, rOutmostColor); 84 break; 85 } 86 case attribute::GRADIENTSTYLE_RADIAL: 87 { 88 texture::GeoTexSvxGradientRadial aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY()); 89 aGradient.appendTransformationsAndColors(rEntries, rOutmostColor); 90 break; 91 } 92 case attribute::GRADIENTSTYLE_ELLIPTICAL: 93 { 94 texture::GeoTexSvxGradientElliptical aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle()); 95 aGradient.appendTransformationsAndColors(rEntries, rOutmostColor); 96 break; 97 } 98 case attribute::GRADIENTSTYLE_SQUARE: 99 { 100 texture::GeoTexSvxGradientSquare aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle()); 101 aGradient.appendTransformationsAndColors(rEntries, rOutmostColor); 102 break; 103 } 104 case attribute::GRADIENTSTYLE_RECT: 105 { 106 texture::GeoTexSvxGradientRect aGradient(getObjectRange(), aStart, aEnd, nSteps, getFillGradient().getBorder(), getFillGradient().getOffsetX(), getFillGradient().getOffsetY(), getFillGradient().getAngle()); 107 aGradient.appendTransformationsAndColors(rEntries, rOutmostColor); 108 break; 109 } 110 } 111 } 112 createOverlappingFill(const std::vector<drawinglayer::texture::B2DHomMatrixAndBColor> & rEntries,const basegfx::BColor & rOutmostColor,const basegfx::B2DPolygon & rUnitPolygon) const113 Primitive2DSequence FillGradientPrimitive2D::createOverlappingFill( 114 const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, 115 const basegfx::BColor& rOutmostColor, 116 const basegfx::B2DPolygon& rUnitPolygon) const 117 { 118 // prepare return value 119 Primitive2DSequence aRetval(rEntries.size() + 1); 120 121 // create solid fill with outmost color 122 aRetval[0] = Primitive2DReference( 123 new PolyPolygonColorPrimitive2D( 124 basegfx::B2DPolyPolygon(basegfx::tools::createPolygonFromRect(getObjectRange())), 125 rOutmostColor)); 126 127 // create solid fill steps 128 for(sal_uInt32 a(0); a < rEntries.size(); a++) 129 { 130 // create part polygon 131 basegfx::B2DPolygon aNewPoly(rUnitPolygon); 132 133 aNewPoly.transform(rEntries[a].maB2DHomMatrix); 134 135 // create solid fill 136 aRetval[a + 1] = Primitive2DReference( 137 new PolyPolygonColorPrimitive2D( 138 basegfx::B2DPolyPolygon(aNewPoly), 139 rEntries[a].maBColor)); 140 } 141 142 return aRetval; 143 } 144 createNonOverlappingFill(const std::vector<drawinglayer::texture::B2DHomMatrixAndBColor> & rEntries,const basegfx::BColor & rOutmostColor,const basegfx::B2DPolygon & rUnitPolygon) const145 Primitive2DSequence FillGradientPrimitive2D::createNonOverlappingFill( 146 const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, 147 const basegfx::BColor& rOutmostColor, 148 const basegfx::B2DPolygon& rUnitPolygon) const 149 { 150 // prepare return value 151 Primitive2DSequence aRetval(rEntries.size() + 1); 152 153 // get outmost range from object 154 basegfx::B2DRange aOutmostRange(getObjectRange()); 155 basegfx::B2DPolyPolygon aCombinedPolyPoly; 156 157 if(rEntries.size()) 158 { 159 // extend aOutmostRange with first polygon 160 basegfx::B2DPolygon aFirstPoly(rUnitPolygon); 161 162 aFirstPoly.transform(rEntries[0].maB2DHomMatrix); 163 aCombinedPolyPoly.append(aFirstPoly); 164 aOutmostRange.expand(aFirstPoly.getB2DRange()); 165 } 166 167 // add outmost range to combined polypolygon (in 1st place), create first primitive 168 aCombinedPolyPoly.insert(0, basegfx::tools::createPolygonFromRect(aOutmostRange)); 169 aRetval[0] = Primitive2DReference( 170 new PolyPolygonColorPrimitive2D( 171 aCombinedPolyPoly, 172 rOutmostColor)); 173 174 if(rEntries.size()) 175 { 176 // reuse first polygon, it's the second one 177 aCombinedPolyPoly.remove(0); 178 179 for(sal_uInt32 a(0); a < rEntries.size() - 1; a++) 180 { 181 // create next inner polygon, combinbe with last one 182 basegfx::B2DPolygon aNextPoly(rUnitPolygon); 183 184 aNextPoly.transform(rEntries[a + 1].maB2DHomMatrix); 185 aCombinedPolyPoly.append(aNextPoly); 186 187 // create primitive with correct color 188 aRetval[a + 1] = Primitive2DReference( 189 new PolyPolygonColorPrimitive2D( 190 aCombinedPolyPoly, 191 rEntries[a].maBColor)); 192 193 // reuse inner polygon, it's the 2nd one 194 aCombinedPolyPoly.remove(0); 195 } 196 197 // add last inner polygon with last color 198 aRetval[rEntries.size()] = Primitive2DReference( 199 new PolyPolygonColorPrimitive2D( 200 aCombinedPolyPoly, 201 rEntries[rEntries.size() - 1].maBColor)); 202 } 203 204 return aRetval; 205 } 206 createFill(bool bOverlapping) const207 Primitive2DSequence FillGradientPrimitive2D::createFill(bool bOverlapping) const 208 { 209 // prepare shape of the Unit Polygon 210 basegfx::B2DPolygon aUnitPolygon; 211 212 switch(getFillGradient().getStyle()) 213 { 214 case attribute::GRADIENTSTYLE_RADIAL: 215 case attribute::GRADIENTSTYLE_ELLIPTICAL: 216 { 217 aUnitPolygon = basegfx::tools::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0); 218 break; 219 } 220 default: // GRADIENTSTYLE_LINEAR, attribute::GRADIENTSTYLE_AXIAL, attribute::GRADIENTSTYLE_SQUARE, attribute::GRADIENTSTYLE_RECT 221 { 222 aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0)); 223 break; 224 } 225 } 226 227 // get the transform matrices and colors (where colors 228 // will have one more entry that matrices) 229 std::vector< drawinglayer::texture::B2DHomMatrixAndBColor > aEntries; 230 basegfx::BColor aOutmostColor; 231 232 generateMatricesAndColors(aEntries, aOutmostColor); 233 234 if(bOverlapping) 235 { 236 return createOverlappingFill(aEntries, aOutmostColor, aUnitPolygon); 237 } 238 else 239 { 240 return createNonOverlappingFill(aEntries, aOutmostColor, aUnitPolygon); 241 } 242 } 243 create2DDecomposition(const geometry::ViewInformation2D &) const244 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 static bool bOverlapping(true); // allow to test non-overlapping in the debugger 256 257 return createFill(bOverlapping); 258 } 259 else 260 { 261 return Primitive2DSequence(); 262 } 263 } 264 FillGradientPrimitive2D(const basegfx::B2DRange & rObjectRange,const attribute::FillGradientAttribute & rFillGradient)265 FillGradientPrimitive2D::FillGradientPrimitive2D( 266 const basegfx::B2DRange& rObjectRange, 267 const attribute::FillGradientAttribute& rFillGradient) 268 : BufferedDecompositionPrimitive2D(), 269 maObjectRange(rObjectRange), 270 maFillGradient(rFillGradient) 271 { 272 } 273 operator ==(const BasePrimitive2D & rPrimitive) const274 bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 275 { 276 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) 277 { 278 const FillGradientPrimitive2D& rCompare = (FillGradientPrimitive2D&)rPrimitive; 279 280 return (getObjectRange() == rCompare.getObjectRange() 281 && getFillGradient() == rCompare.getFillGradient()); 282 } 283 284 return false; 285 } 286 getB2DRange(const geometry::ViewInformation2D &) const287 basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 288 { 289 // return ObjectRange 290 return getObjectRange(); 291 } 292 293 // provide unique ID 294 ImplPrimitrive2DIDBlock(FillGradientPrimitive2D, PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D) 295 296 } // end of namespace primitive2d 297 } // end of namespace drawinglayer 298 299 ////////////////////////////////////////////////////////////////////////////// 300 // eof 301