summaryrefslogtreecommitdiff
path: root/canvas/source/directx/dx_canvashelper.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'canvas/source/directx/dx_canvashelper.cxx')
-rwxr-xr-xcanvas/source/directx/dx_canvashelper.cxx815
1 files changed, 815 insertions, 0 deletions
diff --git a/canvas/source/directx/dx_canvashelper.cxx b/canvas/source/directx/dx_canvashelper.cxx
new file mode 100755
index 000000000000..0642b6c50efb
--- /dev/null
+++ b/canvas/source/directx/dx_canvashelper.cxx
@@ -0,0 +1,815 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: dx_canvashelper.cxx,v $
+ * $Revision: 1.5 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_canvas.hxx"
+
+#include <canvas/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <rtl/logfile.hxx>
+#include <rtl/math.hxx>
+
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/RepaintResult.hpp>
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/tools/canvastools.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <canvas/canvastools.hxx>
+
+#include "dx_spritecanvas.hxx"
+#include "dx_impltools.hxx"
+#include "dx_vcltools.hxx"
+#include "dx_canvasfont.hxx"
+#include "dx_textlayout.hxx"
+#include "dx_canvashelper.hxx"
+
+#include <algorithm>
+
+
+using namespace ::com::sun::star;
+
+namespace dxcanvas
+{
+ namespace
+ {
+ Gdiplus::LineCap gdiCapFromCap( sal_Int8 nCapType )
+ {
+ switch( nCapType )
+ {
+ case rendering::PathCapType::BUTT:
+ return Gdiplus::LineCapFlat;
+
+ case rendering::PathCapType::ROUND:
+ return Gdiplus::LineCapRound;
+
+ case rendering::PathCapType::SQUARE:
+ return Gdiplus::LineCapSquare;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "gdiCapFromCap(): Unexpected cap type" );
+ }
+
+ return Gdiplus::LineCapFlat;
+ }
+
+ Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType )
+ {
+ switch( nJoinType )
+ {
+ case rendering::PathJoinType::NONE:
+ OSL_ENSURE( false,
+ "gdiJoinFromJoin(): Join NONE not possible, mapping to MITER" );
+ // FALLTHROUGH intended
+ case rendering::PathJoinType::MITER:
+ return Gdiplus::LineJoinMiter;
+
+ case rendering::PathJoinType::ROUND:
+ return Gdiplus::LineJoinRound;
+
+ case rendering::PathJoinType::BEVEL:
+ return Gdiplus::LineJoinBevel;
+
+ default:
+ ENSURE_OR_THROW( false,
+ "gdiJoinFromJoin(): Unexpected join type" );
+ }
+
+ return Gdiplus::LineJoinMiter;
+ }
+ }
+
+ CanvasHelper::CanvasHelper() :
+ mpGdiPlusUser( GDIPlusUser::createInstance() ),
+ mpDevice( NULL ),
+ mpGraphicsProvider(),
+ maOutputOffset()
+ {
+ }
+
+ void CanvasHelper::disposing()
+ {
+ mpGraphicsProvider.reset();
+ mpDevice = NULL;
+ mpGdiPlusUser.reset();
+ }
+
+ void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice )
+ {
+ mpDevice = &rDevice;
+ }
+
+ void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget )
+ {
+ ENSURE_OR_THROW( rTarget,
+ "CanvasHelper::setTarget(): Invalid target" );
+ ENSURE_OR_THROW( !mpGraphicsProvider.get(),
+ "CanvasHelper::setTarget(): target set, old target would be overwritten" );
+
+ mpGraphicsProvider = rTarget;
+ }
+
+ void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget,
+ const ::basegfx::B2ISize& rOutputOffset )
+ {
+ ENSURE_OR_THROW( rTarget,
+ "CanvasHelper::setTarget(): invalid target" );
+ ENSURE_OR_THROW( !mpGraphicsProvider.get(),
+ "CanvasHelper::setTarget(): target set, old target would be overwritten" );
+
+ mpGraphicsProvider = rTarget;
+ maOutputOffset = rOutputOffset;
+ }
+
+ void CanvasHelper::clear()
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+ Gdiplus::Color aClearColor = Gdiplus::Color((Gdiplus::ARGB)Gdiplus::Color::White);
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->SetCompositingMode(
+ Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
+ "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->Clear( aClearColor ),
+ "CanvasHelper::clear(): GDI+ Clear call failed" );
+ }
+ }
+
+ void CanvasHelper::drawPoint( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealPoint2D& aPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::SolidBrush aBrush(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)) );
+
+ // determine size of one-by-one device pixel ellipse
+ Gdiplus::Matrix aMatrix;
+ pGraphics->GetTransform(&aMatrix);
+ aMatrix.Invert();
+ Gdiplus::PointF vector(1, 1);
+ aMatrix.TransformVectors(&vector);
+
+ // paint a one-by-one circle, with the given point
+ // in the middle (rounded to float)
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->FillEllipse( &aBrush,
+ // disambiguate call
+ Gdiplus::REAL(aPoint.X),
+ Gdiplus::REAL(aPoint.Y),
+ Gdiplus::REAL(vector.X),
+ Gdiplus::REAL(vector.Y) ),
+ "CanvasHelper::drawPoint(): GDI+ call failed" );
+ }
+ }
+
+ void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealPoint2D& aStartPoint,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::Pen aPen(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)),
+ Gdiplus::REAL(0.0) );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ const Gdiplus::PixelOffsetMode aOldMode(
+ pGraphics->GetPixelOffsetMode() );
+ pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ Gdiplus::Status hr = pGraphics->DrawLine( &aPen,
+ Gdiplus::REAL(aStartPoint.X), // disambiguate call
+ Gdiplus::REAL(aStartPoint.Y),
+ Gdiplus::REAL(aEndPoint.X),
+ Gdiplus::REAL(aEndPoint.Y) );
+ pGraphics->SetPixelOffsetMode( aOldMode );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == hr,
+ "CanvasHelper::drawLine(): GDI+ call failed" );
+ }
+ }
+
+ void CanvasHelper::drawBezier( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealBezierSegment2D& aBezierSegment,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::Pen aPen(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)),
+ Gdiplus::REAL(0.0) );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ const Gdiplus::PixelOffsetMode aOldMode(
+ pGraphics->GetPixelOffsetMode() );
+ pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ Gdiplus::Status hr = pGraphics->DrawBezier( &aPen,
+ Gdiplus::REAL(aBezierSegment.Px), // disambiguate call
+ Gdiplus::REAL(aBezierSegment.Py),
+ Gdiplus::REAL(aBezierSegment.C1x),
+ Gdiplus::REAL(aBezierSegment.C1y),
+ Gdiplus::REAL(aEndPoint.X),
+ Gdiplus::REAL(aEndPoint.Y),
+ Gdiplus::REAL(aBezierSegment.C2x),
+ Gdiplus::REAL(aBezierSegment.C2y) );
+
+ pGraphics->SetPixelOffsetMode( aOldMode );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == hr,
+ "CanvasHelper::drawBezier(): GDI+ call failed" );
+ }
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xPolyPolygon.is(),
+ "CanvasHelper::drawPolyPolygon: polygon is NULL");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::Pen aPen(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)),
+ Gdiplus::REAL(0.0) );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ const Gdiplus::PixelOffsetMode aOldMode(
+ pGraphics->GetPixelOffsetMode() );
+ pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );
+
+ // TODO(E1): Return value
+ Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
+
+ pGraphics->SetPixelOffsetMode( aOldMode );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == hr,
+ "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::StrokeAttributes& strokeAttributes )
+ {
+ ENSURE_OR_THROW( xPolyPolygon.is(),
+ "CanvasHelper::drawPolyPolygon: polygon is NULL");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+
+ // Setup stroke pen
+ // ----------------
+
+ Gdiplus::Pen aPen(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)),
+ static_cast< Gdiplus::REAL >(strokeAttributes.StrokeWidth) );
+
+ // #122683# Switched precedence of pixel offset
+ // mode. Seemingly, polygon stroking needs
+ // PixelOffsetModeNone to achieve visually pleasing
+ // results, whereas all other operations (e.g. polygon
+ // fills, bitmaps) look better with PixelOffsetModeHalf.
+ const Gdiplus::PixelOffsetMode aOldMode(
+ pGraphics->GetPixelOffsetMode() );
+ pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
+
+ aPen.SetMiterLimit( static_cast< Gdiplus::REAL >(strokeAttributes.MiterLimit) );
+
+ const ::std::vector< Gdiplus::REAL >& rDashArray(
+ ::comphelper::sequenceToContainer< ::std::vector< Gdiplus::REAL > >(
+ strokeAttributes.DashArray ) );
+ if( !rDashArray.empty() )
+ {
+ aPen.SetDashPattern( &rDashArray[0],
+ rDashArray.size() );
+ }
+ aPen.SetLineCap( gdiCapFromCap(strokeAttributes.StartCapType),
+ gdiCapFromCap(strokeAttributes.EndCapType),
+ Gdiplus::DashCapFlat );
+ aPen.SetLineJoin( gdiJoinFromJoin(strokeAttributes.JoinType) );
+
+ GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );
+
+ // TODO(E1): Return value
+ Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
+
+ pGraphics->SetPixelOffsetMode( aOldMode );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == hr,
+ "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const uno::Reference< geometry::XMapping2D >& /*xMapping*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XPolyPolygon2D >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xPolyPolygon.is(),
+ "CanvasHelper::fillPolyPolygon: polygon is NULL");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::SolidBrush aBrush(
+ tools::sequenceToArgb(renderState.DeviceColor));
+
+ GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );
+
+ // TODO(F1): FillRule
+ ENSURE_OR_THROW( Gdiplus::Ok == pGraphics->FillPath( &aBrush, pPath.get() ),
+ "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const uno::Reference< geometry::XMapping2D >& /*xMapping*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* /*pCanvas*/,
+ const rendering::FontRequest& fontRequest,
+ const uno::Sequence< beans::PropertyValue >& extraFontProperties,
+ const geometry::Matrix2D& fontMatrix )
+ {
+ if( needOutput() )
+ {
+ return uno::Reference< rendering::XCanvasFont >(
+ new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) );
+ }
+
+ return uno::Reference< rendering::XCanvasFont >();
+ }
+
+ uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/,
+ const rendering::FontInfo& /*aFilter*/,
+ const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
+ {
+ // TODO
+ return uno::Sequence< rendering::FontInfo >();
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* /*pCanvas*/,
+ const rendering::StringContext& text,
+ const uno::Reference< rendering::XCanvasFont >& xFont,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ sal_Int8 /*textDirection*/ )
+ {
+ ENSURE_OR_THROW( xFont.is(),
+ "CanvasHelper::drawText: font is NULL");
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ Gdiplus::SolidBrush aBrush(
+ Gdiplus::Color(
+ tools::sequenceToArgb(renderState.DeviceColor)));
+
+ CanvasFont::ImplRef pFont(
+ tools::canvasFontFromXFont(xFont) );
+
+ // Move glyphs up, such that output happens at the font
+ // baseline.
+ Gdiplus::PointF aPoint( 0.0,
+ static_cast<Gdiplus::REAL>(-(pFont->getFont()->GetSize()*
+ pFont->getCellAscent() /
+ pFont->getEmHeight())) );
+
+ // TODO(F1): According to
+ // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
+ // we might have to revert to GDI and ExTextOut here,
+ // since GDI+ takes the scalability a little bit too
+ // far...
+
+ // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
+ // DrawDriverString here, and perform layouting myself...
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->DrawString( reinterpret_cast<LPCWSTR>(
+ text.Text.copy( text.StartPosition,
+ text.Length ).getStr()),
+ text.Length,
+ pFont->getFont().get(),
+ aPoint,
+ &aBrush ),
+ "CanvasHelper::drawText(): GDI+ call failed" );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XTextLayout >& xLayoutetText,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xLayoutetText.is(),
+ "CanvasHelper::drawTextLayout: layout is NULL");
+
+ if( needOutput() )
+ {
+ TextLayout* pTextLayout =
+ dynamic_cast< TextLayout* >( xLayoutetText.get() );
+
+ ENSURE_OR_THROW( pTextLayout,
+ "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
+
+ pTextLayout->draw( mpGraphicsProvider->getGraphics(),
+ viewState,
+ renderState,
+ maOutputOffset,
+ mpDevice,
+ false );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xBitmap.is(),
+ "CanvasHelper::drawBitmap: bitmap is NULL");
+
+ if( needOutput() )
+ {
+ // check whether one of our own objects - need to retrieve
+ // bitmap _before_ calling
+ // GraphicsProvider::getGraphics(), to avoid locking our
+ // own surface.
+ BitmapSharedPtr pGdiBitmap;
+ BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get());
+ if( pBitmap )
+ {
+ IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() );
+ if( pDXBitmap )
+ pGdiBitmap = pDXBitmap->getBitmap();
+ }
+
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ if( pGdiBitmap )
+ tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap);
+ else
+ tools::drawVCLBitmapFromXBitmap(pGraphics,
+ xBitmap);
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( xBitmap.is(),
+ "CanvasHelper::drawBitmap: bitmap is NULL");
+
+ // no color set -> this is equivalent to a plain drawBitmap(), then
+ if( renderState.DeviceColor.getLength() < 3 )
+ return drawBitmap( pCanvas, xBitmap, viewState, renderState );
+
+ if( needOutput() )
+ {
+ GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
+
+ setupGraphicsState( pGraphics, viewState, renderState );
+
+ BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) );
+ Gdiplus::Rect aRect( 0, 0,
+ pBitmap->GetWidth(),
+ pBitmap->GetHeight() );
+
+ // Setup an ImageAttributes with an alpha-modulating
+ // color matrix.
+ const rendering::ARGBColor& rARGBColor(
+ mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]);
+
+ Gdiplus::ImageAttributes aImgAttr;
+ tools::setModulateImageAttributes( aImgAttr,
+ rARGBColor.Red,
+ rARGBColor.Green,
+ rARGBColor.Blue,
+ rARGBColor.Alpha );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(),
+ aRect,
+ 0, 0,
+ pBitmap->GetWidth(),
+ pBitmap->GetHeight(),
+ Gdiplus::UnitPixel,
+ &aImgAttr,
+ NULL,
+ NULL ),
+ "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
+ }
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
+ {
+ return uno::Reference< rendering::XGraphicDevice >(mpDevice);
+ }
+
+ // private helper
+ // --------------------------------------------------
+
+ Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode )
+ {
+ Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver );
+
+ switch( nMode )
+ {
+ case rendering::CompositeOperation::OVER:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::CLEAR:
+ aRet = Gdiplus::CompositingModeSourceOver;
+ break;
+
+ case rendering::CompositeOperation::SOURCE:
+ aRet = Gdiplus::CompositingModeSourceCopy;
+ break;
+
+ case rendering::CompositeOperation::DESTINATION:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::UNDER:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::INSIDE:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::INSIDE_REVERSE:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::OUTSIDE:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::OUTSIDE_REVERSE:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::ATOP:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::ATOP_REVERSE:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::XOR:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::ADD:
+ // FALLTHROUGH intended
+ case rendering::CompositeOperation::SATURATE:
+ // TODO(F2): Problem, because GDI+ only knows about two compositing modes
+ aRet = Gdiplus::CompositingModeSourceOver;
+ break;
+
+ default:
+ ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
+ break;
+ }
+
+ return aRet;
+ }
+
+ void CanvasHelper::setupGraphicsState( GraphicsSharedPtr& rGraphics,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ ENSURE_OR_THROW( needOutput(),
+ "CanvasHelper::setupGraphicsState: primary graphics invalid" );
+ ENSURE_OR_THROW( mpDevice,
+ "CanvasHelper::setupGraphicsState: reference device invalid" );
+
+ // setup view transform first. Clipping e.g. depends on it
+ ::basegfx::B2DHomMatrix aTransform;
+ ::canvas::tools::getViewStateTransform(aTransform, viewState);
+
+ // add output offset
+ if( !maOutputOffset.equalZero() )
+ {
+ ::basegfx::B2DHomMatrix aOutputOffset;
+ aOutputOffset.translate( maOutputOffset.getX(),
+ maOutputOffset.getY() );
+
+ aTransform = aOutputOffset * aTransform;
+ }
+
+ Gdiplus::Matrix aMatrix;
+ tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
+ "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
+
+ // setup view and render state clipping
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->ResetClip(),
+ "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
+
+ if( viewState.Clip.is() )
+ {
+ GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState.Clip ) );
+
+ // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
+ // Try SetClip( Rect ) or similar for simple clip paths (need some support in
+ // LinePolyPolygon, then)
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
+ Gdiplus::CombineModeIntersect ),
+ "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
+ }
+
+ // setup overall transform only now. View clip above was relative to
+ // view transform
+ ::canvas::tools::mergeViewAndRenderTransform(aTransform,
+ viewState,
+ renderState);
+
+ // add output offset
+ if( !maOutputOffset.equalZero() )
+ {
+ ::basegfx::B2DHomMatrix aOutputOffset;
+ aOutputOffset.translate( maOutputOffset.getX(),
+ maOutputOffset.getY() );
+
+ aTransform = aOutputOffset * aTransform;
+ }
+
+ tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
+
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
+ "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
+
+ if( renderState.Clip.is() )
+ {
+ GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState.Clip ) );
+
+ // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
+ // Try SetClip( Rect ) or similar for simple clip paths (need some support in
+ // LinePolyPolygon, then)
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
+ Gdiplus::CombineModeIntersect ),
+ "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
+ }
+
+ // setup compositing
+ const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) );
+ ENSURE_OR_THROW(
+ Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ),
+ "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
+ }
+
+ void CanvasHelper::flush() const
+ {
+ if( needOutput() )
+ mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );
+ }
+}