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_canvas.hxx" 26 27 #include <canvas/debug.hxx> 28 #include <tools/diagnose_ex.h> 29 30 #include <rtl/math.hxx> 31 32 #include <com/sun/star/rendering/CompositeOperation.hpp> 33 #include <com/sun/star/util/Endianness.hpp> 34 #include <com/sun/star/rendering/TextDirection.hpp> 35 #include <com/sun/star/rendering/TexturingMode.hpp> 36 #include <com/sun/star/rendering/PathCapType.hpp> 37 #include <com/sun/star/rendering/PathJoinType.hpp> 38 #include <com/sun/star/drawing/LineCap.hpp> 39 40 #include <tools/poly.hxx> 41 #include <vcl/window.hxx> 42 #include <vcl/bitmapex.hxx> 43 #include <vcl/bmpacc.hxx> 44 #include <vcl/canvastools.hxx> 45 46 #include <basegfx/matrix/b2dhommatrix.hxx> 47 #include <basegfx/range/b2drectangle.hxx> 48 #include <basegfx/point/b2dpoint.hxx> 49 #include <basegfx/vector/b2dsize.hxx> 50 #include <basegfx/polygon/b2dpolygon.hxx> 51 #include <basegfx/polygon/b2dpolygontools.hxx> 52 #include <basegfx/polygon/b2dpolypolygontools.hxx> 53 #include <basegfx/polygon/b2dlinegeometry.hxx> 54 #include <basegfx/tools/canvastools.hxx> 55 #include <basegfx/numeric/ftools.hxx> 56 57 #include <utility> 58 59 #include <comphelper/sequence.hxx> 60 #include <canvas/canvastools.hxx> 61 62 #include "textlayout.hxx" 63 #include "canvashelper.hxx" 64 #include "canvasbitmap.hxx" 65 #include "impltools.hxx" 66 #include "canvasfont.hxx" 67 68 69 using namespace ::com::sun::star; 70 71 namespace vclcanvas 72 { 73 namespace 74 { 75 basegfx::B2DLineJoin b2DJoineFromJoin( sal_Int8 nJoinType ) 76 { 77 switch( nJoinType ) 78 { 79 case rendering::PathJoinType::NONE: 80 return basegfx::B2DLINEJOIN_NONE; 81 82 case rendering::PathJoinType::MITER: 83 return basegfx::B2DLINEJOIN_MITER; 84 85 case rendering::PathJoinType::ROUND: 86 return basegfx::B2DLINEJOIN_ROUND; 87 88 case rendering::PathJoinType::BEVEL: 89 return basegfx::B2DLINEJOIN_BEVEL; 90 91 default: 92 ENSURE_OR_THROW( false, 93 "b2DJoineFromJoin(): Unexpected join type" ); 94 } 95 96 return basegfx::B2DLINEJOIN_NONE; 97 } 98 99 drawing::LineCap unoCapeFromCap( sal_Int8 nCapType) 100 { 101 switch ( nCapType) 102 { 103 case rendering::PathCapType::BUTT: 104 return drawing::LineCap_BUTT; 105 106 case rendering::PathCapType::ROUND: 107 return drawing::LineCap_ROUND; 108 109 case rendering::PathCapType::SQUARE: 110 return drawing::LineCap_SQUARE; 111 112 default: 113 ENSURE_OR_THROW( false, 114 "unoCapeFromCap(): Unexpected cap type" ); 115 } 116 return drawing::LineCap_BUTT; 117 } 118 } 119 120 CanvasHelper::CanvasHelper() : 121 mpDevice(), 122 mpProtectedOutDev(), 123 mpOutDev(), 124 mp2ndOutDev(), 125 mbHaveAlpha( false ) 126 { 127 } 128 129 void CanvasHelper::disposing() 130 { 131 mpDevice = NULL; 132 mpProtectedOutDev.reset(); 133 mpOutDev.reset(); 134 mp2ndOutDev.reset(); 135 } 136 137 void CanvasHelper::init( rendering::XGraphicDevice& rDevice, 138 const OutDevProviderSharedPtr& rOutDev, 139 bool bProtect, 140 bool bHaveAlpha ) 141 { 142 // cast away const, need to change refcount (as this is 143 // ~invisible to client code, still logically const) 144 mpDevice = &rDevice; 145 mbHaveAlpha = bHaveAlpha; 146 147 setOutDev( rOutDev, bProtect ); 148 } 149 150 void CanvasHelper::setOutDev( const OutDevProviderSharedPtr& rOutDev, 151 bool bProtect ) 152 { 153 if( bProtect ) 154 mpProtectedOutDev = rOutDev; 155 else 156 mpProtectedOutDev.reset(); 157 158 mpOutDev = rOutDev; 159 } 160 161 void CanvasHelper::setBackgroundOutDev( const OutDevProviderSharedPtr& rOutDev ) 162 { 163 mp2ndOutDev = rOutDev; 164 mp2ndOutDev->getOutDev().EnableMapMode( sal_False ); 165 } 166 167 void CanvasHelper::clear() 168 { 169 // are we disposed? 170 if( mpOutDev ) 171 { 172 OutputDevice& rOutDev( mpOutDev->getOutDev() ); 173 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 174 175 rOutDev.EnableMapMode( sal_False ); 176 rOutDev.SetLineColor( COL_WHITE ); 177 rOutDev.SetFillColor( COL_WHITE ); 178 rOutDev.DrawRect( Rectangle( Point(), 179 rOutDev.GetOutputSizePixel()) ); 180 181 if( mp2ndOutDev ) 182 { 183 OutputDevice& rOutDev2( mp2ndOutDev->getOutDev() ); 184 185 rOutDev2.SetDrawMode( DRAWMODE_DEFAULT ); 186 rOutDev2.EnableMapMode( sal_False ); 187 rOutDev2.SetLineColor( COL_WHITE ); 188 rOutDev2.SetFillColor( COL_WHITE ); 189 rOutDev2.DrawRect( Rectangle( Point(), 190 rOutDev2.GetOutputSizePixel()) ); 191 rOutDev2.SetDrawMode( DRAWMODE_BLACKLINE | DRAWMODE_BLACKFILL | DRAWMODE_BLACKTEXT | 192 DRAWMODE_BLACKGRADIENT | DRAWMODE_BLACKBITMAP ); 193 } 194 } 195 } 196 197 void CanvasHelper::drawPoint( const rendering::XCanvas* , 198 const geometry::RealPoint2D& aPoint, 199 const rendering::ViewState& viewState, 200 const rendering::RenderState& renderState ) 201 { 202 // are we disposed? 203 if( mpOutDev ) 204 { 205 // nope, render 206 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 207 setupOutDevState( viewState, renderState, LINE_COLOR ); 208 209 const Point aOutPoint( tools::mapRealPoint2D( aPoint, 210 viewState, renderState ) ); 211 // TODO(F1): alpha 212 mpOutDev->getOutDev().DrawPixel( aOutPoint ); 213 214 if( mp2ndOutDev ) 215 mp2ndOutDev->getOutDev().DrawPixel( aOutPoint ); 216 } 217 } 218 219 void CanvasHelper::drawLine( const rendering::XCanvas* , 220 const geometry::RealPoint2D& aStartRealPoint2D, 221 const geometry::RealPoint2D& aEndRealPoint2D, 222 const rendering::ViewState& viewState, 223 const rendering::RenderState& renderState ) 224 { 225 // are we disposed? 226 if( mpOutDev ) 227 { 228 // nope, render 229 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 230 setupOutDevState( viewState, renderState, LINE_COLOR ); 231 232 const Point aStartPoint( tools::mapRealPoint2D( aStartRealPoint2D, 233 viewState, renderState ) ); 234 const Point aEndPoint( tools::mapRealPoint2D( aEndRealPoint2D, 235 viewState, renderState ) ); 236 // TODO(F2): alpha 237 mpOutDev->getOutDev().DrawLine( aStartPoint, aEndPoint ); 238 239 if( mp2ndOutDev ) 240 mp2ndOutDev->getOutDev().DrawLine( aStartPoint, aEndPoint ); 241 } 242 } 243 244 void CanvasHelper::drawBezier( const rendering::XCanvas* , 245 const geometry::RealBezierSegment2D& aBezierSegment, 246 const geometry::RealPoint2D& _aEndPoint, 247 const rendering::ViewState& viewState, 248 const rendering::RenderState& renderState ) 249 { 250 if( mpOutDev ) 251 { 252 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 253 setupOutDevState( viewState, renderState, LINE_COLOR ); 254 255 const Point& rStartPoint( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.Px, 256 aBezierSegment.Py), 257 viewState, renderState ) ); 258 const Point& rCtrlPoint1( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C1x, 259 aBezierSegment.C1y), 260 viewState, renderState ) ); 261 const Point& rCtrlPoint2( tools::mapRealPoint2D( geometry::RealPoint2D(aBezierSegment.C2x, 262 aBezierSegment.C2y), 263 viewState, renderState ) ); 264 const Point& rEndPoint( tools::mapRealPoint2D( _aEndPoint, 265 viewState, renderState ) ); 266 267 ::Polygon aPoly(4); 268 aPoly.SetPoint( rStartPoint, 0 ); 269 aPoly.SetFlags( 0, POLY_NORMAL ); 270 aPoly.SetPoint( rCtrlPoint1, 1 ); 271 aPoly.SetFlags( 1, POLY_CONTROL ); 272 aPoly.SetPoint( rCtrlPoint2, 2 ); 273 aPoly.SetFlags( 2, POLY_CONTROL ); 274 aPoly.SetPoint( rEndPoint, 3 ); 275 aPoly.SetFlags( 3, POLY_NORMAL ); 276 277 // TODO(F2): alpha 278 mpOutDev->getOutDev().DrawPolygon( aPoly ); 279 if( mp2ndOutDev ) 280 mp2ndOutDev->getOutDev().DrawPolygon( aPoly ); 281 } 282 } 283 284 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* , 285 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 286 const rendering::ViewState& viewState, 287 const rendering::RenderState& renderState ) 288 { 289 ENSURE_ARG_OR_THROW( xPolyPolygon.is(), 290 "polygon is NULL"); 291 292 if( mpOutDev ) 293 { 294 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 295 setupOutDevState( viewState, renderState, LINE_COLOR ); 296 297 const ::basegfx::B2DPolyPolygon& rPolyPoly( 298 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) ); 299 const PolyPolygon aPolyPoly( tools::mapPolyPolygon( rPolyPoly, viewState, renderState ) ); 300 301 if( rPolyPoly.isClosed() ) 302 { 303 mpOutDev->getOutDev().DrawPolyPolygon( aPolyPoly ); 304 305 if( mp2ndOutDev ) 306 mp2ndOutDev->getOutDev().DrawPolyPolygon( aPolyPoly ); 307 } 308 else 309 { 310 // mixed open/closed state. Cannot render open polygon 311 // via DrawPolyPolygon(), since that implicitley 312 // closed every polygon. OTOH, no need to distinguish 313 // further and render closed polygons via 314 // DrawPolygon(), and open ones via DrawPolyLine(): 315 // closed polygons will simply already contain the 316 // closing segment. 317 sal_uInt16 nSize( aPolyPoly.Count() ); 318 319 for( sal_uInt16 i=0; i<nSize; ++i ) 320 { 321 mpOutDev->getOutDev().DrawPolyLine( aPolyPoly[i] ); 322 323 if( mp2ndOutDev ) 324 mp2ndOutDev->getOutDev().DrawPolyLine( aPolyPoly[i] ); 325 } 326 } 327 } 328 329 // TODO(P1): Provide caching here. 330 return uno::Reference< rendering::XCachedPrimitive >(NULL); 331 } 332 333 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* , 334 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 335 const rendering::ViewState& viewState, 336 const rendering::RenderState& renderState, 337 const rendering::StrokeAttributes& strokeAttributes ) 338 { 339 ENSURE_ARG_OR_THROW( xPolyPolygon.is(), 340 "polygon is NULL"); 341 342 if( mpOutDev ) 343 { 344 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 345 346 ::basegfx::B2DHomMatrix aMatrix; 347 ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState); 348 349 ::basegfx::B2DSize aLinePixelSize(strokeAttributes.StrokeWidth, 350 strokeAttributes.StrokeWidth); 351 aLinePixelSize *= aMatrix; 352 353 ::basegfx::B2DPolyPolygon aPolyPoly( 354 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) ); 355 356 if( aPolyPoly.areControlPointsUsed() ) 357 { 358 // AW: Not needed for ApplyLineDashing anymore; should be removed 359 aPolyPoly = ::basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly); 360 } 361 362 // apply dashing, if any 363 if( strokeAttributes.DashArray.getLength() ) 364 { 365 const ::std::vector<double>& aDashArray( 366 ::comphelper::sequenceToContainer< ::std::vector<double> >(strokeAttributes.DashArray) ); 367 368 ::basegfx::B2DPolyPolygon aDashedPolyPoly; 369 370 for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i ) 371 { 372 // AW: new interface; You may also get gaps in the same run now 373 basegfx::tools::applyLineDashing(aPolyPoly.getB2DPolygon(i), aDashArray, &aDashedPolyPoly); 374 //aDashedPolyPoly.append( 375 // ::basegfx::tools::applyLineDashing( aPolyPoly.getB2DPolygon(i), 376 // aDashArray ) ); 377 } 378 379 aPolyPoly = aDashedPolyPoly; 380 } 381 382 ::basegfx::B2DPolyPolygon aStrokedPolyPoly; 383 if( aLinePixelSize.getLength() < 1.42 ) 384 { 385 // line width < 1.0 in device pixel, thus, output as a 386 // simple hairline poly-polygon 387 setupOutDevState( viewState, renderState, LINE_COLOR ); 388 389 aStrokedPolyPoly = aPolyPoly; 390 } 391 else 392 { 393 // render as a 'thick' line 394 setupOutDevState( viewState, renderState, FILL_COLOR ); 395 396 for( sal_uInt32 i=0; i<aPolyPoly.count(); ++i ) 397 { 398 // TODO(F2): Use MiterLimit from StrokeAttributes, 399 // need to convert it here to angle. 400 401 // TODO(F2): Also use Cap settings from 402 // StrokeAttributes, the 403 // createAreaGeometryForLineStartEnd() method does not 404 // seem to fit very well here 405 406 // AW: New interface, will create bezier polygons now 407 aStrokedPolyPoly.append(basegfx::tools::createAreaGeometry( 408 aPolyPoly.getB2DPolygon(i), 409 strokeAttributes.StrokeWidth*0.5, 410 b2DJoineFromJoin(strokeAttributes.JoinType), 411 unoCapeFromCap(strokeAttributes.StartCapType))); 412 //aStrokedPolyPoly.append( 413 // ::basegfx::tools::createAreaGeometryForPolygon( aPolyPoly.getB2DPolygon(i), 414 // strokeAttributes.StrokeWidth*0.5, 415 // b2DJoineFromJoin(strokeAttributes.JoinType) ) ); 416 } 417 } 418 419 // transform only _now_, all the StrokeAttributes are in 420 // user coordinates. 421 aStrokedPolyPoly.transform( aMatrix ); 422 423 const PolyPolygon aVCLPolyPoly( aStrokedPolyPoly ); 424 425 // TODO(F2): When using alpha here, must handle that via 426 // temporary surface or somesuch. 427 428 // Note: the generated stroke poly-polygon is NOT free of 429 // self-intersections. Therefore, if we would render it 430 // via OutDev::DrawPolyPolygon(), on/off fill would 431 // generate off areas on those self-intersections. 432 sal_uInt16 nSize( aVCLPolyPoly.Count() ); 433 434 for( sal_uInt16 i=0; i<nSize; ++i ) 435 { 436 if( aStrokedPolyPoly.getB2DPolygon( i ).isClosed() ) { 437 mpOutDev->getOutDev().DrawPolygon( aVCLPolyPoly[i] ); 438 if( mp2ndOutDev ) 439 mp2ndOutDev->getOutDev().DrawPolygon( aVCLPolyPoly[i] ); 440 } else { 441 const sal_uInt16 nPolySize = aVCLPolyPoly[i].GetSize(); 442 if( nPolySize ) { 443 Point rPrevPoint = aVCLPolyPoly[i].GetPoint( 0 ); 444 Point rPoint; 445 446 for( sal_uInt16 j=1; j<nPolySize; j++ ) { 447 rPoint = aVCLPolyPoly[i].GetPoint( j ); 448 mpOutDev->getOutDev().DrawLine( rPrevPoint, rPoint ); 449 if( mp2ndOutDev ) 450 mp2ndOutDev->getOutDev().DrawLine( rPrevPoint, rPoint ); 451 rPrevPoint = rPoint; 452 } 453 } 454 } 455 } 456 } 457 458 // TODO(P1): Provide caching here. 459 return uno::Reference< rendering::XCachedPrimitive >(NULL); 460 } 461 462 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* , 463 const uno::Reference< rendering::XPolyPolygon2D >& , 464 const rendering::ViewState& , 465 const rendering::RenderState& , 466 const uno::Sequence< rendering::Texture >& , 467 const rendering::StrokeAttributes& ) 468 { 469 return uno::Reference< rendering::XCachedPrimitive >(NULL); 470 } 471 472 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* , 473 const uno::Reference< rendering::XPolyPolygon2D >& , 474 const rendering::ViewState& , 475 const rendering::RenderState& , 476 const uno::Sequence< rendering::Texture >& , 477 const uno::Reference< geometry::XMapping2D >& , 478 const rendering::StrokeAttributes& ) 479 { 480 return uno::Reference< rendering::XCachedPrimitive >(NULL); 481 } 482 483 uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* , 484 const uno::Reference< rendering::XPolyPolygon2D >& , 485 const rendering::ViewState& , 486 const rendering::RenderState& , 487 const rendering::StrokeAttributes& ) 488 { 489 return uno::Reference< rendering::XPolyPolygon2D >(NULL); 490 } 491 492 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* , 493 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, 494 const rendering::ViewState& viewState, 495 const rendering::RenderState& renderState ) 496 { 497 ENSURE_ARG_OR_THROW( xPolyPolygon.is(), 498 "polygon is NULL"); 499 500 if( mpOutDev ) 501 { 502 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 503 504 const int nTransparency( setupOutDevState( viewState, renderState, FILL_COLOR ) ); 505 const int nTransPercent( (nTransparency * 100 + 128) / 255 ); // normal rounding, no truncation here 506 ::basegfx::B2DPolyPolygon aB2DPolyPoly( 507 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon)); 508 aB2DPolyPoly.setClosed(true); // ensure closed poly, otherwise VCL does not fill 509 const PolyPolygon aPolyPoly( tools::mapPolyPolygon( 510 aB2DPolyPoly, 511 viewState, renderState ) ); 512 const bool bSourceAlpha( renderState.CompositeOperation == rendering::CompositeOperation::SOURCE ); 513 if( !nTransparency || bSourceAlpha ) 514 { 515 mpOutDev->getOutDev().DrawPolyPolygon( aPolyPoly ); 516 } 517 else 518 { 519 mpOutDev->getOutDev().DrawTransparent( aPolyPoly, (sal_uInt16)nTransPercent ); 520 } 521 522 if( mp2ndOutDev ) 523 { 524 if( !nTransparency || bSourceAlpha ) 525 { 526 // HACK. Normally, CanvasHelper does not care 527 // about actually what mp2ndOutDev is... 528 if( bSourceAlpha && nTransparency == 255 ) 529 { 530 mp2ndOutDev->getOutDev().SetDrawMode( DRAWMODE_WHITELINE | DRAWMODE_WHITEFILL | DRAWMODE_WHITETEXT | 531 DRAWMODE_WHITEGRADIENT | DRAWMODE_WHITEBITMAP ); 532 mp2ndOutDev->getOutDev().SetFillColor( COL_WHITE ); 533 mp2ndOutDev->getOutDev().DrawPolyPolygon( aPolyPoly ); 534 mp2ndOutDev->getOutDev().SetDrawMode( DRAWMODE_BLACKLINE | DRAWMODE_BLACKFILL | DRAWMODE_BLACKTEXT | 535 DRAWMODE_BLACKGRADIENT | DRAWMODE_BLACKBITMAP ); 536 } 537 else 538 { 539 mp2ndOutDev->getOutDev().DrawPolyPolygon( aPolyPoly ); 540 } 541 } 542 else 543 { 544 mp2ndOutDev->getOutDev().DrawTransparent( aPolyPoly, (sal_uInt16)nTransPercent ); 545 } 546 } 547 } 548 549 // TODO(P1): Provide caching here. 550 return uno::Reference< rendering::XCachedPrimitive >(NULL); 551 } 552 553 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* , 554 const uno::Reference< rendering::XPolyPolygon2D >& , 555 const rendering::ViewState& , 556 const rendering::RenderState& , 557 const uno::Sequence< rendering::Texture >& , 558 const uno::Reference< geometry::XMapping2D >& ) 559 { 560 return uno::Reference< rendering::XCachedPrimitive >(NULL); 561 } 562 563 uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* , 564 const rendering::FontRequest& fontRequest, 565 const uno::Sequence< beans::PropertyValue >& extraFontProperties, 566 const geometry::Matrix2D& fontMatrix ) 567 { 568 if( mpOutDev && mpDevice ) 569 { 570 // TODO(F2): font properties and font matrix 571 return uno::Reference< rendering::XCanvasFont >( 572 new CanvasFont(fontRequest, extraFontProperties, fontMatrix, 573 *mpDevice, mpOutDev) ); 574 } 575 576 return uno::Reference< rendering::XCanvasFont >(); 577 } 578 579 uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* , 580 const rendering::FontInfo& , 581 const uno::Sequence< beans::PropertyValue >& ) 582 { 583 // TODO(F2) 584 return uno::Sequence< rendering::FontInfo >(); 585 } 586 587 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* , 588 const rendering::StringContext& text, 589 const uno::Reference< rendering::XCanvasFont >& xFont, 590 const rendering::ViewState& viewState, 591 const rendering::RenderState& renderState, 592 sal_Int8 textDirection ) 593 { 594 ENSURE_ARG_OR_THROW( xFont.is(), 595 "font is NULL"); 596 597 if( mpOutDev ) 598 { 599 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 600 601 ::Point aOutpos; 602 if( !setupTextOutput( aOutpos, viewState, renderState, xFont ) ) 603 return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary 604 605 // change text direction and layout mode 606 sal_uIntPtr nLayoutMode(0); 607 switch( textDirection ) 608 { 609 case rendering::TextDirection::WEAK_LEFT_TO_RIGHT: 610 nLayoutMode |= TEXT_LAYOUT_BIDI_LTR; 611 // FALLTHROUGH intended 612 case rendering::TextDirection::STRONG_LEFT_TO_RIGHT: 613 nLayoutMode |= TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG; 614 nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_LEFT; 615 break; 616 617 case rendering::TextDirection::WEAK_RIGHT_TO_LEFT: 618 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL; 619 // FALLTHROUGH intended 620 case rendering::TextDirection::STRONG_RIGHT_TO_LEFT: 621 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG; 622 nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_RIGHT; 623 break; 624 } 625 626 // TODO(F2): alpha 627 mpOutDev->getOutDev().SetLayoutMode( nLayoutMode ); 628 mpOutDev->getOutDev().DrawText( aOutpos, 629 text.Text, 630 ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition), 631 ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) ); 632 633 if( mp2ndOutDev ) 634 { 635 mp2ndOutDev->getOutDev().SetLayoutMode( nLayoutMode ); 636 mp2ndOutDev->getOutDev().DrawText( aOutpos, 637 text.Text, 638 ::canvas::tools::numeric_cast<sal_uInt16>(text.StartPosition), 639 ::canvas::tools::numeric_cast<sal_uInt16>(text.Length) ); 640 } 641 } 642 643 return uno::Reference< rendering::XCachedPrimitive >(NULL); 644 } 645 646 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* , 647 const uno::Reference< rendering::XTextLayout >& xLayoutedText, 648 const rendering::ViewState& viewState, 649 const rendering::RenderState& renderState ) 650 { 651 ENSURE_ARG_OR_THROW( xLayoutedText.is(), 652 "layout is NULL"); 653 654 TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() ); 655 656 if( pTextLayout ) 657 { 658 if( mpOutDev ) 659 { 660 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 661 662 // TODO(T3): Race condition. We're taking the font 663 // from xLayoutedText, and then calling draw() at it, 664 // without exclusive access. Move setupTextOutput(), 665 // e.g. to impltools? 666 667 ::Point aOutpos; 668 if( !setupTextOutput( aOutpos, viewState, renderState, xLayoutedText->getFont() ) ) 669 return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary 670 671 // TODO(F2): What about the offset scalings? 672 // TODO(F2): alpha 673 pTextLayout->draw( mpOutDev->getOutDev(), aOutpos, viewState, renderState ); 674 675 if( mp2ndOutDev ) 676 pTextLayout->draw( mp2ndOutDev->getOutDev(), aOutpos, viewState, renderState ); 677 } 678 } 679 else 680 { 681 ENSURE_ARG_OR_THROW( false, 682 "TextLayout not compatible with this canvas" ); 683 } 684 685 return uno::Reference< rendering::XCachedPrimitive >(NULL); 686 } 687 688 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmap( const rendering::XCanvas* pCanvas, 689 const uno::Reference< rendering::XBitmap >& xBitmap, 690 const rendering::ViewState& viewState, 691 const rendering::RenderState& renderState, 692 bool bModulateColors ) 693 { 694 ENSURE_ARG_OR_THROW( xBitmap.is(), 695 "bitmap is NULL"); 696 697 ::canvas::tools::verifyInput( renderState, 698 BOOST_CURRENT_FUNCTION, 699 mpDevice, 700 4, 701 bModulateColors ? 3 : 0 ); 702 703 if( mpOutDev ) 704 { 705 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 706 setupOutDevState( viewState, renderState, IGNORE_COLOR ); 707 708 ::basegfx::B2DHomMatrix aMatrix; 709 ::canvas::tools::mergeViewAndRenderTransform(aMatrix, viewState, renderState); 710 711 ::basegfx::B2DPoint aOutputPos( 0.0, 0.0 ); 712 aOutputPos *= aMatrix; 713 714 BitmapEx aBmpEx( tools::bitmapExFromXBitmap(xBitmap) ); 715 716 // TODO(F2): Implement modulation again for other color 717 // channels (currently, works only for alpha). Note: this 718 // is already implemented in transformBitmap() 719 if( bModulateColors && 720 renderState.DeviceColor.getLength() > 3 ) 721 { 722 // optimize away the case where alpha modulation value 723 // is 1.0 - we then simply switch off modulation at all 724 bModulateColors = !::rtl::math::approxEqual( 725 renderState.DeviceColor[3], 1.0); 726 } 727 728 // check whether we can render bitmap as-is: must not 729 // modulate colors, matrix must either be the identity 730 // transform (that's clear), _or_ contain only 731 // translational components. 732 if( !bModulateColors && 733 (aMatrix.isIdentity() || 734 (::basegfx::fTools::equalZero( aMatrix.get(0,1) ) && 735 ::basegfx::fTools::equalZero( aMatrix.get(1,0) ) && 736 ::rtl::math::approxEqual(aMatrix.get(0,0), 1.0) && 737 ::rtl::math::approxEqual(aMatrix.get(1,1), 1.0)) ) ) 738 { 739 // optimized case: identity matrix, or only 740 // translational components. 741 mpOutDev->getOutDev().DrawBitmapEx( ::vcl::unotools::pointFromB2DPoint( aOutputPos ), 742 aBmpEx ); 743 744 if( mp2ndOutDev ) 745 mp2ndOutDev->getOutDev().DrawBitmapEx( ::vcl::unotools::pointFromB2DPoint( aOutputPos ), 746 aBmpEx ); 747 748 // Returning a cache object is not useful, the XBitmap 749 // itself serves this purpose 750 return uno::Reference< rendering::XCachedPrimitive >(NULL); 751 } 752 else 753 { 754 // Matrix contains non-trivial transformation (or 755 // color modulation is requested), decompose to check 756 // whether GraphicObject suffices 757 ::basegfx::B2DVector aScale; 758 double nRotate; 759 double nShearX; 760 aMatrix.decompose( aScale, aOutputPos, nRotate, nShearX ); 761 762 GraphicAttr aGrfAttr; 763 GraphicObjectSharedPtr pGrfObj; 764 765 ::Size aBmpSize( aBmpEx.GetSizePixel() ); 766 767 // setup alpha modulation 768 if( bModulateColors ) 769 { 770 const double nAlphaModulation( renderState.DeviceColor[3] ); 771 772 // TODO(F1): Note that the GraphicManager has a 773 // subtle difference in how it calculates the 774 // resulting alpha value: it's using the inverse 775 // alpha values (i.e. 'transparency'), and 776 // calculates transOrig + transModulate, instead 777 // of transOrig + transModulate - 778 // transOrig*transModulate (which would be 779 // equivalent to the origAlpha*modulateAlpha the 780 // DX canvas performs) 781 aGrfAttr.SetTransparency( 782 static_cast< sal_uInt8 >( 783 ::basegfx::fround( 255.0*( 1.0 - nAlphaModulation ) ) ) ); 784 } 785 786 if( ::basegfx::fTools::equalZero( nShearX ) ) 787 { 788 // no shear, GraphicObject is enough (the 789 // GraphicObject only supports scaling, rotation 790 // and translation) 791 792 // #i75339# don't apply mirror flags, having 793 // negative size values is enough to make 794 // GraphicObject flip the bitmap 795 796 // The angle has to be mapped from radian to tenths of 797 // degress with the orientation reversed: [0,2Pi) -> 798 // (3600,0]. Note that the original angle may have 799 // values outside the [0,2Pi) interval. 800 const double nAngleInTenthOfDegrees (3600.0 - nRotate * 3600.0 / (2*M_PI)); 801 aGrfAttr.SetRotation( static_cast< sal_uInt16 >(::basegfx::fround(nAngleInTenthOfDegrees)) ); 802 803 pGrfObj.reset( new GraphicObject( aBmpEx ) ); 804 } 805 else 806 { 807 // modify output position, to account for the fact 808 // that transformBitmap() always normalizes its output 809 // bitmap into the smallest enclosing box. 810 ::basegfx::B2DRectangle aDestRect; 811 ::canvas::tools::calcTransformedRectBounds( aDestRect, 812 ::basegfx::B2DRectangle(0, 813 0, 814 aBmpSize.Width(), 815 aBmpSize.Height()), 816 aMatrix ); 817 818 aOutputPos.setX( aDestRect.getMinX() ); 819 aOutputPos.setY( aDestRect.getMinY() ); 820 821 // complex transformation, use generic affine bitmap 822 // transformation 823 aBmpEx = tools::transformBitmap( aBmpEx, 824 aMatrix, 825 renderState.DeviceColor, 826 tools::MODULATE_NONE ); 827 828 pGrfObj.reset( new GraphicObject( aBmpEx ) ); 829 830 // clear scale values, generated bitmap already 831 // contains scaling 832 aScale.setX( 1.0 ); aScale.setY( 1.0 ); 833 834 // update bitmap size, bitmap has changed above. 835 aBmpSize = aBmpEx.GetSizePixel(); 836 } 837 838 // output GraphicObject 839 const ::Point aPt( ::vcl::unotools::pointFromB2DPoint( aOutputPos ) ); 840 const ::Size aSz( ::basegfx::fround( aScale.getX() * aBmpSize.Width() ), 841 ::basegfx::fround( aScale.getY() * aBmpSize.Height() ) ); 842 843 pGrfObj->Draw( &mpOutDev->getOutDev(), 844 aPt, 845 aSz, 846 &aGrfAttr ); 847 848 if( mp2ndOutDev ) 849 pGrfObj->Draw( &mp2ndOutDev->getOutDev(), 850 aPt, 851 aSz, 852 &aGrfAttr ); 853 854 // created GraphicObject, which possibly cached 855 // display bitmap - return cache object, to retain 856 // that information. 857 return uno::Reference< rendering::XCachedPrimitive >( 858 new CachedBitmap( pGrfObj, 859 aPt, 860 aSz, 861 aGrfAttr, 862 viewState, 863 renderState, 864 // cast away const, need to 865 // change refcount (as this is 866 // ~invisible to client code, 867 // still logically const) 868 const_cast< rendering::XCanvas* >(pCanvas)) ); 869 } 870 } 871 872 // Nothing rendered 873 return uno::Reference< rendering::XCachedPrimitive >(NULL); 874 } 875 876 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* pCanvas, 877 const uno::Reference< rendering::XBitmap >& xBitmap, 878 const rendering::ViewState& viewState, 879 const rendering::RenderState& renderState ) 880 { 881 return implDrawBitmap( pCanvas, 882 xBitmap, 883 viewState, 884 renderState, 885 false ); 886 } 887 888 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas, 889 const uno::Reference< rendering::XBitmap >& xBitmap, 890 const rendering::ViewState& viewState, 891 const rendering::RenderState& renderState ) 892 { 893 return implDrawBitmap( pCanvas, 894 xBitmap, 895 viewState, 896 renderState, 897 true ); 898 } 899 900 uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice() 901 { 902 // cast away const, need to change refcount (as this is 903 // ~invisible to client code, still logically const) 904 return uno::Reference< rendering::XGraphicDevice >(mpDevice); 905 } 906 907 void CanvasHelper::copyRect( const rendering::XCanvas* , 908 const uno::Reference< rendering::XBitmapCanvas >& , 909 const geometry::RealRectangle2D& , 910 const rendering::ViewState& , 911 const rendering::RenderState& , 912 const geometry::RealRectangle2D& , 913 const rendering::ViewState& , 914 const rendering::RenderState& ) 915 { 916 // TODO(F1) 917 } 918 919 geometry::IntegerSize2D CanvasHelper::getSize() 920 { 921 if( !mpOutDev.get() ) 922 return geometry::IntegerSize2D(); // we're disposed 923 924 return ::vcl::unotools::integerSize2DFromSize( mpOutDev->getOutDev().GetOutputSizePixel() ); 925 } 926 927 uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize, 928 sal_Bool beFast ) 929 { 930 if( !mpOutDev.get() || !mpDevice ) 931 return uno::Reference< rendering::XBitmap >(); // we're disposed 932 933 OutputDevice& rOutDev( mpOutDev->getOutDev() ); 934 935 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 936 rOutDev.EnableMapMode( sal_False ); 937 938 // TODO(F2): Support alpha vdev canvas here 939 const Point aEmptyPoint(0,0); 940 const Size aBmpSize( rOutDev.GetOutputSizePixel() ); 941 942 Bitmap aBitmap( rOutDev.GetBitmap(aEmptyPoint, aBmpSize) ); 943 944 aBitmap.Scale( ::vcl::unotools::sizeFromRealSize2D(newSize), 945 beFast ? BMP_SCALE_FAST : BMP_SCALE_INTERPOLATE ); 946 947 return uno::Reference< rendering::XBitmap >( 948 new CanvasBitmap( aBitmap, *mpDevice, mpOutDev ) ); 949 } 950 951 uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& rLayout, 952 const geometry::IntegerRectangle2D& rect ) 953 { 954 if( !mpOutDev.get() ) 955 return uno::Sequence< sal_Int8 >(); // we're disposed 956 957 rLayout = getMemoryLayout(); 958 959 // TODO(F2): Support alpha canvas here 960 const Rectangle aRect( ::vcl::unotools::rectangleFromIntegerRectangle2D(rect) ); 961 962 OutputDevice& rOutDev( mpOutDev->getOutDev() ); 963 964 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 965 rOutDev.EnableMapMode( sal_False ); 966 967 Bitmap aBitmap( rOutDev.GetBitmap(aRect.TopLeft(), 968 aRect.GetSize()) ); 969 970 ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(), 971 aBitmap ); 972 973 ENSURE_OR_THROW( pReadAccess.get() != NULL, 974 "Could not acquire read access to OutDev bitmap" ); 975 976 const sal_Int32 nWidth( rect.X2 - rect.X1 ); 977 const sal_Int32 nHeight( rect.Y2 - rect.Y1 ); 978 979 rLayout.ScanLines = nHeight; 980 rLayout.ScanLineBytes = nWidth*4; 981 rLayout.ScanLineStride = rLayout.ScanLineBytes; 982 983 uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight ); 984 sal_Int8* pRes = aRes.getArray(); 985 986 int nCurrPos(0); 987 for( int y=0; y<nHeight; ++y ) 988 { 989 for( int x=0; x<nWidth; ++x ) 990 { 991 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed(); 992 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen(); 993 pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue(); 994 pRes[ nCurrPos++ ] = -1; 995 } 996 } 997 998 return aRes; 999 } 1000 1001 void CanvasHelper::setData( const uno::Sequence< sal_Int8 >& data, 1002 const rendering::IntegerBitmapLayout& aLayout, 1003 const geometry::IntegerRectangle2D& rect ) 1004 { 1005 if( !mpOutDev.get() ) 1006 return; // we're disposed 1007 1008 const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() ); 1009 ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != aLayout.PlaneStride || 1010 aRefLayout.ColorSpace != aLayout.ColorSpace || 1011 aRefLayout.Palette != aLayout.Palette || 1012 aRefLayout.IsMsbFirst != aLayout.IsMsbFirst, 1013 "Mismatching memory layout" ); 1014 1015 OutputDevice& rOutDev( mpOutDev->getOutDev() ); 1016 1017 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 1018 rOutDev.EnableMapMode( sal_False ); 1019 1020 const Rectangle aRect( ::vcl::unotools::rectangleFromIntegerRectangle2D(rect) ); 1021 const sal_uInt16 nBitCount( ::std::min( (sal_uInt16)24U, 1022 (sal_uInt16)rOutDev.GetBitCount() ) ); 1023 const BitmapPalette* pPalette = NULL; 1024 1025 if( nBitCount <= 8 ) 1026 { 1027 // TODO(Q1): Extract this to a common place, e.g. GraphicDevice 1028 1029 // try to determine palette from output device (by 1030 // extracting a 1,1 bitmap, and querying it) 1031 const Point aEmptyPoint; 1032 const Size aSize(1,1); 1033 Bitmap aTmpBitmap( rOutDev.GetBitmap( aEmptyPoint, 1034 aSize ) ); 1035 1036 ScopedBitmapReadAccess pReadAccess( aTmpBitmap.AcquireReadAccess(), 1037 aTmpBitmap ); 1038 1039 pPalette = &pReadAccess->GetPalette(); 1040 } 1041 1042 // TODO(F2): Support alpha canvas here 1043 Bitmap aBitmap( aRect.GetSize(), nBitCount, pPalette ); 1044 1045 bool bCopyBack( false ); // only copy something back, if we 1046 // actually changed some pixel 1047 { 1048 ScopedBitmapWriteAccess pWriteAccess( aBitmap.AcquireWriteAccess(), 1049 aBitmap ); 1050 1051 ENSURE_OR_THROW( pWriteAccess.get() != NULL, 1052 "Could not acquire write access to OutDev bitmap" ); 1053 1054 // for the time being, always read as RGB 1055 const sal_Int32 nWidth( rect.X2 - rect.X1 ); 1056 const sal_Int32 nHeight( rect.Y2 - rect.Y1 ); 1057 int x, y, nCurrPos(0); 1058 for( y=0; y<nHeight; ++y ) 1059 { 1060 switch( pWriteAccess->GetScanlineFormat() ) 1061 { 1062 case BMP_FORMAT_8BIT_PAL: 1063 { 1064 Scanline pScan = pWriteAccess->GetScanline( y ); 1065 1066 for( x=0; x<nWidth; ++x ) 1067 { 1068 *pScan++ = (sal_uInt8)pWriteAccess->GetBestPaletteIndex( 1069 BitmapColor( data[ nCurrPos ], 1070 data[ nCurrPos+1 ], 1071 data[ nCurrPos+2 ] ) ); 1072 1073 nCurrPos += 4; 1074 } 1075 } 1076 break; 1077 1078 case BMP_FORMAT_24BIT_TC_BGR: 1079 { 1080 Scanline pScan = pWriteAccess->GetScanline( y ); 1081 1082 for( x=0; x<nWidth; ++x ) 1083 { 1084 *pScan++ = data[ nCurrPos+2 ]; 1085 *pScan++ = data[ nCurrPos+1 ]; 1086 *pScan++ = data[ nCurrPos ]; 1087 1088 nCurrPos += 4; 1089 } 1090 } 1091 break; 1092 1093 case BMP_FORMAT_24BIT_TC_RGB: 1094 { 1095 Scanline pScan = pWriteAccess->GetScanline( y ); 1096 1097 for( x=0; x<nWidth; ++x ) 1098 { 1099 *pScan++ = data[ nCurrPos ]; 1100 *pScan++ = data[ nCurrPos+1 ]; 1101 *pScan++ = data[ nCurrPos+2 ]; 1102 1103 nCurrPos += 4; 1104 } 1105 } 1106 break; 1107 1108 default: 1109 { 1110 for( x=0; x<nWidth; ++x ) 1111 { 1112 pWriteAccess->SetPixel( y, x, BitmapColor( data[ nCurrPos ], 1113 data[ nCurrPos+1 ], 1114 data[ nCurrPos+2 ] ) ); 1115 nCurrPos += 4; 1116 } 1117 } 1118 break; 1119 } 1120 } 1121 1122 bCopyBack = true; 1123 } 1124 1125 // copy back only here, since the BitmapAccessors must be 1126 // destroyed beforehand 1127 if( bCopyBack ) 1128 { 1129 // TODO(F2): Support alpha canvas here 1130 rOutDev.DrawBitmap(aRect.TopLeft(), aBitmap); 1131 } 1132 } 1133 1134 void CanvasHelper::setPixel( const uno::Sequence< sal_Int8 >& color, 1135 const rendering::IntegerBitmapLayout& rLayout, 1136 const geometry::IntegerPoint2D& pos ) 1137 { 1138 if( !mpOutDev.get() ) 1139 return; // we're disposed 1140 1141 OutputDevice& rOutDev( mpOutDev->getOutDev() ); 1142 1143 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 1144 rOutDev.EnableMapMode( sal_False ); 1145 1146 const Size aBmpSize( rOutDev.GetOutputSizePixel() ); 1147 1148 ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(), 1149 "X coordinate out of bounds" ); 1150 ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(), 1151 "Y coordinate out of bounds" ); 1152 ENSURE_ARG_OR_THROW( color.getLength() > 3, 1153 "not enough color components" ); 1154 1155 const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() ); 1156 ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != rLayout.PlaneStride || 1157 aRefLayout.ColorSpace != rLayout.ColorSpace || 1158 aRefLayout.Palette != rLayout.Palette || 1159 aRefLayout.IsMsbFirst != rLayout.IsMsbFirst, 1160 "Mismatching memory layout" ); 1161 1162 // TODO(F2): Support alpha canvas here 1163 rOutDev.DrawPixel( ::vcl::unotools::pointFromIntegerPoint2D( pos ), 1164 ::canvas::tools::stdIntSequenceToColor( color )); 1165 } 1166 1167 uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& rLayout, 1168 const geometry::IntegerPoint2D& pos ) 1169 { 1170 if( !mpOutDev.get() ) 1171 return uno::Sequence< sal_Int8 >(); // we're disposed 1172 1173 rLayout = getMemoryLayout(); 1174 rLayout.ScanLines = 1; 1175 rLayout.ScanLineBytes = 4; 1176 rLayout.ScanLineStride = rLayout.ScanLineBytes; 1177 1178 OutputDevice& rOutDev( mpOutDev->getOutDev() ); 1179 1180 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 1181 rOutDev.EnableMapMode( sal_False ); 1182 1183 const Size aBmpSize( rOutDev.GetOutputSizePixel() ); 1184 1185 ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(), 1186 "X coordinate out of bounds" ); 1187 ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(), 1188 "Y coordinate out of bounds" ); 1189 1190 // TODO(F2): Support alpha canvas here 1191 return ::canvas::tools::colorToStdIntSequence( 1192 rOutDev.GetPixel( 1193 ::vcl::unotools::pointFromIntegerPoint2D( pos ))); 1194 } 1195 1196 rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout() 1197 { 1198 if( !mpOutDev.get() ) 1199 return rendering::IntegerBitmapLayout(); // we're disposed 1200 1201 return ::canvas::tools::getStdMemoryLayout(getSize()); 1202 } 1203 1204 int CanvasHelper::setupOutDevState( const rendering::ViewState& viewState, 1205 const rendering::RenderState& renderState, 1206 ColorType eColorType ) const 1207 { 1208 ENSURE_OR_THROW( mpOutDev.get(), 1209 "outdev null. Are we disposed?" ); 1210 1211 ::canvas::tools::verifyInput( renderState, 1212 BOOST_CURRENT_FUNCTION, 1213 mpDevice, 1214 2, 1215 eColorType == IGNORE_COLOR ? 0 : 3 ); 1216 1217 OutputDevice& rOutDev( mpOutDev->getOutDev() ); 1218 OutputDevice* p2ndOutDev = NULL; 1219 1220 rOutDev.EnableMapMode( sal_False ); 1221 1222 if( mp2ndOutDev ) 1223 p2ndOutDev = &mp2ndOutDev->getOutDev(); 1224 1225 int nTransparency(0); 1226 1227 // TODO(P2): Don't change clipping all the time, maintain current clip 1228 // state and change only when update is necessary 1229 1230 // accumulate non-empty clips into one region 1231 // ========================================== 1232 1233 Region aClipRegion(true); 1234 1235 if( viewState.Clip.is() ) 1236 { 1237 ::basegfx::B2DPolyPolygon aClipPoly( 1238 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(viewState.Clip) ); 1239 1240 if( aClipPoly.count() ) 1241 { 1242 // setup non-empty clipping 1243 ::basegfx::B2DHomMatrix aMatrix; 1244 aClipPoly.transform( 1245 ::basegfx::unotools::homMatrixFromAffineMatrix( aMatrix, 1246 viewState.AffineTransform ) ); 1247 1248 aClipRegion = Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly ) ); 1249 } 1250 else 1251 { 1252 // clip polygon is empty 1253 aClipRegion.SetEmpty(); 1254 } 1255 } 1256 1257 if( renderState.Clip.is() ) 1258 { 1259 ::basegfx::B2DPolyPolygon aClipPoly( 1260 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(renderState.Clip) ); 1261 1262 ::basegfx::B2DHomMatrix aMatrix; 1263 aClipPoly.transform( 1264 ::canvas::tools::mergeViewAndRenderTransform( aMatrix, 1265 viewState, 1266 renderState ) ); 1267 1268 if( aClipPoly.count() ) 1269 { 1270 // setup non-empty clipping 1271 Region aRegion = Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly ) ); 1272 aClipRegion.Intersect( aRegion ); 1273 } 1274 else 1275 { 1276 // clip polygon is empty 1277 aClipRegion.SetEmpty(); 1278 } 1279 } 1280 1281 // setup accumulated clip region. Note that setting an 1282 // empty clip region denotes "clip everything" on the 1283 // OutputDevice (which is why we translate that into 1284 // SetClipRegion() here). When both view and render clip 1285 // are empty, aClipRegion remains default-constructed, 1286 // i.e. empty, too. 1287 if( aClipRegion.IsNull() ) 1288 { 1289 rOutDev.SetClipRegion(); 1290 1291 if( p2ndOutDev ) 1292 p2ndOutDev->SetClipRegion(); 1293 } 1294 else 1295 { 1296 rOutDev.SetClipRegion( aClipRegion ); 1297 1298 if( p2ndOutDev ) 1299 p2ndOutDev->SetClipRegion( aClipRegion ); 1300 } 1301 1302 if( eColorType != IGNORE_COLOR ) 1303 { 1304 Color aColor( COL_WHITE ); 1305 1306 if( renderState.DeviceColor.getLength() > 2 ) 1307 { 1308 aColor = ::vcl::unotools::stdColorSpaceSequenceToColor( 1309 renderState.DeviceColor ); 1310 } 1311 1312 // extract alpha, and make color opaque 1313 // afterwards. Otherwise, OutputDevice won't draw anything 1314 nTransparency = aColor.GetTransparency(); 1315 aColor.SetTransparency(0); 1316 1317 switch( eColorType ) 1318 { 1319 case LINE_COLOR: 1320 rOutDev.SetLineColor( aColor ); 1321 rOutDev.SetFillColor(); 1322 1323 if( p2ndOutDev ) 1324 { 1325 p2ndOutDev->SetLineColor( aColor ); 1326 p2ndOutDev->SetFillColor(); 1327 } 1328 break; 1329 1330 case FILL_COLOR: 1331 rOutDev.SetFillColor( aColor ); 1332 rOutDev.SetLineColor(); 1333 1334 if( p2ndOutDev ) 1335 { 1336 p2ndOutDev->SetFillColor( aColor ); 1337 p2ndOutDev->SetLineColor(); 1338 } 1339 break; 1340 1341 case TEXT_COLOR: 1342 rOutDev.SetTextColor( aColor ); 1343 1344 if( p2ndOutDev ) 1345 p2ndOutDev->SetTextColor( aColor ); 1346 break; 1347 1348 default: 1349 ENSURE_OR_THROW( false, 1350 "Unexpected color type"); 1351 break; 1352 } 1353 } 1354 1355 return nTransparency; 1356 } 1357 1358 bool CanvasHelper::setupTextOutput( ::Point& o_rOutPos, 1359 const rendering::ViewState& viewState, 1360 const rendering::RenderState& renderState, 1361 const uno::Reference< rendering::XCanvasFont >& xFont ) const 1362 { 1363 ENSURE_OR_THROW( mpOutDev.get(), 1364 "outdev null. Are we disposed?" ); 1365 1366 setupOutDevState( viewState, renderState, TEXT_COLOR ); 1367 1368 OutputDevice& rOutDev( mpOutDev->getOutDev() ); 1369 1370 ::Font aVCLFont; 1371 1372 CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() ); 1373 1374 ENSURE_ARG_OR_THROW( pFont, 1375 "Font not compatible with this canvas" ); 1376 1377 aVCLFont = pFont->getVCLFont(); 1378 1379 Color aColor( COL_BLACK ); 1380 1381 if( renderState.DeviceColor.getLength() > 2 ) 1382 { 1383 aColor = ::vcl::unotools::stdColorSpaceSequenceToColor( 1384 renderState.DeviceColor ); 1385 } 1386 1387 // setup font color 1388 aVCLFont.SetColor( aColor ); 1389 aVCLFont.SetFillColor( aColor ); 1390 1391 // no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here. 1392 if( !tools::setupFontTransform( o_rOutPos, aVCLFont, viewState, renderState, rOutDev ) ) 1393 return false; 1394 1395 rOutDev.SetFont( aVCLFont ); 1396 1397 if( mp2ndOutDev ) 1398 mp2ndOutDev->getOutDev().SetFont( aVCLFont ); 1399 1400 return true; 1401 } 1402 1403 bool CanvasHelper::repaint( const GraphicObjectSharedPtr& rGrf, 1404 const rendering::ViewState& viewState, 1405 const rendering::RenderState& renderState, 1406 const ::Point& rPt, 1407 const ::Size& rSz, 1408 const GraphicAttr& rAttr ) const 1409 { 1410 ENSURE_OR_RETURN_FALSE( rGrf, 1411 "Invalid Graphic" ); 1412 1413 if( !mpOutDev ) 1414 return false; // disposed 1415 else 1416 { 1417 tools::OutDevStateKeeper aStateKeeper( mpProtectedOutDev ); 1418 setupOutDevState( viewState, renderState, IGNORE_COLOR ); 1419 1420 if( !rGrf->Draw( &mpOutDev->getOutDev(), rPt, rSz, &rAttr ) ) 1421 return false; 1422 1423 // #i80779# Redraw also into mask outdev 1424 if( mp2ndOutDev ) 1425 return rGrf->Draw( &mp2ndOutDev->getOutDev(), rPt, rSz, &rAttr ); 1426 1427 return true; 1428 } 1429 } 1430 1431 void CanvasHelper::flush() const 1432 { 1433 if( mpOutDev && mpOutDev->getOutDev().GetOutDevType() == OUTDEV_WINDOW ) 1434 { 1435 // TODO(Q3): Evil downcast. And what's more, Window::Flush is 1436 // not even const. Wah. 1437 static_cast<Window&>(mpOutDev->getOutDev()).Flush(); 1438 } 1439 1440 if( mp2ndOutDev && mp2ndOutDev->getOutDev().GetOutDevType() == OUTDEV_WINDOW ) 1441 { 1442 // TODO(Q3): Evil downcast. And what's more, Window::Flush is 1443 // not even const. Wah. 1444 static_cast<Window&>(mp2ndOutDev->getOutDev()).Flush(); 1445 } 1446 } 1447 1448 } 1449