1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <canvas/debug.hxx>
22 #include <tools/diagnose_ex.h>
24 #include <rtl/logfile.hxx>
25 #include <rtl/math.hxx>
27 #include <com/sun/star/rendering/TexturingMode.hpp>
28 #include <com/sun/star/rendering/CompositeOperation.hpp>
29 #include <com/sun/star/rendering/RepaintResult.hpp>
30 #include <com/sun/star/rendering/PathCapType.hpp>
31 #include <com/sun/star/rendering/PathJoinType.hpp>
33 #include <basegfx/matrix/b2dhommatrix.hxx>
34 #include <basegfx/point/b2dpoint.hxx>
35 #include <basegfx/tools/canvastools.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <canvas/canvastools.hxx>
41 #include "dx_spritecanvas.hxx"
42 #include "dx_impltools.hxx"
43 #include "dx_vcltools.hxx"
44 #include "dx_canvasfont.hxx"
45 #include "dx_textlayout.hxx"
46 #include "dx_canvashelper.hxx"
51 using namespace ::com::sun::star
;
57 Gdiplus::LineCap
gdiCapFromCap( sal_Int8 nCapType
)
61 case rendering::PathCapType::BUTT
:
62 return Gdiplus::LineCapFlat
;
64 case rendering::PathCapType::ROUND
:
65 return Gdiplus::LineCapRound
;
67 case rendering::PathCapType::SQUARE
:
68 return Gdiplus::LineCapSquare
;
71 ENSURE_OR_THROW( false,
72 "gdiCapFromCap(): Unexpected cap type" );
75 return Gdiplus::LineCapFlat
;
78 Gdiplus::LineJoin
gdiJoinFromJoin( sal_Int8 nJoinType
)
82 case rendering::PathJoinType::NONE
:
83 OSL_FAIL( "gdiJoinFromJoin(): Join NONE not possible, mapping to MITER" );
84 // FALLTHROUGH intended
85 case rendering::PathJoinType::MITER
:
86 return Gdiplus::LineJoinMiter
;
88 case rendering::PathJoinType::ROUND
:
89 return Gdiplus::LineJoinRound
;
91 case rendering::PathJoinType::BEVEL
:
92 return Gdiplus::LineJoinBevel
;
95 ENSURE_OR_THROW( false,
96 "gdiJoinFromJoin(): Unexpected join type" );
99 return Gdiplus::LineJoinMiter
;
103 CanvasHelper::CanvasHelper() :
104 mpGdiPlusUser( GDIPlusUser::createInstance() ),
106 mpGraphicsProvider(),
111 void CanvasHelper::disposing()
113 mpGraphicsProvider
.reset();
115 mpGdiPlusUser
.reset();
118 void CanvasHelper::setDevice( rendering::XGraphicDevice
& rDevice
)
123 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr
& rTarget
)
125 ENSURE_OR_THROW( rTarget
,
126 "CanvasHelper::setTarget(): Invalid target" );
127 ENSURE_OR_THROW( !mpGraphicsProvider
.get(),
128 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
130 mpGraphicsProvider
= rTarget
;
133 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr
& rTarget
,
134 const ::basegfx::B2ISize
& rOutputOffset
)
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
;
142 maOutputOffset
= rOutputOffset
;
145 void CanvasHelper::clear()
149 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
150 Gdiplus::Color aClearColor
= Gdiplus::Color((Gdiplus::ARGB
)Gdiplus::Color::White
);
153 Gdiplus::Ok
== pGraphics
->SetCompositingMode(
154 Gdiplus::CompositingModeSourceCopy
), // force set, don't blend
155 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
157 Gdiplus::Ok
== pGraphics
->Clear( aClearColor
),
158 "CanvasHelper::clear(): GDI+ Clear call failed" );
162 void CanvasHelper::drawPoint( const rendering::XCanvas
* /*pCanvas*/,
163 const geometry::RealPoint2D
& aPoint
,
164 const rendering::ViewState
& viewState
,
165 const rendering::RenderState
& renderState
)
169 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
171 setupGraphicsState( pGraphics
, viewState
, renderState
);
173 Gdiplus::SolidBrush
aBrush(
175 tools::sequenceToArgb(renderState
.DeviceColor
)) );
177 // determine size of one-by-one device pixel ellipse
178 Gdiplus::Matrix aMatrix
;
179 pGraphics
->GetTransform(&aMatrix
);
181 Gdiplus::PointF
vector(1, 1);
182 aMatrix
.TransformVectors(&vector
);
184 // paint a one-by-one circle, with the given point
185 // in the middle (rounded to float)
187 Gdiplus::Ok
== pGraphics
->FillEllipse( &aBrush
,
189 Gdiplus::REAL(aPoint
.X
),
190 Gdiplus::REAL(aPoint
.Y
),
191 Gdiplus::REAL(vector
.X
),
192 Gdiplus::REAL(vector
.Y
) ),
193 "CanvasHelper::drawPoint(): GDI+ call failed" );
197 void CanvasHelper::drawLine( const rendering::XCanvas
* /*pCanvas*/,
198 const geometry::RealPoint2D
& aStartPoint
,
199 const geometry::RealPoint2D
& aEndPoint
,
200 const rendering::ViewState
& viewState
,
201 const rendering::RenderState
& renderState
)
205 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
207 setupGraphicsState( pGraphics
, viewState
, renderState
);
211 tools::sequenceToArgb(renderState
.DeviceColor
)),
212 Gdiplus::REAL(0.0) );
214 // #122683# Switched precedence of pixel offset
215 // mode. Seemingly, polygon stroking needs
216 // PixelOffsetModeNone to achieve visually pleasing
217 // results, whereas all other operations (e.g. polygon
218 // fills, bitmaps) look better with PixelOffsetModeHalf.
219 const Gdiplus::PixelOffsetMode
aOldMode(
220 pGraphics
->GetPixelOffsetMode() );
221 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
223 Gdiplus::Status hr
= pGraphics
->DrawLine( &aPen
,
224 Gdiplus::REAL(aStartPoint
.X
), // disambiguate call
225 Gdiplus::REAL(aStartPoint
.Y
),
226 Gdiplus::REAL(aEndPoint
.X
),
227 Gdiplus::REAL(aEndPoint
.Y
) );
228 pGraphics
->SetPixelOffsetMode( aOldMode
);
232 "CanvasHelper::drawLine(): GDI+ call failed" );
236 void CanvasHelper::drawBezier( const rendering::XCanvas
* /*pCanvas*/,
237 const geometry::RealBezierSegment2D
& aBezierSegment
,
238 const geometry::RealPoint2D
& aEndPoint
,
239 const rendering::ViewState
& viewState
,
240 const rendering::RenderState
& renderState
)
244 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
246 setupGraphicsState( pGraphics
, viewState
, renderState
);
250 tools::sequenceToArgb(renderState
.DeviceColor
)),
251 Gdiplus::REAL(0.0) );
253 // #122683# Switched precedence of pixel offset
254 // mode. Seemingly, polygon stroking needs
255 // PixelOffsetModeNone to achieve visually pleasing
256 // results, whereas all other operations (e.g. polygon
257 // fills, bitmaps) look better with PixelOffsetModeHalf.
258 const Gdiplus::PixelOffsetMode
aOldMode(
259 pGraphics
->GetPixelOffsetMode() );
260 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
262 Gdiplus::Status hr
= pGraphics
->DrawBezier( &aPen
,
263 Gdiplus::REAL(aBezierSegment
.Px
), // disambiguate call
264 Gdiplus::REAL(aBezierSegment
.Py
),
265 Gdiplus::REAL(aBezierSegment
.C1x
),
266 Gdiplus::REAL(aBezierSegment
.C1y
),
267 Gdiplus::REAL(aEndPoint
.X
),
268 Gdiplus::REAL(aEndPoint
.Y
),
269 Gdiplus::REAL(aBezierSegment
.C2x
),
270 Gdiplus::REAL(aBezierSegment
.C2y
) );
272 pGraphics
->SetPixelOffsetMode( aOldMode
);
276 "CanvasHelper::drawBezier(): GDI+ call failed" );
280 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
281 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
282 const rendering::ViewState
& viewState
,
283 const rendering::RenderState
& renderState
)
285 ENSURE_OR_THROW( xPolyPolygon
.is(),
286 "CanvasHelper::drawPolyPolygon: polygon is NULL");
290 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
292 setupGraphicsState( pGraphics
, viewState
, renderState
);
296 tools::sequenceToArgb(renderState
.DeviceColor
)),
297 Gdiplus::REAL(0.0) );
299 // #122683# Switched precedence of pixel offset
300 // mode. Seemingly, polygon stroking needs
301 // PixelOffsetModeNone to achieve visually pleasing
302 // results, whereas all other operations (e.g. polygon
303 // fills, bitmaps) look better with PixelOffsetModeHalf.
304 const Gdiplus::PixelOffsetMode
aOldMode(
305 pGraphics
->GetPixelOffsetMode() );
306 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
308 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
310 // TODO(E1): Return value
311 Gdiplus::Status hr
= pGraphics
->DrawPath( &aPen
, pPath
.get() );
313 pGraphics
->SetPixelOffsetMode( aOldMode
);
317 "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
320 // TODO(P1): Provide caching here.
321 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
324 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokePolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
325 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
326 const rendering::ViewState
& viewState
,
327 const rendering::RenderState
& renderState
,
328 const rendering::StrokeAttributes
& strokeAttributes
)
330 ENSURE_OR_THROW( xPolyPolygon
.is(),
331 "CanvasHelper::drawPolyPolygon: polygon is NULL");
335 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
337 setupGraphicsState( pGraphics
, viewState
, renderState
);
345 tools::sequenceToArgb(renderState
.DeviceColor
)),
346 static_cast< Gdiplus::REAL
>(strokeAttributes
.StrokeWidth
) );
348 // #122683# Switched precedence of pixel offset
349 // mode. Seemingly, polygon stroking needs
350 // PixelOffsetModeNone to achieve visually pleasing
351 // results, whereas all other operations (e.g. polygon
352 // fills, bitmaps) look better with PixelOffsetModeHalf.
353 const Gdiplus::PixelOffsetMode
aOldMode(
354 pGraphics
->GetPixelOffsetMode() );
355 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
357 const bool bIsMiter(rendering::PathJoinType::MITER
== strokeAttributes
.JoinType
);
358 const bool bIsNone(rendering::PathJoinType::NONE
== strokeAttributes
.JoinType
);
361 aPen
.SetMiterLimit( static_cast< Gdiplus::REAL
>(strokeAttributes
.MiterLimit
) );
363 const ::std::vector
< Gdiplus::REAL
>& rDashArray(
364 ::comphelper::sequenceToContainer
< ::std::vector
< Gdiplus::REAL
> >(
365 strokeAttributes
.DashArray
) );
366 if( !rDashArray
.empty() )
368 aPen
.SetDashPattern( &rDashArray
[0],
371 aPen
.SetLineCap( gdiCapFromCap(strokeAttributes
.StartCapType
),
372 gdiCapFromCap(strokeAttributes
.EndCapType
),
373 Gdiplus::DashCapFlat
);
375 aPen
.SetLineJoin( gdiJoinFromJoin(strokeAttributes
.JoinType
) );
377 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
, bIsNone
) );
379 // TODO(E1): Return value
380 Gdiplus::Status hr
= pGraphics
->DrawPath( &aPen
, pPath
.get() );
382 pGraphics
->SetPixelOffsetMode( aOldMode
);
386 "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
389 // TODO(P1): Provide caching here.
390 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
393 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
394 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
395 const rendering::ViewState
& /*viewState*/,
396 const rendering::RenderState
& /*renderState*/,
397 const uno::Sequence
< rendering::Texture
>& /*textures*/,
398 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
401 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
404 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTextureMappedPolyPolygon( 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 uno::Reference
< geometry::XMapping2D
>& /*xMapping*/,
410 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
413 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
416 uno::Reference
< rendering::XPolyPolygon2D
> CanvasHelper::queryStrokeShapes( const rendering::XCanvas
* /*pCanvas*/,
417 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
418 const rendering::ViewState
& /*viewState*/,
419 const rendering::RenderState
& /*renderState*/,
420 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
423 return uno::Reference
< rendering::XPolyPolygon2D
>(NULL
);
426 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
427 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
428 const rendering::ViewState
& viewState
,
429 const rendering::RenderState
& renderState
)
431 ENSURE_OR_THROW( xPolyPolygon
.is(),
432 "CanvasHelper::fillPolyPolygon: polygon is NULL");
436 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
438 setupGraphicsState( pGraphics
, viewState
, renderState
);
440 Gdiplus::SolidBrush
aBrush(
441 tools::sequenceToArgb(renderState
.DeviceColor
));
443 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
445 // TODO(F1): FillRule
446 ENSURE_OR_THROW( Gdiplus::Ok
== pGraphics
->FillPath( &aBrush
, pPath
.get() ),
447 "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
450 // TODO(P1): Provide caching here.
451 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
454 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
455 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
456 const rendering::ViewState
& /*viewState*/,
457 const rendering::RenderState
& /*renderState*/,
458 const uno::Sequence
< rendering::Texture
>& /*textures*/,
459 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/ )
462 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
465 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* /*pCanvas*/,
466 const rendering::FontRequest
& fontRequest
,
467 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
468 const geometry::Matrix2D
& fontMatrix
)
472 return uno::Reference
< rendering::XCanvasFont
>(
473 new CanvasFont(fontRequest
, extraFontProperties
, fontMatrix
) );
476 return uno::Reference
< rendering::XCanvasFont
>();
479 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* /*pCanvas*/,
480 const rendering::FontInfo
& /*aFilter*/,
481 const uno::Sequence
< beans::PropertyValue
>& /*aFontProperties*/ )
484 return uno::Sequence
< rendering::FontInfo
>();
487 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* /*pCanvas*/,
488 const rendering::StringContext
& text
,
489 const uno::Reference
< rendering::XCanvasFont
>& xFont
,
490 const rendering::ViewState
& viewState
,
491 const rendering::RenderState
& renderState
,
492 sal_Int8
/*textDirection*/ )
494 ENSURE_OR_THROW( xFont
.is(),
495 "CanvasHelper::drawText: font is NULL");
499 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
501 setupGraphicsState( pGraphics
, viewState
, renderState
);
503 Gdiplus::SolidBrush
aBrush(
505 tools::sequenceToArgb(renderState
.DeviceColor
)));
507 CanvasFont::ImplRef
pFont(
508 tools::canvasFontFromXFont(xFont
) );
510 // Move glyphs up, such that output happens at the font
512 Gdiplus::PointF
aPoint( 0.0,
513 static_cast<Gdiplus::REAL
>(-(pFont
->getFont()->GetSize()*
514 pFont
->getCellAscent() /
515 pFont
->getEmHeight())) );
517 // TODO(F1): According to
518 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
519 // we might have to revert to GDI and ExTextOut here,
520 // since GDI+ takes the scalability a little bit too
523 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
524 // DrawDriverString here, and perform layouting myself...
526 Gdiplus::Ok
== pGraphics
->DrawString( reinterpret_cast<LPCWSTR
>(
527 text
.Text
.copy( text
.StartPosition
,
528 text
.Length
).getStr()),
530 pFont
->getFont().get(),
533 "CanvasHelper::drawText(): GDI+ call failed" );
536 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
539 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* /*pCanvas*/,
540 const uno::Reference
< rendering::XTextLayout
>& xLayoutetText
,
541 const rendering::ViewState
& viewState
,
542 const rendering::RenderState
& renderState
)
544 ENSURE_OR_THROW( xLayoutetText
.is(),
545 "CanvasHelper::drawTextLayout: layout is NULL");
549 TextLayout
* pTextLayout
=
550 dynamic_cast< TextLayout
* >( xLayoutetText
.get() );
552 ENSURE_OR_THROW( pTextLayout
,
553 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
555 pTextLayout
->draw( mpGraphicsProvider
->getGraphics(),
563 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
566 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmap( const rendering::XCanvas
* /*pCanvas*/,
567 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
568 const rendering::ViewState
& viewState
,
569 const rendering::RenderState
& renderState
)
571 ENSURE_OR_THROW( xBitmap
.is(),
572 "CanvasHelper::drawBitmap: bitmap is NULL");
576 // check whether one of our own objects - need to retrieve
577 // bitmap _before_ calling
578 // GraphicsProvider::getGraphics(), to avoid locking our
580 BitmapSharedPtr pGdiBitmap
;
581 BitmapProvider
* pBitmap
= dynamic_cast< BitmapProvider
* >(xBitmap
.get());
584 IBitmapSharedPtr
pDXBitmap( pBitmap
->getBitmap() );
586 pGdiBitmap
= pDXBitmap
->getBitmap();
589 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
590 setupGraphicsState( pGraphics
, viewState
, renderState
);
593 tools::drawGdiPlusBitmap(pGraphics
,pGdiBitmap
);
595 tools::drawVCLBitmapFromXBitmap(pGraphics
,
599 // TODO(P1): Provide caching here.
600 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
603 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmapModulated( const rendering::XCanvas
* pCanvas
,
604 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
605 const rendering::ViewState
& viewState
,
606 const rendering::RenderState
& renderState
)
608 ENSURE_OR_THROW( xBitmap
.is(),
609 "CanvasHelper::drawBitmap: bitmap is NULL");
611 // no color set -> this is equivalent to a plain drawBitmap(), then
612 if( renderState
.DeviceColor
.getLength() < 3 )
613 return drawBitmap( pCanvas
, xBitmap
, viewState
, renderState
);
617 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
619 setupGraphicsState( pGraphics
, viewState
, renderState
);
621 BitmapSharedPtr
pBitmap( tools::bitmapFromXBitmap( xBitmap
) );
622 Gdiplus::Rect
aRect( 0, 0,
624 pBitmap
->GetHeight() );
626 // Setup an ImageAttributes with an alpha-modulating
628 const rendering::ARGBColor
& rARGBColor(
629 mpDevice
->getDeviceColorSpace()->convertToARGB(renderState
.DeviceColor
)[0]);
631 Gdiplus::ImageAttributes aImgAttr
;
632 tools::setModulateImageAttributes( aImgAttr
,
639 Gdiplus::Ok
== pGraphics
->DrawImage( pBitmap
.get(),
643 pBitmap
->GetHeight(),
648 "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
651 // TODO(P1): Provide caching here.
652 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
655 uno::Reference
< rendering::XGraphicDevice
> CanvasHelper::getDevice()
657 return uno::Reference
< rendering::XGraphicDevice
>(mpDevice
);
661 // --------------------------------------------------
663 Gdiplus::CompositingMode
CanvasHelper::calcCompositingMode( sal_Int8 nMode
)
665 Gdiplus::CompositingMode
aRet( Gdiplus::CompositingModeSourceOver
);
669 case rendering::CompositeOperation::OVER
:
670 // FALLTHROUGH intended
671 case rendering::CompositeOperation::CLEAR
:
672 aRet
= Gdiplus::CompositingModeSourceOver
;
675 case rendering::CompositeOperation::SOURCE
:
676 aRet
= Gdiplus::CompositingModeSourceCopy
;
679 case rendering::CompositeOperation::DESTINATION
:
680 // FALLTHROUGH intended
681 case rendering::CompositeOperation::UNDER
:
682 // FALLTHROUGH intended
683 case rendering::CompositeOperation::INSIDE
:
684 // FALLTHROUGH intended
685 case rendering::CompositeOperation::INSIDE_REVERSE
:
686 // FALLTHROUGH intended
687 case rendering::CompositeOperation::OUTSIDE
:
688 // FALLTHROUGH intended
689 case rendering::CompositeOperation::OUTSIDE_REVERSE
:
690 // FALLTHROUGH intended
691 case rendering::CompositeOperation::ATOP
:
692 // FALLTHROUGH intended
693 case rendering::CompositeOperation::ATOP_REVERSE
:
694 // FALLTHROUGH intended
695 case rendering::CompositeOperation::XOR
:
696 // FALLTHROUGH intended
697 case rendering::CompositeOperation::ADD
:
698 // FALLTHROUGH intended
699 case rendering::CompositeOperation::SATURATE
:
700 // TODO(F2): Problem, because GDI+ only knows about two compositing modes
701 aRet
= Gdiplus::CompositingModeSourceOver
;
705 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
712 void CanvasHelper::setupGraphicsState( GraphicsSharedPtr
& rGraphics
,
713 const rendering::ViewState
& viewState
,
714 const rendering::RenderState
& renderState
)
716 ENSURE_OR_THROW( needOutput(),
717 "CanvasHelper::setupGraphicsState: primary graphics invalid" );
718 ENSURE_OR_THROW( mpDevice
,
719 "CanvasHelper::setupGraphicsState: reference device invalid" );
721 // setup view transform first. Clipping e.g. depends on it
722 ::basegfx::B2DHomMatrix aTransform
;
723 ::canvas::tools::getViewStateTransform(aTransform
, viewState
);
726 if( !maOutputOffset
.equalZero() )
728 const basegfx::B2DHomMatrix
aOutputOffset(basegfx::tools::createTranslateB2DHomMatrix(
729 maOutputOffset
.getX(), maOutputOffset
.getY()));
730 aTransform
= aOutputOffset
* aTransform
;
733 Gdiplus::Matrix aMatrix
;
734 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix
, aTransform
);
737 Gdiplus::Ok
== rGraphics
->SetTransform( &aMatrix
),
738 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
740 // setup view and render state clipping
742 Gdiplus::Ok
== rGraphics
->ResetClip(),
743 "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
745 if( viewState
.Clip
.is() )
747 GraphicsPathSharedPtr
aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState
.Clip
) );
749 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
750 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
751 // LinePolyPolygon, then)
753 Gdiplus::Ok
== rGraphics
->SetClip( aClipPath
.get(),
754 Gdiplus::CombineModeIntersect
),
755 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
758 // setup overall transform only now. View clip above was relative to
760 ::canvas::tools::mergeViewAndRenderTransform(aTransform
,
765 if( !maOutputOffset
.equalZero() )
767 const basegfx::B2DHomMatrix
aOutputOffset(basegfx::tools::createTranslateB2DHomMatrix(
768 maOutputOffset
.getX(), maOutputOffset
.getY()));
769 aTransform
= aOutputOffset
* aTransform
;
772 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix
, aTransform
);
775 Gdiplus::Ok
== rGraphics
->SetTransform( &aMatrix
),
776 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
778 if( renderState
.Clip
.is() )
780 GraphicsPathSharedPtr
aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState
.Clip
) );
782 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
783 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
784 // LinePolyPolygon, then)
786 Gdiplus::Ok
== rGraphics
->SetClip( aClipPath
.get(),
787 Gdiplus::CombineModeIntersect
),
788 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
792 const Gdiplus::CompositingMode
eCompositing( calcCompositingMode( renderState
.CompositeOperation
) );
794 Gdiplus::Ok
== rGraphics
->SetCompositingMode( eCompositing
),
795 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
798 void CanvasHelper::flush() const
801 mpGraphicsProvider
->getGraphics()->Flush( Gdiplus::FlushIntentionSync
);
805 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */