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>
34 #include <comphelper/sequence.hxx>
36 #include <rtl/math.hxx>
37 #include <tools/diagnose_ex.h>
38 
39 #include <canvas/canvastools.hxx>
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 
48 using namespace ::com::sun::star;
49 
50 namespace 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  {
148  ENSURE_OR_THROW( rTarget,
149  "CanvasHelper::setTarget(): Invalid target" );
151  "CanvasHelper::setTarget(): target set, old target would be overwritten" );
152 
153  mpGraphicsProvider = rTarget;
154  }
155 
157  const ::basegfx::B2ISize& rOutputOffset )
158  {
159  ENSURE_OR_THROW( rTarget,
160  "CanvasHelper::setTarget(): invalid target" );
162  "CanvasHelper::setTarget(): target set, old target would be overwritten" );
163 
164  mpGraphicsProvider = rTarget;
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 
530  CanvasFont::ImplRef pFont(
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 
721  void CanvasHelper::setupGraphicsState( GraphicsSharedPtr const & rGraphics,
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
735  if( !maOutputOffset.equalZero() )
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  {
756  GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState.Clip ) );
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
774  if( !maOutputOffset.equalZero() )
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 
807  void CanvasHelper::flush() const
808  {
809  if( needOutput() )
810  mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );
811  }
812 }
813 
814 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 getY() const
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)
GraphicsPathSharedPtr graphicsPathFromXPolyPolygon2D(const uno::Reference< rendering::XPolyPolygon2D > &xPoly, bool bNoLineJoin)
void drawPoint(const css::rendering::XCanvas *pCanvas, const css::geometry::RealPoint2D &aPoint, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
::basegfx::B2DHomMatrix & mergeViewAndRenderTransform(::basegfx::B2DHomMatrix &combinedTransform, const rendering::ViewState &viewState, const rendering::RenderState &renderState)
std::shared_ptr< IBitmap > IBitmapSharedPtr
Definition: dx_ibitmap.hxx:58
signed char sal_Int8
EXTERN_C BOOL BOOL const wchar_t *pProgramPath HRESULT hr
void setModulateImageAttributes(Gdiplus::ImageAttributes &o_rAttr, double nRedModulation, double nGreenModulation, double nBlueModulation, double nAlphaModulation)
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 > 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::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)
Gdiplus::ARGB sequenceToArgb(const uno::Sequence< sal_Int8 > &rColor)
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 disposing()
Release all references.
void setDevice(css::rendering::XGraphicDevice &rDevice)
Initialize canvas helper.
void setTarget(const GraphicsProviderSharedPtr &rTarget)
Set the target for rendering operations.
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)
bool equalZero() const
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)
DstType sequenceToContainer(const css::uno::Sequence< SrcType > &i_Sequence)
bool drawGdiPlusBitmap(const GraphicsSharedPtr &rGraphics, const BitmapSharedPtr &rBitmap)
Gdiplus::CompositingMode calcCompositingMode(sal_Int8 nMode)
::basegfx::B2ISize maOutputOffset
Current (transformation-independent) output buffer offset.
GDIPlusUserSharedPtr mpGdiPlusUser
Refcounted global GDI+ state container.
std::shared_ptr< ::cppcanvas::Bitmap > BitmapSharedPtr
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::rendering::XGraphicDevice * mpDevice
Phyical output device.
sal::systools::COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:169
HRESULT createInstance(REFIID iid, Ifc **ppIfc)
void gdiPlusMatrixFromB2DHomMatrix(Gdiplus::Matrix &rGdiplusMatrix, const ::basegfx::B2DHomMatrix &rMatrix)
void setupGraphicsState(GraphicsSharedPtr const &rGraphics, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
::basegfx::B2DHomMatrix & getViewStateTransform(::basegfx::B2DHomMatrix &transform, const rendering::ViewState &viewState)
Definition: canvastools.cxx:94
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)
#define ENSURE_OR_THROW(c, m)
CanvasFont::ImplRef canvasFontFromXFont(const uno::Reference< rendering::XCanvasFont > &xFont)
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)
css::uno::Reference< css::rendering::XGraphicDevice > getDevice()
std::shared_ptr< Gdiplus::GraphicsPath > GraphicsPathSharedPtr
Definition: dx_winstuff.hxx:59
std::shared_ptr< GraphicsProvider > GraphicsProviderSharedPtr
bool drawVCLBitmapFromXBitmap(const std::shared_ptr< Gdiplus::Graphics > &rGraphics, const uno::Reference< rendering::XBitmap > &xBitmap)
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)
rtl::Reference< CanvasFont > ImplRef
GraphicsProviderSharedPtr mpGraphicsProvider
Provides the Gdiplus::Graphics to render into.
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)
#define SAL_WARN(area, stream)
sal_Int32 getX() const
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)
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)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
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)
BitmapSharedPtr bitmapFromXBitmap(const uno::Reference< rendering::XBitmap > &xBitmap)
std::shared_ptr< Gdiplus::Graphics > GraphicsSharedPtr
Definition: dx_winstuff.hxx:58
bool m_bDetectedRangeSegmentation false