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/math.hxx>
26 #include <com/sun/star/rendering/TexturingMode.hpp>
27 #include <com/sun/star/rendering/CompositeOperation.hpp>
28 #include <com/sun/star/rendering/RepaintResult.hpp>
29 #include <com/sun/star/rendering/PathCapType.hpp>
30 #include <com/sun/star/rendering/PathJoinType.hpp>
32 #include <basegfx/matrix/b2dhommatrix.hxx>
33 #include <basegfx/point/b2dpoint.hxx>
34 #include <basegfx/tools/canvastools.hxx>
35 #include <basegfx/matrix/b2dhommatrixtools.hxx>
37 #include <comphelper/sequence.hxx>
38 #include <canvas/canvastools.hxx>
40 #include "dx_spritecanvas.hxx"
41 #include "dx_impltools.hxx"
42 #include "dx_vcltools.hxx"
43 #include "dx_canvasfont.hxx"
44 #include "dx_textlayout.hxx"
45 #include "dx_canvashelper.hxx"
50 using namespace ::com::sun::star
;
56 Gdiplus::LineCap
gdiCapFromCap( sal_Int8 nCapType
)
60 case rendering::PathCapType::BUTT
:
61 return Gdiplus::LineCapFlat
;
63 case rendering::PathCapType::ROUND
:
64 return Gdiplus::LineCapRound
;
66 case rendering::PathCapType::SQUARE
:
67 return Gdiplus::LineCapSquare
;
70 ENSURE_OR_THROW( false,
71 "gdiCapFromCap(): Unexpected cap type" );
74 return Gdiplus::LineCapFlat
;
77 Gdiplus::LineJoin
gdiJoinFromJoin( sal_Int8 nJoinType
)
81 case rendering::PathJoinType::NONE
:
82 SAL_WARN( "canvas.directx", "gdiJoinFromJoin(): Join NONE not possible, mapping to MITER" );
83 // FALLTHROUGH intended
84 case rendering::PathJoinType::MITER
:
85 return Gdiplus::LineJoinMiter
;
87 case rendering::PathJoinType::ROUND
:
88 return Gdiplus::LineJoinRound
;
90 case rendering::PathJoinType::BEVEL
:
91 return Gdiplus::LineJoinBevel
;
94 ENSURE_OR_THROW( false,
95 "gdiJoinFromJoin(): Unexpected join type" );
98 return Gdiplus::LineJoinMiter
;
102 CanvasHelper::CanvasHelper() :
103 mpGdiPlusUser( GDIPlusUser::createInstance() ),
105 mpGraphicsProvider(),
110 void CanvasHelper::disposing()
112 mpGraphicsProvider
.reset();
114 mpGdiPlusUser
.reset();
117 void CanvasHelper::setDevice( rendering::XGraphicDevice
& rDevice
)
122 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr
& rTarget
)
124 ENSURE_OR_THROW( rTarget
,
125 "CanvasHelper::setTarget(): Invalid target" );
126 ENSURE_OR_THROW( !mpGraphicsProvider
.get(),
127 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
129 mpGraphicsProvider
= rTarget
;
132 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr
& rTarget
,
133 const ::basegfx::B2ISize
& rOutputOffset
)
135 ENSURE_OR_THROW( rTarget
,
136 "CanvasHelper::setTarget(): invalid target" );
137 ENSURE_OR_THROW( !mpGraphicsProvider
.get(),
138 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
140 mpGraphicsProvider
= rTarget
;
141 maOutputOffset
= rOutputOffset
;
144 void CanvasHelper::clear()
148 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
149 Gdiplus::Color aClearColor
= Gdiplus::Color((Gdiplus::ARGB
)Gdiplus::Color::White
);
152 Gdiplus::Ok
== pGraphics
->SetCompositingMode(
153 Gdiplus::CompositingModeSourceCopy
), // force set, don't blend
154 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
156 Gdiplus::Ok
== pGraphics
->Clear( aClearColor
),
157 "CanvasHelper::clear(): GDI+ Clear call failed" );
161 void CanvasHelper::drawPoint( const rendering::XCanvas
* /*pCanvas*/,
162 const geometry::RealPoint2D
& aPoint
,
163 const rendering::ViewState
& viewState
,
164 const rendering::RenderState
& renderState
)
168 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
170 setupGraphicsState( pGraphics
, viewState
, renderState
);
172 Gdiplus::SolidBrush
aBrush(
174 tools::sequenceToArgb(renderState
.DeviceColor
)) );
176 // determine size of one-by-one device pixel ellipse
177 Gdiplus::Matrix aMatrix
;
178 pGraphics
->GetTransform(&aMatrix
);
180 Gdiplus::PointF
vector(1, 1);
181 aMatrix
.TransformVectors(&vector
);
183 // paint a one-by-one circle, with the given point
184 // in the middle (rounded to float)
186 Gdiplus::Ok
== pGraphics
->FillEllipse( &aBrush
,
188 Gdiplus::REAL(aPoint
.X
),
189 Gdiplus::REAL(aPoint
.Y
),
190 Gdiplus::REAL(vector
.X
),
191 Gdiplus::REAL(vector
.Y
) ),
192 "CanvasHelper::drawPoint(): GDI+ call failed" );
196 void CanvasHelper::drawLine( const rendering::XCanvas
* /*pCanvas*/,
197 const geometry::RealPoint2D
& aStartPoint
,
198 const geometry::RealPoint2D
& aEndPoint
,
199 const rendering::ViewState
& viewState
,
200 const rendering::RenderState
& renderState
)
204 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
206 setupGraphicsState( pGraphics
, viewState
, renderState
);
210 tools::sequenceToArgb(renderState
.DeviceColor
)),
211 Gdiplus::REAL(0.0) );
213 // #122683# Switched precedence of pixel offset
214 // mode. Seemingly, polygon stroking needs
215 // PixelOffsetModeNone to achieve visually pleasing
216 // results, whereas all other operations (e.g. polygon
217 // fills, bitmaps) look better with PixelOffsetModeHalf.
218 const Gdiplus::PixelOffsetMode
aOldMode(
219 pGraphics
->GetPixelOffsetMode() );
220 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
222 Gdiplus::Status hr
= pGraphics
->DrawLine( &aPen
,
223 Gdiplus::REAL(aStartPoint
.X
), // disambiguate call
224 Gdiplus::REAL(aStartPoint
.Y
),
225 Gdiplus::REAL(aEndPoint
.X
),
226 Gdiplus::REAL(aEndPoint
.Y
) );
227 pGraphics
->SetPixelOffsetMode( aOldMode
);
231 "CanvasHelper::drawLine(): GDI+ call failed" );
235 void CanvasHelper::drawBezier( const rendering::XCanvas
* /*pCanvas*/,
236 const geometry::RealBezierSegment2D
& aBezierSegment
,
237 const geometry::RealPoint2D
& aEndPoint
,
238 const rendering::ViewState
& viewState
,
239 const rendering::RenderState
& renderState
)
243 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
245 setupGraphicsState( pGraphics
, viewState
, renderState
);
249 tools::sequenceToArgb(renderState
.DeviceColor
)),
250 Gdiplus::REAL(0.0) );
252 // #122683# Switched precedence of pixel offset
253 // mode. Seemingly, polygon stroking needs
254 // PixelOffsetModeNone to achieve visually pleasing
255 // results, whereas all other operations (e.g. polygon
256 // fills, bitmaps) look better with PixelOffsetModeHalf.
257 const Gdiplus::PixelOffsetMode
aOldMode(
258 pGraphics
->GetPixelOffsetMode() );
259 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
261 Gdiplus::Status hr
= pGraphics
->DrawBezier( &aPen
,
262 Gdiplus::REAL(aBezierSegment
.Px
), // disambiguate call
263 Gdiplus::REAL(aBezierSegment
.Py
),
264 Gdiplus::REAL(aBezierSegment
.C1x
),
265 Gdiplus::REAL(aBezierSegment
.C1y
),
266 Gdiplus::REAL(aEndPoint
.X
),
267 Gdiplus::REAL(aEndPoint
.Y
),
268 Gdiplus::REAL(aBezierSegment
.C2x
),
269 Gdiplus::REAL(aBezierSegment
.C2y
) );
271 pGraphics
->SetPixelOffsetMode( aOldMode
);
275 "CanvasHelper::drawBezier(): GDI+ call failed" );
279 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
280 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
281 const rendering::ViewState
& viewState
,
282 const rendering::RenderState
& renderState
)
284 ENSURE_OR_THROW( xPolyPolygon
.is(),
285 "CanvasHelper::drawPolyPolygon: polygon is NULL");
289 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
291 setupGraphicsState( pGraphics
, viewState
, renderState
);
295 tools::sequenceToArgb(renderState
.DeviceColor
)),
296 Gdiplus::REAL(0.0) );
298 // #122683# Switched precedence of pixel offset
299 // mode. Seemingly, polygon stroking needs
300 // PixelOffsetModeNone to achieve visually pleasing
301 // results, whereas all other operations (e.g. polygon
302 // fills, bitmaps) look better with PixelOffsetModeHalf.
303 const Gdiplus::PixelOffsetMode
aOldMode(
304 pGraphics
->GetPixelOffsetMode() );
305 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
307 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
309 // TODO(E1): Return value
310 Gdiplus::Status hr
= pGraphics
->DrawPath( &aPen
, pPath
.get() );
312 pGraphics
->SetPixelOffsetMode( aOldMode
);
316 "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
319 // TODO(P1): Provide caching here.
320 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
323 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokePolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
324 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
325 const rendering::ViewState
& viewState
,
326 const rendering::RenderState
& renderState
,
327 const rendering::StrokeAttributes
& strokeAttributes
)
329 ENSURE_OR_THROW( xPolyPolygon
.is(),
330 "CanvasHelper::drawPolyPolygon: polygon is NULL");
334 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
336 setupGraphicsState( pGraphics
, viewState
, renderState
);
344 tools::sequenceToArgb(renderState
.DeviceColor
)),
345 static_cast< Gdiplus::REAL
>(strokeAttributes
.StrokeWidth
) );
347 // #122683# Switched precedence of pixel offset
348 // mode. Seemingly, polygon stroking needs
349 // PixelOffsetModeNone to achieve visually pleasing
350 // results, whereas all other operations (e.g. polygon
351 // fills, bitmaps) look better with PixelOffsetModeHalf.
352 const Gdiplus::PixelOffsetMode
aOldMode(
353 pGraphics
->GetPixelOffsetMode() );
354 pGraphics
->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone
);
356 const bool bIsMiter(rendering::PathJoinType::MITER
== strokeAttributes
.JoinType
);
357 const bool bIsNone(rendering::PathJoinType::NONE
== strokeAttributes
.JoinType
);
360 aPen
.SetMiterLimit( static_cast< Gdiplus::REAL
>(strokeAttributes
.MiterLimit
) );
362 const ::std::vector
< Gdiplus::REAL
>& rDashArray(
363 ::comphelper::sequenceToContainer
< ::std::vector
< Gdiplus::REAL
> >(
364 strokeAttributes
.DashArray
) );
365 if( !rDashArray
.empty() )
367 aPen
.SetDashPattern( &rDashArray
[0],
370 aPen
.SetLineCap( gdiCapFromCap(strokeAttributes
.StartCapType
),
371 gdiCapFromCap(strokeAttributes
.EndCapType
),
372 Gdiplus::DashCapFlat
);
374 aPen
.SetLineJoin( gdiJoinFromJoin(strokeAttributes
.JoinType
) );
376 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
, bIsNone
) );
378 // TODO(E1): Return value
379 Gdiplus::Status hr
= pGraphics
->DrawPath( &aPen
, pPath
.get() );
381 pGraphics
->SetPixelOffsetMode( aOldMode
);
385 "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
388 // TODO(P1): Provide caching here.
389 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
392 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
393 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
394 const rendering::ViewState
& /*viewState*/,
395 const rendering::RenderState
& /*renderState*/,
396 const uno::Sequence
< rendering::Texture
>& /*textures*/,
397 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
400 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
403 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
404 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
405 const rendering::ViewState
& /*viewState*/,
406 const rendering::RenderState
& /*renderState*/,
407 const uno::Sequence
< rendering::Texture
>& /*textures*/,
408 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/,
409 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
412 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
415 uno::Reference
< rendering::XPolyPolygon2D
> CanvasHelper::queryStrokeShapes( const rendering::XCanvas
* /*pCanvas*/,
416 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
417 const rendering::ViewState
& /*viewState*/,
418 const rendering::RenderState
& /*renderState*/,
419 const rendering::StrokeAttributes
& /*strokeAttributes*/ )
422 return uno::Reference
< rendering::XPolyPolygon2D
>(NULL
);
425 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
426 const uno::Reference
< rendering::XPolyPolygon2D
>& xPolyPolygon
,
427 const rendering::ViewState
& viewState
,
428 const rendering::RenderState
& renderState
)
430 ENSURE_OR_THROW( xPolyPolygon
.is(),
431 "CanvasHelper::fillPolyPolygon: polygon is NULL");
435 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
437 setupGraphicsState( pGraphics
, viewState
, renderState
);
439 Gdiplus::SolidBrush
aBrush(
440 tools::sequenceToArgb(renderState
.DeviceColor
));
442 GraphicsPathSharedPtr
pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon
) );
444 // TODO(F1): FillRule
445 ENSURE_OR_THROW( Gdiplus::Ok
== pGraphics
->FillPath( &aBrush
, pPath
.get() ),
446 "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
449 // TODO(P1): Provide caching here.
450 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
453 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas
* /*pCanvas*/,
454 const uno::Reference
< rendering::XPolyPolygon2D
>& /*xPolyPolygon*/,
455 const rendering::ViewState
& /*viewState*/,
456 const rendering::RenderState
& /*renderState*/,
457 const uno::Sequence
< rendering::Texture
>& /*textures*/,
458 const uno::Reference
< geometry::XMapping2D
>& /*xMapping*/ )
461 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
464 uno::Reference
< rendering::XCanvasFont
> CanvasHelper::createFont( const rendering::XCanvas
* /*pCanvas*/,
465 const rendering::FontRequest
& fontRequest
,
466 const uno::Sequence
< beans::PropertyValue
>& extraFontProperties
,
467 const geometry::Matrix2D
& fontMatrix
)
471 return uno::Reference
< rendering::XCanvasFont
>(
472 new CanvasFont(fontRequest
, extraFontProperties
, fontMatrix
) );
475 return uno::Reference
< rendering::XCanvasFont
>();
478 uno::Sequence
< rendering::FontInfo
> CanvasHelper::queryAvailableFonts( const rendering::XCanvas
* /*pCanvas*/,
479 const rendering::FontInfo
& /*aFilter*/,
480 const uno::Sequence
< beans::PropertyValue
>& /*aFontProperties*/ )
483 return uno::Sequence
< rendering::FontInfo
>();
486 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawText( const rendering::XCanvas
* /*pCanvas*/,
487 const rendering::StringContext
& text
,
488 const uno::Reference
< rendering::XCanvasFont
>& xFont
,
489 const rendering::ViewState
& viewState
,
490 const rendering::RenderState
& renderState
,
491 sal_Int8
/*textDirection*/ )
493 ENSURE_OR_THROW( xFont
.is(),
494 "CanvasHelper::drawText: font is NULL");
498 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
500 setupGraphicsState( pGraphics
, viewState
, renderState
);
502 Gdiplus::SolidBrush
aBrush(
504 tools::sequenceToArgb(renderState
.DeviceColor
)));
506 CanvasFont::ImplRef
pFont(
507 tools::canvasFontFromXFont(xFont
) );
509 // Move glyphs up, such that output happens at the font
511 Gdiplus::PointF
aPoint( 0.0,
512 static_cast<Gdiplus::REAL
>(-(pFont
->getFont()->GetSize()*
513 pFont
->getCellAscent() /
514 pFont
->getEmHeight())) );
516 // TODO(F1): According to
517 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
518 // we might have to revert to GDI and ExTextOut here,
519 // since GDI+ takes the scalability a little bit too
522 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
523 // DrawDriverString here, and perform layouting myself...
525 Gdiplus::Ok
== pGraphics
->DrawString( reinterpret_cast<LPCWSTR
>(
526 text
.Text
.copy( text
.StartPosition
,
527 text
.Length
).getStr()),
529 pFont
->getFont().get(),
532 "CanvasHelper::drawText(): GDI+ call failed" );
535 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
538 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawTextLayout( const rendering::XCanvas
* /*pCanvas*/,
539 const uno::Reference
< rendering::XTextLayout
>& xLayoutetText
,
540 const rendering::ViewState
& viewState
,
541 const rendering::RenderState
& renderState
)
543 ENSURE_OR_THROW( xLayoutetText
.is(),
544 "CanvasHelper::drawTextLayout: layout is NULL");
548 TextLayout
* pTextLayout
=
549 dynamic_cast< TextLayout
* >( xLayoutetText
.get() );
551 ENSURE_OR_THROW( pTextLayout
,
552 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
554 pTextLayout
->draw( mpGraphicsProvider
->getGraphics(),
562 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
565 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmap( const rendering::XCanvas
* /*pCanvas*/,
566 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
567 const rendering::ViewState
& viewState
,
568 const rendering::RenderState
& renderState
)
570 ENSURE_OR_THROW( xBitmap
.is(),
571 "CanvasHelper::drawBitmap: bitmap is NULL");
575 // check whether one of our own objects - need to retrieve
576 // bitmap _before_ calling
577 // GraphicsProvider::getGraphics(), to avoid locking our
579 BitmapSharedPtr pGdiBitmap
;
580 BitmapProvider
* pBitmap
= dynamic_cast< BitmapProvider
* >(xBitmap
.get());
583 IBitmapSharedPtr
pDXBitmap( pBitmap
->getBitmap() );
585 pGdiBitmap
= pDXBitmap
->getBitmap();
588 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
589 setupGraphicsState( pGraphics
, viewState
, renderState
);
592 tools::drawGdiPlusBitmap(pGraphics
,pGdiBitmap
);
594 tools::drawVCLBitmapFromXBitmap(pGraphics
,
598 // TODO(P1): Provide caching here.
599 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
602 uno::Reference
< rendering::XCachedPrimitive
> CanvasHelper::drawBitmapModulated( const rendering::XCanvas
* pCanvas
,
603 const uno::Reference
< rendering::XBitmap
>& xBitmap
,
604 const rendering::ViewState
& viewState
,
605 const rendering::RenderState
& renderState
)
607 ENSURE_OR_THROW( xBitmap
.is(),
608 "CanvasHelper::drawBitmap: bitmap is NULL");
610 // no color set -> this is equivalent to a plain drawBitmap(), then
611 if( renderState
.DeviceColor
.getLength() < 3 )
612 return drawBitmap( pCanvas
, xBitmap
, viewState
, renderState
);
616 GraphicsSharedPtr
pGraphics( mpGraphicsProvider
->getGraphics() );
618 setupGraphicsState( pGraphics
, viewState
, renderState
);
620 BitmapSharedPtr
pBitmap( tools::bitmapFromXBitmap( xBitmap
) );
621 Gdiplus::Rect
aRect( 0, 0,
623 pBitmap
->GetHeight() );
625 // Setup an ImageAttributes with an alpha-modulating
627 const rendering::ARGBColor
& rARGBColor(
628 mpDevice
->getDeviceColorSpace()->convertToARGB(renderState
.DeviceColor
)[0]);
630 Gdiplus::ImageAttributes aImgAttr
;
631 tools::setModulateImageAttributes( aImgAttr
,
638 Gdiplus::Ok
== pGraphics
->DrawImage( pBitmap
.get(),
642 pBitmap
->GetHeight(),
647 "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
650 // TODO(P1): Provide caching here.
651 return uno::Reference
< rendering::XCachedPrimitive
>(NULL
);
654 uno::Reference
< rendering::XGraphicDevice
> CanvasHelper::getDevice()
656 return uno::Reference
< rendering::XGraphicDevice
>(mpDevice
);
662 Gdiplus::CompositingMode
CanvasHelper::calcCompositingMode( sal_Int8 nMode
)
664 Gdiplus::CompositingMode
aRet( Gdiplus::CompositingModeSourceOver
);
668 case rendering::CompositeOperation::OVER
:
669 // FALLTHROUGH intended
670 case rendering::CompositeOperation::CLEAR
:
671 aRet
= Gdiplus::CompositingModeSourceOver
;
674 case rendering::CompositeOperation::SOURCE
:
675 aRet
= Gdiplus::CompositingModeSourceCopy
;
678 case rendering::CompositeOperation::DESTINATION
:
679 // FALLTHROUGH intended
680 case rendering::CompositeOperation::UNDER
:
681 // FALLTHROUGH intended
682 case rendering::CompositeOperation::INSIDE
:
683 // FALLTHROUGH intended
684 case rendering::CompositeOperation::INSIDE_REVERSE
:
685 // FALLTHROUGH intended
686 case rendering::CompositeOperation::OUTSIDE
:
687 // FALLTHROUGH intended
688 case rendering::CompositeOperation::OUTSIDE_REVERSE
:
689 // FALLTHROUGH intended
690 case rendering::CompositeOperation::ATOP
:
691 // FALLTHROUGH intended
692 case rendering::CompositeOperation::ATOP_REVERSE
:
693 // FALLTHROUGH intended
694 case rendering::CompositeOperation::XOR
:
695 // FALLTHROUGH intended
696 case rendering::CompositeOperation::ADD
:
697 // FALLTHROUGH intended
698 case rendering::CompositeOperation::SATURATE
:
699 // TODO(F2): Problem, because GDI+ only knows about two compositing modes
700 aRet
= Gdiplus::CompositingModeSourceOver
;
704 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
711 void CanvasHelper::setupGraphicsState( GraphicsSharedPtr
& rGraphics
,
712 const rendering::ViewState
& viewState
,
713 const rendering::RenderState
& renderState
)
715 ENSURE_OR_THROW( needOutput(),
716 "CanvasHelper::setupGraphicsState: primary graphics invalid" );
717 ENSURE_OR_THROW( mpDevice
,
718 "CanvasHelper::setupGraphicsState: reference device invalid" );
720 // setup view transform first. Clipping e.g. depends on it
721 ::basegfx::B2DHomMatrix aTransform
;
722 ::canvas::tools::getViewStateTransform(aTransform
, viewState
);
725 if( !maOutputOffset
.equalZero() )
727 const basegfx::B2DHomMatrix
aOutputOffset(basegfx::tools::createTranslateB2DHomMatrix(
728 maOutputOffset
.getX(), maOutputOffset
.getY()));
729 aTransform
= aOutputOffset
* aTransform
;
732 Gdiplus::Matrix aMatrix
;
733 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix
, aTransform
);
736 Gdiplus::Ok
== rGraphics
->SetTransform( &aMatrix
),
737 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
739 // setup view and render state clipping
741 Gdiplus::Ok
== rGraphics
->ResetClip(),
742 "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
744 if( viewState
.Clip
.is() )
746 GraphicsPathSharedPtr
aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState
.Clip
) );
748 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
749 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
750 // LinePolyPolygon, then)
752 Gdiplus::Ok
== rGraphics
->SetClip( aClipPath
.get(),
753 Gdiplus::CombineModeIntersect
),
754 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
757 // setup overall transform only now. View clip above was relative to
759 ::canvas::tools::mergeViewAndRenderTransform(aTransform
,
764 if( !maOutputOffset
.equalZero() )
766 const basegfx::B2DHomMatrix
aOutputOffset(basegfx::tools::createTranslateB2DHomMatrix(
767 maOutputOffset
.getX(), maOutputOffset
.getY()));
768 aTransform
= aOutputOffset
* aTransform
;
771 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix
, aTransform
);
774 Gdiplus::Ok
== rGraphics
->SetTransform( &aMatrix
),
775 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
777 if( renderState
.Clip
.is() )
779 GraphicsPathSharedPtr
aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState
.Clip
) );
781 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
782 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
783 // LinePolyPolygon, then)
785 Gdiplus::Ok
== rGraphics
->SetClip( aClipPath
.get(),
786 Gdiplus::CombineModeIntersect
),
787 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
791 const Gdiplus::CompositingMode
eCompositing( calcCompositingMode( renderState
.CompositeOperation
) );
793 Gdiplus::Ok
== rGraphics
->SetCompositingMode( eCompositing
),
794 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
797 void CanvasHelper::flush() const
800 mpGraphicsProvider
->getGraphics()->Flush( Gdiplus::FlushIntentionSync
);
804 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */