Branch libreoffice-5-0-4
[LibreOffice.git] / canvas / source / directx / dx_canvashelper.cxx
bloba02455268467977e52f193b463f17be78b72f50c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
47 #include <algorithm>
50 using namespace ::com::sun::star;
52 namespace dxcanvas
54 namespace
56 Gdiplus::LineCap gdiCapFromCap( sal_Int8 nCapType )
58 switch( 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;
69 default:
70 ENSURE_OR_THROW( false,
71 "gdiCapFromCap(): Unexpected cap type" );
74 return Gdiplus::LineCapFlat;
77 Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType )
79 switch( 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;
93 default:
94 ENSURE_OR_THROW( false,
95 "gdiJoinFromJoin(): Unexpected join type" );
98 return Gdiplus::LineJoinMiter;
102 CanvasHelper::CanvasHelper() :
103 mpGdiPlusUser( GDIPlusUser::createInstance() ),
104 mpDevice( NULL ),
105 mpGraphicsProvider(),
106 maOutputOffset()
110 void CanvasHelper::disposing()
112 mpGraphicsProvider.reset();
113 mpDevice = NULL;
114 mpGdiPlusUser.reset();
117 void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice )
119 mpDevice = &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()
146 if( needOutput() )
148 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
149 Gdiplus::Color aClearColor = Gdiplus::Color((Gdiplus::ARGB)Gdiplus::Color::White);
151 ENSURE_OR_THROW(
152 Gdiplus::Ok == pGraphics->SetCompositingMode(
153 Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
154 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
155 ENSURE_OR_THROW(
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 )
166 if( needOutput() )
168 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
170 setupGraphicsState( pGraphics, viewState, renderState );
172 Gdiplus::SolidBrush aBrush(
173 Gdiplus::Color(
174 tools::sequenceToArgb(renderState.DeviceColor)) );
176 // determine size of one-by-one device pixel ellipse
177 Gdiplus::Matrix aMatrix;
178 pGraphics->GetTransform(&aMatrix);
179 aMatrix.Invert();
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)
185 ENSURE_OR_THROW(
186 Gdiplus::Ok == pGraphics->FillEllipse( &aBrush,
187 // disambiguate call
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 )
202 if( needOutput() )
204 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
206 setupGraphicsState( pGraphics, viewState, renderState );
208 Gdiplus::Pen aPen(
209 Gdiplus::Color(
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 );
229 ENSURE_OR_THROW(
230 Gdiplus::Ok == hr,
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 )
241 if( needOutput() )
243 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
245 setupGraphicsState( pGraphics, viewState, renderState );
247 Gdiplus::Pen aPen(
248 Gdiplus::Color(
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 );
273 ENSURE_OR_THROW(
274 Gdiplus::Ok == hr,
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");
287 if( needOutput() )
289 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
291 setupGraphicsState( pGraphics, viewState, renderState );
293 Gdiplus::Pen aPen(
294 Gdiplus::Color(
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 );
314 ENSURE_OR_THROW(
315 Gdiplus::Ok == hr,
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");
332 if( needOutput() )
334 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
336 setupGraphicsState( pGraphics, viewState, renderState );
339 // Setup stroke pen
342 Gdiplus::Pen aPen(
343 Gdiplus::Color(
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);
359 if(bIsMiter)
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],
368 rDashArray.size() );
370 aPen.SetLineCap( gdiCapFromCap(strokeAttributes.StartCapType),
371 gdiCapFromCap(strokeAttributes.EndCapType),
372 Gdiplus::DashCapFlat );
373 if(!bIsNone)
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 );
383 ENSURE_OR_THROW(
384 Gdiplus::Ok == hr,
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*/ )
399 // TODO
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*/ )
411 // TODO
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*/ )
421 // TODO
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");
433 if( needOutput() )
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*/ )
460 // TODO
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 )
469 if( needOutput() )
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*/ )
482 // TODO
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");
496 if( needOutput() )
498 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
500 setupGraphicsState( pGraphics, viewState, renderState );
502 Gdiplus::SolidBrush aBrush(
503 Gdiplus::Color(
504 tools::sequenceToArgb(renderState.DeviceColor)));
506 CanvasFont::ImplRef pFont(
507 tools::canvasFontFromXFont(xFont) );
509 // Move glyphs up, such that output happens at the font
510 // baseline.
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
520 // far...
522 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
523 // DrawDriverString here, and perform layouting myself...
524 ENSURE_OR_THROW(
525 Gdiplus::Ok == pGraphics->DrawString( reinterpret_cast<LPCWSTR>(
526 text.Text.copy( text.StartPosition,
527 text.Length ).getStr()),
528 text.Length,
529 pFont->getFont().get(),
530 aPoint,
531 &aBrush ),
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");
546 if( needOutput() )
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(),
555 viewState,
556 renderState,
557 maOutputOffset,
558 mpDevice,
559 false );
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");
573 if( needOutput() )
575 // check whether one of our own objects - need to retrieve
576 // bitmap _before_ calling
577 // GraphicsProvider::getGraphics(), to avoid locking our
578 // own surface.
579 BitmapSharedPtr pGdiBitmap;
580 BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get());
581 if( pBitmap )
583 IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() );
584 if( pDXBitmap )
585 pGdiBitmap = pDXBitmap->getBitmap();
588 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
589 setupGraphicsState( pGraphics, viewState, renderState );
591 if( pGdiBitmap )
592 tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap);
593 else
594 tools::drawVCLBitmapFromXBitmap(pGraphics,
595 xBitmap);
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 );
614 if( needOutput() )
616 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
618 setupGraphicsState( pGraphics, viewState, renderState );
620 BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) );
621 Gdiplus::Rect aRect( 0, 0,
622 pBitmap->GetWidth(),
623 pBitmap->GetHeight() );
625 // Setup an ImageAttributes with an alpha-modulating
626 // color matrix.
627 const rendering::ARGBColor& rARGBColor(
628 mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]);
630 Gdiplus::ImageAttributes aImgAttr;
631 tools::setModulateImageAttributes( aImgAttr,
632 rARGBColor.Red,
633 rARGBColor.Green,
634 rARGBColor.Blue,
635 rARGBColor.Alpha );
637 ENSURE_OR_THROW(
638 Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(),
639 aRect,
640 0, 0,
641 pBitmap->GetWidth(),
642 pBitmap->GetHeight(),
643 Gdiplus::UnitPixel,
644 &aImgAttr,
645 NULL,
646 NULL ),
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);
659 // private helper
662 Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode )
664 Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver );
666 switch( nMode )
668 case rendering::CompositeOperation::OVER:
669 // FALLTHROUGH intended
670 case rendering::CompositeOperation::CLEAR:
671 aRet = Gdiplus::CompositingModeSourceOver;
672 break;
674 case rendering::CompositeOperation::SOURCE:
675 aRet = Gdiplus::CompositingModeSourceCopy;
676 break;
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;
701 break;
703 default:
704 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
705 break;
708 return aRet;
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);
724 // add output offset
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 );
735 ENSURE_OR_THROW(
736 Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
737 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
739 // setup view and render state clipping
740 ENSURE_OR_THROW(
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)
751 ENSURE_OR_THROW(
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
758 // view transform
759 ::canvas::tools::mergeViewAndRenderTransform(aTransform,
760 viewState,
761 renderState);
763 // add output offset
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 );
773 ENSURE_OR_THROW(
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)
784 ENSURE_OR_THROW(
785 Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
786 Gdiplus::CombineModeIntersect ),
787 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
790 // setup compositing
791 const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) );
792 ENSURE_OR_THROW(
793 Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ),
794 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
797 void CanvasHelper::flush() const
799 if( needOutput() )
800 mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );
804 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */