1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dx_canvashelper.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_canvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
37 #include <rtl/logfile.hxx>
38 #include <rtl/math.hxx>
40 #include <com/sun/star/rendering/TexturingMode.hpp>
41 #include <com/sun/star/rendering/CompositeOperation.hpp>
42 #include <com/sun/star/rendering/RepaintResult.hpp>
43 #include <com/sun/star/rendering/PathCapType.hpp>
44 #include <com/sun/star/rendering/PathJoinType.hpp>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/point/b2dpoint.hxx>
48 #include <basegfx/tools/canvastools.hxx>
50 #include <comphelper/sequence.hxx>
51 #include <canvas/canvastools.hxx>
53 #include "dx_spritecanvas.hxx"
54 #include "dx_impltools.hxx"
55 #include "dx_vcltools.hxx"
56 #include "dx_canvasfont.hxx"
57 #include "dx_textlayout.hxx"
58 #include "dx_canvashelper.hxx"
63 using namespace ::com::sun::star
;
69 Gdiplus::LineCap
gdiCapFromCap( sal_Int8 nCapType
)
73 case rendering::PathCapType::BUTT
:
74 return Gdiplus::LineCapFlat
;
76 case rendering::PathCapType::ROUND
:
77 return Gdiplus::LineCapRound
;
79 case rendering::PathCapType::SQUARE
:
80 return Gdiplus::LineCapSquare
;
83 ENSURE_OR_THROW( false,
84 "gdiCapFromCap(): Unexpected cap type" );
87 return Gdiplus::LineCapFlat
;
90 Gdiplus::LineJoin
gdiJoinFromJoin( sal_Int8 nJoinType
)
94 case rendering::PathJoinType::NONE
:
96 "gdiJoinFromJoin(): Join NONE not possible, mapping to MITER" );
97 // FALLTHROUGH intended
98 case rendering::PathJoinType::MITER
:
99 return Gdiplus::LineJoinMiter
;
101 case rendering::PathJoinType::ROUND
:
102 return Gdiplus::LineJoinRound
;
104 case rendering::PathJoinType::BEVEL
:
105 return Gdiplus::LineJoinBevel
;
108 ENSURE_OR_THROW( false,
109 "gdiJoinFromJoin(): Unexpected join type" );
112 return Gdiplus::LineJoinMiter
;
116 CanvasHelper::CanvasHelper() :
117 mpGdiPlusUser( GDIPlusUser::createInstance() ),
119 mpGraphicsProvider(),
124 void CanvasHelper::disposing()
126 mpGraphicsProvider
.reset();
128 mpGdiPlusUser
.reset();
131 void CanvasHelper::setDevice( rendering::XGraphicDevice
& rDevice
)
136 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr
& rTarget
)
138 ENSURE_OR_THROW( rTarget
,
139 "CanvasHelper::setTarget(): Invalid target" );
140 ENSURE_OR_THROW( !mpGraphicsProvider
.get(),
141 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
143 mpGraphicsProvider
= rTarget
;
146 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr
& rTarget
,
147 const ::basegfx::B2ISize
& rOutputOffset
)
149 ENSURE_OR_THROW( rTarget
,
150 "CanvasHelper::setTarget(): invalid target" );
151 ENSURE_OR_THROW( !mpGraphicsProvider
.get(),
152 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
154 mpGraphicsProvider
= rTarget
;
155 maOutputOffset
= rOutputOffset
;
158 void CanvasHelper::clear()
162 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
163 Gdiplus::Color aClearColor
= Gdiplus::Color((Gdiplus::ARGB
)Gdiplus::Color::White
);
166 Gdiplus::Ok
== pGraphics
->SetCompositingMode(
167 Gdiplus::CompositingModeSourceCopy
), // force set, don't blend
168 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
170 Gdiplus::Ok
== pGraphics
->Clear( aClearColor
),
171 "CanvasHelper::clear(): GDI+ Clear call failed" );
175 void CanvasHelper::drawPoint( const rendering::XCanvas
* /*pCanvas*/,
176 const geometry::RealPoint2D
& aPoint
,
177 const rendering::ViewState
& viewState
,
178 const rendering::RenderState
& renderState
)
182 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
184 setupGraphicsState( pGraphics
, viewState
, renderState
);
186 Gdiplus::SolidBrush
aBrush(
188 tools::sequenceToArgb(renderState
.DeviceColor
)) );
190 // determine size of one-by-one device pixel ellipse
191 Gdiplus::Matrix aMatrix
;
192 pGraphics
->GetTransform(&aMatrix
);
194 Gdiplus::PointF
vector(1, 1);
195 aMatrix
.TransformVectors(&vector
);
197 // paint a one-by-one circle, with the given point
198 // in the middle (rounded to float)
200 Gdiplus::Ok
== pGraphics
->FillEllipse( &aBrush
,
202 Gdiplus::REAL(aPoint
.X
),
203 Gdiplus::REAL(aPoint
.Y
),
204 Gdiplus::REAL(vector
.X
),
205 Gdiplus::REAL(vector
.Y
) ),
206 "CanvasHelper::drawPoint(): GDI+ call failed" );
210 void CanvasHelper::drawLine( const rendering::XCanvas
* /*pCanvas*/,
211 const geometry::RealPoint2D
& aStartPoint
,
212 const geometry::RealPoint2D
& aEndPoint
,
213 const rendering::ViewState
& viewState
,
214 const rendering::RenderState
& renderState
)
218 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
220 setupGraphicsState( pGraphics
, viewState
, renderState
);
224 tools::sequenceToArgb(renderState
.DeviceColor
)),
225 Gdiplus::REAL(0.0) );
227 // #122683# Switched precedence of pixel offset
228 // mode. Seemingly, polygon stroking needs
229 // PixelOffsetModeNone to achieve visually pleasing
230 // results, whereas all other operations (e.g. polygon
231 // fills, bitmaps) look better with PixelOffsetModeHalf.
232 const Gdiplus::PixelOffsetMode
aOldMode(
233 pGraphics
->GetPixelOffsetMode() );
234 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
236 Gdiplus::Status hr
= pGraphics
->DrawLine( &aPen
,
237 Gdiplus::REAL(aStartPoint
.X
), // disambiguate call
238 Gdiplus::REAL(aStartPoint
.Y
),
239 Gdiplus::REAL(aEndPoint
.X
),
240 Gdiplus::REAL(aEndPoint
.Y
) );
241 pGraphics
->SetPixelOffsetMode( aOldMode
);
245 "CanvasHelper::drawLine(): GDI+ call failed" );
249 void CanvasHelper::drawBezier( const rendering::XCanvas
* /*pCanvas*/,
250 const geometry::RealBezierSegment2D
& aBezierSegment
,
251 const geometry::RealPoint2D
& aEndPoint
,
252 const rendering::ViewState
& viewState
,
253 const rendering::RenderState
& renderState
)
257 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
259 setupGraphicsState( pGraphics
, viewState
, renderState
);
263 tools::sequenceToArgb(renderState
.DeviceColor
)),
264 Gdiplus::REAL(0.0) );
266 // #122683# Switched precedence of pixel offset
267 // mode. Seemingly, polygon stroking needs
268 // PixelOffsetModeNone to achieve visually pleasing
269 // results, whereas all other operations (e.g. polygon
270 // fills, bitmaps) look better with PixelOffsetModeHalf.
271 const Gdiplus::PixelOffsetMode
aOldMode(
272 pGraphics
->GetPixelOffsetMode() );
273 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
275 Gdiplus::Status hr
= pGraphics
->DrawBezier( &aPen
,
276 Gdiplus::REAL(aBezierSegment
.Px
), // disambiguate call
277 Gdiplus::REAL(aBezierSegment
.Py
),
278 Gdiplus::REAL(aBezierSegment
.C1x
),
279 Gdiplus::REAL(aBezierSegment
.C1y
),
280 Gdiplus::REAL(aEndPoint
.X
),
281 Gdiplus::REAL(aEndPoint
.Y
),
282 Gdiplus::REAL(aBezierSegment
.C2x
),
283 Gdiplus::REAL(aBezierSegment
.C2y
) );
285 pGraphics
->SetPixelOffsetMode( aOldMode
);
289 "CanvasHelper::drawBezier(): GDI+ call failed" );
293 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
294 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
295 const rendering::ViewState
& viewState
,
296 const rendering::RenderState
& renderState
)
298 ENSURE_OR_THROW( xPolyPolygon
.is(),
299 "CanvasHelper::drawPolyPolygon: polygon is NULL");
303 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
305 setupGraphicsState( pGraphics
, viewState
, renderState
);
309 tools::sequenceToArgb(renderState
.DeviceColor
)),
310 Gdiplus::REAL(0.0) );
312 // #122683# Switched precedence of pixel offset
313 // mode. Seemingly, polygon stroking needs
314 // PixelOffsetModeNone to achieve visually pleasing
315 // results, whereas all other operations (e.g. polygon
316 // fills, bitmaps) look better with PixelOffsetModeHalf.
317 const Gdiplus::PixelOffsetMode
aOldMode(
318 pGraphics
->GetPixelOffsetMode() );
319 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
321 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
323 // TODO(E1): Return value
324 Gdiplus::Status hr
= pGraphics
->DrawPath( &aPen
, pPath
.get() );
326 pGraphics
->SetPixelOffsetMode( aOldMode
);
330 "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
333 // TODO(P1): Provide caching here.
334 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
337 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokePolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
338 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
339 const rendering::ViewState
& viewState
,
340 const rendering::RenderState
& renderState
,
341 const rendering::StrokeAttributes
& strokeAttributes
)
343 ENSURE_OR_THROW( xPolyPolygon
.is(),
344 "CanvasHelper::drawPolyPolygon: polygon is NULL");
348 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
350 setupGraphicsState( pGraphics
, viewState
, renderState
);
358 tools::sequenceToArgb(renderState
.DeviceColor
)),
359 static_cast< Gdiplus::REAL
>(strokeAttributes
.StrokeWidth
) );
361 // #122683# Switched precedence of pixel offset
362 // mode. Seemingly, polygon stroking needs
363 // PixelOffsetModeNone to achieve visually pleasing
364 // results, whereas all other operations (e.g. polygon
365 // fills, bitmaps) look better with PixelOffsetModeHalf.
366 const Gdiplus::PixelOffsetMode
aOldMode(
367 pGraphics
->GetPixelOffsetMode() );
368 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
370 aPen
.SetMiterLimit( static_cast< Gdiplus::REAL
>(strokeAttributes
.MiterLimit
) );
372 const ::std::vector
< Gdiplus::REAL
>& rDashArray(
373 ::comphelper::sequenceToContainer
< ::std::vector
< Gdiplus::REAL
> >(
374 strokeAttributes
.DashArray
) );
375 if( !rDashArray
.empty() )
377 aPen
.SetDashPattern( &rDashArray
[0],
380 aPen
.SetLineCap( gdiCapFromCap(strokeAttributes
.StartCapType
),
381 gdiCapFromCap(strokeAttributes
.EndCapType
),
382 Gdiplus::DashCapFlat
);
383 aPen
.SetLineJoin( gdiJoinFromJoin(strokeAttributes
.JoinType
) );
385 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
387 // TODO(E1): Return value
388 Gdiplus::Status hr
= pGraphics
->DrawPath( &aPen
, pPath
.get() );
390 pGraphics
->SetPixelOffsetMode( aOldMode
);
394 "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
397 // TODO(P1): Provide caching here.
398 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
401 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
402 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
403 const rendering::ViewState
& /*viewState*/,
404 const rendering::RenderState
& /*renderState*/,
405 const uno::Sequence
< rendering::Texture
>& /*textures*/,
406 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
409 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
412 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
413 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
414 const rendering::ViewState
& /*viewState*/,
415 const rendering::RenderState
& /*renderState*/,
416 const uno::Sequence
< rendering::Texture
>& /*textures*/,
417 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/,
418 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
421 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
424 uno::Reference
< rendering::XPolyPolygon2D
> CanvasHelper::queryStrokeShapes( const rendering::XCanvas
* /*pCanvas*/,
425 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
426 const rendering::ViewState
& /*viewState*/,
427 const rendering::RenderState
& /*renderState*/,
428 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
431 return uno::Reference
< rendering::XPolyPolygon2D
>(NULL
);
434 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
435 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
436 const rendering::ViewState
& viewState
,
437 const rendering::RenderState
& renderState
)
439 ENSURE_OR_THROW( xPolyPolygon
.is(),
440 "CanvasHelper::fillPolyPolygon: polygon is NULL");
444 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
446 setupGraphicsState( pGraphics
, viewState
, renderState
);
448 Gdiplus::SolidBrush
aBrush(
449 tools::sequenceToArgb(renderState
.DeviceColor
));
451 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
453 // TODO(F1): FillRule
454 ENSURE_OR_THROW( Gdiplus::Ok
== pGraphics
->FillPath( &aBrush
, pPath
.get() ),
455 "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
458 // TODO(P1): Provide caching here.
459 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
462 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
463 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
464 const rendering::ViewState
& /*viewState*/,
465 const rendering::RenderState
& /*renderState*/,
466 const uno::Sequence
< rendering::Texture
>& /*textures*/,
467 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/ )
470 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
473 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* /*pCanvas*/,
474 const rendering::FontRequest
& fontRequest
,
475 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
476 const geometry::Matrix2D
& fontMatrix
)
480 return uno::Reference
< rendering::XCanvasFont
>(
481 new CanvasFont(fontRequest
, extraFontProperties
, fontMatrix
) );
484 return uno::Reference
< rendering::XCanvasFont
>();
487 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* /*pCanvas*/,
488 const rendering::FontInfo
& /*aFilter*/,
489 const uno::Sequence
< beans::PropertyValue
>& /*aFontProperties*/ )
492 return uno::Sequence
< rendering::FontInfo
>();
495 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* /*pCanvas*/,
496 const rendering::StringContext
& text
,
497 const uno::Reference
< rendering::XCanvasFont
>& xFont
,
498 const rendering::ViewState
& viewState
,
499 const rendering::RenderState
& renderState
,
500 sal_Int8
/*textDirection*/ )
502 ENSURE_OR_THROW( xFont
.is(),
503 "CanvasHelper::drawText: font is NULL");
507 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
509 setupGraphicsState( pGraphics
, viewState
, renderState
);
511 Gdiplus::SolidBrush
aBrush(
513 tools::sequenceToArgb(renderState
.DeviceColor
)));
515 CanvasFont::ImplRef
pFont(
516 tools::canvasFontFromXFont(xFont
) );
518 // Move glyphs up, such that output happens at the font
520 Gdiplus::PointF
aPoint( 0.0,
521 static_cast<Gdiplus::REAL
>(-(pFont
->getFont()->GetSize()*
522 pFont
->getCellAscent() /
523 pFont
->getEmHeight())) );
525 // TODO(F1): According to
526 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
527 // we might have to revert to GDI and ExTextOut here,
528 // since GDI+ takes the scalability a little bit too
531 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
532 // DrawDriverString here, and perform layouting myself...
534 Gdiplus::Ok
== pGraphics
->DrawString( reinterpret_cast<LPCWSTR
>(
535 text
.Text
.copy( text
.StartPosition
,
536 text
.Length
).getStr()),
538 pFont
->getFont().get(),
541 "CanvasHelper::drawText(): GDI+ call failed" );
544 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
547 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* /*pCanvas*/,
548 const uno::Reference
< rendering::XTextLayout
>& xLayoutetText
,
549 const rendering::ViewState
& viewState
,
550 const rendering::RenderState
& renderState
)
552 ENSURE_OR_THROW( xLayoutetText
.is(),
553 "CanvasHelper::drawTextLayout: layout is NULL");
557 TextLayout
* pTextLayout
=
558 dynamic_cast< TextLayout
* >( xLayoutetText
.get() );
560 ENSURE_OR_THROW( pTextLayout
,
561 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
563 pTextLayout
->draw( mpGraphicsProvider
->getGraphics(),
571 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
574 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmap( const rendering::XCanvas
* /*pCanvas*/,
575 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
576 const rendering::ViewState
& viewState
,
577 const rendering::RenderState
& renderState
)
579 ENSURE_OR_THROW( xBitmap
.is(),
580 "CanvasHelper::drawBitmap: bitmap is NULL");
584 // check whether one of our own objects - need to retrieve
585 // bitmap _before_ calling
586 // GraphicsProvider::getGraphics(), to avoid locking our
588 BitmapSharedPtr pGdiBitmap
;
589 BitmapProvider
* pBitmap
= dynamic_cast< BitmapProvider
* >(xBitmap
.get());
592 IBitmapSharedPtr
pDXBitmap( pBitmap
->getBitmap() );
594 pGdiBitmap
= pDXBitmap
->getBitmap();
597 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
598 setupGraphicsState( pGraphics
, viewState
, renderState
);
601 tools::drawGdiPlusBitmap(pGraphics
,pGdiBitmap
);
603 tools::drawVCLBitmapFromXBitmap(pGraphics
,
607 // TODO(P1): Provide caching here.
608 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
611 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmapModulated( const rendering::XCanvas
* pCanvas
,
612 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
613 const rendering::ViewState
& viewState
,
614 const rendering::RenderState
& renderState
)
616 ENSURE_OR_THROW( xBitmap
.is(),
617 "CanvasHelper::drawBitmap: bitmap is NULL");
619 // no color set -> this is equivalent to a plain drawBitmap(), then
620 if( renderState
.DeviceColor
.getLength() < 3 )
621 return drawBitmap( pCanvas
, xBitmap
, viewState
, renderState
);
625 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
627 setupGraphicsState( pGraphics
, viewState
, renderState
);
629 BitmapSharedPtr
pBitmap( tools::bitmapFromXBitmap( xBitmap
) );
630 Gdiplus::Rect
aRect( 0, 0,
632 pBitmap
->GetHeight() );
634 // Setup an ImageAttributes with an alpha-modulating
636 const rendering::ARGBColor
& rARGBColor(
637 mpDevice
->getDeviceColorSpace()->convertToARGB(renderState
.DeviceColor
)[0]);
639 Gdiplus::ImageAttributes aImgAttr
;
640 tools::setModulateImageAttributes( aImgAttr
,
647 Gdiplus::Ok
== pGraphics
->DrawImage( pBitmap
.get(),
651 pBitmap
->GetHeight(),
656 "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
659 // TODO(P1): Provide caching here.
660 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
663 uno::Reference
< rendering::XGraphicDevice
> CanvasHelper::getDevice()
665 return uno::Reference
< rendering::XGraphicDevice
>(mpDevice
);
669 // --------------------------------------------------
671 Gdiplus::CompositingMode
CanvasHelper::calcCompositingMode( sal_Int8 nMode
)
673 Gdiplus::CompositingMode
aRet( Gdiplus::CompositingModeSourceOver
);
677 case rendering::CompositeOperation::OVER
:
678 // FALLTHROUGH intended
679 case rendering::CompositeOperation::CLEAR
:
680 aRet
= Gdiplus::CompositingModeSourceOver
;
683 case rendering::CompositeOperation::SOURCE
:
684 aRet
= Gdiplus::CompositingModeSourceCopy
;
687 case rendering::CompositeOperation::DESTINATION
:
688 // FALLTHROUGH intended
689 case rendering::CompositeOperation::UNDER
:
690 // FALLTHROUGH intended
691 case rendering::CompositeOperation::INSIDE
:
692 // FALLTHROUGH intended
693 case rendering::CompositeOperation::INSIDE_REVERSE
:
694 // FALLTHROUGH intended
695 case rendering::CompositeOperation::OUTSIDE
:
696 // FALLTHROUGH intended
697 case rendering::CompositeOperation::OUTSIDE_REVERSE
:
698 // FALLTHROUGH intended
699 case rendering::CompositeOperation::ATOP
:
700 // FALLTHROUGH intended
701 case rendering::CompositeOperation::ATOP_REVERSE
:
702 // FALLTHROUGH intended
703 case rendering::CompositeOperation::XOR
:
704 // FALLTHROUGH intended
705 case rendering::CompositeOperation::ADD
:
706 // FALLTHROUGH intended
707 case rendering::CompositeOperation::SATURATE
:
708 // TODO(F2): Problem, because GDI+ only knows about two compositing modes
709 aRet
= Gdiplus::CompositingModeSourceOver
;
713 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
720 void CanvasHelper::setupGraphicsState( GraphicsSharedPtr
& rGraphics
,
721 const rendering::ViewState
& viewState
,
722 const rendering::RenderState
& renderState
)
724 ENSURE_OR_THROW( needOutput(),
725 "CanvasHelper::setupGraphicsState: primary graphics invalid" );
726 ENSURE_OR_THROW( mpDevice
,
727 "CanvasHelper::setupGraphicsState: reference device invalid" );
729 // setup view transform first. Clipping e.g. depends on it
730 ::basegfx::B2DHomMatrix aTransform
;
731 ::canvas::tools::getViewStateTransform(aTransform
, viewState
);
734 if( !maOutputOffset
.equalZero() )
736 ::basegfx::B2DHomMatrix aOutputOffset
;
737 aOutputOffset
.translate( maOutputOffset
.getX(),
738 maOutputOffset
.getY() );
740 aTransform
= aOutputOffset
* aTransform
;
743 Gdiplus::Matrix aMatrix
;
744 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix
, aTransform
);
747 Gdiplus::Ok
== rGraphics
->SetTransform( &aMatrix
),
748 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
750 // setup view and render state clipping
752 Gdiplus::Ok
== rGraphics
->ResetClip(),
753 "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
755 if( viewState
.Clip
.is() )
757 GraphicsPathSharedPtr
aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState
.Clip
) );
759 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
760 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
761 // LinePolyPolygon, then)
763 Gdiplus::Ok
== rGraphics
->SetClip( aClipPath
.get(),
764 Gdiplus::CombineModeIntersect
),
765 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
768 // setup overall transform only now. View clip above was relative to
770 ::canvas::tools::mergeViewAndRenderTransform(aTransform
,
775 if( !maOutputOffset
.equalZero() )
777 ::basegfx::B2DHomMatrix aOutputOffset
;
778 aOutputOffset
.translate( maOutputOffset
.getX(),
779 maOutputOffset
.getY() );
781 aTransform
= aOutputOffset
* aTransform
;
784 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix
, aTransform
);
787 Gdiplus::Ok
== rGraphics
->SetTransform( &aMatrix
),
788 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
790 if( renderState
.Clip
.is() )
792 GraphicsPathSharedPtr
aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState
.Clip
) );
794 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
795 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
796 // LinePolyPolygon, then)
798 Gdiplus::Ok
== rGraphics
->SetClip( aClipPath
.get(),
799 Gdiplus::CombineModeIntersect
),
800 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
804 const Gdiplus::CompositingMode
eCompositing( calcCompositingMode( renderState
.CompositeOperation
) );
806 Gdiplus::Ok
== rGraphics
->SetCompositingMode( eCompositing
),
807 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
810 void CanvasHelper::flush() const
813 mpGraphicsProvider
->getGraphics()->Flush( Gdiplus::FlushIntentionSync
);