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