1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include "vclpixelprocessor2d.hxx" 21 #include "vclhelperbufferdevice.hxx" 22 #include "helperwrongspellrenderer.hxx" 23 24 #include <sal/log.hxx> 25 #include <tools/stream.hxx> 26 #include <vcl/BitmapBasicMorphologyFilter.hxx> 27 #include <vcl/BitmapFilterStackBlur.hxx> 28 #include <vcl/outdev.hxx> 29 #include <vcl/dibtools.hxx> 30 #include <vcl/hatch.hxx> 31 #include <basegfx/polygon/b2dpolygontools.hxx> 32 33 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> 34 #include <drawinglayer/primitive2d/Tools.hxx> 35 #include <drawinglayer/primitive2d/textprimitive2d.hxx> 36 #include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> 37 #include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> 38 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> 39 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> 40 #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> 41 #include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> 42 #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> 43 #include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx> 44 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 45 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> 46 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx> 47 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 48 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> 49 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> 50 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 51 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> 52 #include <drawinglayer/primitive2d/glowprimitive2d.hxx> 53 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> 54 #include <drawinglayer/primitive2d/controlprimitive2d.hxx> 55 #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> 56 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 57 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> 58 #include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx> 59 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> 60 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx> 61 #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx> 62 #include <drawinglayer/primitive2d/epsprimitive2d.hxx> 63 #include <drawinglayer/primitive2d/softedgeprimitive2d.hxx> 64 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx> 65 66 #include <com/sun/star/awt/XWindow2.hpp> 67 #include <com/sun/star/awt/XControl.hpp> 68 69 using namespace com::sun::star; 70 71 namespace drawinglayer::processor2d 72 { 73 struct VclPixelProcessor2D::Impl 74 { 75 AntialiasingFlags m_nOrigAntiAliasing; 76 77 explicit Impl(OutputDevice const& rOutDev) 78 : m_nOrigAntiAliasing(rOutDev.GetAntialiasing()) 79 { 80 } 81 }; 82 83 VclPixelProcessor2D::VclPixelProcessor2D(const geometry::ViewInformation2D& rViewInformation, 84 OutputDevice& rOutDev) 85 : VclProcessor2D(rViewInformation, rOutDev) 86 , m_pImpl(new Impl(rOutDev)) 87 { 88 // prepare maCurrentTransformation matrix with viewTransformation to target directly to pixels 89 maCurrentTransformation = rViewInformation.getObjectToViewTransformation(); 90 91 // prepare output directly to pixels 92 mpOutputDevice->Push(PushFlags::MAPMODE); 93 mpOutputDevice->SetMapMode(); 94 95 // react on AntiAliasing settings 96 if (getOptionsDrawinglayer().IsAntiAliasing()) 97 { 98 mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing 99 | AntialiasingFlags::EnableB2dDraw); 100 } 101 else 102 { 103 mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing 104 & ~AntialiasingFlags::EnableB2dDraw); 105 } 106 } 107 108 VclPixelProcessor2D::~VclPixelProcessor2D() 109 { 110 // restore MapMode 111 mpOutputDevice->Pop(); 112 113 // restore AntiAliasing 114 mpOutputDevice->SetAntialiasing(m_pImpl->m_nOrigAntiAliasing); 115 } 116 117 void VclPixelProcessor2D::tryDrawPolyPolygonColorPrimitive2DDirect( 118 const drawinglayer::primitive2d::PolyPolygonColorPrimitive2D& rSource, double fTransparency) 119 { 120 if (!rSource.getB2DPolyPolygon().count() || fTransparency < 0.0 || fTransparency >= 1.0) 121 { 122 // no geometry, done 123 return; 124 } 125 126 const basegfx::BColor aPolygonColor( 127 maBColorModifierStack.getModifiedColor(rSource.getBColor())); 128 129 mpOutputDevice->SetFillColor(Color(aPolygonColor)); 130 mpOutputDevice->SetLineColor(); 131 mpOutputDevice->DrawTransparent(maCurrentTransformation, rSource.getB2DPolyPolygon(), 132 fTransparency); 133 } 134 135 bool VclPixelProcessor2D::tryDrawPolygonHairlinePrimitive2DDirect( 136 const drawinglayer::primitive2d::PolygonHairlinePrimitive2D& rSource, double fTransparency) 137 { 138 const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon()); 139 140 if (!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) 141 { 142 // no geometry, done 143 return true; 144 } 145 146 const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rSource.getBColor())); 147 148 mpOutputDevice->SetFillColor(); 149 mpOutputDevice->SetLineColor(Color(aLineColor)); 150 //aLocalPolygon.transform(maCurrentTransformation); 151 152 // try drawing; if it did not work, use standard fallback 153 return mpOutputDevice->DrawPolyLineDirect(maCurrentTransformation, rLocalPolygon, 0.0, 154 fTransparency); 155 } 156 157 bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect( 158 const drawinglayer::primitive2d::PolygonStrokePrimitive2D& rSource, double fTransparency) 159 { 160 const basegfx::B2DPolygon& rLocalPolygon(rSource.getB2DPolygon()); 161 162 if (!rLocalPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) 163 { 164 // no geometry, done 165 return true; 166 } 167 168 if (basegfx::B2DLineJoin::NONE == rSource.getLineAttribute().getLineJoin() 169 && css::drawing::LineCap_BUTT != rSource.getLineAttribute().getLineCap()) 170 { 171 // better use decompose to get that combination done for now, see discussion 172 // at https://bugs.documentfoundation.org/show_bug.cgi?id=130478#c17 and ff 173 return false; 174 } 175 176 // MM01: Radically change here - no dismantle/applyLineDashing, 177 // let that happen low-level at DrawPolyLineDirect implementations 178 // to open up for buffering and evtl. direct draw with sys-dep 179 // graphic systems. Check for stroke is in use 180 const bool bStrokeAttributeNotUsed(rSource.getStrokeAttribute().isDefault() 181 || 0.0 == rSource.getStrokeAttribute().getFullDotDashLen()); 182 183 const basegfx::BColor aLineColor( 184 maBColorModifierStack.getModifiedColor(rSource.getLineAttribute().getColor())); 185 186 mpOutputDevice->SetFillColor(); 187 mpOutputDevice->SetLineColor(Color(aLineColor)); 188 189 // MM01 draw direct, hand over dash data if available 190 return mpOutputDevice->DrawPolyLineDirect( 191 maCurrentTransformation, rLocalPolygon, 192 // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline) 193 rSource.getLineAttribute().getWidth(), fTransparency, 194 bStrokeAttributeNotUsed ? nullptr : &rSource.getStrokeAttribute().getDotDashArray(), 195 rSource.getLineAttribute().getLineJoin(), rSource.getLineAttribute().getLineCap(), 196 rSource.getLineAttribute().getMiterMinimumAngle() 197 /* false bBypassAACheck, default*/); 198 } 199 200 void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) 201 { 202 switch (rCandidate.getPrimitive2DID()) 203 { 204 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D: 205 { 206 processWrongSpellPrimitive2D( 207 static_cast<const primitive2d::WrongSpellPrimitive2D&>(rCandidate)); 208 break; 209 } 210 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D: 211 { 212 processTextSimplePortionPrimitive2D( 213 static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); 214 break; 215 } 216 case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D: 217 { 218 processTextDecoratedPortionPrimitive2D( 219 static_cast<const primitive2d::TextSimplePortionPrimitive2D&>(rCandidate)); 220 break; 221 } 222 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: 223 { 224 processPolygonHairlinePrimitive2D( 225 static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate)); 226 break; 227 } 228 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D: 229 { 230 // direct draw of transformed BitmapEx primitive 231 processBitmapPrimitive2D( 232 static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate)); 233 break; 234 } 235 case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D: 236 { 237 // direct draw of fillBitmapPrimitive 238 RenderFillGraphicPrimitive2D( 239 static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate)); 240 break; 241 } 242 case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D: 243 { 244 processPolyPolygonGradientPrimitive2D( 245 static_cast<const primitive2d::PolyPolygonGradientPrimitive2D&>(rCandidate)); 246 break; 247 } 248 case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D: 249 { 250 // direct draw of bitmap 251 RenderPolyPolygonGraphicPrimitive2D( 252 static_cast<const primitive2d::PolyPolygonGraphicPrimitive2D&>(rCandidate)); 253 break; 254 } 255 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: 256 { 257 processPolyPolygonColorPrimitive2D( 258 static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate)); 259 break; 260 } 261 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D: 262 { 263 processMetaFilePrimitive2D(rCandidate); 264 break; 265 } 266 case PRIMITIVE2D_ID_MASKPRIMITIVE2D: 267 { 268 // mask group. 269 RenderMaskPrimitive2DPixel( 270 static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate)); 271 break; 272 } 273 case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D: 274 { 275 // modified color group. Force output to unified color. 276 RenderModifiedColorPrimitive2D( 277 static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate)); 278 break; 279 } 280 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D: 281 { 282 processUnifiedTransparencePrimitive2D( 283 static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate)); 284 break; 285 } 286 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D: 287 { 288 // sub-transparence group. Draw to VDev first. 289 RenderTransparencePrimitive2D( 290 static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate)); 291 break; 292 } 293 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: 294 { 295 // transform group. 296 RenderTransformPrimitive2D( 297 static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate)); 298 break; 299 } 300 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D: 301 { 302 // new XDrawPage for ViewInformation2D 303 RenderPagePreviewPrimitive2D( 304 static_cast<const primitive2d::PagePreviewPrimitive2D&>(rCandidate)); 305 break; 306 } 307 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D: 308 { 309 // marker array 310 RenderMarkerArrayPrimitive2D( 311 static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate)); 312 break; 313 } 314 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D: 315 { 316 // point array 317 RenderPointArrayPrimitive2D( 318 static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate)); 319 break; 320 } 321 case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D: 322 { 323 processControlPrimitive2D( 324 static_cast<const primitive2d::ControlPrimitive2D&>(rCandidate)); 325 break; 326 } 327 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: 328 { 329 processPolygonStrokePrimitive2D( 330 static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate)); 331 break; 332 } 333 case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D: 334 { 335 processFillHatchPrimitive2D( 336 static_cast<const primitive2d::FillHatchPrimitive2D&>(rCandidate)); 337 break; 338 } 339 case PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D: 340 { 341 processBackgroundColorPrimitive2D( 342 static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate)); 343 break; 344 } 345 case PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D: 346 { 347 // #i97628# 348 // This primitive means that the content is derived from an active text edit, 349 // not from model data itself. Some renderers need to suppress this content, e.g. 350 // the pixel renderer used for displaying the edit view (like this one). It's 351 // not to be suppressed by the MetaFile renderers, so that the edited text is 352 // part of the MetaFile, e.g. needed for presentation previews. 353 // Action: Ignore here, do nothing. 354 break; 355 } 356 case PRIMITIVE2D_ID_INVERTPRIMITIVE2D: 357 { 358 processInvertPrimitive2D(rCandidate); 359 break; 360 } 361 case PRIMITIVE2D_ID_EPSPRIMITIVE2D: 362 { 363 RenderEpsPrimitive2D(static_cast<const primitive2d::EpsPrimitive2D&>(rCandidate)); 364 break; 365 } 366 case PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D: 367 { 368 RenderSvgLinearAtomPrimitive2D( 369 static_cast<const primitive2d::SvgLinearAtomPrimitive2D&>(rCandidate)); 370 break; 371 } 372 case PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D: 373 { 374 RenderSvgRadialAtomPrimitive2D( 375 static_cast<const primitive2d::SvgRadialAtomPrimitive2D&>(rCandidate)); 376 break; 377 } 378 case PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D: 379 { 380 processBorderLinePrimitive2D( 381 static_cast<const drawinglayer::primitive2d::BorderLinePrimitive2D&>(rCandidate)); 382 break; 383 } 384 case PRIMITIVE2D_ID_GLOWPRIMITIVE2D: 385 { 386 processGlowPrimitive2D( 387 static_cast<const drawinglayer::primitive2d::GlowPrimitive2D&>(rCandidate)); 388 break; 389 } 390 case PRIMITIVE2D_ID_SOFTEDGEPRIMITIVE2D: 391 { 392 processSoftEdgePrimitive2D( 393 static_cast<const drawinglayer::primitive2d::SoftEdgePrimitive2D&>(rCandidate)); 394 break; 395 } 396 case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D: 397 { 398 processShadowPrimitive2D( 399 static_cast<const drawinglayer::primitive2d::ShadowPrimitive2D&>(rCandidate)); 400 break; 401 } 402 default: 403 { 404 SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString( 405 rCandidate.getPrimitive2DID())); 406 // process recursively 407 process(rCandidate); 408 break; 409 } 410 } 411 } 412 413 void VclPixelProcessor2D::processWrongSpellPrimitive2D( 414 const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive) 415 { 416 if (!renderWrongSpellPrimitive2D(rWrongSpellPrimitive, *mpOutputDevice, maCurrentTransformation, 417 maBColorModifierStack)) 418 { 419 // fallback to decomposition (MetaFile) 420 process(rWrongSpellPrimitive); 421 } 422 } 423 424 void VclPixelProcessor2D::processTextSimplePortionPrimitive2D( 425 const primitive2d::TextSimplePortionPrimitive2D& rCandidate) 426 { 427 // Adapt evtl. used special DrawMode 428 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); 429 adaptTextToFillDrawMode(); 430 431 if (getOptionsDrawinglayer().IsRenderSimpleTextDirect()) 432 { 433 RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); 434 } 435 else 436 { 437 process(rCandidate); 438 } 439 440 // restore DrawMode 441 mpOutputDevice->SetDrawMode(nOriginalDrawMode); 442 } 443 444 void VclPixelProcessor2D::processTextDecoratedPortionPrimitive2D( 445 const primitive2d::TextSimplePortionPrimitive2D& rCandidate) 446 { 447 // Adapt evtl. used special DrawMode 448 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); 449 adaptTextToFillDrawMode(); 450 451 if (getOptionsDrawinglayer().IsRenderDecoratedTextDirect()) 452 { 453 RenderTextSimpleOrDecoratedPortionPrimitive2D(rCandidate); 454 } 455 else 456 { 457 process(rCandidate); 458 } 459 460 // restore DrawMode 461 mpOutputDevice->SetDrawMode(nOriginalDrawMode); 462 } 463 464 void VclPixelProcessor2D::processPolygonHairlinePrimitive2D( 465 const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D) 466 { 467 if (tryDrawPolygonHairlinePrimitive2DDirect(rPolygonHairlinePrimitive2D, 0.0)) 468 { 469 return; 470 } 471 472 // direct draw of hairline 473 RenderPolygonHairlinePrimitive2D(rPolygonHairlinePrimitive2D, true); 474 } 475 476 void VclPixelProcessor2D::processBitmapPrimitive2D( 477 const primitive2d::BitmapPrimitive2D& rBitmapCandidate) 478 { 479 // check if graphic content is inside discrete local ViewPort 480 const basegfx::B2DRange& rDiscreteViewPort(getViewInformation2D().getDiscreteViewport()); 481 const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation 482 * rBitmapCandidate.getTransform()); 483 484 if (!rDiscreteViewPort.isEmpty()) 485 { 486 basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0); 487 488 aUnitRange.transform(aLocalTransform); 489 490 if (!aUnitRange.overlaps(rDiscreteViewPort)) 491 { 492 // content is outside discrete local ViewPort 493 return; 494 } 495 } 496 497 RenderBitmapPrimitive2D(rBitmapCandidate); 498 } 499 500 void VclPixelProcessor2D::processPolyPolygonGradientPrimitive2D( 501 const primitive2d::PolyPolygonGradientPrimitive2D& rPolygonCandidate) 502 { 503 // direct draw of gradient 504 const attribute::FillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient()); 505 basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor())); 506 basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor())); 507 basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon()); 508 509 if (!aLocalPolyPolygon.count()) 510 return; 511 512 if (aStartColor == aEndColor) 513 { 514 // no gradient at all, draw as polygon in AA and non-AA case 515 aLocalPolyPolygon.transform(maCurrentTransformation); 516 mpOutputDevice->SetLineColor(); 517 mpOutputDevice->SetFillColor(Color(aStartColor)); 518 mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon); 519 } 520 else 521 { 522 // use the primitive decomposition of the metafile 523 process(rPolygonCandidate); 524 } 525 } 526 527 void VclPixelProcessor2D::processPolyPolygonColorPrimitive2D( 528 const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D) 529 { 530 // try to use directly 531 basegfx::B2DPolyPolygon aLocalPolyPolygon; 532 533 tryDrawPolyPolygonColorPrimitive2DDirect(rPolyPolygonColorPrimitive2D, 0.0); 534 // okay, done. In this case no gaps should have to be repaired, too 535 536 // when AA is on and this filled polygons are the result of stroked line geometry, 537 // draw the geometry once extra as lines to avoid AA 'gaps' between partial polygons 538 // Caution: This is needed in both cases (!) 539 if (!(mnPolygonStrokePrimitive2D && getOptionsDrawinglayer().IsAntiAliasing() 540 && (mpOutputDevice->GetAntialiasing() & AntialiasingFlags::EnableB2dDraw))) 541 return; 542 543 const basegfx::BColor aPolygonColor( 544 maBColorModifierStack.getModifiedColor(rPolyPolygonColorPrimitive2D.getBColor())); 545 sal_uInt32 nCount(aLocalPolyPolygon.count()); 546 547 if (!nCount) 548 { 549 aLocalPolyPolygon = rPolyPolygonColorPrimitive2D.getB2DPolyPolygon(); 550 aLocalPolyPolygon.transform(maCurrentTransformation); 551 nCount = aLocalPolyPolygon.count(); 552 } 553 554 mpOutputDevice->SetFillColor(); 555 mpOutputDevice->SetLineColor(Color(aPolygonColor)); 556 557 for (sal_uInt32 a(0); a < nCount; a++) 558 { 559 mpOutputDevice->DrawPolyLine(aLocalPolyPolygon.getB2DPolygon(a), 0.0); 560 } 561 } 562 563 void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D( 564 const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate) 565 { 566 // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case, 567 // use the faster OutputDevice::DrawTransparent method 568 const primitive2d::Primitive2DContainer& rContent = rUniTransparenceCandidate.getChildren(); 569 570 if (rContent.empty()) 571 return; 572 573 if (0.0 == rUniTransparenceCandidate.getTransparence()) 574 { 575 // not transparent at all, use content 576 process(rUniTransparenceCandidate.getChildren()); 577 } 578 else if (rUniTransparenceCandidate.getTransparence() > 0.0 579 && rUniTransparenceCandidate.getTransparence() < 1.0) 580 { 581 bool bDrawTransparentUsed(false); 582 583 if (1 == rContent.size()) 584 { 585 const primitive2d::Primitive2DReference xReference(rContent[0]); 586 const primitive2d::BasePrimitive2D* pBasePrimitive 587 = dynamic_cast<const primitive2d::BasePrimitive2D*>(xReference.get()); 588 589 if (pBasePrimitive) 590 { 591 switch (pBasePrimitive->getPrimitive2DID()) 592 { 593 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D: 594 { 595 // single transparent tools::PolyPolygon identified, use directly 596 const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor 597 = static_cast<const primitive2d::PolyPolygonColorPrimitive2D*>( 598 pBasePrimitive); 599 SAL_WARN_IF(!pPoPoColor, "drawinglayer", 600 "OOps, PrimitiveID and PrimitiveType do not match (!)"); 601 bDrawTransparentUsed = true; 602 tryDrawPolyPolygonColorPrimitive2DDirect( 603 *pPoPoColor, rUniTransparenceCandidate.getTransparence()); 604 break; 605 } 606 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D: 607 { 608 // single transparent PolygonHairlinePrimitive2D identified, use directly 609 const primitive2d::PolygonHairlinePrimitive2D* pPoHair 610 = static_cast<const primitive2d::PolygonHairlinePrimitive2D*>( 611 pBasePrimitive); 612 SAL_WARN_IF(!pPoHair, "drawinglayer", 613 "OOps, PrimitiveID and PrimitiveType do not match (!)"); 614 615 // do no tallow by default - problem is that self-overlapping parts of this geometry will 616 // not be in an all-same transparency but will already alpha-cover themselves with blending. 617 // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its 618 // content to be uniformly transparent. 619 // For hairline the effect is pretty minimal, but still not correct. 620 bDrawTransparentUsed = false; 621 break; 622 } 623 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D: 624 { 625 // single transparent PolygonStrokePrimitive2D identified, use directly 626 const primitive2d::PolygonStrokePrimitive2D* pPoStroke 627 = static_cast<const primitive2d::PolygonStrokePrimitive2D*>( 628 pBasePrimitive); 629 SAL_WARN_IF(!pPoStroke, "drawinglayer", 630 "OOps, PrimitiveID and PrimitiveType do not match (!)"); 631 632 // do no tallow by default - problem is that self-overlapping parts of this geometry will 633 // not be in an all-same transparency but will already alpha-cover themselves with blending. 634 // This is not what the UnifiedTransparencePrimitive2D defines: It requires all its 635 // content to be uniformly transparent. 636 // To check, activate and draw a wide transparent self-crossing line/curve 637 bDrawTransparentUsed = false; 638 break; 639 } 640 default: 641 SAL_INFO("drawinglayer", 642 "default case for " << drawinglayer::primitive2d::idToString( 643 rUniTransparenceCandidate.getPrimitive2DID())); 644 break; 645 } 646 } 647 } 648 649 if (!bDrawTransparentUsed) 650 { 651 // unified sub-transparence. Draw to VDev first. 652 RenderUnifiedTransparencePrimitive2D(rUniTransparenceCandidate); 653 } 654 } 655 } 656 657 void VclPixelProcessor2D::processControlPrimitive2D( 658 const primitive2d::ControlPrimitive2D& rControlPrimitive) 659 { 660 // control primitive 661 const uno::Reference<awt::XControl>& rXControl(rControlPrimitive.getXControl()); 662 663 try 664 { 665 // remember old graphics and create new 666 uno::Reference<awt::XView> xControlView(rXControl, uno::UNO_QUERY_THROW); 667 const uno::Reference<awt::XGraphics> xOriginalGraphics(xControlView->getGraphics()); 668 const uno::Reference<awt::XGraphics> xNewGraphics(mpOutputDevice->CreateUnoGraphics()); 669 670 if (xNewGraphics.is()) 671 { 672 // link graphics and view 673 xControlView->setGraphics(xNewGraphics); 674 675 // get position 676 const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation 677 * rControlPrimitive.getTransform()); 678 const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); 679 680 // find out if the control is already visualized as a VCL-ChildWindow. If yes, 681 // it does not need to be painted at all. 682 uno::Reference<awt::XWindow2> xControlWindow(rXControl, uno::UNO_QUERY_THROW); 683 const bool bControlIsVisibleAsChildWindow(rXControl->getPeer().is() 684 && xControlWindow->isVisible()); 685 686 if (!bControlIsVisibleAsChildWindow) 687 { 688 // draw it. Do not forget to use the evtl. offsetted origin of the target device, 689 // e.g. when used with mask/transparence buffer device 690 const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); 691 xControlView->draw(aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), 692 aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); 693 } 694 695 // restore original graphics 696 xControlView->setGraphics(xOriginalGraphics); 697 } 698 } 699 catch (const uno::Exception&) 700 { 701 // #i116763# removing since there is a good alternative when the xControlView 702 // is not found and it is allowed to happen 703 // DBG_UNHANDLED_EXCEPTION(); 704 705 // process recursively and use the decomposition as Bitmap 706 process(rControlPrimitive); 707 } 708 } 709 710 void VclPixelProcessor2D::processPolygonStrokePrimitive2D( 711 const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive2D) 712 { 713 // try to use directly 714 if (tryDrawPolygonStrokePrimitive2DDirect(rPolygonStrokePrimitive2D, 0.0)) 715 { 716 return; 717 } 718 719 // the stroke primitive may be decomposed to filled polygons. To keep 720 // evtl. set DrawModes aka DrawModeFlags::BlackLine, DrawModeFlags::GrayLine, 721 // DrawModeFlags::GhostedLine, DrawModeFlags::WhiteLine or DrawModeFlags::SettingsLine 722 // working, these need to be copied to the corresponding fill modes 723 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode()); 724 adaptLineToFillDrawMode(); 725 726 // polygon stroke primitive 727 728 // Lines with 1 and 2 pixel width without AA need special treatment since their visualization 729 // as filled polygons is geometrically correct but looks wrong since polygon filling avoids 730 // the right and bottom pixels. The used method evaluates that and takes the correct action, 731 // including calling recursively with decomposition if line is wide enough 732 RenderPolygonStrokePrimitive2D(rPolygonStrokePrimitive2D); 733 734 // restore DrawMode 735 mpOutputDevice->SetDrawMode(nOriginalDrawMode); 736 } 737 738 void VclPixelProcessor2D::processFillHatchPrimitive2D( 739 const primitive2d::FillHatchPrimitive2D& rFillHatchPrimitive) 740 { 741 if (getOptionsDrawinglayer().IsAntiAliasing()) 742 { 743 // if AA is used (or ignore smoothing is on), there is no need to smooth 744 // hatch painting, use decomposition 745 process(rFillHatchPrimitive); 746 } 747 else 748 { 749 // without AA, use VCL to draw the hatch. It snaps hatch distances to the next pixel 750 // and forces hatch distance to be >= 3 pixels to make the hatch display look smoother. 751 // This is wrong in principle, but looks nicer. This could also be done here directly 752 // without VCL usage if needed 753 const attribute::FillHatchAttribute& rFillHatchAttributes 754 = rFillHatchPrimitive.getFillHatch(); 755 756 // create hatch polygon in range size and discrete coordinates 757 basegfx::B2DRange aHatchRange(rFillHatchPrimitive.getOutputRange()); 758 aHatchRange.transform(maCurrentTransformation); 759 const basegfx::B2DPolygon aHatchPolygon(basegfx::utils::createPolygonFromRect(aHatchRange)); 760 761 if (rFillHatchAttributes.isFillBackground()) 762 { 763 // #i111846# background fill is active; draw fill polygon 764 const basegfx::BColor aPolygonColor( 765 maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); 766 767 mpOutputDevice->SetFillColor(Color(aPolygonColor)); 768 mpOutputDevice->SetLineColor(); 769 mpOutputDevice->DrawPolygon(aHatchPolygon); 770 } 771 772 // set hatch line color 773 const basegfx::BColor aHatchColor( 774 maBColorModifierStack.getModifiedColor(rFillHatchPrimitive.getBColor())); 775 mpOutputDevice->SetFillColor(); 776 mpOutputDevice->SetLineColor(Color(aHatchColor)); 777 778 // get hatch style 779 HatchStyle eHatchStyle(HatchStyle::Single); 780 781 switch (rFillHatchAttributes.getStyle()) 782 { 783 default: // HatchStyle::Single 784 { 785 break; 786 } 787 case attribute::HatchStyle::Double: 788 { 789 eHatchStyle = HatchStyle::Double; 790 break; 791 } 792 case attribute::HatchStyle::Triple: 793 { 794 eHatchStyle = HatchStyle::Triple; 795 break; 796 } 797 } 798 799 // create hatch 800 const basegfx::B2DVector aDiscreteDistance( 801 maCurrentTransformation * basegfx::B2DVector(rFillHatchAttributes.getDistance(), 0.0)); 802 const sal_uInt32 nDistance(basegfx::fround(aDiscreteDistance.getLength())); 803 const sal_uInt16 nAngle10( 804 static_cast<sal_uInt16>(basegfx::fround(rFillHatchAttributes.getAngle() / F_PI1800))); 805 ::Hatch aVCLHatch(eHatchStyle, Color(rFillHatchAttributes.getColor()), nDistance, nAngle10); 806 807 // draw hatch using VCL 808 mpOutputDevice->DrawHatch(::tools::PolyPolygon(::tools::Polygon(aHatchPolygon)), aVCLHatch); 809 } 810 } 811 812 void VclPixelProcessor2D::processBackgroundColorPrimitive2D( 813 const primitive2d::BackgroundColorPrimitive2D& rPrimitive) 814 { 815 // #i98404# Handle directly, especially when AA is active 816 const AntialiasingFlags nOriginalAA(mpOutputDevice->GetAntialiasing()); 817 818 // switch AA off in all cases 819 mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() 820 & ~AntialiasingFlags::EnableB2dDraw); 821 822 // create color for fill 823 const basegfx::BColor aPolygonColor( 824 maBColorModifierStack.getModifiedColor(rPrimitive.getBColor())); 825 Color aFillColor(aPolygonColor); 826 aFillColor.SetTransparency(sal_uInt8((rPrimitive.getTransparency() * 255.0) + 0.5)); 827 mpOutputDevice->SetFillColor(aFillColor); 828 mpOutputDevice->SetLineColor(); 829 830 // create rectangle for fill 831 const basegfx::B2DRange& aViewport(getViewInformation2D().getDiscreteViewport()); 832 const ::tools::Rectangle aRectangle(static_cast<sal_Int32>(floor(aViewport.getMinX())), 833 static_cast<sal_Int32>(floor(aViewport.getMinY())), 834 static_cast<sal_Int32>(ceil(aViewport.getMaxX())), 835 static_cast<sal_Int32>(ceil(aViewport.getMaxY()))); 836 mpOutputDevice->DrawRect(aRectangle); 837 838 // restore AA setting 839 mpOutputDevice->SetAntialiasing(nOriginalAA); 840 } 841 842 void VclPixelProcessor2D::processBorderLinePrimitive2D( 843 const drawinglayer::primitive2d::BorderLinePrimitive2D& rBorder) 844 { 845 // Process recursively, but switch off AntiAliasing for 846 // horizontal/vertical lines (*not* diagonal lines). 847 // Checked using AntialiasingFlags::PixelSnapHairline instead, 848 // but with AntiAliasing on the display really is too 'ghosty' when 849 // using fine stroking. Correct, but 'ghosty'. 850 851 // It has shown that there are quite some problems here: 852 // - vcl OutDev renderer methods still use fallbacks to paint 853 // multiple single lines between discrete sizes of < 3.5 what 854 // looks bad and does not match 855 // - mix of filled Polygons and Lines is bad when AA switched off 856 // - Alignment of AA with non-AA may be bad in diverse different 857 // renderers 858 // 859 // Due to these reasons I change the strategy: Always draw AAed, but 860 // allow fallback to test/check and if needed. The normal case 861 // where BorderLines will be system-dependently snapped to have at 862 // least a single discrete width per partial line (there may be up to 863 // three) works well nowadays due to most renderers moving the AA stuff 864 // by 0.5 pixels (discrete units) to match well with the non-AAed parts. 865 // 866 // Env-Switch for steering this, default is off. 867 // Enable by setting at all (and to something) 868 static const char* pSwitchOffAntiAliasingForHorVerBorderlines( 869 getenv("SAL_SWITCH_OFF_ANTIALIASING_FOR_HOR_VER_BORTDERLINES")); 870 static bool bSwitchOffAntiAliasingForHorVerBorderlines( 871 nullptr != pSwitchOffAntiAliasingForHorVerBorderlines); 872 873 if (bSwitchOffAntiAliasingForHorVerBorderlines 874 && rBorder.isHorizontalOrVertical(getViewInformation2D())) 875 { 876 AntialiasingFlags nAntiAliasing = mpOutputDevice->GetAntialiasing(); 877 mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); 878 process(rBorder); 879 mpOutputDevice->SetAntialiasing(nAntiAliasing); 880 } 881 else 882 { 883 process(rBorder); 884 } 885 } 886 887 void VclPixelProcessor2D::processInvertPrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) 888 { 889 // invert primitive (currently only used for HighContrast fallback for selection in SW and SC). 890 // (Not true, also used at least for the drawing of dragged column and row boundaries in SC.) 891 // Set OutDev to XOR and switch AA off (XOR does not work with AA) 892 mpOutputDevice->Push(); 893 mpOutputDevice->SetRasterOp(RasterOp::Xor); 894 const AntialiasingFlags nAntiAliasing(mpOutputDevice->GetAntialiasing()); 895 mpOutputDevice->SetAntialiasing(nAntiAliasing & ~AntialiasingFlags::EnableB2dDraw); 896 897 // process content recursively 898 process(rCandidate); 899 900 // restore OutDev 901 mpOutputDevice->Pop(); 902 mpOutputDevice->SetAntialiasing(nAntiAliasing); 903 } 904 905 void VclPixelProcessor2D::processMetaFilePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) 906 { 907 // #i98289# 908 const bool bForceLineSnap(getOptionsDrawinglayer().IsAntiAliasing() 909 && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete()); 910 const AntialiasingFlags nOldAntiAliase(mpOutputDevice->GetAntialiasing()); 911 912 if (bForceLineSnap) 913 { 914 mpOutputDevice->SetAntialiasing(nOldAntiAliase | AntialiasingFlags::PixelSnapHairline); 915 } 916 917 process(rCandidate); 918 919 if (bForceLineSnap) 920 { 921 mpOutputDevice->SetAntialiasing(nOldAntiAliase); 922 } 923 } 924 925 namespace 926 { 927 /* Returns 8-bit alpha mask created from passed mask. 928 929 Negative fErodeDilateRadius values mean erode, positive - dilate. 930 nTransparency defines minimal transparency level. 931 */ 932 AlphaMask ProcessAndBlurAlphaMask(const Bitmap& rMask, double fErodeDilateRadius, 933 double fBlurRadius, sal_uInt8 nTransparency) 934 { 935 // Only completely white pixels on the initial mask must be considered for transparency. Any 936 // other color must be treated as black. This creates 1-bit B&W bitmap. 937 BitmapEx mask(rMask.CreateMask(COL_WHITE)); 938 939 // Scaling down increases performance without noticeable quality loss. Additionally, 940 // current blur implementation can only handle blur radius between 2 and 254. 941 Size aSize = mask.GetSizePixel(); 942 double fScale = 1.0; 943 while (fBlurRadius > 254 || aSize.Height() > 1000 || aSize.Width() > 1000) 944 { 945 fScale /= 2; 946 fBlurRadius /= 2; 947 fErodeDilateRadius /= 2; 948 aSize.setHeight(aSize.Height() / 2); 949 aSize.setWidth(aSize.Width() / 2); 950 } 951 952 // BmpScaleFlag::Fast is important for following color replacement 953 mask.Scale(fScale, fScale, BmpScaleFlag::Fast); 954 955 if (fErodeDilateRadius > 0) 956 BitmapFilter::Filter(mask, BitmapDilateFilter(fErodeDilateRadius)); 957 else if (fErodeDilateRadius < 0) 958 BitmapFilter::Filter(mask, BitmapErodeFilter(-fErodeDilateRadius, 0xFF)); 959 960 if (nTransparency) 961 { 962 const Color aTransparency(nTransparency, nTransparency, nTransparency); 963 mask.Replace(COL_BLACK, aTransparency); 964 } 965 966 // We need 8-bit grey mask for blurring 967 mask.Convert(BmpConversion::N8BitGreys); 968 969 // calculate blurry effect 970 BitmapFilter::Filter(mask, BitmapFilterStackBlur(fBlurRadius)); 971 972 mask.Scale(rMask.GetSizePixel()); 973 974 return AlphaMask(mask.GetBitmap()); 975 } 976 } 977 978 void VclPixelProcessor2D::processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate) 979 { 980 basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); 981 aRange.transform(maCurrentTransformation); 982 basegfx::B2DVector aGlowRadiusVector(rCandidate.getGlowRadius(), 0); 983 // Calculate the pixel size of glow radius in current transformation 984 aGlowRadiusVector *= maCurrentTransformation; 985 // Glow radius is the size of the halo from each side of the object. The halo is the 986 // border of glow color that fades from glow transparency level to fully transparent 987 // When blurring a sharp boundary (our case), it gets 50% of original intensity, and 988 // fades to both sides by the blur radius; thus blur radius is half of glow radius. 989 const double fBlurRadius = aGlowRadiusVector.getLength() / 2; 990 // Consider glow transparency (initial transparency near the object edge) 991 const sal_uInt8 nTransparency = rCandidate.getGlowColor().GetTransparency(); 992 993 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); 994 if (aBufferDevice.isVisible()) 995 { 996 // remember last OutDev and set to content 997 OutputDevice* pLastOutputDevice = mpOutputDevice; 998 mpOutputDevice = &aBufferDevice.getContent(); 999 // We don't need antialiased mask here, which would only make effect thicker 1000 const auto aPrevAA = mpOutputDevice->GetAntialiasing(); 1001 mpOutputDevice->SetAntialiasing(AntialiasingFlags::NONE); 1002 mpOutputDevice->Erase(); 1003 process(rCandidate); 1004 const tools::Rectangle aRect(static_cast<long>(std::floor(aRange.getMinX())), 1005 static_cast<long>(std::floor(aRange.getMinY())), 1006 static_cast<long>(std::ceil(aRange.getMaxX())), 1007 static_cast<long>(std::ceil(aRange.getMaxY()))); 1008 BitmapEx bmpEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize()); 1009 mpOutputDevice->SetAntialiasing(aPrevAA); 1010 1011 AlphaMask mask 1012 = ProcessAndBlurAlphaMask(bmpEx.GetAlpha(), fBlurRadius, fBlurRadius, nTransparency); 1013 1014 // The end result is the bitmap filled with glow color and blurred 8-bit alpha mask 1015 const basegfx::BColor aGlowColor( 1016 maBColorModifierStack.getModifiedColor(rCandidate.getGlowColor().getBColor())); 1017 Bitmap bmp = bmpEx.GetBitmap(); 1018 bmp.Erase(Color(aGlowColor)); 1019 BitmapEx result(bmp, mask); 1020 1021 // back to old OutDev 1022 mpOutputDevice = pLastOutputDevice; 1023 mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result); 1024 } 1025 else 1026 SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); 1027 } 1028 1029 void VclPixelProcessor2D::processSoftEdgePrimitive2D( 1030 const primitive2d::SoftEdgePrimitive2D& rCandidate) 1031 { 1032 // TODO: don't limit the object at view range. This is needed to not blur objects at window 1033 // borders, where they don't end. Ideally, process the full object once at maximal reasonable 1034 // resolution, and store the resulting alpha mask in primitive's cache; then reuse it later, 1035 // applying the transform. 1036 basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); 1037 aRange.transform(maCurrentTransformation); 1038 basegfx::B2DVector aRadiusVector(rCandidate.getRadius(), 0); 1039 // Calculate the pixel size of soft edge radius in current transformation 1040 aRadiusVector *= maCurrentTransformation; 1041 // Blur radius is equal to soft edge radius 1042 const double fBlurRadius = aRadiusVector.getLength(); 1043 1044 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); 1045 if (aBufferDevice.isVisible()) 1046 { 1047 // remember last OutDev and set to content 1048 OutputDevice* pLastOutputDevice = mpOutputDevice; 1049 mpOutputDevice = &aBufferDevice.getContent(); 1050 mpOutputDevice->Erase(); 1051 // Since the effect converts all children to bitmap, we can't disable antialiasing here, 1052 // because it would result in poor quality in areas not affected by the effect 1053 process(rCandidate); 1054 1055 const tools::Rectangle aRect(static_cast<long>(std::floor(aRange.getMinX())), 1056 static_cast<long>(std::floor(aRange.getMinY())), 1057 static_cast<long>(std::ceil(aRange.getMaxX())), 1058 static_cast<long>(std::ceil(aRange.getMaxY()))); 1059 BitmapEx bitmap = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize()); 1060 1061 AlphaMask aMask = bitmap.GetAlpha(); 1062 AlphaMask blurMask = ProcessAndBlurAlphaMask(aMask, -fBlurRadius, fBlurRadius, 0); 1063 1064 aMask.BlendWith(blurMask); 1065 1066 // The end result is the original bitmap with blurred 8-bit alpha mask 1067 BitmapEx result(bitmap.GetBitmap(), aMask); 1068 1069 // back to old OutDev 1070 mpOutputDevice = pLastOutputDevice; 1071 mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result); 1072 } 1073 else 1074 SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); 1075 } 1076 1077 void VclPixelProcessor2D::processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate) 1078 { 1079 if (rCandidate.getShadowBlur() == 0) 1080 { 1081 process(rCandidate); 1082 return; 1083 } 1084 1085 basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D())); 1086 aRange.transform(maCurrentTransformation); 1087 basegfx::B2DVector aBlurRadiusVector(rCandidate.getShadowBlur(), 0); 1088 aBlurRadiusVector *= maCurrentTransformation; 1089 const double fBlurRadius = aBlurRadiusVector.getLength(); 1090 1091 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true); 1092 if (aBufferDevice.isVisible()) 1093 { 1094 OutputDevice* pLastOutputDevice = mpOutputDevice; 1095 mpOutputDevice = &aBufferDevice.getContent(); 1096 mpOutputDevice->Erase(); 1097 1098 process(rCandidate); 1099 1100 const tools::Rectangle aRect(static_cast<long>(std::floor(aRange.getMinX())), 1101 static_cast<long>(std::floor(aRange.getMinY())), 1102 static_cast<long>(std::ceil(aRange.getMaxX())), 1103 static_cast<long>(std::ceil(aRange.getMaxY()))); 1104 1105 BitmapEx bitmapEx = mpOutputDevice->GetBitmapEx(aRect.TopLeft(), aRect.GetSize()); 1106 1107 AlphaMask mask = ProcessAndBlurAlphaMask(bitmapEx.GetAlpha(), 0, fBlurRadius, 0); 1108 1109 const basegfx::BColor aShadowColor( 1110 maBColorModifierStack.getModifiedColor(rCandidate.getShadowColor())); 1111 1112 Bitmap bitmap = bitmapEx.GetBitmap(); 1113 bitmap.Erase(Color(aShadowColor)); 1114 BitmapEx result(bitmap, mask); 1115 1116 mpOutputDevice = pLastOutputDevice; 1117 mpOutputDevice->DrawBitmapEx(aRect.TopLeft(), result); 1118 } 1119 else 1120 SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible"); 1121 } 1122 1123 } // end of namespace 1124 1125 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1126
