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