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