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/processor2d/vclprocessor2d.hxx> 28 #include <drawinglayer/primitive2d/textprimitive2d.hxx> 29 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> 30 #include <tools/debug.hxx> 31 #include <vcl/outdev.hxx> 32 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 33 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> 34 #include <vclhelperbitmaptransform.hxx> 35 #include <basegfx/polygon/b2dpolygontools.hxx> 36 #include <vclhelperbitmaprender.hxx> 37 #include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> 38 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> 39 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> 40 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx> 41 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 42 #include <basegfx/polygon/b2dpolypolygontools.hxx> 43 #include <vclhelperbufferdevice.hxx> 44 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> 45 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 46 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> 47 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 48 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> 49 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> 50 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> 51 #include <svl/ctloptions.hxx> 52 #include <vcl/svapp.hxx> 53 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> 54 #include <tools/diagnose_ex.h> 55 #include <vcl/metric.hxx> 56 #include <drawinglayer/primitive2d/textenumsprimitive2d.hxx> 57 #include <drawinglayer/primitive2d/epsprimitive2d.hxx> 58 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> 59 #include <basegfx/color/bcolor.hxx> 60 #include <basegfx/matrix/b2dhommatrixtools.hxx> 61 #include <vcl/graph.hxx> 62 63 ////////////////////////////////////////////////////////////////////////////// 64 // control support 65 66 #include <com/sun/star/awt/XWindow2.hpp> 67 #include <com/sun/star/awt/PosSize.hpp> 68 #include <com/sun/star/awt/XView.hpp> 69 #include <drawinglayer/primitive2d/controlprimitive2d.hxx> 70 #include <drawinglayer/primitive2d/textlayoutdevice.hxx> 71 72 ////////////////////////////////////////////////////////////////////////////// 73 // for test, can be removed again 74 75 #include <basegfx/polygon/b2dpolygonclipper.hxx> 76 #include <basegfx/polygon/b2dtrapezoid.hxx> 77 78 ////////////////////////////////////////////////////////////////////////////// 79 80 using namespace com::sun::star; 81 82 ////////////////////////////////////////////////////////////////////////////// 83 84 namespace 85 { 86 sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit) 87 { 88 // use color distance, assume to do every color step 89 sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0)); 90 91 if(nSteps) 92 { 93 // calc discrete length to change color each disctete unit (pixel) 94 const sal_uInt32 nDistSteps(basegfx::fround(fDelta / fDiscreteUnit)); 95 96 nSteps = std::min(nSteps, nDistSteps); 97 } 98 99 // reduce quality to 3 discrete units or every 3rd color step for rendering 100 nSteps /= 2; 101 102 // roughly cut when too big or too small (not full quality, reduce complexity) 103 nSteps = std::min(nSteps, sal_uInt32(255)); 104 nSteps = std::max(nSteps, sal_uInt32(1)); 105 106 return nSteps; 107 } 108 } // end of anonymous namespace 109 110 ////////////////////////////////////////////////////////////////////////////// 111 112 namespace drawinglayer 113 { 114 namespace processor2d 115 { 116 ////////////////////////////////////////////////////////////////////////////// 117 // UNO class usages 118 using ::com::sun::star::uno::Reference; 119 using ::com::sun::star::uno::UNO_QUERY; 120 using ::com::sun::star::uno::UNO_QUERY_THROW; 121 using ::com::sun::star::uno::Exception; 122 using ::com::sun::star::awt::XView; 123 using ::com::sun::star::awt::XGraphics; 124 using ::com::sun::star::awt::XWindow; 125 using ::com::sun::star::awt::PosSize::POSSIZE; 126 127 ////////////////////////////////////////////////////////////////////////////// 128 // rendering support 129 130 // directdraw of text simple portion or decorated portion primitive. When decorated, all the extra 131 // information is translated to VCL parameters and set at the font. 132 // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring 133 // for VCL) 134 void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate) 135 { 136 // decompose matrix to have position and size of text 137 basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rTextCandidate.getTextTransform()); 138 basegfx::B2DVector aFontScaling, aTranslate; 139 double fRotate, fShearX; 140 aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX); 141 bool bPrimitiveAccepted(false); 142 143 if(basegfx::fTools::equalZero(fShearX)) 144 { 145 if(basegfx::fTools::less(aFontScaling.getX(), 0.0) && basegfx::fTools::less(aFontScaling.getY(), 0.0)) 146 { 147 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can 148 // be expressed as rotation by PI. Use this since the Font rendering will not 149 // apply the negative scales in any form 150 aFontScaling = basegfx::absolute(aFontScaling); 151 fRotate += F_PI; 152 } 153 154 if(basegfx::fTools::more(aFontScaling.getX(), 0.0) && basegfx::fTools::more(aFontScaling.getY(), 0.0)) 155 { 156 // Get the VCL font (use FontHeight as FontWidth) 157 Font aFont(primitive2d::getVclFontFromFontAttribute( 158 rTextCandidate.getFontAttribute(), 159 aFontScaling.getX(), 160 aFontScaling.getY(), 161 fRotate, 162 rTextCandidate.getLocale())); 163 164 // handle additional font attributes 165 const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = 166 dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>( &rTextCandidate ); 167 168 if( pTCPP != NULL ) 169 { 170 171 // set the color of text decorations 172 const basegfx::BColor aTextlineColor = maBColorModifierStack.getModifiedColor(pTCPP->getTextlineColor()); 173 mpOutputDevice->SetTextLineColor( Color(aTextlineColor) ); 174 175 // set Overline attribute 176 const FontUnderline eFontOverline(primitive2d::mapTextLineToFontUnderline( pTCPP->getFontOverline() )); 177 if( eFontOverline != UNDERLINE_NONE ) 178 { 179 aFont.SetOverline( eFontOverline ); 180 const basegfx::BColor aOverlineColor = maBColorModifierStack.getModifiedColor(pTCPP->getOverlineColor()); 181 mpOutputDevice->SetOverlineColor( Color(aOverlineColor) ); 182 if( pTCPP->getWordLineMode() ) 183 aFont.SetWordLineMode( true ); 184 } 185 186 // set Underline attribute 187 const FontUnderline eFontUnderline(primitive2d::mapTextLineToFontUnderline( pTCPP->getFontUnderline() )); 188 if( eFontUnderline != UNDERLINE_NONE ) 189 { 190 aFont.SetUnderline( eFontUnderline ); 191 if( pTCPP->getWordLineMode() ) 192 aFont.SetWordLineMode( true ); 193 //TODO: ??? if( pTCPP->getUnderlineAbove() ) 194 // aFont.SetUnderlineAbove( true ); 195 } 196 197 // set Strikeout attribute 198 const FontStrikeout eFontStrikeout(primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP->getTextStrikeout())); 199 200 if( eFontStrikeout != STRIKEOUT_NONE ) 201 aFont.SetStrikeout( eFontStrikeout ); 202 203 // set EmphasisMark attribute 204 FontEmphasisMark eFontEmphasisMark = EMPHASISMARK_NONE; 205 switch( pTCPP->getTextEmphasisMark() ) 206 { 207 default: 208 DBG_WARNING1( "DrawingLayer: Unknown EmphasisMark style (%d)!", pTCPP->getTextEmphasisMark() ); 209 // fall through 210 case primitive2d::TEXT_EMPHASISMARK_NONE: eFontEmphasisMark = EMPHASISMARK_NONE; break; 211 case primitive2d::TEXT_EMPHASISMARK_DOT: eFontEmphasisMark = EMPHASISMARK_DOT; break; 212 case primitive2d::TEXT_EMPHASISMARK_CIRCLE: eFontEmphasisMark = EMPHASISMARK_CIRCLE; break; 213 case primitive2d::TEXT_EMPHASISMARK_DISC: eFontEmphasisMark = EMPHASISMARK_DISC; break; 214 case primitive2d::TEXT_EMPHASISMARK_ACCENT: eFontEmphasisMark = EMPHASISMARK_ACCENT; break; 215 } 216 217 if( eFontEmphasisMark != EMPHASISMARK_NONE ) 218 { 219 DBG_ASSERT( (pTCPP->getEmphasisMarkAbove() != pTCPP->getEmphasisMarkBelow()), 220 "DrawingLayer: Bad EmphasisMark position!" ); 221 if( pTCPP->getEmphasisMarkAbove() ) 222 eFontEmphasisMark |= EMPHASISMARK_POS_ABOVE; 223 else 224 eFontEmphasisMark |= EMPHASISMARK_POS_BELOW; 225 aFont.SetEmphasisMark( eFontEmphasisMark ); 226 } 227 228 // set Relief attribute 229 FontRelief eFontRelief = RELIEF_NONE; 230 switch( pTCPP->getTextRelief() ) 231 { 232 default: 233 DBG_WARNING1( "DrawingLayer: Unknown Relief style (%d)!", pTCPP->getTextRelief() ); 234 // fall through 235 case primitive2d::TEXT_RELIEF_NONE: eFontRelief = RELIEF_NONE; break; 236 case primitive2d::TEXT_RELIEF_EMBOSSED: eFontRelief = RELIEF_EMBOSSED; break; 237 case primitive2d::TEXT_RELIEF_ENGRAVED: eFontRelief = RELIEF_ENGRAVED; break; 238 } 239 240 if( eFontRelief != RELIEF_NONE ) 241 aFont.SetRelief( eFontRelief ); 242 243 // set Shadow attribute 244 if( pTCPP->getShadow() ) 245 aFont.SetShadow( true ); 246 } 247 248 // create transformed integer DXArray in view coordinate system 249 ::std::vector< sal_Int32 > aTransformedDXArray; 250 251 if(rTextCandidate.getDXArray().size()) 252 { 253 aTransformedDXArray.reserve(rTextCandidate.getDXArray().size()); 254 const basegfx::B2DVector aPixelVector(maCurrentTransformation * basegfx::B2DVector(1.0, 0.0)); 255 const double fPixelVectorFactor(aPixelVector.getLength()); 256 257 for(::std::vector< double >::const_iterator aStart(rTextCandidate.getDXArray().begin()); 258 aStart != rTextCandidate.getDXArray().end(); aStart++) 259 { 260 aTransformedDXArray.push_back(basegfx::fround((*aStart) * fPixelVectorFactor)); 261 } 262 } 263 264 // set parameters and paint text snippet 265 const basegfx::BColor aRGBFontColor(maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor())); 266 const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0)); 267 const Point aStartPoint(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY())); 268 const sal_uInt32 nOldLayoutMode(mpOutputDevice->GetLayoutMode()); 269 270 if(rTextCandidate.getFontAttribute().getRTL()) 271 { 272 sal_uInt32 nRTLLayoutMode(nOldLayoutMode & ~(TEXT_LAYOUT_COMPLEX_DISABLED|TEXT_LAYOUT_BIDI_STRONG)); 273 nRTLLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT; 274 mpOutputDevice->SetLayoutMode(nRTLLayoutMode); 275 } 276 277 mpOutputDevice->SetFont(aFont); 278 mpOutputDevice->SetTextColor(Color(aRGBFontColor)); 279 280 if(aTransformedDXArray.size()) 281 { 282 mpOutputDevice->DrawTextArray( 283 aStartPoint, 284 rTextCandidate.getText(), 285 &(aTransformedDXArray[0]), 286 rTextCandidate.getTextPosition(), 287 rTextCandidate.getTextLength()); 288 } 289 else 290 { 291 mpOutputDevice->DrawText( 292 aStartPoint, 293 rTextCandidate.getText(), 294 rTextCandidate.getTextPosition(), 295 rTextCandidate.getTextLength()); 296 } 297 298 if(rTextCandidate.getFontAttribute().getRTL()) 299 { 300 mpOutputDevice->SetLayoutMode(nOldLayoutMode); 301 } 302 303 bPrimitiveAccepted = true; 304 } 305 } 306 307 if(!bPrimitiveAccepted) 308 { 309 // let break down 310 process(rTextCandidate.get2DDecomposition(getViewInformation2D())); 311 } 312 } 313 314 // direct draw of hairline 315 void VclProcessor2D::RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate, bool bPixelBased) 316 { 317 const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); 318 mpOutputDevice->SetLineColor(Color(aHairlineColor)); 319 mpOutputDevice->SetFillColor(); 320 321 basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon()); 322 aLocalPolygon.transform(maCurrentTransformation); 323 324 static bool bCheckTrapezoidDecomposition(false); 325 static bool bShowOutlinesThere(false); 326 if(bCheckTrapezoidDecomposition) 327 { 328 // clip against discrete ViewPort 329 const basegfx::B2DRange& rDiscreteViewport = getViewInformation2D().getDiscreteViewport(); 330 basegfx::B2DPolyPolygon aLocalPolyPolygon(basegfx::tools::clipPolygonOnRange( 331 aLocalPolygon, rDiscreteViewport, true, false)); 332 333 if(aLocalPolyPolygon.count()) 334 { 335 // subdivide 336 aLocalPolyPolygon = basegfx::tools::adaptiveSubdivideByDistance( 337 aLocalPolyPolygon, 0.5); 338 339 // trapezoidize 340 static double fLineWidth(2.0); 341 basegfx::B2DTrapezoidVector aB2DTrapezoidVector; 342 basegfx::tools::createLineTrapezoidFromB2DPolyPolygon(aB2DTrapezoidVector, aLocalPolyPolygon, fLineWidth); 343 344 const sal_uInt32 nCount(aB2DTrapezoidVector.size()); 345 346 if(nCount) 347 { 348 basegfx::BColor aInvPolygonColor(aHairlineColor); 349 aInvPolygonColor.invert(); 350 351 for(sal_uInt32 a(0); a < nCount; a++) 352 { 353 const basegfx::B2DPolygon aTempPolygon(aB2DTrapezoidVector[a].getB2DPolygon()); 354 355 if(bShowOutlinesThere) 356 { 357 mpOutputDevice->SetFillColor(Color(aHairlineColor)); 358 mpOutputDevice->SetLineColor(); 359 } 360 361 mpOutputDevice->DrawPolygon(aTempPolygon); 362 363 if(bShowOutlinesThere) 364 { 365 mpOutputDevice->SetFillColor(); 366 mpOutputDevice->SetLineColor(Color(aInvPolygonColor)); 367 mpOutputDevice->DrawPolyLine(aTempPolygon, 0.0); 368 } 369 } 370 } 371 } 372 } 373 else 374 { 375 if(bPixelBased && getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()) 376 { 377 // #i98289# 378 // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete 379 // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since 380 // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This 381 // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering. 382 aLocalPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon); 383 } 384 385 mpOutputDevice->DrawPolyLine(aLocalPolygon, 0.0); 386 } 387 } 388 389 // direct draw of transformed BitmapEx primitive 390 void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate) 391 { 392 // check local ViewPort 393 const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport()); 394 const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rBitmapCandidate.getTransform()); 395 396 if(!rDiscreteViewPort.isEmpty()) 397 { 398 // check if we are visible 399 basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); 400 401 aUnitRange.transform(aLocalTransform); 402 403 if(!aUnitRange.overlaps(rDiscreteViewPort)) 404 { 405 return; 406 } 407 } 408 409 BitmapEx aBitmapEx(rBitmapCandidate.getBitmapEx()); 410 411 if(maBColorModifierStack.count()) 412 { 413 aBitmapEx = impModifyBitmapEx(maBColorModifierStack, aBitmapEx); 414 415 if(aBitmapEx.IsEmpty()) 416 { 417 // color gets completely replaced, get it 418 const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor())); 419 basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon()); 420 aPolygon.transform(aLocalTransform); 421 422 mpOutputDevice->SetFillColor(Color(aModifiedColor)); 423 mpOutputDevice->SetLineColor(); 424 mpOutputDevice->DrawPolygon(aPolygon); 425 426 return; 427 } 428 } 429 430 { 431 static bool bForceUseOfOwnTransformer(false); 432 433 // decompose matrix to check for shear, rotate and mirroring 434 basegfx::B2DVector aScale, aTranslate; 435 double fRotate, fShearX; 436 aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); 437 438 // #121387# when mirrored and rotated, avoid the GraphicManager output which has low quality 439 const bool bRotated(!basegfx::fTools::equalZero(fRotate)); 440 const bool bSheared(!basegfx::fTools::equalZero(fShearX)); 441 //const bool bMirrored(aScale.getX() < 0.0 || aScale.getY() < 0.0); 442 // const bool bMirroredAndRotated(bRotated && bMirrored); 443 444 if(!bForceUseOfOwnTransformer && !bRotated && !bSheared) // && !bMirrored) 445 { 446 RenderBitmapPrimitive2D_BitmapEx(*mpOutputDevice, aBitmapEx, aLocalTransform); 447 } 448 else 449 { 450 if(!aBitmapEx.IsTransparent() && (bSheared || bRotated)) 451 { 452 // parts will be uncovered, extend aBitmapEx with a mask bitmap 453 const Bitmap aContent(aBitmapEx.GetBitmap()); 454 aBitmapEx = BitmapEx(aContent, Bitmap(aContent.GetSizePixel(), 1)); 455 } 456 457 RenderBitmapPrimitive2D_self(*mpOutputDevice, aBitmapEx, aLocalTransform); 458 } 459 } 460 } 461 462 void VclProcessor2D::RenderFillGraphicPrimitive2D(const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate) 463 { 464 const attribute::FillGraphicAttribute& rFillGraphicAttribute(rFillBitmapCandidate.getFillGraphic()); 465 bool bPrimitiveAccepted(false); 466 static bool bTryTilingDirect = true; 467 468 // #121194# when tiling is used and content is bitmap-based, do direct tiling in the 469 // renderer on pixel base to ensure tight fitting. Do not do this when 470 // the fill is rotated or sheared. 471 472 // ovveride static bool (for debug) and tiling is active 473 if(bTryTilingDirect && rFillGraphicAttribute.getTiling()) 474 { 475 // content is bitmap(ex) 476 // 477 // for SVG support, force decomposition when SVG is present. This will lead to use 478 // the primitive representation of the svg directly. 479 // 480 // when graphic is animated, force decomposition to use the correct graphic, else 481 // fill style will not be animated 482 if(GRAPHIC_BITMAP == rFillGraphicAttribute.getGraphic().GetType() 483 && !rFillGraphicAttribute.getGraphic().getSvgData().get() 484 && !rFillGraphicAttribute.getGraphic().IsAnimated()) 485 { 486 // decompose matrix to check for shear, rotate and mirroring 487 basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rFillBitmapCandidate.getTransformation()); 488 basegfx::B2DVector aScale, aTranslate; 489 double fRotate, fShearX; 490 aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX); 491 492 // when nopt rotated/sheared 493 if(basegfx::fTools::equalZero(fRotate) && basegfx::fTools::equalZero(fShearX)) 494 { 495 // no shear or rotate, draw direct in pixel coordinates 496 bPrimitiveAccepted = true; 497 498 // transform object range to device coordinates (pixels). Use 499 // the device transformation for better accuracy 500 basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale); 501 aObjectRange.transform(mpOutputDevice->GetViewTransformation()); 502 503 // extract discrete size of object 504 const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth())); 505 const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight())); 506 507 // only do something when object has a size in discrete units 508 if(nOWidth > 0 && nOHeight > 0) 509 { 510 // transform graphic range to device coordinates (pixels). Use 511 // the device transformation for better accuracy 512 basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange()); 513 aGraphicRange.transform(mpOutputDevice->GetViewTransformation() * aLocalTransform); 514 515 // extract discrete size of graphic 516 const sal_Int32 nBWidth(basegfx::fround(aGraphicRange.getWidth())); 517 const sal_Int32 nBHeight(basegfx::fround(aGraphicRange.getHeight())); 518 519 // only do something when bitmap fill has a size in discrete units 520 if(nBWidth > 0 && nBHeight > 0) 521 { 522 // nBWidth, nBHeight is the pixel size of the neede bitmap. To not need to scale it 523 // in vcl many times, create a size-optimized version 524 const Size aNeededBitmapSizePixel(nBWidth, nBHeight); 525 BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx()); 526 static bool bEnablePreScaling(true); 527 const bool bPreScaled(bEnablePreScaling && nBWidth * nBHeight < (250 * 250)); 528 529 if(bPreScaled) 530 { 531 // ... but only up to a maximum size, else it gets too expensive 532 aBitmapEx.Scale(aNeededBitmapSizePixel, BMP_SCALE_INTERPOLATE); 533 } 534 535 bool bPainted(false); 536 537 if(maBColorModifierStack.count()) 538 { 539 // when color modifier, apply to bitmap 540 aBitmapEx = impModifyBitmapEx(maBColorModifierStack, aBitmapEx); 541 542 // impModifyBitmapEx uses empty bitmap as sign to return that 543 // the content will be completely replaced to mono color, use shortcut 544 if(aBitmapEx.IsEmpty()) 545 { 546 // color gets completely replaced, get it 547 const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor())); 548 basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon()); 549 aPolygon.transform(aLocalTransform); 550 551 mpOutputDevice->SetFillColor(Color(aModifiedColor)); 552 mpOutputDevice->SetLineColor(); 553 mpOutputDevice->DrawPolygon(aPolygon); 554 555 bPainted = true; 556 } 557 } 558 559 if(!bPainted) 560 { 561 sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX())); 562 sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY())); 563 const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX())); 564 const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY())); 565 sal_Int32 nPosX(0); 566 sal_Int32 nPosY(0); 567 568 if(nBLeft > nOLeft) 569 { 570 const sal_Int32 nDiff((nBLeft / nBWidth) + 1); 571 572 nPosX -= nDiff; 573 nBLeft -= nDiff * nBWidth; 574 } 575 576 if(nBLeft + nBWidth <= nOLeft) 577 { 578 const sal_Int32 nDiff(-nBLeft / nBWidth); 579 580 nPosX += nDiff; 581 nBLeft += nDiff * nBWidth; 582 } 583 584 if(nBTop > nOTop) 585 { 586 const sal_Int32 nDiff((nBTop / nBHeight) + 1); 587 588 nPosY -= nDiff; 589 nBTop -= nDiff * nBHeight; 590 } 591 592 if(nBTop + nBHeight <= nOTop) 593 { 594 const sal_Int32 nDiff(-nBTop / nBHeight); 595 596 nPosY += nDiff; 597 nBTop += nDiff * nBHeight; 598 } 599 600 // prepare OutDev 601 const Point aEmptyPoint(0, 0); 602 const Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel()); 603 const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); 604 mpOutputDevice->EnableMapMode(false); 605 606 // check if offset is used 607 const sal_Int32 nOffsetX(basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth)); 608 609 if(nOffsetX) 610 { 611 // offset in X, so iterate over Y first and draw lines 612 for(sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; nYPos += nBHeight, nPosY++) 613 { 614 for(sal_Int32 nXPos(nPosY % 2 ? nBLeft - nBWidth + nOffsetX : nBLeft); 615 nXPos < nOLeft + nOWidth; nXPos += nBWidth) 616 { 617 const Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel); 618 619 if(aOutRectPixel.IsOver(aVisiblePixel)) 620 { 621 if(bPreScaled) 622 { 623 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx); 624 } 625 else 626 { 627 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, aBitmapEx); 628 } 629 } 630 } 631 } 632 } 633 else 634 { 635 // check if offset is used 636 const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight)); 637 638 // possible offset in Y, so iterate over X first and draw columns 639 for(sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; nXPos += nBWidth, nPosX++) 640 { 641 for(sal_Int32 nYPos(nPosX % 2 ? nBTop - nBHeight + nOffsetY : nBTop); 642 nYPos < nOTop + nOHeight; nYPos += nBHeight) 643 { 644 const Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel); 645 646 if(aOutRectPixel.IsOver(aVisiblePixel)) 647 { 648 if(bPreScaled) 649 { 650 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx); 651 } 652 else 653 { 654 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, aBitmapEx); 655 } 656 } 657 } 658 } 659 } 660 661 // restore OutDev 662 mpOutputDevice->EnableMapMode(bWasEnabled); 663 } 664 } 665 } 666 } 667 } 668 } 669 670 if(!bPrimitiveAccepted) 671 { 672 // do not accept, use decomposition 673 process(rFillBitmapCandidate.get2DDecomposition(getViewInformation2D())); 674 } 675 } 676 677 // direct draw of Graphic 678 void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D& rPolygonCandidate) 679 { 680 bool bDone(false); 681 const basegfx::B2DPolyPolygon& rPolyPolygon = rPolygonCandidate.getB2DPolyPolygon(); 682 683 // #121194# Todo: check if this works 684 if(!rPolyPolygon.count()) 685 { 686 // empty polyPolygon, done 687 bDone = true; 688 } 689 else 690 { 691 const attribute::FillGraphicAttribute& rFillGraphicAttribute = rPolygonCandidate.getFillGraphic(); 692 693 // try to catch cases where the graphic will be color-modified to a single 694 // color (e.g. shadow) 695 switch(rFillGraphicAttribute.getGraphic().GetType()) 696 { 697 case GRAPHIC_GDIMETAFILE: 698 { 699 // metafiles are potentially transparent, cannot optimize�, not done 700 break; 701 } 702 case GRAPHIC_BITMAP: 703 { 704 if(!rFillGraphicAttribute.getGraphic().IsTransparent() && !rFillGraphicAttribute.getGraphic().IsAlpha()) 705 { 706 // bitmap is not transparent and has no alpha 707 const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count()); 708 709 if(nBColorModifierStackCount) 710 { 711 const basegfx::BColorModifier& rTopmostModifier = maBColorModifierStack.getBColorModifier(nBColorModifierStackCount - 1); 712 713 if(basegfx::BCOLORMODIFYMODE_REPLACE == rTopmostModifier.getMode()) 714 { 715 // the bitmap fill is in unified color, so we can replace it with 716 // a single polygon fill. The form of the fill depends on tiling 717 if(rFillGraphicAttribute.getTiling()) 718 { 719 // with tiling, fill the whole PolyPolygon with the modifier color 720 basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon); 721 722 aLocalPolyPolygon.transform(maCurrentTransformation); 723 mpOutputDevice->SetLineColor(); 724 mpOutputDevice->SetFillColor(Color(rTopmostModifier.getBColor())); 725 mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); 726 } 727 else 728 { 729 // without tiling, only the area common to the bitmap tile and the 730 // PolyPolygon is filled. Create the bitmap tile area in object 731 // coordinates. For this, the object transformation needs to be created 732 // from the already scaled PolyPolygon. The tile area in object 733 // coordinates wil always be non-rotated, so it's not necessary to 734 // work with a polygon here 735 basegfx::B2DRange aTileRange(rFillGraphicAttribute.getGraphicRange()); 736 const basegfx::B2DRange aPolyPolygonRange(rPolyPolygon.getB2DRange()); 737 const basegfx::B2DHomMatrix aNewObjectTransform( 738 basegfx::tools::createScaleTranslateB2DHomMatrix( 739 aPolyPolygonRange.getRange(), 740 aPolyPolygonRange.getMinimum())); 741 742 aTileRange.transform(aNewObjectTransform); 743 744 // now clip the object polyPolygon against the tile range 745 // to get the common area 746 basegfx::B2DPolyPolygon aTarget = basegfx::tools::clipPolyPolygonOnRange( 747 rPolyPolygon, 748 aTileRange, 749 true, 750 false); 751 752 if(aTarget.count()) 753 { 754 aTarget.transform(maCurrentTransformation); 755 mpOutputDevice->SetLineColor(); 756 mpOutputDevice->SetFillColor(Color(rTopmostModifier.getBColor())); 757 mpOutputDevice->DrawPolyPolygon(aTarget); 758 } 759 } 760 761 // simplified output executed, we are done 762 bDone = true; 763 } 764 } 765 } 766 break; 767 } 768 default: //GRAPHIC_NONE, GRAPHIC_DEFAULT 769 { 770 // empty graphic, we are done 771 bDone = true; 772 break; 773 } 774 } 775 } 776 777 if(!bDone) 778 { 779 // use default decomposition 780 process(rPolygonCandidate.get2DDecomposition(getViewInformation2D())); 781 } 782 } 783 784 // direct draw of PolyPolygon with color 785 void VclProcessor2D::RenderPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate) 786 { 787 const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor())); 788 mpOutputDevice->SetFillColor(Color(aPolygonColor)); 789 mpOutputDevice->SetLineColor(); 790 791 basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); 792 aLocalPolyPolygon.transform(maCurrentTransformation); 793 794 static bool bCheckTrapezoidDecomposition(false); 795 static bool bShowOutlinesThere(false); 796 if(bCheckTrapezoidDecomposition) 797 { 798 // clip against discrete ViewPort 799 const basegfx::B2DRange& rDiscreteViewport = getViewInformation2D().getDiscreteViewport(); 800 aLocalPolyPolygon = basegfx::tools::clipPolyPolygonOnRange( 801 aLocalPolyPolygon, rDiscreteViewport, true, false); 802 803 if(aLocalPolyPolygon.count()) 804 { 805 // subdivide 806 aLocalPolyPolygon = basegfx::tools::adaptiveSubdivideByDistance( 807 aLocalPolyPolygon, 0.5); 808 809 // trapezoidize 810 basegfx::B2DTrapezoidVector aB2DTrapezoidVector; 811 basegfx::tools::trapezoidSubdivide(aB2DTrapezoidVector, aLocalPolyPolygon); 812 813 const sal_uInt32 nCount(aB2DTrapezoidVector.size()); 814 815 if(nCount) 816 { 817 basegfx::BColor aInvPolygonColor(aPolygonColor); 818 aInvPolygonColor.invert(); 819 820 for(sal_uInt32 a(0); a < nCount; a++) 821 { 822 const basegfx::B2DPolygon aTempPolygon(aB2DTrapezoidVector[a].getB2DPolygon()); 823 824 if(bShowOutlinesThere) 825 { 826 mpOutputDevice->SetFillColor(Color(aPolygonColor)); 827 mpOutputDevice->SetLineColor(); 828 } 829 830 mpOutputDevice->DrawPolygon(aTempPolygon); 831 832 if(bShowOutlinesThere) 833 { 834 mpOutputDevice->SetFillColor(); 835 mpOutputDevice->SetLineColor(Color(aInvPolygonColor)); 836 mpOutputDevice->DrawPolyLine(aTempPolygon, 0.0); 837 } 838 } 839 } 840 } 841 } 842 else 843 { 844 mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); 845 846 if(mnPolygonStrokePrimitive2D 847 && getOptionsDrawinglayer().IsAntiAliasing() 848 && (mpOutputDevice->GetAntialiasing() & ANTIALIASING_ENABLE_B2DDRAW)) 849 { 850 // when AA is on and this filled polygons are the result of stroked line geometry, 851 // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons 852 mpOutputDevice->SetFillColor(); 853 mpOutputDevice->SetLineColor(Color(aPolygonColor)); 854 const sal_uInt32 nCount(aLocalPolyPolygon.count()); 855 856 for(sal_uInt32 a(0); a < nCount; a++) 857 { 858 mpOutputDevice->DrawPolyLine(aLocalPolyPolygon.getB2DPolygon(a), 0.0); 859 } 860 } 861 } 862 } 863 864 // mask group. Force output to VDev and create mask from given mask 865 void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate) 866 { 867 if(rMaskCandidate.getChildren().hasElements()) 868 { 869 basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask()); 870 871 if(aMask.count()) 872 { 873 aMask.transform(maCurrentTransformation); 874 const basegfx::B2DRange aRange(basegfx::tools::getRange(aMask)); 875 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); 876 877 if(aBufferDevice.isVisible()) 878 { 879 // remember last OutDev and set to content 880 OutputDevice* pLastOutputDevice = mpOutputDevice; 881 mpOutputDevice = &aBufferDevice.getContent(); 882 883 // paint to it 884 process(rMaskCandidate.getChildren()); 885 886 // back to old OutDev 887 mpOutputDevice = pLastOutputDevice; 888 889 // draw mask 890 if(getOptionsDrawinglayer().IsAntiAliasing()) 891 { 892 // with AA, use 8bit AlphaMask to get nice borders 893 VirtualDevice& rTransparence = aBufferDevice.getTransparence(); 894 rTransparence.SetLineColor(); 895 rTransparence.SetFillColor(COL_BLACK); 896 rTransparence.DrawPolyPolygon(aMask); 897 898 // dump buffer to outdev 899 aBufferDevice.paint(); 900 } 901 else 902 { 903 // No AA, use 1bit mask 904 VirtualDevice& rMask = aBufferDevice.getMask(); 905 rMask.SetLineColor(); 906 rMask.SetFillColor(COL_BLACK); 907 rMask.DrawPolyPolygon(aMask); 908 909 // dump buffer to outdev 910 aBufferDevice.paint(); 911 } 912 } 913 } 914 } 915 } 916 917 // modified color group. Force output to unified color. 918 void VclProcessor2D::RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate) 919 { 920 if(rModifiedCandidate.getChildren().hasElements()) 921 { 922 maBColorModifierStack.push(rModifiedCandidate.getColorModifier()); 923 process(rModifiedCandidate.getChildren()); 924 maBColorModifierStack.pop(); 925 } 926 } 927 928 // unified sub-transparence. Draw to VDev first. 929 void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate) 930 { 931 static bool bForceToDecomposition(false); 932 933 if(rTransCandidate.getChildren().hasElements()) 934 { 935 if(bForceToDecomposition) 936 { 937 // use decomposition 938 process(rTransCandidate.get2DDecomposition(getViewInformation2D())); 939 } 940 else 941 { 942 if(0.0 == rTransCandidate.getTransparence()) 943 { 944 // no transparence used, so just use the content 945 process(rTransCandidate.getChildren()); 946 } 947 else if(rTransCandidate.getTransparence() > 0.0 && rTransCandidate.getTransparence() < 1.0) 948 { 949 // transparence is in visible range 950 basegfx::B2DRange aRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rTransCandidate.getChildren(), getViewInformation2D())); 951 aRange.transform(maCurrentTransformation); 952 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); 953 954 if(aBufferDevice.isVisible()) 955 { 956 // remember last OutDev and set to content 957 OutputDevice* pLastOutputDevice = mpOutputDevice; 958 mpOutputDevice = &aBufferDevice.getContent(); 959 960 // paint content to it 961 process(rTransCandidate.getChildren()); 962 963 // back to old OutDev 964 mpOutputDevice = pLastOutputDevice; 965 966 // dump buffer to outdev using given transparence 967 aBufferDevice.paint(rTransCandidate.getTransparence()); 968 } 969 } 970 } 971 } 972 } 973 974 // sub-transparence group. Draw to VDev first. 975 void VclProcessor2D::RenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransCandidate) 976 { 977 if(rTransCandidate.getChildren().hasElements()) 978 { 979 basegfx::B2DRange aRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rTransCandidate.getChildren(), getViewInformation2D())); 980 aRange.transform(maCurrentTransformation); 981 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); 982 983 if(aBufferDevice.isVisible()) 984 { 985 // remember last OutDev and set to content 986 OutputDevice* pLastOutputDevice = mpOutputDevice; 987 mpOutputDevice = &aBufferDevice.getContent(); 988 989 // paint content to it 990 process(rTransCandidate.getChildren()); 991 992 // set to mask 993 mpOutputDevice = &aBufferDevice.getTransparence(); 994 995 // when painting transparence masks, reset the color stack 996 basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack); 997 maBColorModifierStack = basegfx::BColorModifierStack(); 998 999 // paint mask to it (always with transparence intensities, evtl. with AA) 1000 process(rTransCandidate.getTransparence()); 1001 1002 // back to old color stack 1003 maBColorModifierStack = aLastBColorModifierStack; 1004 1005 // back to old OutDev 1006 mpOutputDevice = pLastOutputDevice; 1007 1008 // dump buffer to outdev 1009 aBufferDevice.paint(); 1010 } 1011 } 1012 } 1013 1014 // transform group. 1015 void VclProcessor2D::RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D& rTransformCandidate) 1016 { 1017 // remember current transformation and ViewInformation 1018 const basegfx::B2DHomMatrix aLastCurrentTransformation(maCurrentTransformation); 1019 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); 1020 1021 // create new transformations for CurrentTransformation 1022 // and for local ViewInformation2D 1023 maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation(); 1024 const geometry::ViewInformation2D aViewInformation2D( 1025 getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(), 1026 getViewInformation2D().getViewTransformation(), 1027 getViewInformation2D().getViewport(), 1028 getViewInformation2D().getVisualizedPage(), 1029 getViewInformation2D().getViewTime(), 1030 getViewInformation2D().getExtendedInformationSequence()); 1031 updateViewInformation(aViewInformation2D); 1032 1033 // proccess content 1034 process(rTransformCandidate.getChildren()); 1035 1036 // restore transformations 1037 maCurrentTransformation = aLastCurrentTransformation; 1038 updateViewInformation(aLastViewInformation2D); 1039 } 1040 1041 // new XDrawPage for ViewInformation2D 1042 void VclProcessor2D::RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate) 1043 { 1044 // remember current transformation and ViewInformation 1045 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D()); 1046 1047 // create new local ViewInformation2D 1048 const geometry::ViewInformation2D aViewInformation2D( 1049 getViewInformation2D().getObjectTransformation(), 1050 getViewInformation2D().getViewTransformation(), 1051 getViewInformation2D().getViewport(), 1052 rPagePreviewCandidate.getXDrawPage(), 1053 getViewInformation2D().getViewTime(), 1054 getViewInformation2D().getExtendedInformationSequence()); 1055 updateViewInformation(aViewInformation2D); 1056 1057 // proccess decomposed content 1058 process(rPagePreviewCandidate.get2DDecomposition(getViewInformation2D())); 1059 1060 // restore transformations 1061 updateViewInformation(aLastViewInformation2D); 1062 } 1063 1064 // marker 1065 void VclProcessor2D::RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D& rMarkArrayCandidate) 1066 { 1067 static bool bCheckCompleteMarkerDecompose(false); 1068 if(bCheckCompleteMarkerDecompose) 1069 { 1070 process(rMarkArrayCandidate.get2DDecomposition(getViewInformation2D())); 1071 return; 1072 } 1073 1074 // get data 1075 const std::vector< basegfx::B2DPoint >& rPositions = rMarkArrayCandidate.getPositions(); 1076 const sal_uInt32 nCount(rPositions.size()); 1077 1078 if(nCount && !rMarkArrayCandidate.getMarker().IsEmpty()) 1079 { 1080 // get pixel size 1081 const BitmapEx& rMarker(rMarkArrayCandidate.getMarker()); 1082 const Size aBitmapSize(rMarker.GetSizePixel()); 1083 1084 if(aBitmapSize.Width() && aBitmapSize.Height()) 1085 { 1086 // get discrete half size 1087 const basegfx::B2DVector aDiscreteHalfSize( 1088 (aBitmapSize.getWidth() - 1.0) * 0.5, 1089 (aBitmapSize.getHeight() - 1.0) * 0.5); 1090 const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled()); 1091 1092 // do not forget evtl. moved origin in target device MapMode when 1093 // switching it off; it would be missing and lead to wrong positions. 1094 // All his could be done using logic sizes and coordinates, too, but 1095 // we want a 1:1 bitmap rendering here, so it's more safe and faster 1096 // to work with switching off MapMode usage completely. 1097 const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); 1098 1099 mpOutputDevice->EnableMapMode(false); 1100 1101 for(std::vector< basegfx::B2DPoint >::const_iterator aIter(rPositions.begin()); aIter != rPositions.end(); aIter++) 1102 { 1103 const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * (*aIter)) - aDiscreteHalfSize); 1104 const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()), basegfx::fround(aDiscreteTopLeft.getY())); 1105 1106 mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker); 1107 } 1108 1109 mpOutputDevice->EnableMapMode(bWasEnabled); 1110 } 1111 } 1112 } 1113 1114 // point 1115 void VclProcessor2D::RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate) 1116 { 1117 const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions(); 1118 const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor())); 1119 const Color aVCLColor(aRGBColor); 1120 1121 for(std::vector< basegfx::B2DPoint >::const_iterator aIter(rPositions.begin()); aIter != rPositions.end(); aIter++) 1122 { 1123 const basegfx::B2DPoint aViewPosition(maCurrentTransformation * (*aIter)); 1124 const Point aPos(basegfx::fround(aViewPosition.getX()), basegfx::fround(aViewPosition.getY())); 1125 1126 mpOutputDevice->DrawPixel(aPos, aVCLColor); 1127 } 1128 } 1129 1130 void VclProcessor2D::RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate) 1131 { 1132 // #i101491# method restructured to clearly use the DrawPolyLine 1133 // calls starting from a deined line width 1134 const attribute::LineAttribute& rLineAttribute = rPolygonStrokeCandidate.getLineAttribute(); 1135 const double fLineWidth(rLineAttribute.getWidth()); 1136 bool bDone(false); 1137 1138 if(basegfx::fTools::more(fLineWidth, 0.0)) 1139 { 1140 const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(fLineWidth, 0.0)); 1141 const double fDiscreteLineWidth(aDiscreteUnit.getLength()); 1142 const attribute::StrokeAttribute& rStrokeAttribute = rPolygonStrokeCandidate.getStrokeAttribute(); 1143 const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor())); 1144 basegfx::B2DPolyPolygon aHairlinePolyPolygon; 1145 1146 mpOutputDevice->SetLineColor(Color(aHairlineColor)); 1147 mpOutputDevice->SetFillColor(); 1148 1149 if(0.0 == rStrokeAttribute.getFullDotDashLen()) 1150 { 1151 // no line dashing, just copy 1152 aHairlinePolyPolygon.append(rPolygonStrokeCandidate.getB2DPolygon()); 1153 } 1154 else 1155 { 1156 // else apply LineStyle 1157 basegfx::tools::applyLineDashing(rPolygonStrokeCandidate.getB2DPolygon(), 1158 rStrokeAttribute.getDotDashArray(), 1159 &aHairlinePolyPolygon, 0, rStrokeAttribute.getFullDotDashLen()); 1160 } 1161 1162 const sal_uInt32 nCount(aHairlinePolyPolygon.count()); 1163 1164 if(nCount) 1165 { 1166 const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing()); 1167 aHairlinePolyPolygon.transform(maCurrentTransformation); 1168 1169 if(bAntiAliased) 1170 { 1171 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0)) 1172 { 1173 // line in range ]0.0 .. 1.0[ 1174 // paint as simple hairline 1175 for(sal_uInt32 a(0); a < nCount; a++) 1176 { 1177 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0); 1178 } 1179 1180 bDone = true; 1181 } 1182 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0)) 1183 { 1184 // line in range [1.0 .. 2.0[ 1185 // paint as 2x2 with dynamic line distance 1186 basegfx::B2DHomMatrix aMat; 1187 const double fDistance(fDiscreteLineWidth - 1.0); 1188 const double fHalfDistance(fDistance * 0.5); 1189 1190 for(sal_uInt32 a(0); a < nCount; a++) 1191 { 1192 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a)); 1193 1194 aMat.set(0, 2, -fHalfDistance); 1195 aMat.set(1, 2, -fHalfDistance); 1196 aCandidate.transform(aMat); 1197 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1198 1199 aMat.set(0, 2, fDistance); 1200 aMat.set(1, 2, 0.0); 1201 aCandidate.transform(aMat); 1202 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1203 1204 aMat.set(0, 2, 0.0); 1205 aMat.set(1, 2, fDistance); 1206 aCandidate.transform(aMat); 1207 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1208 1209 aMat.set(0, 2, -fDistance); 1210 aMat.set(1, 2, 0.0); 1211 aCandidate.transform(aMat); 1212 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1213 } 1214 1215 bDone = true; 1216 } 1217 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 3.0)) 1218 { 1219 // line in range [2.0 .. 3.0] 1220 // paint as cross in a 3x3 with dynamic line distance 1221 basegfx::B2DHomMatrix aMat; 1222 const double fDistance((fDiscreteLineWidth - 1.0) * 0.5); 1223 1224 for(sal_uInt32 a(0); a < nCount; a++) 1225 { 1226 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a)); 1227 1228 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1229 1230 aMat.set(0, 2, -fDistance); 1231 aMat.set(1, 2, 0.0); 1232 aCandidate.transform(aMat); 1233 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1234 1235 aMat.set(0, 2, fDistance); 1236 aMat.set(1, 2, -fDistance); 1237 aCandidate.transform(aMat); 1238 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1239 1240 aMat.set(0, 2, fDistance); 1241 aMat.set(1, 2, fDistance); 1242 aCandidate.transform(aMat); 1243 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1244 1245 aMat.set(0, 2, -fDistance); 1246 aMat.set(1, 2, fDistance); 1247 aCandidate.transform(aMat); 1248 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1249 } 1250 1251 bDone = true; 1252 } 1253 else 1254 { 1255 // #i101491# line width above 3.0 1256 } 1257 } 1258 else 1259 { 1260 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.5)) 1261 { 1262 // line width below 1.5, draw the basic hairline polygon 1263 for(sal_uInt32 a(0); a < nCount; a++) 1264 { 1265 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0); 1266 } 1267 1268 bDone = true; 1269 } 1270 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.5)) 1271 { 1272 // line width is in range ]1.5 .. 2.5], use four hairlines 1273 // drawn in a square 1274 for(sal_uInt32 a(0); a < nCount; a++) 1275 { 1276 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a)); 1277 basegfx::B2DHomMatrix aMat; 1278 1279 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1280 1281 aMat.set(0, 2, 1.0); 1282 aMat.set(1, 2, 0.0); 1283 aCandidate.transform(aMat); 1284 1285 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1286 1287 aMat.set(0, 2, 0.0); 1288 aMat.set(1, 2, 1.0); 1289 aCandidate.transform(aMat); 1290 1291 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1292 1293 aMat.set(0, 2, -1.0); 1294 aMat.set(1, 2, 0.0); 1295 aCandidate.transform(aMat); 1296 1297 mpOutputDevice->DrawPolyLine(aCandidate, 0.0); 1298 } 1299 1300 bDone = true; 1301 } 1302 else 1303 { 1304 // #i101491# line width is above 2.5 1305 } 1306 } 1307 1308 if(!bDone && rPolygonStrokeCandidate.getB2DPolygon().count() > 1000) 1309 { 1310 // #i101491# If the polygon complexity uses more than a given amount, do 1311 // use OuputDevice::DrawPolyLine directly; this will avoid buffering all 1312 // decompositions in primtives (memory) and fallback to old line painting 1313 // for very complex polygons, too 1314 for(sal_uInt32 a(0); a < nCount; a++) 1315 { 1316 mpOutputDevice->DrawPolyLine( 1317 aHairlinePolyPolygon.getB2DPolygon(a), 1318 fDiscreteLineWidth, 1319 rLineAttribute.getLineJoin(), 1320 rLineAttribute.getLineCap()); 1321 } 1322 1323 bDone = true; 1324 } 1325 } 1326 } 1327 1328 if(!bDone) 1329 { 1330 // remeber that we enter a PolygonStrokePrimitive2D decomposition, 1331 // used for AA thick line drawing 1332 mnPolygonStrokePrimitive2D++; 1333 1334 // line width is big enough for standard filled polygon visualisation or zero 1335 process(rPolygonStrokeCandidate.get2DDecomposition(getViewInformation2D())); 1336 1337 // leave PolygonStrokePrimitive2D 1338 mnPolygonStrokePrimitive2D--; 1339 } 1340 } 1341 1342 void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D) 1343 { 1344 // The new decomposition of Metafiles made it necessary to add an Eps 1345 // primitive to handle embedded Eps data. On some devices, this can be 1346 // painted directly (mac, printer). 1347 // To be able to handle the replacement correctly, i need to handle it myself 1348 // since DrawEPS will not be able e.g. to rotate the replacement. To be able 1349 // to do that, i added a boolean return to OutputDevice::DrawEPS(..) 1350 // to know when EPS was handled directly already. 1351 basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0); 1352 aRange.transform(maCurrentTransformation * rEpsPrimitive2D.getEpsTransform()); 1353 1354 if(!aRange.isEmpty()) 1355 { 1356 const Rectangle aRectangle( 1357 (sal_Int32)floor(aRange.getMinX()), (sal_Int32)floor(aRange.getMinY()), 1358 (sal_Int32)ceil(aRange.getMaxX()), (sal_Int32)ceil(aRange.getMaxY())); 1359 1360 if(!aRectangle.IsEmpty()) 1361 { 1362 // try to paint EPS directly without fallback visualisation 1363 const bool bEPSPaintedDirectly(mpOutputDevice->DrawEPS( 1364 aRectangle.TopLeft(), 1365 aRectangle.GetSize(), 1366 rEpsPrimitive2D.getGfxLink(), 1367 0)); 1368 1369 if(!bEPSPaintedDirectly) 1370 { 1371 // use the decomposition which will correctly handle the 1372 // fallback visualisation using full transformation (e.g. rotation) 1373 process(rEpsPrimitive2D.get2DDecomposition(getViewInformation2D())); 1374 } 1375 } 1376 } 1377 } 1378 1379 void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D& rCandidate) 1380 { 1381 const double fDelta(rCandidate.getOffsetB() - rCandidate.getOffsetA()); 1382 1383 if(basegfx::fTools::more(fDelta, 0.0)) 1384 { 1385 const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA())); 1386 const basegfx::BColor aColorB(maBColorModifierStack.getModifiedColor(rCandidate.getColorB())); 1387 const double fDiscreteUnit((getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)).getLength()); 1388 1389 // use color distance and discrete lengths to calculate step count 1390 const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit)); 1391 1392 // switch off line painting 1393 mpOutputDevice->SetLineColor(); 1394 1395 // prepare polygon in needed width at start position (with discrete overlap) 1396 const basegfx::B2DPolygon aPolygon( 1397 basegfx::tools::createPolygonFromRect( 1398 basegfx::B2DRange( 1399 rCandidate.getOffsetA() - fDiscreteUnit, 1400 0.0, 1401 rCandidate.getOffsetA() + (fDelta / nSteps) + fDiscreteUnit, 1402 1.0))); 1403 1404 1405 // prepare loop ([0.0 .. 1.0[) 1406 double fUnitScale(0.0); 1407 const double fUnitStep(1.0 / nSteps); 1408 1409 // loop and paint 1410 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep) 1411 { 1412 basegfx::B2DPolygon aNew(aPolygon); 1413 1414 aNew.transform(maCurrentTransformation * basegfx::tools::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0)); 1415 mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorA, aColorB, fUnitScale))); 1416 mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew)); 1417 } 1418 } 1419 } 1420 1421 void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D& rCandidate) 1422 { 1423 const double fDeltaScale(rCandidate.getScaleB() - rCandidate.getScaleA()); 1424 1425 if(basegfx::fTools::more(fDeltaScale, 0.0)) 1426 { 1427 const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA())); 1428 const basegfx::BColor aColorB(maBColorModifierStack.getModifiedColor(rCandidate.getColorB())); 1429 const double fDiscreteUnit((getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)).getLength()); 1430 1431 // use color distance and discrete lengths to calculate step count 1432 const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDeltaScale, fDiscreteUnit)); 1433 1434 // switch off line painting 1435 mpOutputDevice->SetLineColor(); 1436 1437 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes) 1438 double fUnitScale(0.0); 1439 const double fUnitStep(1.0 / nSteps); 1440 1441 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep) 1442 { 1443 basegfx::B2DHomMatrix aTransform; 1444 const double fEndScale(rCandidate.getScaleB() - (fDeltaScale * fUnitScale)); 1445 1446 if(rCandidate.isTranslateSet()) 1447 { 1448 const basegfx::B2DVector aTranslate( 1449 basegfx::interpolate( 1450 rCandidate.getTranslateB(), 1451 rCandidate.getTranslateA(), 1452 fUnitScale)); 1453 1454 aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix( 1455 fEndScale, 1456 fEndScale, 1457 aTranslate.getX(), 1458 aTranslate.getY()); 1459 } 1460 else 1461 { 1462 aTransform = basegfx::tools::createScaleB2DHomMatrix( 1463 fEndScale, 1464 fEndScale); 1465 } 1466 1467 basegfx::B2DPolygon aNew(basegfx::tools::createPolygonFromUnitCircle()); 1468 1469 aNew.transform(maCurrentTransformation * aTransform); 1470 mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorB, aColorA, fUnitScale))); 1471 mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew)); 1472 } 1473 } 1474 } 1475 1476 void VclProcessor2D::adaptLineToFillDrawMode() const 1477 { 1478 const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); 1479 1480 if(nOriginalDrawMode & (DRAWMODE_BLACKLINE|DRAWMODE_GRAYLINE|DRAWMODE_GHOSTEDLINE|DRAWMODE_WHITELINE|DRAWMODE_SETTINGSLINE)) 1481 { 1482 sal_uInt32 nAdaptedDrawMode(nOriginalDrawMode); 1483 1484 if(nOriginalDrawMode & DRAWMODE_BLACKLINE) 1485 { 1486 nAdaptedDrawMode |= DRAWMODE_BLACKFILL; 1487 } 1488 else 1489 { 1490 nAdaptedDrawMode &= ~DRAWMODE_BLACKFILL; 1491 } 1492 1493 if(nOriginalDrawMode & DRAWMODE_GRAYLINE) 1494 { 1495 nAdaptedDrawMode |= DRAWMODE_GRAYFILL; 1496 } 1497 else 1498 { 1499 nAdaptedDrawMode &= ~DRAWMODE_GRAYFILL; 1500 } 1501 1502 if(nOriginalDrawMode & DRAWMODE_GHOSTEDLINE) 1503 { 1504 nAdaptedDrawMode |= DRAWMODE_GHOSTEDFILL; 1505 } 1506 else 1507 { 1508 nAdaptedDrawMode &= ~DRAWMODE_GHOSTEDFILL; 1509 } 1510 1511 if(nOriginalDrawMode & DRAWMODE_WHITELINE) 1512 { 1513 nAdaptedDrawMode |= DRAWMODE_WHITEFILL; 1514 } 1515 else 1516 { 1517 nAdaptedDrawMode &= ~DRAWMODE_WHITEFILL; 1518 } 1519 1520 if(nOriginalDrawMode & DRAWMODE_SETTINGSLINE) 1521 { 1522 nAdaptedDrawMode |= DRAWMODE_SETTINGSFILL; 1523 } 1524 else 1525 { 1526 nAdaptedDrawMode &= ~DRAWMODE_SETTINGSFILL; 1527 } 1528 1529 mpOutputDevice->SetDrawMode(nAdaptedDrawMode); 1530 } 1531 } 1532 1533 void VclProcessor2D::adaptTextToFillDrawMode() const 1534 { 1535 const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode()); 1536 if(nOriginalDrawMode & (DRAWMODE_BLACKTEXT|DRAWMODE_GRAYTEXT|DRAWMODE_GHOSTEDTEXT|DRAWMODE_WHITETEXT|DRAWMODE_SETTINGSTEXT)) 1537 { 1538 sal_uInt32 nAdaptedDrawMode(nOriginalDrawMode); 1539 1540 if(nOriginalDrawMode & DRAWMODE_BLACKTEXT) 1541 { 1542 nAdaptedDrawMode |= DRAWMODE_BLACKFILL; 1543 } 1544 else 1545 { 1546 nAdaptedDrawMode &= ~DRAWMODE_BLACKFILL; 1547 } 1548 1549 if(nOriginalDrawMode & DRAWMODE_GRAYTEXT) 1550 { 1551 nAdaptedDrawMode |= DRAWMODE_GRAYFILL; 1552 } 1553 else 1554 { 1555 nAdaptedDrawMode &= ~DRAWMODE_GRAYFILL; 1556 } 1557 1558 if(nOriginalDrawMode & DRAWMODE_GHOSTEDTEXT) 1559 { 1560 nAdaptedDrawMode |= DRAWMODE_GHOSTEDFILL; 1561 } 1562 else 1563 { 1564 nAdaptedDrawMode &= ~DRAWMODE_GHOSTEDFILL; 1565 } 1566 1567 if(nOriginalDrawMode & DRAWMODE_WHITETEXT) 1568 { 1569 nAdaptedDrawMode |= DRAWMODE_WHITEFILL; 1570 } 1571 else 1572 { 1573 nAdaptedDrawMode &= ~DRAWMODE_WHITEFILL; 1574 } 1575 1576 if(nOriginalDrawMode & DRAWMODE_SETTINGSTEXT) 1577 { 1578 nAdaptedDrawMode |= DRAWMODE_SETTINGSFILL; 1579 } 1580 else 1581 { 1582 nAdaptedDrawMode &= ~DRAWMODE_SETTINGSFILL; 1583 } 1584 1585 mpOutputDevice->SetDrawMode(nAdaptedDrawMode); 1586 } 1587 } 1588 1589 ////////////////////////////////////////////////////////////////////////////// 1590 // process support 1591 1592 VclProcessor2D::VclProcessor2D( 1593 const geometry::ViewInformation2D& rViewInformation, 1594 OutputDevice& rOutDev) 1595 : BaseProcessor2D(rViewInformation), 1596 mpOutputDevice(&rOutDev), 1597 maBColorModifierStack(), 1598 maCurrentTransformation(), 1599 maDrawinglayerOpt(), 1600 mnPolygonStrokePrimitive2D(0) 1601 { 1602 // set digit language, derived from SvtCTLOptions to have the correct 1603 // number display for arabic/hindi numerals 1604 const SvtCTLOptions aSvtCTLOptions; 1605 LanguageType eLang(LANGUAGE_SYSTEM); 1606 1607 if(SvtCTLOptions::NUMERALS_HINDI == aSvtCTLOptions.GetCTLTextNumerals()) 1608 { 1609 eLang = LANGUAGE_ARABIC_SAUDI_ARABIA; 1610 } 1611 else if(SvtCTLOptions::NUMERALS_ARABIC == aSvtCTLOptions.GetCTLTextNumerals()) 1612 { 1613 eLang = LANGUAGE_ENGLISH; 1614 } 1615 else 1616 { 1617 eLang = (LanguageType)Application::GetSettings().GetLanguage(); 1618 } 1619 1620 rOutDev.SetDigitLanguage(eLang); 1621 } 1622 1623 VclProcessor2D::~VclProcessor2D() 1624 { 1625 } 1626 } // end of namespace processor2d 1627 } // end of namespace drawinglayer 1628 1629 ////////////////////////////////////////////////////////////////////////////// 1630 // eof 1631