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