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 <sal/config.h> 21 22 #include <basegfx/matrix/b2dhommatrix.hxx> 23 #include <basegfx/numeric/ftools.hxx> 24 #include <basegfx/point/b2dpoint.hxx> 25 #include <basegfx/polygon/b2dlinegeometry.hxx> 26 #include <basegfx/polygon/b2dpolygon.hxx> 27 #include <basegfx/polygon/b2dpolygontools.hxx> 28 #include <basegfx/polygon/b2dpolypolygontools.hxx> 29 #include <basegfx/range/b2drectangle.hxx> 30 #include <basegfx/utils/canvastools.hxx> 31 #include <basegfx/vector/b2dsize.hxx> 32 #include <com/sun/star/drawing/LineCap.hpp> 33 #include <com/sun/star/rendering/CompositeOperation.hpp> 34 #include <com/sun/star/rendering/PathCapType.hpp> 35 #include <com/sun/star/rendering/PathJoinType.hpp> 36 #include <com/sun/star/rendering/TextDirection.hpp> 37 #include <comphelper/sequence.hxx> 38 #include <rtl/math.hxx> 39 #include <tools/diagnose_ex.h> 40 #include <tools/poly.hxx> 41 #include <vcl/bitmapex.hxx> 42 #include <vcl/bitmapaccess.hxx> 43 #include <vcl/canvastools.hxx> 44 #include <vcl/BitmapAlphaClampFilter.hxx> 45 46 #include <canvas/canvastools.hxx> 47 48 #include "canvasbitmap.hxx" 49 #include "canvasfont.hxx" 50 #include "canvashelper.hxx" 51 #include "impltools.hxx" 52 #include "textlayout.hxx" 53 54 55 using namespace ::com::sun::star; 56 57 namespace vclcanvas 58 { 59 namespace 60 { b2DJoineFromJoin(sal_Int8 nJoinType)61 basegfx::B2DLineJoin b2DJoineFromJoin( sal_Int8 nJoinType ) 62 { 63 switch( nJoinType ) 64 { 65 case rendering::PathJoinType::NONE: 66 return basegfx::B2DLineJoin::NONE; 67 68 case rendering::PathJoinType::MITER: 69 return basegfx::B2DLineJoin::Miter; 70 71 case rendering::PathJoinType::ROUND: 72 return basegfx::B2DLineJoin::Round; 73 74 case rendering::PathJoinType::BEVEL: 75 return basegfx::B2DLineJoin::Bevel; 76 77 default: 78 ENSURE_OR_THROW( false, 79 "b2DJoineFromJoin(): Unexpected join type" ); 80 } 81 82 return basegfx::B2DLineJoin::NONE; 83 } 84 unoCapeFromCap(sal_Int8 nCapType)85 drawing::LineCap unoCapeFromCap( sal_Int8 nCapType) 86 { 87 switch ( nCapType) 88 { 89 case rendering::PathCapType::BUTT: 90 return drawing::LineCap_BUTT; 91 92 case rendering::PathCapType::ROUND: 93 return drawing::LineCap_ROUND; 94 95 case rendering::PathCapType::SQUARE: 96 return drawing::LineCap_SQUARE; 97 98 default: 99 ENSURE_OR_THROW( false, 100 "unoCapeFromCap(): Unexpected cap type" ); 101 } 102 return drawing::LineCap_BUTT; 103 } 104 } 105 CanvasHelper()106 CanvasHelper::CanvasHelper() : 107 mpDevice(), 108 mpProtectedOutDevProvider(), 109 mpOutDevProvider(), 110 mp2ndOutDevProvider(), 111 mbHaveAlpha( false ) 112 { 113 } 114 disposing()115 void CanvasHelper::disposing() 116 { 117 mpDevice = nullptr; 118 mpProtectedOutDevProvider.reset(); 119 mpOutDevProvider.reset(); 120 mp2ndOutDevProvider.reset(); 121 } 122 init(rendering::XGraphicDevice & rDevice,const OutDevProviderSharedPtr & rOutDev,bool bProtect,bool bHaveAlpha)123 void CanvasHelper::init( rendering::XGraphicDevice& rDevice, 124 const OutDevProviderSharedPtr& rOutDev, 125 bool bProtect, 126 bool bHaveAlpha ) 127 { 128 // cast away const, need to change refcount (as this is 129 // ~invisible to client code, still logically const) 130 mpDevice = &rDevice; 131 mbHaveAlpha = bHaveAlpha; 132 133 setOutDev( rOutDev, bProtect ); 134 } 135 setOutDev(const OutDevProviderSharedPtr & rOutDev,bool bProtect)136 void CanvasHelper::setOutDev( const OutDevProviderSharedPtr& rOutDev, 137 bool bProtect ) 138 { 139 if( bProtect ) 140 mpProtectedOutDevProvider = rOutDev; 141 else 142 mpProtectedOutDevProvider.reset(); 143 144 mpOutDevProvider = rOutDev; 145 } 146 setBackgroundOutDev(const OutDevProviderSharedPtr & rOutDev)147 void CanvasHelper::setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev ) 148 { 149 mp2ndOutDevProvider = rOutDev; 150 mp2ndOutDevProvider->getOutDev().EnableMapMode( false ); 151 mp2ndOutDevProvider->getOutDev().SetAntialiasing( AntialiasingFlags::EnableB2dDraw ); 152 } 153 clear()154 void CanvasHelper::clear() 155 { 156 // are we disposed? 157 if( mpOutDevProvider ) 158 { 159 OutputDevice& rOutDev( mpOutDevProvider->getOutDev() ); 160 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 161 162 rOutDev.EnableMapMode( false ); 163 rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw ); 164 rOutDev.SetLineColor( COL_WHITE ); 165 rOutDev.SetFillColor( COL_WHITE ); 166 rOutDev.SetClipRegion(); 167 rOutDev.DrawRect( ::tools::Rectangle( Point(), 168 rOutDev.GetOutputSizePixel()) ); 169 170 if( mp2ndOutDevProvider ) 171 { 172 OutputDevice& rOutDev2( mp2ndOutDevProvider->getOutDev() ); 173 174 rOutDev2.SetDrawMode( DrawModeFlags::Default ); 175 rOutDev2.EnableMapMode( false ); 176 rOutDev2.SetAntialiasing( AntialiasingFlags::EnableB2dDraw ); 177 rOutDev2.SetLineColor( COL_WHITE ); 178 rOutDev2.SetFillColor( COL_WHITE ); 179 rOutDev2.SetClipRegion(); 180 rOutDev2.DrawRect( ::tools::Rectangle( Point(), 181 rOutDev2.GetOutputSizePixel()) ); 182 rOutDev2.SetDrawMode( DrawModeFlags::BlackLine | DrawModeFlags::BlackFill | DrawModeFlags::BlackText | 183 DrawModeFlags::BlackGradient | DrawModeFlags::BlackBitmap ); 184 } 185 } 186 } 187 drawLine(const rendering::XCanvas *,const geometry::RealPoint2D & aStartRealPoint2D,const geometry::RealPoint2D & aEndRealPoint2D,const rendering::ViewState & viewState,const rendering::RenderState & renderState)188 void CanvasHelper::drawLine( const rendering::XCanvas* , 189 const geometry::RealPoint2D& aStartRealPoint2D, 190 const geometry::RealPoint2D& aEndRealPoint2D, 191 const rendering::ViewState& viewState, 192 const rendering::RenderState& renderState ) 193 { 194 // are we disposed? 195 if( mpOutDevProvider ) 196 { 197 // nope, render 198 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 199 setupOutDevState( viewState, renderState, LINE_COLOR ); 200 201 const Point aStartPoint( tools::mapRealPoint2D( aStartRealPoint2D, 202 viewState, renderState ) ); 203 const Point aEndPoint( tools::mapRealPoint2D( aEndRealPoint2D, 204 viewState, renderState ) ); 205 // TODO(F2): alpha 206 mpOutDevProvider->getOutDev().DrawLine( aStartPoint, aEndPoint ); 207 208 if( mp2ndOutDevProvider ) 209 mp2ndOutDevProvider->getOutDev().DrawLine( aStartPoint, aEndPoint ); 210 } 211 } 212 drawBezier(const rendering::XCanvas *,const geometry::RealBezierSegment2D & aBezierSegment,const geometry::RealPoint2D & _aEndPoint,const rendering::ViewState & viewState,const rendering::RenderState & renderState)213 void CanvasHelper::drawBezier( const rendering::XCanvas* , 214 const geometry::RealBezierSegment2D& aBezierSegment, 215 const geometry::RealPoint2D& _aEndPoint, 216 const rendering::ViewState& viewState, 217 const rendering::RenderState& renderState ) 218 { 219 if( mpOutDevProvider ) 220 { 221 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 222 setupOutDevState( viewState, renderState, LINE_COLOR ); 223 224 const Point& rStartPoint( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.Px, 225 aBezierSegment.Py), 226 viewState, renderState ) ); 227 const Point& rCtrlPoint1( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C1x, 228 aBezierSegment.C1y), 229 viewState, renderState ) ); 230 const Point& rCtrlPoint2( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C2x, 231 aBezierSegment.C2y), 232 viewState, renderState ) ); 233 const Point& rEndPoint( tools::mapRealPoint2D( _aEndPoint, 234 viewState, renderState ) ); 235 236 ::tools::Polygon aPoly(4); 237 aPoly.SetPoint( rStartPoint, 0 ); 238 aPoly.SetFlags( 0, PolyFlags::Normal ); 239 aPoly.SetPoint( rCtrlPoint1, 1 ); 240 aPoly.SetFlags( 1, PolyFlags::Control ); 241 aPoly.SetPoint( rCtrlPoint2, 2 ); 242 aPoly.SetFlags( 2, PolyFlags::Control ); 243 aPoly.SetPoint( rEndPoint, 3 ); 244 aPoly.SetFlags( 3, PolyFlags::Normal ); 245 246 // TODO(F2): alpha 247 mpOutDevProvider->getOutDev().DrawPolygon( aPoly ); 248 if( mp2ndOutDevProvider ) 249 mp2ndOutDevProvider->getOutDev().DrawPolygon( aPoly ); 250 } 251 } 252 drawPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState)253 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* , 254 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 255 const rendering::ViewState& viewState, 256 const rendering::RenderState& renderState ) 257 { 258 ENSURE_ARG_OR_THROW( xPolyPolygon.is(), 259 "polygon is NULL"); 260 261 if( mpOutDevProvider ) 262 { 263 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 264 setupOutDevState( viewState, renderState, LINE_COLOR ); 265 266 const ::basegfx::B2DPolyPolygon& rPolyPoly( 267 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) ); 268 const ::tools::PolyPolygon aPolyPoly( tools::mapPolyPolygon( rPolyPoly, viewState, renderState ) ); 269 270 if( rPolyPoly.isClosed() ) 271 { 272 mpOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly ); 273 274 if( mp2ndOutDevProvider ) 275 mp2ndOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly ); 276 } 277 else 278 { 279 // mixed open/closed state. Cannot render open polygon 280 // via DrawPolyPolygon(), since that implicitly 281 // closed every polygon. OTOH, no need to distinguish 282 // further and render closed polygons via 283 // DrawPolygon(), and open ones via DrawPolyLine(): 284 // closed polygons will simply already contain the 285 // closing segment. 286 sal_uInt16 nSize( aPolyPoly.Count() ); 287 288 for( sal_uInt16 i=0; i<nSize; ++i ) 289 { 290 mpOutDevProvider->getOutDev().DrawPolyLine( aPolyPoly[i] ); 291 292 if( mp2ndOutDevProvider ) 293 mp2ndOutDevProvider->getOutDev().DrawPolyLine( aPolyPoly[i] ); 294 } 295 } 296 } 297 298 // TODO(P1): Provide caching here. 299 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 300 } 301 strokePolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState,const rendering::StrokeAttributes & strokeAttributes)302 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* , 303 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 304 const rendering::ViewState& viewState, 305 const rendering::RenderState& renderState, 306 const rendering::StrokeAttributes& strokeAttributes ) 307 { 308 ENSURE_ARG_OR_THROW( xPolyPolygon.is(), 309 "polygon is NULL"); 310 311 if( mpOutDevProvider ) 312 { 313 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 314 315 ::basegfx::B2DHomMatrix aMatrix; 316 ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState); 317 318 ::basegfx::B2DSize aLinePixelSize(strokeAttributes.StrokeWidth, 319 strokeAttributes.StrokeWidth); 320 aLinePixelSize *= aMatrix; 321 322 ::basegfx::B2DPolyPolygon aPolyPoly( 323 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) ); 324 325 if( aPolyPoly.areControlPointsUsed() ) 326 { 327 // AW: Not needed for ApplyLineDashing anymore; should be removed 328 aPolyPoly = ::basegfx::utils::adaptiveSubdivideByAngle(aPolyPoly); 329 } 330 331 // apply dashing, if any 332 if( strokeAttributes.DashArray.hasElements() ) 333 { 334 const std::vector<double>& aDashArray( 335 ::comphelper::sequenceToContainer< std::vector<double> >(strokeAttributes.DashArray) ); 336 337 ::basegfx::B2DPolyPolygon aDashedPolyPoly; 338 339 for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i ) 340 { 341 // AW: new interface; You may also get gaps in the same run now 342 basegfx::utils::applyLineDashing(aPolyPoly.getB2DPolygon(i), aDashArray, &aDashedPolyPoly); 343 //aDashedPolyPoly.append( 344 // ::basegfx::utils::applyLineDashing( aPolyPoly.getB2DPolygon(i), 345 // aDashArray ) ); 346 } 347 348 aPolyPoly = aDashedPolyPoly; 349 } 350 351 ::basegfx::B2DPolyPolygon aStrokedPolyPoly; 352 if( aLinePixelSize.getLength() < 1.42 ) 353 { 354 // line width < 1.0 in device pixel, thus, output as a 355 // simple hairline poly-polygon 356 setupOutDevState( viewState, renderState, LINE_COLOR ); 357 358 aStrokedPolyPoly = aPolyPoly; 359 } 360 else 361 { 362 // render as a 'thick' line 363 setupOutDevState( viewState, renderState, FILL_COLOR ); 364 365 for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i ) 366 { 367 double fMiterMinimumAngle; 368 if (strokeAttributes.MiterLimit <= 1.0) 369 { 370 fMiterMinimumAngle = F_PI2; 371 } 372 else 373 { 374 fMiterMinimumAngle = 2.0 * asin(1.0/strokeAttributes.MiterLimit); 375 } 376 377 // TODO(F2): Also use Cap settings from 378 // StrokeAttributes, the 379 // createAreaGeometryForLineStartEnd() method does not 380 // seem to fit very well here 381 382 // AW: New interface, will create bezier polygons now 383 aStrokedPolyPoly.append(basegfx::utils::createAreaGeometry( 384 aPolyPoly.getB2DPolygon(i), 385 strokeAttributes.StrokeWidth*0.5, 386 b2DJoineFromJoin(strokeAttributes.JoinType), 387 unoCapeFromCap(strokeAttributes.StartCapType), 388 basegfx::deg2rad(12.5) /* default fMaxAllowedAngle*/ , 389 0.4 /* default fMaxPartOfEdge*/ , 390 fMiterMinimumAngle 391 )); 392 //aStrokedPolyPoly.append( 393 // ::basegfx::utils::createAreaGeometryForPolygon( aPolyPoly.getB2DPolygon(i), 394 // strokeAttributes.StrokeWidth*0.5, 395 // b2DJoineFromJoin(strokeAttributes.JoinType) ) ); 396 } 397 } 398 399 // transform only _now_, all the StrokeAttributes are in 400 // user coordinates. 401 aStrokedPolyPoly.transform( aMatrix ); 402 403 const ::tools::PolyPolygon aVCLPolyPoly( aStrokedPolyPoly ); 404 405 // TODO(F2): When using alpha here, must handle that via 406 // temporary surface or somesuch. 407 408 // Note: the generated stroke poly-polygon is NOT free of 409 // self-intersections. Therefore, if we would render it 410 // via OutDev::DrawPolyPolygon(), on/off fill would 411 // generate off areas on those self-intersections. 412 sal_uInt16 nSize( aVCLPolyPoly.Count() ); 413 414 for( sal_uInt16 i=0; i<nSize; ++i ) 415 { 416 if( aStrokedPolyPoly.getB2DPolygon( i ).isClosed() ) { 417 mpOutDevProvider->getOutDev().DrawPolygon( aVCLPolyPoly[i] ); 418 if( mp2ndOutDevProvider ) 419 mp2ndOutDevProvider->getOutDev().DrawPolygon( aVCLPolyPoly[i] ); 420 } else { 421 const sal_uInt16 nPolySize = aVCLPolyPoly[i].GetSize(); 422 if( nPolySize ) { 423 Point rPrevPoint = aVCLPolyPoly[i].GetPoint( 0 ); 424 Point rPoint; 425 426 for( sal_uInt16 j=1; j<nPolySize; j++ ) { 427 rPoint = aVCLPolyPoly[i].GetPoint( j ); 428 mpOutDevProvider->getOutDev().DrawLine( rPrevPoint, rPoint ); 429 if( mp2ndOutDevProvider ) 430 mp2ndOutDevProvider->getOutDev().DrawLine( rPrevPoint, rPoint ); 431 rPrevPoint = rPoint; 432 } 433 } 434 } 435 } 436 } 437 438 // TODO(P1): Provide caching here. 439 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 440 } 441 strokeTexturedPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const uno::Sequence<rendering::Texture> &,const rendering::StrokeAttributes &)442 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* , 443 const uno::Reference< rendering::XPolyPolygon2D >& , 444 const rendering::ViewState& , 445 const rendering::RenderState& , 446 const uno::Sequence< rendering::Texture >& , 447 const rendering::StrokeAttributes& ) 448 { 449 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 450 } 451 strokeTextureMappedPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const uno::Sequence<rendering::Texture> &,const uno::Reference<geometry::XMapping2D> &,const rendering::StrokeAttributes &)452 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* , 453 const uno::Reference< rendering::XPolyPolygon2D >& , 454 const rendering::ViewState& , 455 const rendering::RenderState& , 456 const uno::Sequence< rendering::Texture >& , 457 const uno::Reference< geometry::XMapping2D >& , 458 const rendering::StrokeAttributes& ) 459 { 460 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 461 } 462 queryStrokeShapes(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const rendering::StrokeAttributes &)463 uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* , 464 const uno::Reference< rendering::XPolyPolygon2D >& , 465 const rendering::ViewState& , 466 const rendering::RenderState& , 467 const rendering::StrokeAttributes& ) 468 { 469 return uno::Reference< rendering::XPolyPolygon2D >(nullptr); 470 } 471 fillPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> & xPolyPolygon,const rendering::ViewState & viewState,const rendering::RenderState & renderState)472 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* , 473 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 474 const rendering::ViewState& viewState, 475 const rendering::RenderState& renderState ) 476 { 477 ENSURE_ARG_OR_THROW( xPolyPolygon.is(), 478 "polygon is NULL"); 479 480 if( mpOutDevProvider ) 481 { 482 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 483 484 const int nTransparency( setupOutDevState( viewState, renderState, FILL_COLOR ) ); 485 ::basegfx::B2DPolyPolygon aB2DPolyPoly( 486 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon)); 487 aB2DPolyPoly.setClosed(true); // ensure closed poly, otherwise VCL does not fill 488 const ::tools::PolyPolygon aPolyPoly( tools::mapPolyPolygon( 489 aB2DPolyPoly, 490 viewState, renderState ) ); 491 const bool bSourceAlpha( renderState.CompositeOperation == rendering::CompositeOperation::SOURCE ); 492 if( !nTransparency || bSourceAlpha ) 493 { 494 mpOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly ); 495 } 496 else 497 { 498 const int nTransPercent( (nTransparency * 100 + 128) / 255 ); // normal rounding, no truncation here 499 mpOutDevProvider->getOutDev().DrawTransparent( aPolyPoly, static_cast<sal_uInt16>(nTransPercent) ); 500 } 501 502 if( mp2ndOutDevProvider ) 503 { 504 // HACK. Normally, CanvasHelper does not care about 505 // actually what mp2ndOutDev is... well, here we do & 506 // assume a 1bpp target - everything beyond 97% 507 // transparency is fully transparent 508 if( nTransparency < 253 ) 509 { 510 mp2ndOutDevProvider->getOutDev().SetFillColor( COL_BLACK ); 511 mp2ndOutDevProvider->getOutDev().DrawPolyPolygon( aPolyPoly ); 512 } 513 } 514 } 515 516 // TODO(P1): Provide caching here. 517 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 518 } 519 fillTextureMappedPolyPolygon(const rendering::XCanvas *,const uno::Reference<rendering::XPolyPolygon2D> &,const rendering::ViewState &,const rendering::RenderState &,const uno::Sequence<rendering::Texture> &,const uno::Reference<geometry::XMapping2D> &)520 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* , 521 const uno::Reference< rendering::XPolyPolygon2D >& , 522 const rendering::ViewState& , 523 const rendering::RenderState& , 524 const uno::Sequence< rendering::Texture >& , 525 const uno::Reference< geometry::XMapping2D >& ) 526 { 527 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 528 } 529 createFont(const rendering::XCanvas *,const rendering::FontRequest & fontRequest,const uno::Sequence<beans::PropertyValue> & extraFontProperties,const geometry::Matrix2D & fontMatrix)530 uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* , 531 const rendering::FontRequest& fontRequest, 532 const uno::Sequence< beans::PropertyValue >& extraFontProperties, 533 const geometry::Matrix2D& fontMatrix ) 534 { 535 if( mpOutDevProvider && mpDevice ) 536 { 537 // TODO(F2): font properties and font matrix 538 return uno::Reference< rendering::XCanvasFont >( 539 new CanvasFont(fontRequest, extraFontProperties, fontMatrix, 540 *mpDevice, mpOutDevProvider) ); 541 } 542 543 return uno::Reference< rendering::XCanvasFont >(); 544 } 545 queryAvailableFonts(const rendering::XCanvas *,const rendering::FontInfo &,const uno::Sequence<beans::PropertyValue> &)546 uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* , 547 const rendering::FontInfo& , 548 const uno::Sequence< beans::PropertyValue >& ) 549 { 550 // TODO(F2) 551 return uno::Sequence< rendering::FontInfo >(); 552 } 553 drawText(const rendering::XCanvas *,const rendering::StringContext & text,const uno::Reference<rendering::XCanvasFont> & xFont,const rendering::ViewState & viewState,const rendering::RenderState & renderState,sal_Int8 textDirection)554 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* , 555 const rendering::StringContext& text, 556 const uno::Reference< rendering::XCanvasFont >& xFont, 557 const rendering::ViewState& viewState, 558 const rendering::RenderState& renderState, 559 sal_Int8 textDirection ) 560 { 561 ENSURE_ARG_OR_THROW( xFont.is(), 562 "font is NULL"); 563 564 if( mpOutDevProvider ) 565 { 566 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 567 568 ::Point aOutpos; 569 if( !setupTextOutput( aOutpos, viewState, renderState, xFont ) ) 570 return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary 571 572 // change text direction and layout mode 573 ComplexTextLayoutFlags nLayoutMode(ComplexTextLayoutFlags::Default); 574 switch( textDirection ) 575 { 576 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT: 577 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT: 578 nLayoutMode |= ComplexTextLayoutFlags::BiDiStrong; 579 nLayoutMode |= ComplexTextLayoutFlags::TextOriginLeft; 580 break; 581 582 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT: 583 nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl; 584 [[fallthrough]]; 585 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT: 586 nLayoutMode |= ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::BiDiStrong; 587 nLayoutMode |= ComplexTextLayoutFlags::TextOriginRight; 588 break; 589 } 590 591 // TODO(F2): alpha 592 mpOutDevProvider->getOutDev().SetLayoutMode( nLayoutMode ); 593 mpOutDevProvider->getOutDev().DrawText( aOutpos, 594 text.Text, 595 ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition), 596 ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) ); 597 598 if( mp2ndOutDevProvider ) 599 { 600 mp2ndOutDevProvider->getOutDev().SetLayoutMode( nLayoutMode ); 601 mp2ndOutDevProvider->getOutDev().DrawText( aOutpos, 602 text.Text, 603 ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition), 604 ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) ); 605 } 606 } 607 608 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 609 } 610 drawTextLayout(const rendering::XCanvas *,const uno::Reference<rendering::XTextLayout> & xLayoutedText,const rendering::ViewState & viewState,const rendering::RenderState & renderState)611 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* , 612 const uno::Reference< rendering::XTextLayout >& xLayoutedText, 613 const rendering::ViewState& viewState, 614 const rendering::RenderState& renderState ) 615 { 616 ENSURE_ARG_OR_THROW( xLayoutedText.is(), 617 "layout is NULL"); 618 619 TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() ); 620 621 if( pTextLayout ) 622 { 623 if( mpOutDevProvider ) 624 { 625 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 626 627 // TODO(T3): Race condition. We're taking the font 628 // from xLayoutedText, and then calling draw() at it, 629 // without exclusive access. Move setupTextOutput(), 630 // e.g. to impltools? 631 632 ::Point aOutpos; 633 if( !setupTextOutput( aOutpos, viewState, renderState, xLayoutedText->getFont() ) ) 634 return uno::Reference< rendering::XCachedPrimitive >(nullptr); // no output necessary 635 636 // TODO(F2): What about the offset scalings? 637 // TODO(F2): alpha 638 pTextLayout->draw( mpOutDevProvider->getOutDev(), aOutpos, viewState, renderState ); 639 640 if( mp2ndOutDevProvider ) 641 pTextLayout->draw( mp2ndOutDevProvider->getOutDev(), aOutpos, viewState, renderState ); 642 } 643 } 644 else 645 { 646 ENSURE_ARG_OR_THROW( false, 647 "TextLayout not compatible with this canvas" ); 648 } 649 650 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 651 } 652 implDrawBitmap(const rendering::XCanvas * pCanvas,const uno::Reference<rendering::XBitmap> & xBitmap,const rendering::ViewState & viewState,const rendering::RenderState & renderState,bool bModulateColors)653 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmap( const rendering::XCanvas* pCanvas, 654 const uno::Reference< rendering::XBitmap >& xBitmap, 655 const rendering::ViewState& viewState, 656 const rendering::RenderState& renderState, 657 bool bModulateColors ) 658 { 659 ENSURE_ARG_OR_THROW( xBitmap.is(), 660 "bitmap is NULL"); 661 662 ::canvas::tools::verifyInput( renderState, 663 OSL_THIS_FUNC, 664 mpDevice, 665 4, 666 bModulateColors ? 3 : 0 ); 667 668 if( mpOutDevProvider ) 669 { 670 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 671 setupOutDevState( viewState, renderState, IGNORE_COLOR ); 672 673 ::basegfx::B2DHomMatrix aMatrix; 674 ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState); 675 676 ::basegfx::B2DPoint aOutputPos( 0.0, 0.0 ); 677 aOutputPos *= aMatrix; 678 679 BitmapEx aBmpEx( tools::bitmapExFromXBitmap(xBitmap) ); 680 681 // TODO(F2): Implement modulation again for other color 682 // channels (currently, works only for alpha). Note: this 683 // is already implemented in transformBitmap() 684 if( bModulateColors && 685 renderState.DeviceColor.getLength() > 3 ) 686 { 687 // optimize away the case where alpha modulation value 688 // is 1.0 - we then simply switch off modulation at all 689 bModulateColors = !::rtl::math::approxEqual( 690 renderState.DeviceColor[3], 1.0); 691 } 692 693 // check whether we can render bitmap as-is: must not 694 // modulate colors, matrix must either be the identity 695 // transform (that's clear), _or_ contain only 696 // translational components. 697 if( !bModulateColors && 698 (aMatrix.isIdentity() || 699 (::basegfx::fTools::equalZero( aMatrix.get(0,1) ) && 700 ::basegfx::fTools::equalZero( aMatrix.get(1,0) ) && 701 ::rtl::math::approxEqual(aMatrix.get(0,0), 1.0) && 702 ::rtl::math::approxEqual(aMatrix.get(1,1), 1.0)) ) ) 703 { 704 // optimized case: identity matrix, or only 705 // translational components. 706 mpOutDevProvider->getOutDev().DrawBitmapEx( vcl::unotools::pointFromB2DPoint( aOutputPos ), 707 aBmpEx ); 708 709 if( mp2ndOutDevProvider ) 710 { 711 // HACK. Normally, CanvasHelper does not care about 712 // actually what mp2ndOutDev is... well, here we do & 713 // assume a 1bpp target - everything beyond 97% 714 // transparency is fully transparent 715 if( aBmpEx.IsAlpha() ) 716 { 717 BitmapFilter::Filter(aBmpEx, BitmapAlphaClampFilter(253)); 718 } 719 720 mp2ndOutDevProvider->getOutDev().DrawBitmapEx( vcl::unotools::pointFromB2DPoint( aOutputPos ), 721 aBmpEx ); 722 } 723 724 // Returning a cache object is not useful, the XBitmap 725 // itself serves this purpose 726 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 727 } 728 else 729 { 730 // Matrix contains non-trivial transformation (or 731 // color modulation is requested), decompose to check 732 // whether GraphicObject suffices 733 ::basegfx::B2DVector aScale; 734 double nRotate; 735 double nShearX; 736 aMatrix.decompose( aScale, aOutputPos, nRotate, nShearX ); 737 738 GraphicAttr aGrfAttr; 739 GraphicObjectSharedPtr pGrfObj; 740 741 ::Size aBmpSize( aBmpEx.GetSizePixel() ); 742 743 // setup alpha modulation 744 if( bModulateColors ) 745 { 746 const double nAlphaModulation( renderState.DeviceColor[3] ); 747 748 // TODO(F1): Note that the GraphicManager has a 749 // subtle difference in how it calculates the 750 // resulting alpha value: it's using the inverse 751 // alpha values (i.e. 'transparency'), and 752 // calculates transOrig + transModulate, instead 753 // of transOrig + transModulate - 754 // transOrig*transModulate (which would be 755 // equivalent to the origAlpha*modulateAlpha the 756 // DX canvas performs) 757 aGrfAttr.SetTransparency( 758 static_cast< sal_uInt8 >( 759 ::basegfx::fround( 255.0*( 1.0 - nAlphaModulation ) ) ) ); 760 } 761 762 if( ::basegfx::fTools::equalZero( nShearX ) ) 763 { 764 // no shear, GraphicObject is enough (the 765 // GraphicObject only supports scaling, rotation 766 // and translation) 767 768 // #i75339# don't apply mirror flags, having 769 // negative size values is enough to make 770 // GraphicObject flip the bitmap 771 772 // The angle has to be mapped from radian to tenths of 773 // degress with the orientation reversed: [0,2Pi) -> 774 // (3600,0]. Note that the original angle may have 775 // values outside the [0,2Pi) interval. 776 const double nAngleInTenthOfDegrees (3600.0 - nRotate * 3600.0 / (2*M_PI)); 777 aGrfAttr.SetRotation( static_cast< sal_uInt16 >(::basegfx::fround(nAngleInTenthOfDegrees)) ); 778 779 pGrfObj.reset( new GraphicObject( aBmpEx ) ); 780 } 781 else 782 { 783 // modify output position, to account for the fact 784 // that transformBitmap() always normalizes its output 785 // bitmap into the smallest enclosing box. 786 ::basegfx::B2DRectangle aDestRect; 787 ::canvas::tools::calcTransformedRectBounds( aDestRect, 788 ::basegfx::B2DRectangle(0, 789 0, 790 aBmpSize.Width(), 791 aBmpSize.Height()), 792 aMatrix ); 793 794 aOutputPos.setX( aDestRect.getMinX() ); 795 aOutputPos.setY( aDestRect.getMinY() ); 796 797 // complex transformation, use generic affine bitmap 798 // transformation 799 aBmpEx = tools::transformBitmap( aBmpEx, 800 aMatrix ); 801 802 pGrfObj.reset( new GraphicObject( aBmpEx ) ); 803 804 // clear scale values, generated bitmap already 805 // contains scaling 806 aScale.setX( 1.0 ); aScale.setY( 1.0 ); 807 808 // update bitmap size, bitmap has changed above. 809 aBmpSize = aBmpEx.GetSizePixel(); 810 } 811 812 // output GraphicObject 813 const ::Point aPt( vcl::unotools::pointFromB2DPoint( aOutputPos ) ); 814 const ::Size aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width() ), 815 ::basegfx::fround( aScale.getY() * aBmpSize.Height() ) ); 816 817 pGrfObj->Draw( &mpOutDevProvider->getOutDev(), 818 aPt, 819 aSz, 820 &aGrfAttr ); 821 822 if( mp2ndOutDevProvider ) 823 pGrfObj->Draw( &mp2ndOutDevProvider->getOutDev(), 824 aPt, 825 aSz, 826 &aGrfAttr ); 827 828 // created GraphicObject, which possibly cached 829 // display bitmap - return cache object, to retain 830 // that information. 831 return uno::Reference< rendering::XCachedPrimitive >( 832 new CachedBitmap( pGrfObj, 833 aPt, 834 aSz, 835 aGrfAttr, 836 viewState, 837 renderState, 838 // cast away const, need to 839 // change refcount (as this is 840 // ~invisible to client code, 841 // still logically const) 842 const_cast< rendering::XCanvas* >(pCanvas)) ); 843 } 844 } 845 846 // Nothing rendered 847 return uno::Reference< rendering::XCachedPrimitive >(nullptr); 848 } 849 drawBitmap(const rendering::XCanvas * pCanvas,const uno::Reference<rendering::XBitmap> & xBitmap,const rendering::ViewState & viewState,const rendering::RenderState & renderState)850 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* pCanvas, 851 const uno::Reference< rendering::XBitmap >& xBitmap, 852 const rendering::ViewState& viewState, 853 const rendering::RenderState& renderState ) 854 { 855 return implDrawBitmap( pCanvas, 856 xBitmap, 857 viewState, 858 renderState, 859 false ); 860 } 861 drawBitmapModulated(const rendering::XCanvas * pCanvas,const uno::Reference<rendering::XBitmap> & xBitmap,const rendering::ViewState & viewState,const rendering::RenderState & renderState)862 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas, 863 const uno::Reference< rendering::XBitmap >& xBitmap, 864 const rendering::ViewState& viewState, 865 const rendering::RenderState& renderState ) 866 { 867 return implDrawBitmap( pCanvas, 868 xBitmap, 869 viewState, 870 renderState, 871 true ); 872 } 873 getSize()874 geometry::IntegerSize2D CanvasHelper::getSize() 875 { 876 if( !mpOutDevProvider.get() ) 877 return geometry::IntegerSize2D(); // we're disposed 878 879 return vcl::unotools::integerSize2DFromSize( mpOutDevProvider->getOutDev().GetOutputSizePixel() ); 880 } 881 getScaledBitmap(const geometry::RealSize2D & newSize,bool beFast)882 uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize, 883 bool beFast ) 884 { 885 if( !mpOutDevProvider.get() || !mpDevice ) 886 return uno::Reference< rendering::XBitmap >(); // we're disposed 887 888 OutputDevice& rOutDev( mpOutDevProvider->getOutDev() ); 889 890 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 891 rOutDev.EnableMapMode( false ); 892 rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw ); 893 894 // TODO(F2): Support alpha vdev canvas here 895 const Point aEmptyPoint(0,0); 896 const Size aBmpSize( rOutDev.GetOutputSizePixel() ); 897 898 BitmapEx aBitmap( rOutDev.GetBitmapEx(aEmptyPoint, aBmpSize) ); 899 900 aBitmap.Scale( vcl::unotools::sizeFromRealSize2D(newSize), 901 beFast ? BmpScaleFlag::Default : BmpScaleFlag::BestQuality ); 902 903 return uno::Reference< rendering::XBitmap >( 904 new CanvasBitmap( aBitmap, *mpDevice, mpOutDevProvider ) ); 905 } 906 getData(rendering::IntegerBitmapLayout & rLayout,const geometry::IntegerRectangle2D & rect)907 uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& rLayout, 908 const geometry::IntegerRectangle2D& rect ) 909 { 910 if( !mpOutDevProvider.get() ) 911 return uno::Sequence< sal_Int8 >(); // we're disposed 912 913 rLayout = getMemoryLayout(); 914 915 // TODO(F2): Support alpha canvas here 916 const ::tools::Rectangle aRect( vcl::unotools::rectangleFromIntegerRectangle2D(rect) ); 917 918 OutputDevice& rOutDev( mpOutDevProvider->getOutDev() ); 919 920 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 921 rOutDev.EnableMapMode( false ); 922 rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw ); 923 924 Bitmap aBitmap( rOutDev.GetBitmapEx(aRect.TopLeft(), 925 aRect.GetSize()).GetBitmap() ); 926 927 Bitmap::ScopedReadAccess pReadAccess( aBitmap ); 928 929 ENSURE_OR_THROW( pReadAccess.get() != nullptr, 930 "Could not acquire read access to OutDev bitmap" ); 931 932 const sal_Int32 nWidth( rect.X2 - rect.X1 ); 933 const sal_Int32 nHeight( rect.Y2 - rect.Y1 ); 934 935 rLayout.ScanLines = nHeight; 936 rLayout.ScanLineBytes = nWidth*4; 937 rLayout.ScanLineStride = rLayout.ScanLineBytes; 938 939 uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight ); 940 sal_Int8* pRes = aRes.getArray(); 941 942 int nCurrPos(0); 943 for( int y=0; y<nHeight; ++y ) 944 { 945 for( int x=0; x<nWidth; ++x ) 946 { 947 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed(); 948 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen(); 949 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue(); 950 pRes[ nCurrPos++ ] = -1; 951 } 952 } 953 954 return aRes; 955 } 956 getPixel(rendering::IntegerBitmapLayout & rLayout,const geometry::IntegerPoint2D & pos)957 uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& rLayout, 958 const geometry::IntegerPoint2D& pos ) 959 { 960 if( !mpOutDevProvider.get() ) 961 return uno::Sequence< sal_Int8 >(); // we're disposed 962 963 rLayout = getMemoryLayout(); 964 rLayout.ScanLines = 1; 965 rLayout.ScanLineBytes = 4; 966 rLayout.ScanLineStride = rLayout.ScanLineBytes; 967 968 OutputDevice& rOutDev( mpOutDevProvider->getOutDev() ); 969 970 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 971 rOutDev.EnableMapMode( false ); 972 rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw ); 973 974 const Size aBmpSize( rOutDev.GetOutputSizePixel() ); 975 976 ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(), 977 "X coordinate out of bounds" ); 978 ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(), 979 "Y coordinate out of bounds" ); 980 981 // TODO(F2): Support alpha canvas here 982 return ::canvas::tools::colorToStdIntSequence( 983 rOutDev.GetPixel( 984 vcl::unotools::pointFromIntegerPoint2D( pos ))); 985 } 986 getMemoryLayout()987 rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout() 988 { 989 if( !mpOutDevProvider.get() ) 990 return rendering::IntegerBitmapLayout(); // we're disposed 991 992 rendering::IntegerBitmapLayout aBitmapLayout( ::canvas::tools::getStdMemoryLayout(getSize()) ); 993 if ( !mbHaveAlpha ) 994 aBitmapLayout.ColorSpace = canvas::tools::getStdColorSpaceWithoutAlpha(); 995 996 return aBitmapLayout; 997 } 998 setupOutDevState(const rendering::ViewState & viewState,const rendering::RenderState & renderState,ColorType eColorType) const999 int CanvasHelper::setupOutDevState( const rendering::ViewState& viewState, 1000 const rendering::RenderState& renderState, 1001 ColorType eColorType ) const 1002 { 1003 ENSURE_OR_THROW( mpOutDevProvider.get(), 1004 "outdev null. Are we disposed?" ); 1005 1006 ::canvas::tools::verifyInput( renderState, 1007 OSL_THIS_FUNC, 1008 mpDevice, 1009 2, 1010 eColorType == IGNORE_COLOR ? 0 : 3 ); 1011 1012 OutputDevice& rOutDev( mpOutDevProvider->getOutDev() ); 1013 OutputDevice* p2ndOutDev = nullptr; 1014 1015 rOutDev.EnableMapMode( false ); 1016 rOutDev.SetAntialiasing( AntialiasingFlags::EnableB2dDraw ); 1017 1018 if( mp2ndOutDevProvider ) 1019 p2ndOutDev = &mp2ndOutDevProvider->getOutDev(); 1020 1021 int nTransparency(0); 1022 1023 // TODO(P2): Don't change clipping all the time, maintain current clip 1024 // state and change only when update is necessary 1025 ::canvas::tools::clipOutDev(viewState, renderState, rOutDev, p2ndOutDev); 1026 1027 Color aColor( COL_WHITE ); 1028 1029 if( renderState.DeviceColor.getLength() > 2 ) 1030 { 1031 aColor = vcl::unotools::stdColorSpaceSequenceToColor( 1032 renderState.DeviceColor ); 1033 } 1034 1035 // extract alpha, and make color opaque 1036 // afterwards. Otherwise, OutputDevice won't draw anything 1037 nTransparency = aColor.GetTransparency(); 1038 aColor.SetTransparency(0); 1039 1040 if( eColorType != IGNORE_COLOR ) 1041 { 1042 switch( eColorType ) 1043 { 1044 case LINE_COLOR: 1045 rOutDev.SetLineColor( aColor ); 1046 rOutDev.SetFillColor(); 1047 1048 if( p2ndOutDev ) 1049 { 1050 p2ndOutDev->SetLineColor( aColor ); 1051 p2ndOutDev->SetFillColor(); 1052 } 1053 break; 1054 1055 case FILL_COLOR: 1056 rOutDev.SetFillColor( aColor ); 1057 rOutDev.SetLineColor(); 1058 1059 if( p2ndOutDev ) 1060 { 1061 p2ndOutDev->SetFillColor( aColor ); 1062 p2ndOutDev->SetLineColor(); 1063 } 1064 break; 1065 1066 case TEXT_COLOR: 1067 rOutDev.SetTextColor( aColor ); 1068 1069 if( p2ndOutDev ) 1070 p2ndOutDev->SetTextColor( aColor ); 1071 break; 1072 1073 default: 1074 ENSURE_OR_THROW( false, 1075 "Unexpected color type"); 1076 break; 1077 } 1078 } 1079 1080 return nTransparency; 1081 } 1082 setupTextOutput(::Point & o_rOutPos,const rendering::ViewState & viewState,const rendering::RenderState & renderState,const uno::Reference<rendering::XCanvasFont> & xFont) const1083 bool CanvasHelper::setupTextOutput( ::Point& o_rOutPos, 1084 const rendering::ViewState& viewState, 1085 const rendering::RenderState& renderState, 1086 const uno::Reference< rendering::XCanvasFont >& xFont ) const 1087 { 1088 ENSURE_OR_THROW( mpOutDevProvider.get(), 1089 "outdev null. Are we disposed?" ); 1090 1091 OutputDevice& rOutDev( mpOutDevProvider->getOutDev() ); 1092 1093 setupOutDevState( viewState, renderState, TEXT_COLOR ); 1094 1095 CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() ); 1096 1097 ENSURE_ARG_OR_THROW( pFont, 1098 "Font not compatible with this canvas" ); 1099 1100 vcl::Font aVCLFont = pFont->getVCLFont(); 1101 1102 Color aColor( COL_BLACK ); 1103 1104 if( renderState.DeviceColor.getLength() > 2 ) 1105 { 1106 aColor = vcl::unotools::stdColorSpaceSequenceToColor( 1107 renderState.DeviceColor ); 1108 } 1109 1110 // setup font color 1111 aVCLFont.SetColor( aColor ); 1112 aVCLFont.SetFillColor( aColor ); 1113 1114 // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here. 1115 if( !tools::setupFontTransform( o_rOutPos, aVCLFont, viewState, renderState, rOutDev ) ) 1116 return false; 1117 1118 rOutDev.SetFont( aVCLFont ); 1119 1120 if( mp2ndOutDevProvider ) 1121 mp2ndOutDevProvider->getOutDev().SetFont( aVCLFont ); 1122 1123 return true; 1124 } 1125 repaint(const GraphicObjectSharedPtr & rGrf,const rendering::ViewState & viewState,const rendering::RenderState & renderState,const::Point & rPt,const::Size & rSz,const GraphicAttr & rAttr) const1126 bool CanvasHelper::repaint( const GraphicObjectSharedPtr& rGrf, 1127 const rendering::ViewState& viewState, 1128 const rendering::RenderState& renderState, 1129 const ::Point& rPt, 1130 const ::Size& rSz, 1131 const GraphicAttr& rAttr ) const 1132 { 1133 ENSURE_OR_RETURN_FALSE( rGrf, 1134 "Invalid Graphic" ); 1135 1136 if( !mpOutDevProvider ) 1137 return false; // disposed 1138 else 1139 { 1140 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDevProvider ); 1141 setupOutDevState( viewState, renderState, IGNORE_COLOR ); 1142 1143 if( !rGrf->Draw( &mpOutDevProvider->getOutDev(), rPt, rSz, &rAttr ) ) 1144 return false; 1145 1146 // #i80779# Redraw also into mask outdev 1147 if( mp2ndOutDevProvider ) 1148 return rGrf->Draw( &mp2ndOutDevProvider->getOutDev(), rPt, rSz, &rAttr ); 1149 1150 return true; 1151 } 1152 } 1153 flush() const1154 void CanvasHelper::flush() const 1155 { 1156 if (mpOutDevProvider) 1157 mpOutDevProvider->getOutDev().Flush(); 1158 1159 if (mp2ndOutDevProvider) 1160 mp2ndOutDevProvider->getOutDev().Flush(); 1161 } 1162 1163 } 1164 1165 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1166