update dev300-m58
[ooovba.git] / canvas / source / directx / dx_canvashelper.cxx
blob964b8923e36dc4f92e7357ce42d108878d5f30f1
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dx_canvashelper.cxx,v $
10 * $Revision: 1.5 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_canvas.hxx"
34 #include <canvas/debug.hxx>
35 #include <tools/diagnose_ex.h>
37 #include <rtl/logfile.hxx>
38 #include <rtl/math.hxx>
40 #include <com/sun/star/rendering/TexturingMode.hpp>
41 #include <com/sun/star/rendering/CompositeOperation.hpp>
42 #include <com/sun/star/rendering/RepaintResult.hpp>
43 #include <com/sun/star/rendering/PathCapType.hpp>
44 #include <com/sun/star/rendering/PathJoinType.hpp>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/point/b2dpoint.hxx>
48 #include <basegfx/tools/canvastools.hxx>
50 #include <comphelper/sequence.hxx>
51 #include <canvas/canvastools.hxx>
53 #include "dx_spritecanvas.hxx"
54 #include "dx_impltools.hxx"
55 #include "dx_vcltools.hxx"
56 #include "dx_canvasfont.hxx"
57 #include "dx_textlayout.hxx"
58 #include "dx_canvashelper.hxx"
60 #include <algorithm>
63 using namespace ::com::sun::star;
65 namespace dxcanvas
67 namespace
69 Gdiplus::LineCap gdiCapFromCap( sal_Int8 nCapType )
71 switch( nCapType )
73 case rendering::PathCapType::BUTT:
74 return Gdiplus::LineCapFlat;
76 case rendering::PathCapType::ROUND:
77 return Gdiplus::LineCapRound;
79 case rendering::PathCapType::SQUARE:
80 return Gdiplus::LineCapSquare;
82 default:
83 ENSURE_OR_THROW( false,
84 "gdiCapFromCap(): Unexpected cap type" );
87 return Gdiplus::LineCapFlat;
90 Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType )
92 switch( nJoinType )
94 case rendering::PathJoinType::NONE:
95 OSL_ENSURE( false,
96 "gdiJoinFromJoin(): Join NONE not possible, mapping to MITER" );
97 // FALLTHROUGH intended
98 case rendering::PathJoinType::MITER:
99 return Gdiplus::LineJoinMiter;
101 case rendering::PathJoinType::ROUND:
102 return Gdiplus::LineJoinRound;
104 case rendering::PathJoinType::BEVEL:
105 return Gdiplus::LineJoinBevel;
107 default:
108 ENSURE_OR_THROW( false,
109 "gdiJoinFromJoin(): Unexpected join type" );
112 return Gdiplus::LineJoinMiter;
116 CanvasHelper::CanvasHelper() :
117 mpGdiPlusUser( GDIPlusUser::createInstance() ),
118 mpDevice( NULL ),
119 mpGraphicsProvider(),
120 maOutputOffset()
124 void CanvasHelper::disposing()
126 mpGraphicsProvider.reset();
127 mpDevice = NULL;
128 mpGdiPlusUser.reset();
131 void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice )
133 mpDevice = &rDevice;
136 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget )
138 ENSURE_OR_THROW( rTarget,
139 "CanvasHelper::setTarget(): Invalid target" );
140 ENSURE_OR_THROW( !mpGraphicsProvider.get(),
141 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
143 mpGraphicsProvider = rTarget;
146 void CanvasHelper::setTarget( const GraphicsProviderSharedPtr& rTarget,
147 const ::basegfx::B2ISize& rOutputOffset )
149 ENSURE_OR_THROW( rTarget,
150 "CanvasHelper::setTarget(): invalid target" );
151 ENSURE_OR_THROW( !mpGraphicsProvider.get(),
152 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
154 mpGraphicsProvider = rTarget;
155 maOutputOffset = rOutputOffset;
158 void CanvasHelper::clear()
160 if( needOutput() )
162 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
163 Gdiplus::Color aClearColor = Gdiplus::Color((Gdiplus::ARGB)Gdiplus::Color::White);
165 ENSURE_OR_THROW(
166 Gdiplus::Ok == pGraphics->SetCompositingMode(
167 Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
168 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
169 ENSURE_OR_THROW(
170 Gdiplus::Ok == pGraphics->Clear( aClearColor ),
171 "CanvasHelper::clear(): GDI+ Clear call failed" );
175 void CanvasHelper::drawPoint( const rendering::XCanvas* /*pCanvas*/,
176 const geometry::RealPoint2D& aPoint,
177 const rendering::ViewState& viewState,
178 const rendering::RenderState& renderState )
180 if( needOutput() )
182 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
184 setupGraphicsState( pGraphics, viewState, renderState );
186 Gdiplus::SolidBrush aBrush(
187 Gdiplus::Color(
188 tools::sequenceToArgb(renderState.DeviceColor)) );
190 // determine size of one-by-one device pixel ellipse
191 Gdiplus::Matrix aMatrix;
192 pGraphics->GetTransform(&aMatrix);
193 aMatrix.Invert();
194 Gdiplus::PointF vector(1, 1);
195 aMatrix.TransformVectors(&vector);
197 // paint a one-by-one circle, with the given point
198 // in the middle (rounded to float)
199 ENSURE_OR_THROW(
200 Gdiplus::Ok == pGraphics->FillEllipse( &aBrush,
201 // disambiguate call
202 Gdiplus::REAL(aPoint.X),
203 Gdiplus::REAL(aPoint.Y),
204 Gdiplus::REAL(vector.X),
205 Gdiplus::REAL(vector.Y) ),
206 "CanvasHelper::drawPoint(): GDI+ call failed" );
210 void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
211 const geometry::RealPoint2D& aStartPoint,
212 const geometry::RealPoint2D& aEndPoint,
213 const rendering::ViewState& viewState,
214 const rendering::RenderState& renderState )
216 if( needOutput() )
218 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
220 setupGraphicsState( pGraphics, viewState, renderState );
222 Gdiplus::Pen aPen(
223 Gdiplus::Color(
224 tools::sequenceToArgb(renderState.DeviceColor)),
225 Gdiplus::REAL(0.0) );
227 // #122683# Switched precedence of pixel offset
228 // mode. Seemingly, polygon stroking needs
229 // PixelOffsetModeNone to achieve visually pleasing
230 // results, whereas all other operations (e.g. polygon
231 // fills, bitmaps) look better with PixelOffsetModeHalf.
232 const Gdiplus::PixelOffsetMode aOldMode(
233 pGraphics->GetPixelOffsetMode() );
234 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
236 Gdiplus::Status hr = pGraphics->DrawLine( &aPen,
237 Gdiplus::REAL(aStartPoint.X), // disambiguate call
238 Gdiplus::REAL(aStartPoint.Y),
239 Gdiplus::REAL(aEndPoint.X),
240 Gdiplus::REAL(aEndPoint.Y) );
241 pGraphics->SetPixelOffsetMode( aOldMode );
243 ENSURE_OR_THROW(
244 Gdiplus::Ok == hr,
245 "CanvasHelper::drawLine(): GDI+ call failed" );
249 void CanvasHelper::drawBezier( const rendering::XCanvas* /*pCanvas*/,
250 const geometry::RealBezierSegment2D& aBezierSegment,
251 const geometry::RealPoint2D& aEndPoint,
252 const rendering::ViewState& viewState,
253 const rendering::RenderState& renderState )
255 if( needOutput() )
257 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
259 setupGraphicsState( pGraphics, viewState, renderState );
261 Gdiplus::Pen aPen(
262 Gdiplus::Color(
263 tools::sequenceToArgb(renderState.DeviceColor)),
264 Gdiplus::REAL(0.0) );
266 // #122683# Switched precedence of pixel offset
267 // mode. Seemingly, polygon stroking needs
268 // PixelOffsetModeNone to achieve visually pleasing
269 // results, whereas all other operations (e.g. polygon
270 // fills, bitmaps) look better with PixelOffsetModeHalf.
271 const Gdiplus::PixelOffsetMode aOldMode(
272 pGraphics->GetPixelOffsetMode() );
273 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
275 Gdiplus::Status hr = pGraphics->DrawBezier( &aPen,
276 Gdiplus::REAL(aBezierSegment.Px), // disambiguate call
277 Gdiplus::REAL(aBezierSegment.Py),
278 Gdiplus::REAL(aBezierSegment.C1x),
279 Gdiplus::REAL(aBezierSegment.C1y),
280 Gdiplus::REAL(aEndPoint.X),
281 Gdiplus::REAL(aEndPoint.Y),
282 Gdiplus::REAL(aBezierSegment.C2x),
283 Gdiplus::REAL(aBezierSegment.C2y) );
285 pGraphics->SetPixelOffsetMode( aOldMode );
287 ENSURE_OR_THROW(
288 Gdiplus::Ok == hr,
289 "CanvasHelper::drawBezier(): GDI+ call failed" );
293 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
294 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
295 const rendering::ViewState& viewState,
296 const rendering::RenderState& renderState )
298 ENSURE_OR_THROW( xPolyPolygon.is(),
299 "CanvasHelper::drawPolyPolygon: polygon is NULL");
301 if( needOutput() )
303 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
305 setupGraphicsState( pGraphics, viewState, renderState );
307 Gdiplus::Pen aPen(
308 Gdiplus::Color(
309 tools::sequenceToArgb(renderState.DeviceColor)),
310 Gdiplus::REAL(0.0) );
312 // #122683# Switched precedence of pixel offset
313 // mode. Seemingly, polygon stroking needs
314 // PixelOffsetModeNone to achieve visually pleasing
315 // results, whereas all other operations (e.g. polygon
316 // fills, bitmaps) look better with PixelOffsetModeHalf.
317 const Gdiplus::PixelOffsetMode aOldMode(
318 pGraphics->GetPixelOffsetMode() );
319 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
321 GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );
323 // TODO(E1): Return value
324 Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
326 pGraphics->SetPixelOffsetMode( aOldMode );
328 ENSURE_OR_THROW(
329 Gdiplus::Ok == hr,
330 "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
333 // TODO(P1): Provide caching here.
334 return uno::Reference< rendering::XCachedPrimitive >(NULL);
337 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* /*pCanvas*/,
338 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
339 const rendering::ViewState& viewState,
340 const rendering::RenderState& renderState,
341 const rendering::StrokeAttributes& strokeAttributes )
343 ENSURE_OR_THROW( xPolyPolygon.is(),
344 "CanvasHelper::drawPolyPolygon: polygon is NULL");
346 if( needOutput() )
348 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
350 setupGraphicsState( pGraphics, viewState, renderState );
353 // Setup stroke pen
354 // ----------------
356 Gdiplus::Pen aPen(
357 Gdiplus::Color(
358 tools::sequenceToArgb(renderState.DeviceColor)),
359 static_cast< Gdiplus::REAL >(strokeAttributes.StrokeWidth) );
361 // #122683# Switched precedence of pixel offset
362 // mode. Seemingly, polygon stroking needs
363 // PixelOffsetModeNone to achieve visually pleasing
364 // results, whereas all other operations (e.g. polygon
365 // fills, bitmaps) look better with PixelOffsetModeHalf.
366 const Gdiplus::PixelOffsetMode aOldMode(
367 pGraphics->GetPixelOffsetMode() );
368 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
370 aPen.SetMiterLimit( static_cast< Gdiplus::REAL >(strokeAttributes.MiterLimit) );
372 const ::std::vector< Gdiplus::REAL >& rDashArray(
373 ::comphelper::sequenceToContainer< ::std::vector< Gdiplus::REAL > >(
374 strokeAttributes.DashArray ) );
375 if( !rDashArray.empty() )
377 aPen.SetDashPattern( &rDashArray[0],
378 rDashArray.size() );
380 aPen.SetLineCap( gdiCapFromCap(strokeAttributes.StartCapType),
381 gdiCapFromCap(strokeAttributes.EndCapType),
382 Gdiplus::DashCapFlat );
383 aPen.SetLineJoin( gdiJoinFromJoin(strokeAttributes.JoinType) );
385 GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );
387 // TODO(E1): Return value
388 Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
390 pGraphics->SetPixelOffsetMode( aOldMode );
392 ENSURE_OR_THROW(
393 Gdiplus::Ok == hr,
394 "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
397 // TODO(P1): Provide caching here.
398 return uno::Reference< rendering::XCachedPrimitive >(NULL);
401 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
402 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
403 const rendering::ViewState& /*viewState*/,
404 const rendering::RenderState& /*renderState*/,
405 const uno::Sequence< rendering::Texture >& /*textures*/,
406 const rendering::StrokeAttributes& /*strokeAttributes*/ )
408 // TODO
409 return uno::Reference< rendering::XCachedPrimitive >(NULL);
412 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
413 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
414 const rendering::ViewState& /*viewState*/,
415 const rendering::RenderState& /*renderState*/,
416 const uno::Sequence< rendering::Texture >& /*textures*/,
417 const uno::Reference< geometry::XMapping2D >& /*xMapping*/,
418 const rendering::StrokeAttributes& /*strokeAttributes*/ )
420 // TODO
421 return uno::Reference< rendering::XCachedPrimitive >(NULL);
424 uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* /*pCanvas*/,
425 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
426 const rendering::ViewState& /*viewState*/,
427 const rendering::RenderState& /*renderState*/,
428 const rendering::StrokeAttributes& /*strokeAttributes*/ )
430 // TODO
431 return uno::Reference< rendering::XPolyPolygon2D >(NULL);
434 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
435 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
436 const rendering::ViewState& viewState,
437 const rendering::RenderState& renderState )
439 ENSURE_OR_THROW( xPolyPolygon.is(),
440 "CanvasHelper::fillPolyPolygon: polygon is NULL");
442 if( needOutput() )
444 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
446 setupGraphicsState( pGraphics, viewState, renderState );
448 Gdiplus::SolidBrush aBrush(
449 tools::sequenceToArgb(renderState.DeviceColor));
451 GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon ) );
453 // TODO(F1): FillRule
454 ENSURE_OR_THROW( Gdiplus::Ok == pGraphics->FillPath( &aBrush, pPath.get() ),
455 "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
458 // TODO(P1): Provide caching here.
459 return uno::Reference< rendering::XCachedPrimitive >(NULL);
462 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
463 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
464 const rendering::ViewState& /*viewState*/,
465 const rendering::RenderState& /*renderState*/,
466 const uno::Sequence< rendering::Texture >& /*textures*/,
467 const uno::Reference< geometry::XMapping2D >& /*xMapping*/ )
469 // TODO
470 return uno::Reference< rendering::XCachedPrimitive >(NULL);
473 uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* /*pCanvas*/,
474 const rendering::FontRequest& fontRequest,
475 const uno::Sequence< beans::PropertyValue >& extraFontProperties,
476 const geometry::Matrix2D& fontMatrix )
478 if( needOutput() )
480 return uno::Reference< rendering::XCanvasFont >(
481 new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) );
484 return uno::Reference< rendering::XCanvasFont >();
487 uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/,
488 const rendering::FontInfo& /*aFilter*/,
489 const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
491 // TODO
492 return uno::Sequence< rendering::FontInfo >();
495 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* /*pCanvas*/,
496 const rendering::StringContext& text,
497 const uno::Reference< rendering::XCanvasFont >& xFont,
498 const rendering::ViewState& viewState,
499 const rendering::RenderState& renderState,
500 sal_Int8 /*textDirection*/ )
502 ENSURE_OR_THROW( xFont.is(),
503 "CanvasHelper::drawText: font is NULL");
505 if( needOutput() )
507 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
509 setupGraphicsState( pGraphics, viewState, renderState );
511 Gdiplus::SolidBrush aBrush(
512 Gdiplus::Color(
513 tools::sequenceToArgb(renderState.DeviceColor)));
515 CanvasFont::ImplRef pFont(
516 tools::canvasFontFromXFont(xFont) );
518 // Move glyphs up, such that output happens at the font
519 // baseline.
520 Gdiplus::PointF aPoint( 0.0,
521 static_cast<Gdiplus::REAL>(-(pFont->getFont()->GetSize()*
522 pFont->getCellAscent() /
523 pFont->getEmHeight())) );
525 // TODO(F1): According to
526 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
527 // we might have to revert to GDI and ExTextOut here,
528 // since GDI+ takes the scalability a little bit too
529 // far...
531 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
532 // DrawDriverString here, and perform layouting myself...
533 ENSURE_OR_THROW(
534 Gdiplus::Ok == pGraphics->DrawString( reinterpret_cast<LPCWSTR>(
535 text.Text.copy( text.StartPosition,
536 text.Length ).getStr()),
537 text.Length,
538 pFont->getFont().get(),
539 aPoint,
540 &aBrush ),
541 "CanvasHelper::drawText(): GDI+ call failed" );
544 return uno::Reference< rendering::XCachedPrimitive >(NULL);
547 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/,
548 const uno::Reference< rendering::XTextLayout >& xLayoutetText,
549 const rendering::ViewState& viewState,
550 const rendering::RenderState& renderState )
552 ENSURE_OR_THROW( xLayoutetText.is(),
553 "CanvasHelper::drawTextLayout: layout is NULL");
555 if( needOutput() )
557 TextLayout* pTextLayout =
558 dynamic_cast< TextLayout* >( xLayoutetText.get() );
560 ENSURE_OR_THROW( pTextLayout,
561 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
563 pTextLayout->draw( mpGraphicsProvider->getGraphics(),
564 viewState,
565 renderState,
566 maOutputOffset,
567 mpDevice,
568 false );
571 return uno::Reference< rendering::XCachedPrimitive >(NULL);
574 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/,
575 const uno::Reference< rendering::XBitmap >& xBitmap,
576 const rendering::ViewState& viewState,
577 const rendering::RenderState& renderState )
579 ENSURE_OR_THROW( xBitmap.is(),
580 "CanvasHelper::drawBitmap: bitmap is NULL");
582 if( needOutput() )
584 // check whether one of our own objects - need to retrieve
585 // bitmap _before_ calling
586 // GraphicsProvider::getGraphics(), to avoid locking our
587 // own surface.
588 BitmapSharedPtr pGdiBitmap;
589 BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get());
590 if( pBitmap )
592 IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() );
593 if( pDXBitmap )
594 pGdiBitmap = pDXBitmap->getBitmap();
597 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
598 setupGraphicsState( pGraphics, viewState, renderState );
600 if( pGdiBitmap )
601 tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap);
602 else
603 tools::drawVCLBitmapFromXBitmap(pGraphics,
604 xBitmap);
607 // TODO(P1): Provide caching here.
608 return uno::Reference< rendering::XCachedPrimitive >(NULL);
611 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
612 const uno::Reference< rendering::XBitmap >& xBitmap,
613 const rendering::ViewState& viewState,
614 const rendering::RenderState& renderState )
616 ENSURE_OR_THROW( xBitmap.is(),
617 "CanvasHelper::drawBitmap: bitmap is NULL");
619 // no color set -> this is equivalent to a plain drawBitmap(), then
620 if( renderState.DeviceColor.getLength() < 3 )
621 return drawBitmap( pCanvas, xBitmap, viewState, renderState );
623 if( needOutput() )
625 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
627 setupGraphicsState( pGraphics, viewState, renderState );
629 BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) );
630 Gdiplus::Rect aRect( 0, 0,
631 pBitmap->GetWidth(),
632 pBitmap->GetHeight() );
634 // Setup an ImageAttributes with an alpha-modulating
635 // color matrix.
636 const rendering::ARGBColor& rARGBColor(
637 mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]);
639 Gdiplus::ImageAttributes aImgAttr;
640 tools::setModulateImageAttributes( aImgAttr,
641 rARGBColor.Red,
642 rARGBColor.Green,
643 rARGBColor.Blue,
644 rARGBColor.Alpha );
646 ENSURE_OR_THROW(
647 Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(),
648 aRect,
649 0, 0,
650 pBitmap->GetWidth(),
651 pBitmap->GetHeight(),
652 Gdiplus::UnitPixel,
653 &aImgAttr,
654 NULL,
655 NULL ),
656 "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
659 // TODO(P1): Provide caching here.
660 return uno::Reference< rendering::XCachedPrimitive >(NULL);
663 uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
665 return uno::Reference< rendering::XGraphicDevice >(mpDevice);
668 // private helper
669 // --------------------------------------------------
671 Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode )
673 Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver );
675 switch( nMode )
677 case rendering::CompositeOperation::OVER:
678 // FALLTHROUGH intended
679 case rendering::CompositeOperation::CLEAR:
680 aRet = Gdiplus::CompositingModeSourceOver;
681 break;
683 case rendering::CompositeOperation::SOURCE:
684 aRet = Gdiplus::CompositingModeSourceCopy;
685 break;
687 case rendering::CompositeOperation::DESTINATION:
688 // FALLTHROUGH intended
689 case rendering::CompositeOperation::UNDER:
690 // FALLTHROUGH intended
691 case rendering::CompositeOperation::INSIDE:
692 // FALLTHROUGH intended
693 case rendering::CompositeOperation::INSIDE_REVERSE:
694 // FALLTHROUGH intended
695 case rendering::CompositeOperation::OUTSIDE:
696 // FALLTHROUGH intended
697 case rendering::CompositeOperation::OUTSIDE_REVERSE:
698 // FALLTHROUGH intended
699 case rendering::CompositeOperation::ATOP:
700 // FALLTHROUGH intended
701 case rendering::CompositeOperation::ATOP_REVERSE:
702 // FALLTHROUGH intended
703 case rendering::CompositeOperation::XOR:
704 // FALLTHROUGH intended
705 case rendering::CompositeOperation::ADD:
706 // FALLTHROUGH intended
707 case rendering::CompositeOperation::SATURATE:
708 // TODO(F2): Problem, because GDI+ only knows about two compositing modes
709 aRet = Gdiplus::CompositingModeSourceOver;
710 break;
712 default:
713 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
714 break;
717 return aRet;
720 void CanvasHelper::setupGraphicsState( GraphicsSharedPtr& rGraphics,
721 const rendering::ViewState& viewState,
722 const rendering::RenderState& renderState )
724 ENSURE_OR_THROW( needOutput(),
725 "CanvasHelper::setupGraphicsState: primary graphics invalid" );
726 ENSURE_OR_THROW( mpDevice,
727 "CanvasHelper::setupGraphicsState: reference device invalid" );
729 // setup view transform first. Clipping e.g. depends on it
730 ::basegfx::B2DHomMatrix aTransform;
731 ::canvas::tools::getViewStateTransform(aTransform, viewState);
733 // add output offset
734 if( !maOutputOffset.equalZero() )
736 ::basegfx::B2DHomMatrix aOutputOffset;
737 aOutputOffset.translate( maOutputOffset.getX(),
738 maOutputOffset.getY() );
740 aTransform = aOutputOffset * aTransform;
743 Gdiplus::Matrix aMatrix;
744 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
746 ENSURE_OR_THROW(
747 Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
748 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
750 // setup view and render state clipping
751 ENSURE_OR_THROW(
752 Gdiplus::Ok == rGraphics->ResetClip(),
753 "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
755 if( viewState.Clip.is() )
757 GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState.Clip ) );
759 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
760 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
761 // LinePolyPolygon, then)
762 ENSURE_OR_THROW(
763 Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
764 Gdiplus::CombineModeIntersect ),
765 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
768 // setup overall transform only now. View clip above was relative to
769 // view transform
770 ::canvas::tools::mergeViewAndRenderTransform(aTransform,
771 viewState,
772 renderState);
774 // add output offset
775 if( !maOutputOffset.equalZero() )
777 ::basegfx::B2DHomMatrix aOutputOffset;
778 aOutputOffset.translate( maOutputOffset.getX(),
779 maOutputOffset.getY() );
781 aTransform = aOutputOffset * aTransform;
784 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
786 ENSURE_OR_THROW(
787 Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
788 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
790 if( renderState.Clip.is() )
792 GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState.Clip ) );
794 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
795 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
796 // LinePolyPolygon, then)
797 ENSURE_OR_THROW(
798 Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
799 Gdiplus::CombineModeIntersect ),
800 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
803 // setup compositing
804 const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) );
805 ENSURE_OR_THROW(
806 Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ),
807 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
810 void CanvasHelper::flush() const
812 if( needOutput() )
813 mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );