1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_canvas.hxx"
31 #include <canvas/debug.hxx>
32 #include <tools/diagnose_ex.h>
34 #include <rtl/logfile.hxx>
35 #include <rtl/math.hxx>
37 #include <com/sun/star/rendering/TexturingMode.hpp>
38 #include <com/sun/star/rendering/CompositeOperation.hpp>
39 #include <com/sun/star/rendering/RepaintResult.hpp>
40 #include <com/sun/star/rendering/PathCapType.hpp>
41 #include <com/sun/star/rendering/PathJoinType.hpp>
43 #include <basegfx/matrix/b2dhommatrix.hxx>
44 #include <basegfx/point/b2dpoint.hxx>
45 #include <basegfx/tools/canvastools.hxx>
46 #include <basegfx/matrix/b2dhommatrixtools.hxx>
48 #include <comphelper/sequence.hxx>
49 #include <canvas/canvastools.hxx>
51 #include "dx_spritecanvas.hxx"
52 #include "dx_impltools.hxx"
53 #include "dx_vcltools.hxx"
54 #include "dx_canvasfont.hxx"
55 #include "dx_textlayout.hxx"
56 #include "dx_canvashelper.hxx"
61 using namespace ::com::sun::star
;
67 Gdiplus::LineCap
gdiCapFromCap( sal_Int8 nCapType
)
71 case rendering::PathCapType::BUTT
:
72 return Gdiplus::LineCapFlat
;
74 case rendering::PathCapType::ROUND
:
75 return Gdiplus::LineCapRound
;
77 case rendering::PathCapType::SQUARE
:
78 return Gdiplus::LineCapSquare
;
81 ENSURE_OR_THROW( false,
82 "gdiCapFromCap(): Unexpected cap type" );
85 return Gdiplus::LineCapFlat
;
88 Gdiplus::LineJoin
gdiJoinFromJoin( sal_Int8 nJoinType
)
92 case rendering::PathJoinType::NONE
:
94 "gdiJoinFromJoin(): Join NONE not possible, mapping to MITER" );
95 // FALLTHROUGH intended
96 case rendering::PathJoinType::MITER
:
97 return Gdiplus::LineJoinMiter
;
99 case rendering::PathJoinType::ROUND
:
100 return Gdiplus::LineJoinRound
;
102 case rendering::PathJoinType::BEVEL
:
103 return Gdiplus::LineJoinBevel
;
106 ENSURE_OR_THROW( false,
107 "gdiJoinFromJoin(): Unexpected join type" );
110 return Gdiplus::LineJoinMiter
;
114 CanvasHelper::CanvasHelper() :
115 mpGdiPlusUser( GDIPlusUser::createInstance() ),
117 mpGraphicsProvider(),
122 void CanvasHelper::disposing()
124 mpGraphicsProvider
.reset();
126 mpGdiPlusUser
.reset();
129 void CanvasHelper::setDevice( rendering::XGraphicDevice
& rDevice
)
134 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr
& rTarget
)
136 ENSURE_OR_THROW( rTarget
,
137 "CanvasHelper::setTarget(): Invalid target" );
138 ENSURE_OR_THROW( !mpGraphicsProvider
.get(),
139 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
141 mpGraphicsProvider
= rTarget
;
144 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr
& rTarget
,
145 const ::basegfx::B2ISize
& rOutputOffset
)
147 ENSURE_OR_THROW( rTarget
,
148 "CanvasHelper::setTarget(): invalid target" );
149 ENSURE_OR_THROW( !mpGraphicsProvider
.get(),
150 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
152 mpGraphicsProvider
= rTarget
;
153 maOutputOffset
= rOutputOffset
;
156 void CanvasHelper::clear()
160 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
161 Gdiplus::Color aClearColor
= Gdiplus::Color((Gdiplus::ARGB
)Gdiplus::Color::White
);
164 Gdiplus::Ok
== pGraphics
->SetCompositingMode(
165 Gdiplus::CompositingModeSourceCopy
), // force set, don't blend
166 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
168 Gdiplus::Ok
== pGraphics
->Clear( aClearColor
),
169 "CanvasHelper::clear(): GDI+ Clear call failed" );
173 void CanvasHelper::drawPoint( const rendering::XCanvas
* /*pCanvas*/,
174 const geometry::RealPoint2D
& aPoint
,
175 const rendering::ViewState
& viewState
,
176 const rendering::RenderState
& renderState
)
180 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
182 setupGraphicsState( pGraphics
, viewState
, renderState
);
184 Gdiplus::SolidBrush
aBrush(
186 tools::sequenceToArgb(renderState
.DeviceColor
)) );
188 // determine size of one-by-one device pixel ellipse
189 Gdiplus::Matrix aMatrix
;
190 pGraphics
->GetTransform(&aMatrix
);
192 Gdiplus::PointF
vector(1, 1);
193 aMatrix
.TransformVectors(&vector
);
195 // paint a one-by-one circle, with the given point
196 // in the middle (rounded to float)
198 Gdiplus::Ok
== pGraphics
->FillEllipse( &aBrush
,
200 Gdiplus::REAL(aPoint
.X
),
201 Gdiplus::REAL(aPoint
.Y
),
202 Gdiplus::REAL(vector
.X
),
203 Gdiplus::REAL(vector
.Y
) ),
204 "CanvasHelper::drawPoint(): GDI+ call failed" );
208 void CanvasHelper::drawLine( const rendering::XCanvas
* /*pCanvas*/,
209 const geometry::RealPoint2D
& aStartPoint
,
210 const geometry::RealPoint2D
& aEndPoint
,
211 const rendering::ViewState
& viewState
,
212 const rendering::RenderState
& renderState
)
216 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
218 setupGraphicsState( pGraphics
, viewState
, renderState
);
222 tools::sequenceToArgb(renderState
.DeviceColor
)),
223 Gdiplus::REAL(0.0) );
225 // #122683# Switched precedence of pixel offset
226 // mode. Seemingly, polygon stroking needs
227 // PixelOffsetModeNone to achieve visually pleasing
228 // results, whereas all other operations (e.g. polygon
229 // fills, bitmaps) look better with PixelOffsetModeHalf.
230 const Gdiplus::PixelOffsetMode
aOldMode(
231 pGraphics
->GetPixelOffsetMode() );
232 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
234 Gdiplus::Status hr
= pGraphics
->DrawLine( &aPen
,
235 Gdiplus::REAL(aStartPoint
.X
), // disambiguate call
236 Gdiplus::REAL(aStartPoint
.Y
),
237 Gdiplus::REAL(aEndPoint
.X
),
238 Gdiplus::REAL(aEndPoint
.Y
) );
239 pGraphics
->SetPixelOffsetMode( aOldMode
);
243 "CanvasHelper::drawLine(): GDI+ call failed" );
247 void CanvasHelper::drawBezier( const rendering::XCanvas
* /*pCanvas*/,
248 const geometry::RealBezierSegment2D
& aBezierSegment
,
249 const geometry::RealPoint2D
& aEndPoint
,
250 const rendering::ViewState
& viewState
,
251 const rendering::RenderState
& renderState
)
255 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
257 setupGraphicsState( pGraphics
, viewState
, renderState
);
261 tools::sequenceToArgb(renderState
.DeviceColor
)),
262 Gdiplus::REAL(0.0) );
264 // #122683# Switched precedence of pixel offset
265 // mode. Seemingly, polygon stroking needs
266 // PixelOffsetModeNone to achieve visually pleasing
267 // results, whereas all other operations (e.g. polygon
268 // fills, bitmaps) look better with PixelOffsetModeHalf.
269 const Gdiplus::PixelOffsetMode
aOldMode(
270 pGraphics
->GetPixelOffsetMode() );
271 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
273 Gdiplus::Status hr
= pGraphics
->DrawBezier( &aPen
,
274 Gdiplus::REAL(aBezierSegment
.Px
), // disambiguate call
275 Gdiplus::REAL(aBezierSegment
.Py
),
276 Gdiplus::REAL(aBezierSegment
.C1x
),
277 Gdiplus::REAL(aBezierSegment
.C1y
),
278 Gdiplus::REAL(aEndPoint
.X
),
279 Gdiplus::REAL(aEndPoint
.Y
),
280 Gdiplus::REAL(aBezierSegment
.C2x
),
281 Gdiplus::REAL(aBezierSegment
.C2y
) );
283 pGraphics
->SetPixelOffsetMode( aOldMode
);
287 "CanvasHelper::drawBezier(): GDI+ call failed" );
291 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
292 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
293 const rendering::ViewState
& viewState
,
294 const rendering::RenderState
& renderState
)
296 ENSURE_OR_THROW( xPolyPolygon
.is(),
297 "CanvasHelper::drawPolyPolygon: polygon is NULL");
301 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
303 setupGraphicsState( pGraphics
, viewState
, renderState
);
307 tools::sequenceToArgb(renderState
.DeviceColor
)),
308 Gdiplus::REAL(0.0) );
310 // #122683# Switched precedence of pixel offset
311 // mode. Seemingly, polygon stroking needs
312 // PixelOffsetModeNone to achieve visually pleasing
313 // results, whereas all other operations (e.g. polygon
314 // fills, bitmaps) look better with PixelOffsetModeHalf.
315 const Gdiplus::PixelOffsetMode
aOldMode(
316 pGraphics
->GetPixelOffsetMode() );
317 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
319 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
321 // TODO(E1): Return value
322 Gdiplus::Status hr
= pGraphics
->DrawPath( &aPen
, pPath
.get() );
324 pGraphics
->SetPixelOffsetMode( aOldMode
);
328 "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
331 // TODO(P1): Provide caching here.
332 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
335 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokePolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
336 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
337 const rendering::ViewState
& viewState
,
338 const rendering::RenderState
& renderState
,
339 const rendering::StrokeAttributes
& strokeAttributes
)
341 ENSURE_OR_THROW( xPolyPolygon
.is(),
342 "CanvasHelper::drawPolyPolygon: polygon is NULL");
346 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
348 setupGraphicsState( pGraphics
, viewState
, renderState
);
356 tools::sequenceToArgb(renderState
.DeviceColor
)),
357 static_cast< Gdiplus::REAL
>(strokeAttributes
.StrokeWidth
) );
359 // #122683# Switched precedence of pixel offset
360 // mode. Seemingly, polygon stroking needs
361 // PixelOffsetModeNone to achieve visually pleasing
362 // results, whereas all other operations (e.g. polygon
363 // fills, bitmaps) look better with PixelOffsetModeHalf.
364 const Gdiplus::PixelOffsetMode
aOldMode(
365 pGraphics
->GetPixelOffsetMode() );
366 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
368 const bool bIsMiter(rendering::PathJoinType::MITER
== strokeAttributes
.JoinType
);
369 const bool bIsNone(rendering::PathJoinType::NONE
== strokeAttributes
.JoinType
);
372 aPen
.SetMiterLimit( static_cast< Gdiplus::REAL
>(strokeAttributes
.MiterLimit
) );
374 const ::std::vector
< Gdiplus::REAL
>& rDashArray(
375 ::comphelper::sequenceToContainer
< ::std::vector
< Gdiplus::REAL
> >(
376 strokeAttributes
.DashArray
) );
377 if( !rDashArray
.empty() )
379 aPen
.SetDashPattern( &rDashArray
[0],
382 aPen
.SetLineCap( gdiCapFromCap(strokeAttributes
.StartCapType
),
383 gdiCapFromCap(strokeAttributes
.EndCapType
),
384 Gdiplus::DashCapFlat
);
386 aPen
.SetLineJoin( gdiJoinFromJoin(strokeAttributes
.JoinType
) );
388 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
, bIsNone
) );
390 // TODO(E1): Return value
391 Gdiplus::Status hr
= pGraphics
->DrawPath( &aPen
, pPath
.get() );
393 pGraphics
->SetPixelOffsetMode( aOldMode
);
397 "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
400 // TODO(P1): Provide caching here.
401 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
404 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
405 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
406 const rendering::ViewState
& /*viewState*/,
407 const rendering::RenderState
& /*renderState*/,
408 const uno::Sequence
< rendering::Texture
>& /*textures*/,
409 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
412 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
415 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
416 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
417 const rendering::ViewState
& /*viewState*/,
418 const rendering::RenderState
& /*renderState*/,
419 const uno::Sequence
< rendering::Texture
>& /*textures*/,
420 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/,
421 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
424 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
427 uno::Reference
< rendering::XPolyPolygon2D
> CanvasHelper::queryStrokeShapes( const rendering::XCanvas
* /*pCanvas*/,
428 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
429 const rendering::ViewState
& /*viewState*/,
430 const rendering::RenderState
& /*renderState*/,
431 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
434 return uno::Reference
< rendering::XPolyPolygon2D
>(NULL
);
437 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
438 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
439 const rendering::ViewState
& viewState
,
440 const rendering::RenderState
& renderState
)
442 ENSURE_OR_THROW( xPolyPolygon
.is(),
443 "CanvasHelper::fillPolyPolygon: polygon is NULL");
447 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
449 setupGraphicsState( pGraphics
, viewState
, renderState
);
451 Gdiplus::SolidBrush
aBrush(
452 tools::sequenceToArgb(renderState
.DeviceColor
));
454 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
456 // TODO(F1): FillRule
457 ENSURE_OR_THROW( Gdiplus::Ok
== pGraphics
->FillPath( &aBrush
, pPath
.get() ),
458 "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
461 // TODO(P1): Provide caching here.
462 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
465 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
466 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
467 const rendering::ViewState
& /*viewState*/,
468 const rendering::RenderState
& /*renderState*/,
469 const uno::Sequence
< rendering::Texture
>& /*textures*/,
470 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/ )
473 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
476 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* /*pCanvas*/,
477 const rendering::FontRequest
& fontRequest
,
478 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
479 const geometry::Matrix2D
& fontMatrix
)
483 return uno::Reference
< rendering::XCanvasFont
>(
484 new CanvasFont(fontRequest
, extraFontProperties
, fontMatrix
) );
487 return uno::Reference
< rendering::XCanvasFont
>();
490 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* /*pCanvas*/,
491 const rendering::FontInfo
& /*aFilter*/,
492 const uno::Sequence
< beans::PropertyValue
>& /*aFontProperties*/ )
495 return uno::Sequence
< rendering::FontInfo
>();
498 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* /*pCanvas*/,
499 const rendering::StringContext
& text
,
500 const uno::Reference
< rendering::XCanvasFont
>& xFont
,
501 const rendering::ViewState
& viewState
,
502 const rendering::RenderState
& renderState
,
503 sal_Int8
/*textDirection*/ )
505 ENSURE_OR_THROW( xFont
.is(),
506 "CanvasHelper::drawText: font is NULL");
510 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
512 setupGraphicsState( pGraphics
, viewState
, renderState
);
514 Gdiplus::SolidBrush
aBrush(
516 tools::sequenceToArgb(renderState
.DeviceColor
)));
518 CanvasFont::ImplRef
pFont(
519 tools::canvasFontFromXFont(xFont
) );
521 // Move glyphs up, such that output happens at the font
523 Gdiplus::PointF
aPoint( 0.0,
524 static_cast<Gdiplus::REAL
>(-(pFont
->getFont()->GetSize()*
525 pFont
->getCellAscent() /
526 pFont
->getEmHeight())) );
528 // TODO(F1): According to
529 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
530 // we might have to revert to GDI and ExTextOut here,
531 // since GDI+ takes the scalability a little bit too
534 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
535 // DrawDriverString here, and perform layouting myself...
537 Gdiplus::Ok
== pGraphics
->DrawString( reinterpret_cast<LPCWSTR
>(
538 text
.Text
.copy( text
.StartPosition
,
539 text
.Length
).getStr()),
541 pFont
->getFont().get(),
544 "CanvasHelper::drawText(): GDI+ call failed" );
547 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
550 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* /*pCanvas*/,
551 const uno::Reference
< rendering::XTextLayout
>& xLayoutetText
,
552 const rendering::ViewState
& viewState
,
553 const rendering::RenderState
& renderState
)
555 ENSURE_OR_THROW( xLayoutetText
.is(),
556 "CanvasHelper::drawTextLayout: layout is NULL");
560 TextLayout
* pTextLayout
=
561 dynamic_cast< TextLayout
* >( xLayoutetText
.get() );
563 ENSURE_OR_THROW( pTextLayout
,
564 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
566 pTextLayout
->draw( mpGraphicsProvider
->getGraphics(),
574 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
577 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmap( const rendering::XCanvas
* /*pCanvas*/,
578 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
579 const rendering::ViewState
& viewState
,
580 const rendering::RenderState
& renderState
)
582 ENSURE_OR_THROW( xBitmap
.is(),
583 "CanvasHelper::drawBitmap: bitmap is NULL");
587 // check whether one of our own objects - need to retrieve
588 // bitmap _before_ calling
589 // GraphicsProvider::getGraphics(), to avoid locking our
591 BitmapSharedPtr pGdiBitmap
;
592 BitmapProvider
* pBitmap
= dynamic_cast< BitmapProvider
* >(xBitmap
.get());
595 IBitmapSharedPtr
pDXBitmap( pBitmap
->getBitmap() );
597 pGdiBitmap
= pDXBitmap
->getBitmap();
600 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
601 setupGraphicsState( pGraphics
, viewState
, renderState
);
604 tools::drawGdiPlusBitmap(pGraphics
,pGdiBitmap
);
606 tools::drawVCLBitmapFromXBitmap(pGraphics
,
610 // TODO(P1): Provide caching here.
611 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
614 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmapModulated( const rendering::XCanvas
* pCanvas
,
615 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
616 const rendering::ViewState
& viewState
,
617 const rendering::RenderState
& renderState
)
619 ENSURE_OR_THROW( xBitmap
.is(),
620 "CanvasHelper::drawBitmap: bitmap is NULL");
622 // no color set -> this is equivalent to a plain drawBitmap(), then
623 if( renderState
.DeviceColor
.getLength() < 3 )
624 return drawBitmap( pCanvas
, xBitmap
, viewState
, renderState
);
628 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
630 setupGraphicsState( pGraphics
, viewState
, renderState
);
632 BitmapSharedPtr
pBitmap( tools::bitmapFromXBitmap( xBitmap
) );
633 Gdiplus::Rect
aRect( 0, 0,
635 pBitmap
->GetHeight() );
637 // Setup an ImageAttributes with an alpha-modulating
639 const rendering::ARGBColor
& rARGBColor(
640 mpDevice
->getDeviceColorSpace()->convertToARGB(renderState
.DeviceColor
)[0]);
642 Gdiplus::ImageAttributes aImgAttr
;
643 tools::setModulateImageAttributes( aImgAttr
,
650 Gdiplus::Ok
== pGraphics
->DrawImage( pBitmap
.get(),
654 pBitmap
->GetHeight(),
659 "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
662 // TODO(P1): Provide caching here.
663 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
666 uno::Reference
< rendering::XGraphicDevice
> CanvasHelper::getDevice()
668 return uno::Reference
< rendering::XGraphicDevice
>(mpDevice
);
672 // --------------------------------------------------
674 Gdiplus::CompositingMode
CanvasHelper::calcCompositingMode( sal_Int8 nMode
)
676 Gdiplus::CompositingMode
aRet( Gdiplus::CompositingModeSourceOver
);
680 case rendering::CompositeOperation::OVER
:
681 // FALLTHROUGH intended
682 case rendering::CompositeOperation::CLEAR
:
683 aRet
= Gdiplus::CompositingModeSourceOver
;
686 case rendering::CompositeOperation::SOURCE
:
687 aRet
= Gdiplus::CompositingModeSourceCopy
;
690 case rendering::CompositeOperation::DESTINATION
:
691 // FALLTHROUGH intended
692 case rendering::CompositeOperation::UNDER
:
693 // FALLTHROUGH intended
694 case rendering::CompositeOperation::INSIDE
:
695 // FALLTHROUGH intended
696 case rendering::CompositeOperation::INSIDE_REVERSE
:
697 // FALLTHROUGH intended
698 case rendering::CompositeOperation::OUTSIDE
:
699 // FALLTHROUGH intended
700 case rendering::CompositeOperation::OUTSIDE_REVERSE
:
701 // FALLTHROUGH intended
702 case rendering::CompositeOperation::ATOP
:
703 // FALLTHROUGH intended
704 case rendering::CompositeOperation::ATOP_REVERSE
:
705 // FALLTHROUGH intended
706 case rendering::CompositeOperation::XOR
:
707 // FALLTHROUGH intended
708 case rendering::CompositeOperation::ADD
:
709 // FALLTHROUGH intended
710 case rendering::CompositeOperation::SATURATE
:
711 // TODO(F2): Problem, because GDI+ only knows about two compositing modes
712 aRet
= Gdiplus::CompositingModeSourceOver
;
716 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
723 void CanvasHelper::setupGraphicsState( GraphicsSharedPtr
& rGraphics
,
724 const rendering::ViewState
& viewState
,
725 const rendering::RenderState
& renderState
)
727 ENSURE_OR_THROW( needOutput(),
728 "CanvasHelper::setupGraphicsState: primary graphics invalid" );
729 ENSURE_OR_THROW( mpDevice
,
730 "CanvasHelper::setupGraphicsState: reference device invalid" );
732 // setup view transform first. Clipping e.g. depends on it
733 ::basegfx::B2DHomMatrix aTransform
;
734 ::canvas::tools::getViewStateTransform(aTransform
, viewState
);
737 if( !maOutputOffset
.equalZero() )
739 const basegfx::B2DHomMatrix
aOutputOffset(basegfx::tools::createTranslateB2DHomMatrix(
740 maOutputOffset
.getX(), maOutputOffset
.getY()));
741 aTransform
= aOutputOffset
* aTransform
;
744 Gdiplus::Matrix aMatrix
;
745 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix
, aTransform
);
748 Gdiplus::Ok
== rGraphics
->SetTransform( &aMatrix
),
749 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
751 // setup view and render state clipping
753 Gdiplus::Ok
== rGraphics
->ResetClip(),
754 "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
756 if( viewState
.Clip
.is() )
758 GraphicsPathSharedPtr
aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState
.Clip
) );
760 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
761 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
762 // LinePolyPolygon, then)
764 Gdiplus::Ok
== rGraphics
->SetClip( aClipPath
.get(),
765 Gdiplus::CombineModeIntersect
),
766 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
769 // setup overall transform only now. View clip above was relative to
771 ::canvas::tools::mergeViewAndRenderTransform(aTransform
,
776 if( !maOutputOffset
.equalZero() )
778 const basegfx::B2DHomMatrix
aOutputOffset(basegfx::tools::createTranslateB2DHomMatrix(
779 maOutputOffset
.getX(), maOutputOffset
.getY()));
780 aTransform
= aOutputOffset
* aTransform
;
783 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix
, aTransform
);
786 Gdiplus::Ok
== rGraphics
->SetTransform( &aMatrix
),
787 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
789 if( renderState
.Clip
.is() )
791 GraphicsPathSharedPtr
aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState
.Clip
) );
793 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
794 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
795 // LinePolyPolygon, then)
797 Gdiplus::Ok
== rGraphics
->SetClip( aClipPath
.get(),
798 Gdiplus::CombineModeIntersect
),
799 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
803 const Gdiplus::CompositingMode
eCompositing( calcCompositingMode( renderState
.CompositeOperation
) );
805 Gdiplus::Ok
== rGraphics
->SetCompositingMode( eCompositing
),
806 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
809 void CanvasHelper::flush() const
812 mpGraphicsProvider
->getGraphics()->Flush( Gdiplus::FlushIntentionSync
);