CWS-TOOLING: integrate CWS os146
[LibreOffice.git] / canvas / source / directx / dx_canvashelper.cxx
blob866cc0f03ec372865512b3ead1f4d497575580f5
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"
58 #include <algorithm>
61 using namespace ::com::sun::star;
63 namespace dxcanvas
65 namespace
67 Gdiplus::LineCap gdiCapFromCap( sal_Int8 nCapType )
69 switch( 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;
80 default:
81 ENSURE_OR_THROW( false,
82 "gdiCapFromCap(): Unexpected cap type" );
85 return Gdiplus::LineCapFlat;
88 Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType )
90 switch( nJoinType )
92 case rendering::PathJoinType::NONE:
93 OSL_ENSURE( false,
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;
105 default:
106 ENSURE_OR_THROW( false,
107 "gdiJoinFromJoin(): Unexpected join type" );
110 return Gdiplus::LineJoinMiter;
114 CanvasHelper::CanvasHelper() :
115 mpGdiPlusUser( GDIPlusUser::createInstance() ),
116 mpDevice( NULL ),
117 mpGraphicsProvider(),
118 maOutputOffset()
122 void CanvasHelper::disposing()
124 mpGraphicsProvider.reset();
125 mpDevice = NULL;
126 mpGdiPlusUser.reset();
129 void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice )
131 mpDevice = &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()
158 if( needOutput() )
160 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
161 Gdiplus::Color aClearColor = Gdiplus::Color((Gdiplus::ARGB)Gdiplus::Color::White);
163 ENSURE_OR_THROW(
164 Gdiplus::Ok == pGraphics->SetCompositingMode(
165 Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
166 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
167 ENSURE_OR_THROW(
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 )
178 if( needOutput() )
180 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
182 setupGraphicsState( pGraphics, viewState, renderState );
184 Gdiplus::SolidBrush aBrush(
185 Gdiplus::Color(
186 tools::sequenceToArgb(renderState.DeviceColor)) );
188 // determine size of one-by-one device pixel ellipse
189 Gdiplus::Matrix aMatrix;
190 pGraphics->GetTransform(&aMatrix);
191 aMatrix.Invert();
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)
197 ENSURE_OR_THROW(
198 Gdiplus::Ok == pGraphics->FillEllipse( &aBrush,
199 // disambiguate call
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 )
214 if( needOutput() )
216 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
218 setupGraphicsState( pGraphics, viewState, renderState );
220 Gdiplus::Pen aPen(
221 Gdiplus::Color(
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 );
241 ENSURE_OR_THROW(
242 Gdiplus::Ok == hr,
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 )
253 if( needOutput() )
255 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
257 setupGraphicsState( pGraphics, viewState, renderState );
259 Gdiplus::Pen aPen(
260 Gdiplus::Color(
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 );
285 ENSURE_OR_THROW(
286 Gdiplus::Ok == hr,
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");
299 if( needOutput() )
301 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
303 setupGraphicsState( pGraphics, viewState, renderState );
305 Gdiplus::Pen aPen(
306 Gdiplus::Color(
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 );
326 ENSURE_OR_THROW(
327 Gdiplus::Ok == hr,
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");
344 if( needOutput() )
346 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
348 setupGraphicsState( pGraphics, viewState, renderState );
351 // Setup stroke pen
352 // ----------------
354 Gdiplus::Pen aPen(
355 Gdiplus::Color(
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);
371 if(bIsMiter)
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],
380 rDashArray.size() );
382 aPen.SetLineCap( gdiCapFromCap(strokeAttributes.StartCapType),
383 gdiCapFromCap(strokeAttributes.EndCapType),
384 Gdiplus::DashCapFlat );
385 if(!bIsNone)
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 );
395 ENSURE_OR_THROW(
396 Gdiplus::Ok == hr,
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*/ )
411 // TODO
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*/ )
423 // TODO
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*/ )
433 // TODO
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");
445 if( needOutput() )
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*/ )
472 // TODO
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 )
481 if( needOutput() )
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*/ )
494 // TODO
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");
508 if( needOutput() )
510 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
512 setupGraphicsState( pGraphics, viewState, renderState );
514 Gdiplus::SolidBrush aBrush(
515 Gdiplus::Color(
516 tools::sequenceToArgb(renderState.DeviceColor)));
518 CanvasFont::ImplRef pFont(
519 tools::canvasFontFromXFont(xFont) );
521 // Move glyphs up, such that output happens at the font
522 // baseline.
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
532 // far...
534 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
535 // DrawDriverString here, and perform layouting myself...
536 ENSURE_OR_THROW(
537 Gdiplus::Ok == pGraphics->DrawString( reinterpret_cast<LPCWSTR>(
538 text.Text.copy( text.StartPosition,
539 text.Length ).getStr()),
540 text.Length,
541 pFont->getFont().get(),
542 aPoint,
543 &aBrush ),
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");
558 if( needOutput() )
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(),
567 viewState,
568 renderState,
569 maOutputOffset,
570 mpDevice,
571 false );
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");
585 if( needOutput() )
587 // check whether one of our own objects - need to retrieve
588 // bitmap _before_ calling
589 // GraphicsProvider::getGraphics(), to avoid locking our
590 // own surface.
591 BitmapSharedPtr pGdiBitmap;
592 BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get());
593 if( pBitmap )
595 IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() );
596 if( pDXBitmap )
597 pGdiBitmap = pDXBitmap->getBitmap();
600 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
601 setupGraphicsState( pGraphics, viewState, renderState );
603 if( pGdiBitmap )
604 tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap);
605 else
606 tools::drawVCLBitmapFromXBitmap(pGraphics,
607 xBitmap);
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 );
626 if( needOutput() )
628 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
630 setupGraphicsState( pGraphics, viewState, renderState );
632 BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) );
633 Gdiplus::Rect aRect( 0, 0,
634 pBitmap->GetWidth(),
635 pBitmap->GetHeight() );
637 // Setup an ImageAttributes with an alpha-modulating
638 // color matrix.
639 const rendering::ARGBColor& rARGBColor(
640 mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]);
642 Gdiplus::ImageAttributes aImgAttr;
643 tools::setModulateImageAttributes( aImgAttr,
644 rARGBColor.Red,
645 rARGBColor.Green,
646 rARGBColor.Blue,
647 rARGBColor.Alpha );
649 ENSURE_OR_THROW(
650 Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(),
651 aRect,
652 0, 0,
653 pBitmap->GetWidth(),
654 pBitmap->GetHeight(),
655 Gdiplus::UnitPixel,
656 &aImgAttr,
657 NULL,
658 NULL ),
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);
671 // private helper
672 // --------------------------------------------------
674 Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode )
676 Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver );
678 switch( nMode )
680 case rendering::CompositeOperation::OVER:
681 // FALLTHROUGH intended
682 case rendering::CompositeOperation::CLEAR:
683 aRet = Gdiplus::CompositingModeSourceOver;
684 break;
686 case rendering::CompositeOperation::SOURCE:
687 aRet = Gdiplus::CompositingModeSourceCopy;
688 break;
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;
713 break;
715 default:
716 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
717 break;
720 return aRet;
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);
736 // add output offset
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 );
747 ENSURE_OR_THROW(
748 Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
749 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
751 // setup view and render state clipping
752 ENSURE_OR_THROW(
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)
763 ENSURE_OR_THROW(
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
770 // view transform
771 ::canvas::tools::mergeViewAndRenderTransform(aTransform,
772 viewState,
773 renderState);
775 // add output offset
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 );
785 ENSURE_OR_THROW(
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)
796 ENSURE_OR_THROW(
797 Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
798 Gdiplus::CombineModeIntersect ),
799 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
802 // setup compositing
803 const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) );
804 ENSURE_OR_THROW(
805 Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ),
806 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
809 void CanvasHelper::flush() const
811 if( needOutput() )
812 mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );