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