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 gdiCapFromCap( 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  "gdiCapFromCap(): Unexpected cap type" );
70  }
71 
72  return Gdiplus::LineCapFlat;
73  }
74 
75  Gdiplus::LineJoin gdiJoinFromJoin( sal_Int8 nJoinType )
76  {
77  switch( nJoinType )
78  {
79  case rendering::PathJoinType::NONE:
80  SAL_WARN( "canvas.directx", "gdiJoinFromJoin(): Join NONE not possible, mapping to BEVEL (closest to NONE)" );
81  return Gdiplus::LineJoinBevel;
82 
83  case rendering::PathJoinType::MITER:
84  // in GDI+ fallback to Bevel, if miter limit is exceeded, is not done
85  // by Gdiplus::LineJoinMiter but by Gdiplus::LineJoinMiterClipped
86  return Gdiplus::LineJoinMiterClipped;
87 
88  case rendering::PathJoinType::ROUND:
89  return Gdiplus::LineJoinRound;
90 
91  case rendering::PathJoinType::BEVEL:
92  return Gdiplus::LineJoinBevel;
93 
94  default:
95  ENSURE_OR_THROW( false,
96  "gdiJoinFromJoin(): Unexpected join type" );
97  }
98 
99  return Gdiplus::LineJoinMiter;
100  }
101  }
102 
104  mpGdiPlusUser( GDIPlusUser::createInstance() ),
105  mpDevice( nullptr ),
106  mpGraphicsProvider(),
107  maOutputOffset()
108  {
109  }
110 
112  {
113  mpGraphicsProvider.reset();
114  mpDevice = nullptr;
115  mpGdiPlusUser.reset();
116  }
117 
118  void CanvasHelper::setDevice( rendering::XGraphicDevice& rDevice )
119  {
120  mpDevice = &rDevice;
121  }
122 
124  {
125  ENSURE_OR_THROW( rTarget,
126  "CanvasHelper::setTarget(): Invalid target" );
128  "CanvasHelper::setTarget(): target set, old target would be overwritten" );
129 
130  mpGraphicsProvider = rTarget;
131  }
132 
134  const ::basegfx::B2ISize& rOutputOffset )
135  {
136  ENSURE_OR_THROW( rTarget,
137  "CanvasHelper::setTarget(): invalid target" );
139  "CanvasHelper::setTarget(): target set, old target would be overwritten" );
140 
141  mpGraphicsProvider = rTarget;
142  maOutputOffset = rOutputOffset;
143  }
144 
146  {
147  if( needOutput() )
148  {
149  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
150  Gdiplus::Color aClearColor{Gdiplus::ARGB(Gdiplus::Color::White)};
151 
153  Gdiplus::Ok == pGraphics->SetCompositingMode(
154  Gdiplus::CompositingModeSourceCopy ), // force set, don't blend
155  "CanvasHelper::clear(): GDI+ SetCompositingMode call failed" );
157  Gdiplus::Ok == pGraphics->Clear( aClearColor ),
158  "CanvasHelper::clear(): GDI+ Clear call failed" );
159  }
160  }
161 
162  void CanvasHelper::drawPoint( const rendering::XCanvas* /*pCanvas*/,
163  const geometry::RealPoint2D& aPoint,
164  const rendering::ViewState& viewState,
165  const rendering::RenderState& renderState )
166  {
167  if( needOutput() )
168  {
169  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
170 
171  setupGraphicsState( pGraphics, viewState, renderState );
172 
173  Gdiplus::SolidBrush aBrush(
174  Gdiplus::Color(
175  tools::sequenceToArgb(renderState.DeviceColor)) );
176 
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);
183 
184  // paint a one-by-one circle, with the given point
185  // in the middle (rounded to float)
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" );
194  }
195  }
196 
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 )
202  {
203  if( needOutput() )
204  {
205  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
206 
207  setupGraphicsState( pGraphics, viewState, renderState );
208 
209  Gdiplus::Pen aPen(
210  Gdiplus::Color(
211  tools::sequenceToArgb(renderState.DeviceColor)),
212  Gdiplus::REAL(0.0) );
213 
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 );
222 
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 );
229 
231  Gdiplus::Ok == hr,
232  "CanvasHelper::drawLine(): GDI+ call failed" );
233  }
234  }
235 
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 )
241  {
242  if( needOutput() )
243  {
244  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
245 
246  setupGraphicsState( pGraphics, viewState, renderState );
247 
248  Gdiplus::Pen aPen(
249  Gdiplus::Color(
250  tools::sequenceToArgb(renderState.DeviceColor)),
251  Gdiplus::REAL(0.0) );
252 
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 );
261 
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) );
271 
272  pGraphics->SetPixelOffsetMode( aOldMode );
273 
275  Gdiplus::Ok == hr,
276  "CanvasHelper::drawBezier(): GDI+ call failed" );
277  }
278  }
279 
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 )
284  {
285  ENSURE_OR_THROW( xPolyPolygon.is(),
286  "CanvasHelper::drawPolyPolygon: polygon is NULL");
287 
288  if( needOutput() )
289  {
290  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
291 
292  setupGraphicsState( pGraphics, viewState, renderState );
293 
294  Gdiplus::Pen aPen(
295  Gdiplus::Color(
296  tools::sequenceToArgb(renderState.DeviceColor)),
297  Gdiplus::REAL(0.0) );
298 
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 );
307 
309 
310  // TODO(E1): Return value
311  Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
312 
313  pGraphics->SetPixelOffsetMode( aOldMode );
314 
316  Gdiplus::Ok == hr,
317  "CanvasHelper::drawPolyPolygon(): GDI+ call failed" );
318  }
319 
320  // TODO(P1): Provide caching here.
321  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
322  }
323 
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 )
329  {
330  ENSURE_OR_THROW( xPolyPolygon.is(),
331  "CanvasHelper::drawPolyPolygon: polygon is NULL");
332 
333  if( needOutput() )
334  {
335  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
336 
337  setupGraphicsState( pGraphics, viewState, renderState );
338 
339 
340  // Setup stroke pen
341 
342 
343  Gdiplus::Pen aPen(
344  Gdiplus::Color(
345  tools::sequenceToArgb(renderState.DeviceColor)),
346  static_cast< Gdiplus::REAL >(strokeAttributes.StrokeWidth) );
347 
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 );
356 
357  const bool bIsMiter(rendering::PathJoinType::MITER == strokeAttributes.JoinType);
358  const bool bIsNone(rendering::PathJoinType::NONE == strokeAttributes.JoinType);
359 
360  if(bIsMiter)
361  aPen.SetMiterLimit( static_cast< Gdiplus::REAL >(strokeAttributes.MiterLimit) );
362 
363  const std::vector< Gdiplus::REAL >& rDashArray(
364  ::comphelper::sequenceToContainer< std::vector< Gdiplus::REAL >, double >(
365  strokeAttributes.DashArray ) );
366  if( !rDashArray.empty() )
367  {
368  aPen.SetDashPattern( rDashArray.data(),
369  rDashArray.size() );
370  }
371  aPen.SetLineCap( gdiCapFromCap(strokeAttributes.StartCapType),
372  gdiCapFromCap(strokeAttributes.EndCapType),
373  Gdiplus::DashCapFlat );
374  if(!bIsNone)
375  aPen.SetLineJoin( gdiJoinFromJoin(strokeAttributes.JoinType) );
376 
377  GraphicsPathSharedPtr pPath( tools::graphicsPathFromXPolyPolygon2D( xPolyPolygon, bIsNone ) );
378 
379  // TODO(E1): Return value
380  Gdiplus::Status hr = pGraphics->DrawPath( &aPen, pPath.get() );
381 
382  pGraphics->SetPixelOffsetMode( aOldMode );
383 
385  Gdiplus::Ok == hr,
386  "CanvasHelper::strokePolyPolygon(): GDI+ call failed" );
387  }
388 
389  // TODO(P1): Provide caching here.
390  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
391  }
392 
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*/ )
399  {
400  // TODO
401  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
402  }
403 
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*/ )
411  {
412  // TODO
413  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
414  }
415 
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*/ )
421  {
422  // TODO
423  return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
424  }
425 
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 )
430  {
431  ENSURE_OR_THROW( xPolyPolygon.is(),
432  "CanvasHelper::fillPolyPolygon: polygon is NULL");
433 
434  if( needOutput() )
435  {
436  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
437 
438  setupGraphicsState( pGraphics, viewState, renderState );
439 
440  Gdiplus::SolidBrush aBrush(
441  tools::sequenceToArgb(renderState.DeviceColor));
442 
444 
445  // TODO(F1): FillRule
446  ENSURE_OR_THROW( Gdiplus::Ok == pGraphics->FillPath( &aBrush, pPath.get() ),
447  "CanvasHelper::fillPolyPolygon(): GDI+ call failed " );
448  }
449 
450  // TODO(P1): Provide caching here.
451  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
452  }
453 
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*/ )
460  {
461  // TODO
462  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
463  }
464 
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 )
469  {
470  if( needOutput() )
471  {
472  return uno::Reference< rendering::XCanvasFont >(
473  new CanvasFont(fontRequest, extraFontProperties, fontMatrix ) );
474  }
475 
476  return uno::Reference< rendering::XCanvasFont >();
477  }
478 
479  uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* /*pCanvas*/,
480  const rendering::FontInfo& /*aFilter*/,
481  const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ )
482  {
483  // TODO
484  return uno::Sequence< rendering::FontInfo >();
485  }
486 
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*/ )
493  {
494  ENSURE_OR_THROW( xFont.is(),
495  "CanvasHelper::drawText: font is NULL");
496 
497  if( needOutput() )
498  {
499  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
500 
501  setupGraphicsState( pGraphics, viewState, renderState );
502 
503  Gdiplus::SolidBrush aBrush(
504  Gdiplus::Color(
505  tools::sequenceToArgb(renderState.DeviceColor)));
506 
507  CanvasFont::ImplRef pFont(
509 
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())) );
516 
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...
522 
523  // TODO(F2): Proper layout (BiDi, CTL)! IMHO must use
524  // DrawDriverString here, and perform layouting myself...
526  Gdiplus::Ok == pGraphics->DrawString( o3tl::toW(text.Text.copy( text.StartPosition,
527  text.Length ).getStr()),
528  text.Length,
529  pFont->getFont().get(),
530  aPoint,
531  &aBrush ),
532  "CanvasHelper::drawText(): GDI+ call failed" );
533  }
534 
535  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
536  }
537 
538  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* /*pCanvas*/,
539  const uno::Reference< rendering::XTextLayout >& xLayoutetText,
540  const rendering::ViewState& viewState,
541  const rendering::RenderState& renderState )
542  {
543  ENSURE_OR_THROW( xLayoutetText.is(),
544  "CanvasHelper::drawTextLayout: layout is NULL");
545 
546  if( needOutput() )
547  {
548  TextLayout* pTextLayout =
549  dynamic_cast< TextLayout* >( xLayoutetText.get() );
550 
551  ENSURE_OR_THROW( pTextLayout,
552  "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
553 
554  pTextLayout->draw( mpGraphicsProvider->getGraphics(),
555  viewState,
556  renderState,
558  mpDevice,
559  false );
560  }
561 
562  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
563  }
564 
565  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* /*pCanvas*/,
566  const uno::Reference< rendering::XBitmap >& xBitmap,
567  const rendering::ViewState& viewState,
568  const rendering::RenderState& renderState )
569  {
570  ENSURE_OR_THROW( xBitmap.is(),
571  "CanvasHelper::drawBitmap: bitmap is NULL");
572 
573  if( needOutput() )
574  {
575  // check whether one of our own objects - need to retrieve
576  // bitmap _before_ calling
577  // GraphicsProvider::getGraphics(), to avoid locking our
578  // own surface.
579  BitmapSharedPtr pGdiBitmap;
580  BitmapProvider* pBitmap = dynamic_cast< BitmapProvider* >(xBitmap.get());
581  if( pBitmap )
582  {
583  IBitmapSharedPtr pDXBitmap( pBitmap->getBitmap() );
584  if( pDXBitmap )
585  pGdiBitmap = pDXBitmap->getBitmap();
586  }
587 
588  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
589  setupGraphicsState( pGraphics, viewState, renderState );
590 
591  if( pGdiBitmap )
592  tools::drawGdiPlusBitmap(pGraphics,pGdiBitmap);
593  else
595  xBitmap);
596  }
597 
598  // TODO(P1): Provide caching here.
599  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
600  }
601 
602  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
603  const uno::Reference< rendering::XBitmap >& xBitmap,
604  const rendering::ViewState& viewState,
605  const rendering::RenderState& renderState )
606  {
607  ENSURE_OR_THROW( xBitmap.is(),
608  "CanvasHelper::drawBitmap: bitmap is NULL");
609 
610  // no color set -> this is equivalent to a plain drawBitmap(), then
611  if( renderState.DeviceColor.getLength() < 3 )
612  return drawBitmap( pCanvas, xBitmap, viewState, renderState );
613 
614  if( needOutput() )
615  {
616  GraphicsSharedPtr pGraphics( mpGraphicsProvider->getGraphics() );
617 
618  setupGraphicsState( pGraphics, viewState, renderState );
619 
620  BitmapSharedPtr pBitmap( tools::bitmapFromXBitmap( xBitmap ) );
621  Gdiplus::Rect aRect( 0, 0,
622  pBitmap->GetWidth(),
623  pBitmap->GetHeight() );
624 
625  // Setup an ImageAttributes with an alpha-modulating
626  // color matrix.
627  rendering::ARGBColor aARGBColor(
628  mpDevice->getDeviceColorSpace()->convertToARGB(renderState.DeviceColor)[0]);
629 
630  Gdiplus::ImageAttributes aImgAttr;
632  aARGBColor.Red,
633  aARGBColor.Green,
634  aARGBColor.Blue,
635  aARGBColor.Alpha );
636 
638  Gdiplus::Ok == pGraphics->DrawImage( pBitmap.get(),
639  aRect,
640  0, 0,
641  pBitmap->GetWidth(),
642  pBitmap->GetHeight(),
643  Gdiplus::UnitPixel,
644  &aImgAttr ),
645  "CanvasHelper::drawBitmapModulated(): GDI+ call failed" );
646  }
647 
648  // TODO(P1): Provide caching here.
649  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
650  }
651 
652  uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
653  {
654  return uno::Reference< rendering::XGraphicDevice >(mpDevice);
655  }
656 
657  // private helper
658 
659 
660  Gdiplus::CompositingMode CanvasHelper::calcCompositingMode( sal_Int8 nMode )
661  {
662  Gdiplus::CompositingMode aRet( Gdiplus::CompositingModeSourceOver );
663 
664  switch( nMode )
665  {
666  case rendering::CompositeOperation::OVER:
667  case rendering::CompositeOperation::CLEAR:
668  aRet = Gdiplus::CompositingModeSourceOver;
669  break;
670 
671  case rendering::CompositeOperation::SOURCE:
672  aRet = Gdiplus::CompositingModeSourceCopy;
673  break;
674 
675  case rendering::CompositeOperation::DESTINATION:
676  case rendering::CompositeOperation::UNDER:
677  case rendering::CompositeOperation::INSIDE:
678  case rendering::CompositeOperation::INSIDE_REVERSE:
679  case rendering::CompositeOperation::OUTSIDE:
680  case rendering::CompositeOperation::OUTSIDE_REVERSE:
681  case rendering::CompositeOperation::ATOP:
682  case rendering::CompositeOperation::ATOP_REVERSE:
683  case rendering::CompositeOperation::XOR:
684  case rendering::CompositeOperation::ADD:
685  case rendering::CompositeOperation::SATURATE:
686  // TODO(F2): Problem, because GDI+ only knows about two compositing modes
687  aRet = Gdiplus::CompositingModeSourceOver;
688  break;
689 
690  default:
691  ENSURE_OR_THROW( false, "CanvasHelper::calcCompositingMode: unexpected mode" );
692  break;
693  }
694 
695  return aRet;
696  }
697 
698  void CanvasHelper::setupGraphicsState( GraphicsSharedPtr const & rGraphics,
699  const rendering::ViewState& viewState,
700  const rendering::RenderState& renderState )
701  {
703  "CanvasHelper::setupGraphicsState: primary graphics invalid" );
705  "CanvasHelper::setupGraphicsState: reference device invalid" );
706 
707  // setup view transform first. Clipping e.g. depends on it
708  ::basegfx::B2DHomMatrix aTransform;
709  ::canvas::tools::getViewStateTransform(aTransform, viewState);
710 
711  // add output offset
712  if( !maOutputOffset.equalZero() )
713  {
716  aTransform = aOutputOffset * aTransform;
717  }
718 
719  Gdiplus::Matrix aMatrix;
720  tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
721 
723  Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
724  "CanvasHelper::setupGraphicsState(): Failed to set GDI+ transformation" );
725 
726  // setup view and render state clipping
728  Gdiplus::Ok == rGraphics->ResetClip(),
729  "CanvasHelper::setupGraphicsState(): Failed to reset GDI+ clip" );
730 
731  if( viewState.Clip.is() )
732  {
733  GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( viewState.Clip ) );
734 
735  // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
736  // Try SetClip( Rect ) or similar for simple clip paths (need some support in
737  // LinePolyPolygon, then)
739  Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
740  Gdiplus::CombineModeIntersect ),
741  "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
742  }
743 
744  // setup overall transform only now. View clip above was relative to
745  // view transform
747  viewState,
748  renderState);
749 
750  // add output offset
751  if( !maOutputOffset.equalZero() )
752  {
755  aTransform = aOutputOffset * aTransform;
756  }
757 
758  tools::gdiPlusMatrixFromB2DHomMatrix( aMatrix, aTransform );
759 
761  Gdiplus::Ok == rGraphics->SetTransform( &aMatrix ),
762  "CanvasHelper::setupGraphicsState(): Cannot set GDI+ transformation" );
763 
764  if( renderState.Clip.is() )
765  {
766  GraphicsPathSharedPtr aClipPath( tools::graphicsPathFromXPolyPolygon2D( renderState.Clip ) );
767 
768  // TODO(P3): Cache clip. SetClip( GraphicsPath ) performs abyssmally on GDI+.
769  // Try SetClip( Rect ) or similar for simple clip paths (need some support in
770  // LinePolyPolygon, then)
772  Gdiplus::Ok == rGraphics->SetClip( aClipPath.get(),
773  Gdiplus::CombineModeIntersect ),
774  "CanvasHelper::setupGraphicsState(): Cannot set GDI+ clip" );
775  }
776 
777  // setup compositing
778  const Gdiplus::CompositingMode eCompositing( calcCompositingMode( renderState.CompositeOperation ) );
780  Gdiplus::Ok == rGraphics->SetCompositingMode( eCompositing ),
781  "CanvasHelper::setupGraphicsState(): Cannot set GDI* compositing mode)" );
782  }
783 
784  void CanvasHelper::flush() const
785  {
786  if( needOutput() )
787  mpGraphicsProvider->getGraphics()->Flush( Gdiplus::FlushIntentionSync );
788  }
789 }
790 
791 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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:59
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)
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)
COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:168
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
sal_Int32 getX() const
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.
bool equalZero() const
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:96
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:62
std::shared_ptr< GraphicsProvider > GraphicsProviderSharedPtr
bool drawVCLBitmapFromXBitmap(const std::shared_ptr< Gdiplus::Graphics > &rGraphics, const uno::Reference< rendering::XBitmap > &xBitmap)
sal_Int32 getY() const
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)
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:61