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