LibreOffice Module canvas (master) 1
dx_canvashelper.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include <algorithm>
24
29#include <com/sun/star/rendering/CompositeOperation.hpp>
30#include <com/sun/star/rendering/PathCapType.hpp>
31#include <com/sun/star/rendering/PathJoinType.hpp>
32#include <com/sun/star/rendering/RepaintResult.hpp>
33#include <com/sun/star/rendering/TexturingMode.hpp>
36#include <rtl/math.hxx>
38
40
41#include "dx_canvasfont.hxx"
42#include "dx_canvashelper.hxx"
43#include "dx_impltools.hxx"
44#include "dx_spritecanvas.hxx"
45#include "dx_textlayout.hxx"
46#include "dx_vcltools.hxx"
47
48using namespace ::com::sun::star;
49
50namespace dxcanvas
51{
52 namespace
53 {
54 Gdiplus::LineCap gdiLineCapFromCap( sal_Int8 nCapType )
55 {
56 switch( nCapType )
57 {
58 case rendering::PathCapType::BUTT:
59 return Gdiplus::LineCapFlat;
60
61 case rendering::PathCapType::ROUND:
62 return Gdiplus::LineCapRound;
63
64 case rendering::PathCapType::SQUARE:
65 return Gdiplus::LineCapSquare;
66
67 default:
68 ENSURE_OR_THROW( false,
69 "gdiLineCapFromCap(): Unexpected cap type" );
70 }
71
72 return Gdiplus::LineCapFlat;
73 }
74
75 Gdiplus::DashCap gdiDashCapFromCap( sal_Int8 nCapType )
76 {
77 switch( nCapType )
78 {
79 case rendering::PathCapType::BUTT:
80 return Gdiplus::DashCapFlat;
81
82 case rendering::PathCapType::ROUND:
83 return Gdiplus::DashCapRound;
84
85 // Gdiplus does not know square, using flat would make short
86 // dashes disappear, so use triangle as the closest one.
87 case rendering::PathCapType::SQUARE:
88 return Gdiplus::DashCapTriangle;
89
90 default:
91 ENSURE_OR_THROW( false,
92 "gdiDashCapFromCap(): Unexpected cap type" );
93 }
94
95 return Gdiplus::DashCapFlat;
96 }
97
98 Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType )
99 {
100 switch( nJoinType )
101 {
102 case rendering::PathJoinType::NONE:
103 SAL_WARN( "canvas.directx", "gdiJoinFromJoin(): Join NONE not possible, mapping to BEVEL (closest to NONE)" );
104 return Gdiplus::LineJoinBevel;
105
106 case rendering::PathJoinType::MITER:
107 // in GDI+ fallback to Bevel, if miter limit is exceeded, is not done
108 // by Gdiplus::LineJoinMiter but by Gdiplus::LineJoinMiterClipped
109 return Gdiplus::LineJoinMiterClipped;
110
111 case rendering::PathJoinType::ROUND:
112 return Gdiplus::LineJoinRound;
113
114 case rendering::PathJoinType::BEVEL:
115 return Gdiplus::LineJoinBevel;
116
117 default:
118 ENSURE_OR_THROW( false,
119 "gdiJoinFromJoin(): Unexpected join type" );
120 }
121
122 return Gdiplus::LineJoinMiter;
123 }
124 }
125
127 mpGdiPlusUser( GDIPlusUser::createInstance() ),
128 mpDevice( nullptr ),
129 mpGraphicsProvider(),
130 maOutputOffset()
131 {
132 }
133
135 {
136 mpGraphicsProvider.reset();
137 mpDevice = nullptr;
138 mpGdiPlusUser.reset();
139 }
140
141 void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice )
142 {
143 mpDevice = &rDevice;
144 }
145
147 {
149 "CanvasHelper::setTarget(): Invalid target" );
151 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
152
154 }
155
157 const ::basegfx::B2ISize& rOutputOffset )
158 {
160 "CanvasHelper::setTarget(): invalid target" );
162 "CanvasHelper::setTarget(): target set, old target would be overwritten" );
163
165 maOutputOffset = rOutputOffset;
166 }
167
169 {
170 if( needOutput() )
171 {
172 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
173 Gdiplus::Color aClearColor{Gdiplus::ARGB(Gdiplus::Color::White)};
174
176 Gdiplus::Ok == pGraphics->SetCompositingMode(
177 Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
178 "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
180 Gdiplus::Ok == pGraphics->Clear( aClearColor ),
181 "CanvasHelper::clear(): GDI+ Clear call failed" );
182 }
183 }
184
185 void CanvasHelper::drawPoint( const rendering::XCanvas* /*pCanvas*/,
186 const geometry::RealPoint2D& aPoint,
187 const rendering::ViewState& viewState,
188 const rendering::RenderState& renderState )
189 {
190 if( needOutput() )
191 {
192 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
193
194 setupGraphicsState( pGraphics, viewState, renderState );
195
196 Gdiplus::SolidBrush aBrush(
197 Gdiplus::Color(
198 tools::sequenceToArgb(renderState.DeviceColor)) );
199
200 // determine size of one-by-one device pixel ellipse
201 Gdiplus::Matrix aMatrix;
202 pGraphics->GetTransform(&aMatrix);
203 aMatrix.Invert();
204 Gdiplus::PointF vector(1, 1);
205 aMatrix.TransformVectors(&vector);
206
207 // paint a one-by-one circle, with the given point
208 // in the middle (rounded to float)
210 Gdiplus::Ok == pGraphics->FillEllipse( &aBrush,
211 // disambiguate call
212 Gdiplus::REAL(aPoint.X),
213 Gdiplus::REAL(aPoint.Y),
214 Gdiplus::REAL(vector.X),
215 Gdiplus::REAL(vector.Y) ),
216 "CanvasHelper::drawPoint(): GDI+ call failed" );
217 }
218 }
219
220 void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
221 const geometry::RealPoint2D& aStartPoint,
222 const geometry::RealPoint2D& aEndPoint,
223 const rendering::ViewState& viewState,
224 const rendering::RenderState& renderState )
225 {
226 if( needOutput() )
227 {
228 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
229
230 setupGraphicsState( pGraphics, viewState, renderState );
231
232 Gdiplus::Pen aPen(
233 Gdiplus::Color(
234 tools::sequenceToArgb(renderState.DeviceColor)),
235 Gdiplus::REAL(0.0) );
236
237 // #122683# Switched precedence of pixel offset
238 // mode. Seemingly, polygon stroking needs
239 // PixelOffsetModeNone to achieve visually pleasing
240 // results, whereas all other operations (e.g. polygon
241 // fills, bitmaps) look better with PixelOffsetModeHalf.
242 const Gdiplus::PixelOffsetMode aOldMode(
243 pGraphics->GetPixelOffsetMode() );
244 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
245
246 Gdiplus::Status hr = pGraphics->DrawLine( &aPen,
247 Gdiplus::REAL(aStartPoint.X), // disambiguate call
248 Gdiplus::REAL(aStartPoint.Y),
249 Gdiplus::REAL(aEndPoint.X),
250 Gdiplus::REAL(aEndPoint.Y) );
251 pGraphics->SetPixelOffsetMode( aOldMode );
252
254 Gdiplus::Ok == hr,
255 "CanvasHelper::drawLine(): GDI+ call failed" );
256 }
257 }
258
259 void CanvasHelper::drawBezier( const rendering::XCanvas* /*pCanvas*/,
260 const geometry::RealBezierSegment2D& aBezierSegment,
261 const geometry::RealPoint2D& aEndPoint,
262 const rendering::ViewState& viewState,
263 const rendering::RenderState& renderState )
264 {
265 if( needOutput() )
266 {
267 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
268
269 setupGraphicsState( pGraphics, viewState, renderState );
270
271 Gdiplus::Pen aPen(
272 Gdiplus::Color(
273 tools::sequenceToArgb(renderState.DeviceColor)),
274 Gdiplus::REAL(0.0) );
275
276 // #122683# Switched precedence of pixel offset
277 // mode. Seemingly, polygon stroking needs
278 // PixelOffsetModeNone to achieve visually pleasing
279 // results, whereas all other operations (e.g. polygon
280 // fills, bitmaps) look better with PixelOffsetModeHalf.
281 const Gdiplus::PixelOffsetMode aOldMode(
282 pGraphics->GetPixelOffsetMode() );
283 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
284
285 Gdiplus::Status hr = pGraphics->DrawBezier( &aPen,
286 Gdiplus::REAL(aBezierSegment.Px), // disambiguate call
287 Gdiplus::REAL(aBezierSegment.Py),
288 Gdiplus::REAL(aBezierSegment.C1x),
289 Gdiplus::REAL(aBezierSegment.C1y),
290 Gdiplus::REAL(aEndPoint.X),
291 Gdiplus::REAL(aEndPoint.Y),
292 Gdiplus::REAL(aBezierSegment.C2x),
293 Gdiplus::REAL(aBezierSegment.C2y) );
294
295 pGraphics->SetPixelOffsetMode( aOldMode );
296
298 Gdiplus::Ok == hr,
299 "CanvasHelper::drawBezier(): GDI+ call failed" );
300 }
301 }
302
303 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
304 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
305 const rendering::ViewState& viewState,
306 const rendering::RenderState& renderState )
307 {
308 ENSURE_OR_THROW( xPolyPolygon.is(),
309 "CanvasHelper::drawPolyPolygon: polygon is NULL");
310
311 if( needOutput() )
312 {
313 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
314
315 setupGraphicsState( pGraphics, viewState, renderState );
316
317 Gdiplus::Pen aPen(
318 Gdiplus::Color(
319 tools::sequenceToArgb(renderState.DeviceColor)),
320 Gdiplus::REAL(0.0) );
321
322 // #122683# Switched precedence of pixel offset
323 // mode. Seemingly, polygon stroking needs
324 // PixelOffsetModeNone to achieve visually pleasing
325 // results, whereas all other operations (e.g. polygon
326 // fills, bitmaps) look better with PixelOffsetModeHalf.
327 const Gdiplus::PixelOffsetMode aOldMode(
328 pGraphics->GetPixelOffsetMode() );
329 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
330
332
333 // TODO(E1): Return value
334 Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
335
336 pGraphics->SetPixelOffsetMode( aOldMode );
337
339 Gdiplus::Ok == hr,
340 "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
341 }
342
343 // TODO(P1): Provide caching here.
344 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
345 }
346
347 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* /*pCanvas*/,
348 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
349 const rendering::ViewState& viewState,
350 const rendering::RenderState& renderState,
351 const rendering::StrokeAttributes& strokeAttributes )
352 {
353 ENSURE_OR_THROW( xPolyPolygon.is(),
354 "CanvasHelper::drawPolyPolygon: polygon is NULL");
355
356 if( needOutput() )
357 {
358 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
359
360 setupGraphicsState( pGraphics, viewState, renderState );
361
362
363 // Setup stroke pen
364
365
366 Gdiplus::Pen aPen(
367 Gdiplus::Color(
368 tools::sequenceToArgb(renderState.DeviceColor)),
369 static_cast< Gdiplus::REAL >(strokeAttributes.StrokeWidth) );
370
371 // #122683# Switched precedence of pixel offset
372 // mode. Seemingly, polygon stroking needs
373 // PixelOffsetModeNone to achieve visually pleasing
374 // results, whereas all other operations (e.g. polygon
375 // fills, bitmaps) look better with PixelOffsetModeHalf.
376 const Gdiplus::PixelOffsetMode aOldMode(
377 pGraphics->GetPixelOffsetMode() );
378 pGraphics->SetPixelOffsetMode( Gdiplus::PixelOffsetModeNone );
379
380 const bool bIsMiter(rendering::PathJoinType::MITER == strokeAttributes.JoinType);
381 const bool bIsNone(rendering::PathJoinType::NONE == strokeAttributes.JoinType);
382
383 if(bIsMiter)
384 aPen.SetMiterLimit( static_cast< Gdiplus::REAL >(strokeAttributes.MiterLimit) );
385
386 const std::vector< Gdiplus::REAL >& rDashArray(
387 ::comphelper::sequenceToContainer< std::vector< Gdiplus::REAL >, double >(
388 strokeAttributes.DashArray ) );
389 if( !rDashArray.empty() )
390 {
391 aPen.SetDashPattern( rDashArray.data(),
392 rDashArray.size() );
393 }
394 aPen.SetLineCap( gdiLineCapFromCap(strokeAttributes.StartCapType),
395 gdiLineCapFromCap(strokeAttributes.EndCapType),
396 gdiDashCapFromCap(strokeAttributes.StartCapType));
397 if(!bIsNone)
398 aPen.SetLineJoin( gdiJoinFromJoin(strokeAttributes.JoinType) );
399
400 GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon, bIsNone ) );
401
402 // TODO(E1): Return value
403 Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
404
405 pGraphics->SetPixelOffsetMode( aOldMode );
406
408 Gdiplus::Ok == hr,
409 "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
410 }
411
412 // TODO(P1): Provide caching here.
413 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
414 }
415
416 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
417 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
418 const rendering::ViewState& /*viewState*/,
419 const rendering::RenderState& /*renderState*/,
420 const uno::Sequence< rendering::Texture >& /*textures*/,
421 const rendering::StrokeAttributes& /*strokeAttributes*/ )
422 {
423 // TODO
424 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
425 }
426
427 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
428 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
429 const rendering::ViewState& /*viewState*/,
430 const rendering::RenderState& /*renderState*/,
431 const uno::Sequence< rendering::Texture >& /*textures*/,
432 const uno::Reference< geometry::XMapping2D >& /*xMapping*/,
433 const rendering::StrokeAttributes& /*strokeAttributes*/ )
434 {
435 // TODO
436 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
437 }
438
439 uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* /*pCanvas*/,
440 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
441 const rendering::ViewState& /*viewState*/,
442 const rendering::RenderState& /*renderState*/,
443 const rendering::StrokeAttributes& /*strokeAttributes*/ )
444 {
445 // TODO
446 return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
447 }
448
449 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
450 const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
451 const rendering::ViewState& viewState,
452 const rendering::RenderState& renderState )
453 {
454 ENSURE_OR_THROW( xPolyPolygon.is(),
455 "CanvasHelper::fillPolyPolygon: polygon is NULL");
456
457 if( needOutput() )
458 {
459 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
460
461 setupGraphicsState( pGraphics, viewState, renderState );
462
463 Gdiplus::SolidBrush aBrush(
464 tools::sequenceToArgb(renderState.DeviceColor));
465
467
468 // TODO(F1): FillRule
469 ENSURE_OR_THROW( Gdiplus::Ok == pGraphics->FillPath( &aBrush, pPath.get() ),
470 "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
471 }
472
473 // TODO(P1): Provide caching here.
474 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
475 }
476
477 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* /*pCanvas*/,
478 const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
479 const rendering::ViewState& /*viewState*/,
480 const rendering::RenderState& /*renderState*/,
481 const uno::Sequence< rendering::Texture >& /*textures*/,
482 const uno::Reference< geometry::XMapping2D >& /*xMapping*/ )
483 {
484 // TODO
485 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
486 }
487
488 uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* /*pCanvas*/,
489 const rendering::FontRequest& fontRequest,
490 const uno::Sequence< beans::PropertyValue >& extraFontProperties,
491 const geometry::Matrix2D& fontMatrix )
492 {
493 if( needOutput() )
494 {
495 return uno::Reference< rendering::XCanvasFont >(
496 new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) );
497 }
498
499 return uno::Reference< rendering::XCanvasFont >();
500 }
501
502 uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/,
503 const rendering::FontInfo& /*aFilter*/,
504 const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
505 {
506 // TODO
507 return uno::Sequence< rendering::FontInfo >();
508 }
509
510 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* /*pCanvas*/,
511 const rendering::StringContext& text,
512 const uno::Reference< rendering::XCanvasFont >& xFont,
513 const rendering::ViewState& viewState,
514 const rendering::RenderState& renderState,
515 sal_Int8 /*textDirection*/ )
516 {
517 ENSURE_OR_THROW( xFont.is(),
518 "CanvasHelper::drawText: font is NULL");
519
520 if( needOutput() )
521 {
522 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
523
524 setupGraphicsState( pGraphics, viewState, renderState );
525
526 Gdiplus::SolidBrush aBrush(
527 Gdiplus::Color(
528 tools::sequenceToArgb(renderState.DeviceColor)));
529
532
533 // Move glyphs up, such that output happens at the font
534 // baseline.
535 Gdiplus::PointF aPoint( 0.0,
536 static_cast<Gdiplus::REAL>(-(pFont->getFont()->GetSize()*
537 pFont->getCellAscent() /
538 pFont->getEmHeight())) );
539
540 // TODO(F1): According to
541 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q307208,
542 // we might have to revert to GDI and ExTextOut here,
543 // since GDI+ takes the scalability a little bit too
544 // far...
545
546 // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
547 // DrawDriverString here, and perform layouting myself...
549 Gdiplus::Ok == pGraphics->DrawString( o3tl::toW(text.Text.copy( text.StartPosition,
550 text.Length ).getStr()),
551 text.Length,
552 pFont->getFont().get(),
553 aPoint,
554 &aBrush ),
555 "CanvasHelper::drawText(): GDI+ call failed" );
556 }
557
558 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
559 }
560
561 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/,
562 const uno::Reference< rendering::XTextLayout >& xLayoutetText,
563 const rendering::ViewState& viewState,
564 const rendering::RenderState& renderState )
565 {
566 ENSURE_OR_THROW( xLayoutetText.is(),
567 "CanvasHelper::drawTextLayout: layout is NULL");
568
569 if( needOutput() )
570 {
571 TextLayout* pTextLayout =
572 dynamic_cast< TextLayout* >( xLayoutetText.get() );
573
574 ENSURE_OR_THROW( pTextLayout,
575 "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
576
577 pTextLayout->draw( mpGraphicsProvider->getGraphics(),
578 viewState,
579 renderState,
581 mpDevice,
582 false );
583 }
584
585 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
586 }
587
588 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/,
589 const uno::Reference< rendering::XBitmap >& xBitmap,
590 const rendering::ViewState& viewState,
591 const rendering::RenderState& renderState )
592 {
593 ENSURE_OR_THROW( xBitmap.is(),
594 "CanvasHelper::drawBitmap: bitmap is NULL");
595
596 if( needOutput() )
597 {
598 // check whether one of our own objects - need to retrieve
599 // bitmap _before_ calling
600 // GraphicsProvider::getGraphics(), to avoid locking our
601 // own surface.
602 BitmapSharedPtr pGdiBitmap;
603 BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get());
604 if( pBitmap )
605 {
606 IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() );
607 if( pDXBitmap )
608 pGdiBitmap = pDXBitmap->getBitmap();
609 }
610
611 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
612 setupGraphicsState( pGraphics, viewState, renderState );
613
614 if( pGdiBitmap )
615 tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap);
616 else
618 xBitmap);
619 }
620
621 // TODO(P1): Provide caching here.
622 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
623 }
624
625 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
626 const uno::Reference< rendering::XBitmap >& xBitmap,
627 const rendering::ViewState& viewState,
628 const rendering::RenderState& renderState )
629 {
630 ENSURE_OR_THROW( xBitmap.is(),
631 "CanvasHelper::drawBitmap: bitmap is NULL");
632
633 // no color set -> this is equivalent to a plain drawBitmap(), then
634 if( renderState.DeviceColor.getLength() < 3 )
635 return drawBitmap( pCanvas, xBitmap, viewState, renderState );
636
637 if( needOutput() )
638 {
639 GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
640
641 setupGraphicsState( pGraphics, viewState, renderState );
642
643 BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) );
644 Gdiplus::Rect aRect( 0, 0,
645 pBitmap->GetWidth(),
646 pBitmap->GetHeight() );
647
648 // Setup an ImageAttributes with an alpha-modulating
649 // color matrix.
650 rendering::ARGBColor aARGBColor(
651 mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]);
652
653 Gdiplus::ImageAttributes aImgAttr;
655 aARGBColor.Red,
656 aARGBColor.Green,
657 aARGBColor.Blue,
658 aARGBColor.Alpha );
659
661 Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(),
662 aRect,
663 0, 0,
664 pBitmap->GetWidth(),
665 pBitmap->GetHeight(),
666 Gdiplus::UnitPixel,
667 &aImgAttr ),
668 "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
669 }
670
671 // TODO(P1): Provide caching here.
672 return uno::Reference< rendering::XCachedPrimitive >(nullptr);
673 }
674
675 uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
676 {
677 return uno::Reference< rendering::XGraphicDevice >(mpDevice);
678 }
679
680 // private helper
681
682
683 Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode )
684 {
685 Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver );
686
687 switch( nMode )
688 {
689 case rendering::CompositeOperation::OVER:
690 case rendering::CompositeOperation::CLEAR:
691 aRet = Gdiplus::CompositingModeSourceOver;
692 break;
693
694 case rendering::CompositeOperation::SOURCE:
695 aRet = Gdiplus::CompositingModeSourceCopy;
696 break;
697
698 case rendering::CompositeOperation::DESTINATION:
699 case rendering::CompositeOperation::UNDER:
700 case rendering::CompositeOperation::INSIDE:
701 case rendering::CompositeOperation::INSIDE_REVERSE:
702 case rendering::CompositeOperation::OUTSIDE:
703 case rendering::CompositeOperation::OUTSIDE_REVERSE:
704 case rendering::CompositeOperation::ATOP:
705 case rendering::CompositeOperation::ATOP_REVERSE:
706 case rendering::CompositeOperation::XOR:
707 case rendering::CompositeOperation::ADD:
708 case rendering::CompositeOperation::SATURATE:
709 // TODO(F2): Problem, because GDI+ only knows about two compositing modes
710 aRet = Gdiplus::CompositingModeSourceOver;
711 break;
712
713 default:
714 ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
715 break;
716 }
717
718 return aRet;
719 }
720
722 const rendering::ViewState& viewState,
723 const rendering::RenderState& renderState )
724 {
726 "CanvasHelper::setupGraphicsState: primary graphics invalid" );
728 "CanvasHelper::setupGraphicsState: reference device invalid" );
729
730 // setup view transform first. Clipping e.g. depends on it
731 ::basegfx::B2DHomMatrix aTransform;
732 ::canvas::tools::getViewStateTransform(aTransform, viewState);
733
734 // add output offset
736 {
739 aTransform = aOutputOffset * aTransform;
740 }
741
742 Gdiplus::Matrix aMatrix;
743 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
744
746 Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
747 "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
748
749 // setup view and render state clipping
751 Gdiplus::Ok == rGraphics->ResetClip(),
752 "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
753
754 if( viewState.Clip.is() )
755 {
757
758 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
759 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
760 // LinePolyPolygon, then)
762 Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
763 Gdiplus::CombineModeIntersect ),
764 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
765 }
766
767 // setup overall transform only now. View clip above was relative to
768 // view transform
770 viewState,
771 renderState);
772
773 // add output offset
775 {
778 aTransform = aOutputOffset * aTransform;
779 }
780
781 tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
782
784 Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
785 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
786
787 if( renderState.Clip.is() )
788 {
789 GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState.Clip ) );
790
791 // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
792 // Try SetClip( Rect ) or similar for simple clip paths (need some support in
793 // LinePolyPolygon, then)
795 Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
796 Gdiplus::CombineModeIntersect ),
797 "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
798 }
799
800 // setup compositing
801 const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) );
803 Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ),
804 "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
805 }
806
808 {
809 if( needOutput() )
810 mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );
811 }
812}
813
814/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
HRESULT createInstance(REFIID iid, Ifc **ppIfc)
TYPE getWidth() const
TYPE getHeight() const
bool equalZero() const
rtl::Reference< CanvasFont > ImplRef
void setDevice(css::rendering::XGraphicDevice &rDevice)
Initialize canvas helper.
css::rendering::XGraphicDevice * mpDevice
Phyical output device.
::basegfx::B2ISize maOutputOffset
Current (transformation-independent) output buffer offset.
css::uno::Reference< css::rendering::XCachedPrimitive > drawBitmapModulated(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XBitmap > &xBitmap, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Reference< css::rendering::XCachedPrimitive > drawText(const css::rendering::XCanvas *pCanvas, const css::rendering::StringContext &text, const css::uno::Reference< css::rendering::XCanvasFont > &xFont, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, sal_Int8 textDirection)
css::uno::Reference< css::rendering::XCachedPrimitive > fillPolyPolygon(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Reference< css::rendering::XCachedPrimitive > strokePolyPolygon(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, const css::rendering::StrokeAttributes &strokeAttributes)
Gdiplus::CompositingMode calcCompositingMode(sal_Int8 nMode)
void drawLine(const css::rendering::XCanvas *pCanvas, const css::geometry::RealPoint2D &aStartPoint, const css::geometry::RealPoint2D &aEndPoint, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Sequence< css::rendering::FontInfo > queryAvailableFonts(const css::rendering::XCanvas *pCanvas, const css::rendering::FontInfo &aFilter, const css::uno::Sequence< css::beans::PropertyValue > &aFontProperties)
void drawPoint(const css::rendering::XCanvas *pCanvas, const css::geometry::RealPoint2D &aPoint, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Reference< css::rendering::XCanvasFont > createFont(const css::rendering::XCanvas *pCanvas, const css::rendering::FontRequest &fontRequest, const css::uno::Sequence< css::beans::PropertyValue > &extraFontProperties, const css::geometry::Matrix2D &fontMatrix)
css::uno::Reference< css::rendering::XCachedPrimitive > drawPolyPolygon(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Reference< css::rendering::XCachedPrimitive > drawBitmap(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XBitmap > &xBitmap, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
GDIPlusUserSharedPtr mpGdiPlusUser
Refcounted global GDI+ state container.
void setTarget(const GraphicsProviderSharedPtr &rTarget)
Set the target for rendering operations.
GraphicsProviderSharedPtr mpGraphicsProvider
Provides the Gdiplus::Graphics to render into.
css::uno::Reference< css::rendering::XCachedPrimitive > strokeTextureMappedPolyPolygon(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, const css::uno::Sequence< css::rendering::Texture > &textures, const css::uno::Reference< css::geometry::XMapping2D > &xMapping, const css::rendering::StrokeAttributes &strokeAttributes)
css::uno::Reference< css::rendering::XCachedPrimitive > drawTextLayout(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XTextLayout > &laidOutText, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
void disposing()
Release all references.
css::uno::Reference< css::rendering::XPolyPolygon2D > queryStrokeShapes(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, const css::rendering::StrokeAttributes &strokeAttributes)
css::uno::Reference< css::rendering::XCachedPrimitive > strokeTexturedPolyPolygon(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, const css::uno::Sequence< css::rendering::Texture > &textures, const css::rendering::StrokeAttributes &strokeAttributes)
css::uno::Reference< css::rendering::XCachedPrimitive > fillTextureMappedPolyPolygon(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, const css::uno::Sequence< css::rendering::Texture > &textures, const css::uno::Reference< css::geometry::XMapping2D > &xMapping)
void setupGraphicsState(GraphicsSharedPtr const &rGraphics, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
void drawBezier(const css::rendering::XCanvas *pCanvas, const css::geometry::RealBezierSegment2D &aBezierSegment, const css::geometry::RealPoint2D &aEndPoint, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Reference< css::rendering::XGraphicDevice > getDevice()
#define ENSURE_OR_THROW(c, m)
sal::systools::COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:169
FilterGroup & rTarget
#define SAL_WARN(area, stream)
def text(shape, orig_st)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
::basegfx::B2DHomMatrix & getViewStateTransform(::basegfx::B2DHomMatrix &transform, const rendering::ViewState &viewState)
Definition: canvastools.cxx:94
std::shared_ptr< ::cppcanvas::Bitmap > BitmapSharedPtr
GraphicsPathSharedPtr graphicsPathFromXPolyPolygon2D(const uno::Reference< rendering::XPolyPolygon2D > &xPoly, bool bNoLineJoin)
bool drawVCLBitmapFromXBitmap(const std::shared_ptr< Gdiplus::Graphics > &rGraphics, const uno::Reference< rendering::XBitmap > &xBitmap)
CanvasFont::ImplRef canvasFontFromXFont(const uno::Reference< rendering::XCanvasFont > &xFont)
BitmapSharedPtr bitmapFromXBitmap(const uno::Reference< rendering::XBitmap > &xBitmap)
void gdiPlusMatrixFromB2DHomMatrix(Gdiplus::Matrix &rGdiplusMatrix, const ::basegfx::B2DHomMatrix &rMatrix)
Gdiplus::ARGB sequenceToArgb(const uno::Sequence< sal_Int8 > &rColor)
void setModulateImageAttributes(Gdiplus::ImageAttributes &o_rAttr, double nRedModulation, double nGreenModulation, double nBlueModulation, double nAlphaModulation)
bool drawGdiPlusBitmap(const GraphicsSharedPtr &rGraphics, const BitmapSharedPtr &rBitmap)
std::shared_ptr< Gdiplus::Graphics > GraphicsSharedPtr
Definition: dx_winstuff.hxx:58
std::shared_ptr< IBitmap > IBitmapSharedPtr
Definition: dx_ibitmap.hxx:58
std::shared_ptr< GraphicsProvider > GraphicsProviderSharedPtr
std::shared_ptr< Gdiplus::GraphicsPath > GraphicsPathSharedPtr
Definition: dx_winstuff.hxx:59
return hr
signed char sal_Int8