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 & rOuterColor) const45 void FillGradientPrimitive2D::generateMatricesAndColors( 46 std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, 47 basegfx::BColor& rOuterColor) 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( 77 getDefinitionRange(), 78 getOutputRange(), 79 aStart, 80 aEnd, 81 nSteps, 82 getFillGradient().getBorder(), 83 getFillGradient().getAngle()); 84 aGradient.appendTransformationsAndColors(rEntries, rOuterColor); 85 break; 86 } 87 case attribute::GRADIENTSTYLE_AXIAL: 88 { 89 texture::GeoTexSvxGradientAxial aGradient( 90 getDefinitionRange(), 91 getOutputRange(), 92 aStart, 93 aEnd, 94 nSteps, 95 getFillGradient().getBorder(), 96 getFillGradient().getAngle()); 97 aGradient.appendTransformationsAndColors(rEntries, rOuterColor); 98 break; 99 } 100 case attribute::GRADIENTSTYLE_RADIAL: 101 { 102 texture::GeoTexSvxGradientRadial aGradient( 103 getDefinitionRange(), 104 aStart, 105 aEnd, 106 nSteps, 107 getFillGradient().getBorder(), 108 getFillGradient().getOffsetX(), 109 getFillGradient().getOffsetY()); 110 aGradient.appendTransformationsAndColors(rEntries, rOuterColor); 111 break; 112 } 113 case attribute::GRADIENTSTYLE_ELLIPTICAL: 114 { 115 texture::GeoTexSvxGradientElliptical aGradient( 116 getDefinitionRange(), 117 aStart, 118 aEnd, 119 nSteps, 120 getFillGradient().getBorder(), 121 getFillGradient().getOffsetX(), 122 getFillGradient().getOffsetY(), 123 getFillGradient().getAngle()); 124 aGradient.appendTransformationsAndColors(rEntries, rOuterColor); 125 break; 126 } 127 case attribute::GRADIENTSTYLE_SQUARE: 128 { 129 texture::GeoTexSvxGradientSquare aGradient( 130 getDefinitionRange(), 131 aStart, 132 aEnd, 133 nSteps, 134 getFillGradient().getBorder(), 135 getFillGradient().getOffsetX(), 136 getFillGradient().getOffsetY(), 137 getFillGradient().getAngle()); 138 aGradient.appendTransformationsAndColors(rEntries, rOuterColor); 139 break; 140 } 141 case attribute::GRADIENTSTYLE_RECT: 142 { 143 texture::GeoTexSvxGradientRect aGradient( 144 getDefinitionRange(), 145 aStart, 146 aEnd, 147 nSteps, 148 getFillGradient().getBorder(), 149 getFillGradient().getOffsetX(), 150 getFillGradient().getOffsetY(), 151 getFillGradient().getAngle()); 152 aGradient.appendTransformationsAndColors(rEntries, rOuterColor); 153 break; 154 } 155 } 156 } 157 createOverlappingFill(const std::vector<drawinglayer::texture::B2DHomMatrixAndBColor> & rEntries,const basegfx::BColor & rOuterColor,const basegfx::B2DPolygon & rUnitPolygon) const158 Primitive2DSequence FillGradientPrimitive2D::createOverlappingFill( 159 const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, 160 const basegfx::BColor& rOuterColor, 161 const basegfx::B2DPolygon& rUnitPolygon) const 162 { 163 // prepare return value 164 Primitive2DSequence aRetval(rEntries.size() + 1); 165 166 // create solid fill with outmost color 167 aRetval[0] = Primitive2DReference( 168 new PolyPolygonColorPrimitive2D( 169 basegfx::B2DPolyPolygon( 170 basegfx::tools::createPolygonFromRect(getOutputRange())), 171 rOuterColor)); 172 173 // create solid fill steps 174 for(sal_uInt32 a(0); a < rEntries.size(); a++) 175 { 176 // create part polygon 177 basegfx::B2DPolygon aNewPoly(rUnitPolygon); 178 179 aNewPoly.transform(rEntries[a].maB2DHomMatrix); 180 181 // create solid fill 182 aRetval[a + 1] = Primitive2DReference( 183 new PolyPolygonColorPrimitive2D( 184 basegfx::B2DPolyPolygon(aNewPoly), 185 rEntries[a].maBColor)); 186 } 187 188 return aRetval; 189 } 190 createNonOverlappingFill(const std::vector<drawinglayer::texture::B2DHomMatrixAndBColor> & rEntries,const basegfx::BColor & rOuterColor,const basegfx::B2DPolygon & rUnitPolygon) const191 Primitive2DSequence FillGradientPrimitive2D::createNonOverlappingFill( 192 const std::vector< drawinglayer::texture::B2DHomMatrixAndBColor >& rEntries, 193 const basegfx::BColor& rOuterColor, 194 const basegfx::B2DPolygon& rUnitPolygon) const 195 { 196 // prepare return value 197 Primitive2DSequence aRetval(rEntries.size() + 1); 198 199 // get outmost viusible range from object 200 basegfx::B2DRange aOutmostRange(getOutputRange()); 201 basegfx::B2DPolyPolygon aCombinedPolyPoly; 202 203 if(rEntries.size()) 204 { 205 // extend aOutmostRange with first polygon 206 basegfx::B2DPolygon aFirstPoly(rUnitPolygon); 207 208 aFirstPoly.transform(rEntries[0].maB2DHomMatrix); 209 aCombinedPolyPoly.append(aFirstPoly); 210 aOutmostRange.expand(aFirstPoly.getB2DRange()); 211 } 212 213 // add outmost range to combined polypolygon (in 1st place), create first primitive 214 aCombinedPolyPoly.insert(0, basegfx::tools::createPolygonFromRect(aOutmostRange)); 215 aRetval[0] = Primitive2DReference( 216 new PolyPolygonColorPrimitive2D( 217 aCombinedPolyPoly, 218 rOuterColor)); 219 220 if(rEntries.size()) 221 { 222 // reuse first polygon, it's the second one 223 aCombinedPolyPoly.remove(0); 224 225 for(sal_uInt32 a(0); a < rEntries.size() - 1; a++) 226 { 227 // create next inner polygon, combinbe with last one 228 basegfx::B2DPolygon aNextPoly(rUnitPolygon); 229 230 aNextPoly.transform(rEntries[a + 1].maB2DHomMatrix); 231 aCombinedPolyPoly.append(aNextPoly); 232 233 // create primitive with correct color 234 aRetval[a + 1] = Primitive2DReference( 235 new PolyPolygonColorPrimitive2D( 236 aCombinedPolyPoly, 237 rEntries[a].maBColor)); 238 239 // reuse inner polygon, it's the 2nd one 240 aCombinedPolyPoly.remove(0); 241 } 242 243 // add last inner polygon with last color 244 aRetval[rEntries.size()] = Primitive2DReference( 245 new PolyPolygonColorPrimitive2D( 246 aCombinedPolyPoly, 247 rEntries[rEntries.size() - 1].maBColor)); 248 } 249 250 return aRetval; 251 } 252 createFill(bool bOverlapping) const253 Primitive2DSequence FillGradientPrimitive2D::createFill(bool bOverlapping) const 254 { 255 // prepare shape of the Unit Polygon 256 basegfx::B2DPolygon aUnitPolygon; 257 258 switch(getFillGradient().getStyle()) 259 { 260 case attribute::GRADIENTSTYLE_RADIAL: 261 case attribute::GRADIENTSTYLE_ELLIPTICAL: 262 { 263 aUnitPolygon = basegfx::tools::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 1.0); 264 break; 265 } 266 default: // GRADIENTSTYLE_LINEAR, attribute::GRADIENTSTYLE_AXIAL, attribute::GRADIENTSTYLE_SQUARE, attribute::GRADIENTSTYLE_RECT 267 { 268 aUnitPolygon = basegfx::tools::createPolygonFromRect(basegfx::B2DRange(-1.0, -1.0, 1.0, 1.0)); 269 break; 270 } 271 } 272 273 // get the transform matrices and colors (where colors 274 // will have one more entry that matrices) 275 std::vector< drawinglayer::texture::B2DHomMatrixAndBColor > aEntries; 276 basegfx::BColor aOuterColor; 277 278 generateMatricesAndColors(aEntries, aOuterColor); 279 280 if(bOverlapping) 281 { 282 return createOverlappingFill(aEntries, aOuterColor, aUnitPolygon); 283 } 284 else 285 { 286 return createNonOverlappingFill(aEntries, aOuterColor, aUnitPolygon); 287 } 288 } 289 create2DDecomposition(const geometry::ViewInformation2D &) const290 Primitive2DSequence FillGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 291 { 292 // default creates overlapping fill which works with AntiAliasing and without. 293 // The non-overlapping version does not create single filled polygons, but 294 // PolyPolygons where each one describes a 'ring' for the gradient such 295 // that the rings will not overlap. This is useful fir the old XOR-paint 296 // 'trick' of VCL which is recorded in Metafiles; so this version may be 297 // used from the MetafilePrimitive2D in it's decomposition. 298 299 if(!getFillGradient().isDefault()) 300 { 301 static bool bOverlapping(true); // allow to test non-overlapping in the debugger 302 303 return createFill(bOverlapping); 304 } 305 else 306 { 307 return Primitive2DSequence(); 308 } 309 } 310 FillGradientPrimitive2D(const basegfx::B2DRange & rOutputRange,const attribute::FillGradientAttribute & rFillGradient)311 FillGradientPrimitive2D::FillGradientPrimitive2D( 312 const basegfx::B2DRange& rOutputRange, 313 const attribute::FillGradientAttribute& rFillGradient) 314 : BufferedDecompositionPrimitive2D(), 315 maOutputRange(rOutputRange), 316 maDefinitionRange(rOutputRange), 317 maFillGradient(rFillGradient) 318 { 319 } 320 FillGradientPrimitive2D(const basegfx::B2DRange & rOutputRange,const basegfx::B2DRange & rDefinitionRange,const attribute::FillGradientAttribute & rFillGradient)321 FillGradientPrimitive2D::FillGradientPrimitive2D( 322 const basegfx::B2DRange& rOutputRange, 323 const basegfx::B2DRange& rDefinitionRange, 324 const attribute::FillGradientAttribute& rFillGradient) 325 : BufferedDecompositionPrimitive2D(), 326 maOutputRange(rOutputRange), 327 maDefinitionRange(rDefinitionRange), 328 maFillGradient(rFillGradient) 329 { 330 } 331 operator ==(const BasePrimitive2D & rPrimitive) const332 bool FillGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 333 { 334 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) 335 { 336 const FillGradientPrimitive2D& rCompare = (FillGradientPrimitive2D&)rPrimitive; 337 338 return (getOutputRange() == rCompare.getOutputRange() 339 && getDefinitionRange() == rCompare.getDefinitionRange() 340 && getFillGradient() == rCompare.getFillGradient()); 341 } 342 343 return false; 344 } 345 getB2DRange(const geometry::ViewInformation2D &) const346 basegfx::B2DRange FillGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 347 { 348 // return the geometrically visible area 349 return getOutputRange(); 350 } 351 352 // provide unique ID 353 ImplPrimitrive2DIDBlock(FillGradientPrimitive2D, PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D) 354 355 } // end of namespace primitive2d 356 } // end of namespace drawinglayer 357 358 ////////////////////////////////////////////////////////////////////////////// 359 // eof 360