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