LibreOffice Module canvas (master)  1
cairo_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 #include <tuple>
25 
31 #include <basegfx/utils/lerp.hxx>
32 #include <com/sun/star/rendering/ColorComponentTag.hpp>
33 #include <com/sun/star/rendering/ColorSpaceType.hpp>
34 #include <com/sun/star/rendering/CompositeOperation.hpp>
35 #include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
36 #include <com/sun/star/rendering/PathCapType.hpp>
37 #include <com/sun/star/rendering/PathJoinType.hpp>
38 #include <com/sun/star/rendering/RenderingIntent.hpp>
39 #include <com/sun/star/rendering/TexturingMode.hpp>
40 #include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
41 #include <com/sun/star/util/Endianness.hpp>
42 #include <comphelper/sequence.hxx>
43 #include <cppuhelper/implbase.hxx>
44 #include <rtl/math.hxx>
45 #include <tools/diagnose_ex.h>
46 #include <vcl/bitmapex.hxx>
47 #include <vcl/BitmapTools.hxx>
48 #include <vcl/canvastools.hxx>
49 #include <vcl/virdev.hxx>
50 
51 #include <canvas/canvastools.hxx>
52 #include <parametricpolypolygon.hxx>
53 #include <cairo.h>
54 
55 #include "cairo_cachedbitmap.hxx"
56 #include "cairo_canvasbitmap.hxx"
57 #include "cairo_canvashelper.hxx"
58 
59 using namespace ::cairo;
60 using namespace ::com::sun::star;
61 
62 namespace cairocanvas
63 {
65  mpSurfaceProvider(nullptr),
66  mpDevice(nullptr),
67  mbHaveAlpha()
68  {
69  }
70 
72  {
73  mpSurface.reset();
74  mpCairo.reset();
76  mpDevice = nullptr;
77  mpSurfaceProvider = nullptr;
78  }
79 
80  void CanvasHelper::init( const ::basegfx::B2ISize& rSizePixel,
81  SurfaceProvider& rSurfaceProvider,
82  rendering::XGraphicDevice* pDevice )
83  {
84  maSize = rSizePixel;
85  mpSurfaceProvider = &rSurfaceProvider;
86  mpDevice = pDevice;
87  }
88 
89  void CanvasHelper::setSize( const ::basegfx::B2ISize& rSize )
90  {
91  maSize = rSize;
92  }
93 
94  void CanvasHelper::setSurface( const SurfaceSharedPtr& pSurface, bool bHasAlpha )
95  {
96  mbHaveAlpha = bHasAlpha;
98  mpSurface = pSurface;
99  mpCairo = pSurface->getCairo();
100  }
101 
102  static void setColor( cairo_t* pCairo,
103  const uno::Sequence<double>& rColor )
104  {
105  if( rColor.getLength() > 3 )
106  {
107  cairo_set_source_rgba( pCairo,
108  rColor[0],
109  rColor[1],
110  rColor[2],
111  rColor[3] );
112  }
113  else if( rColor.getLength() == 3 )
114  cairo_set_source_rgb( pCairo,
115  rColor[0],
116  rColor[1],
117  rColor[2] );
118  }
119 
120  void CanvasHelper::useStates( const rendering::ViewState& viewState,
121  const rendering::RenderState& renderState,
122  bool bSetColor )
123  {
124  cairo_matrix_t aViewMatrix;
125  cairo_matrix_t aRenderMatrix;
126  cairo_matrix_t aCombinedMatrix;
127 
128  cairo_matrix_init( &aViewMatrix,
129  viewState.AffineTransform.m00, viewState.AffineTransform.m10, viewState.AffineTransform.m01,
130  viewState.AffineTransform.m11, viewState.AffineTransform.m02, viewState.AffineTransform.m12);
131  cairo_matrix_init( &aRenderMatrix,
132  renderState.AffineTransform.m00, renderState.AffineTransform.m10, renderState.AffineTransform.m01,
133  renderState.AffineTransform.m11, renderState.AffineTransform.m02, renderState.AffineTransform.m12);
134  cairo_matrix_multiply( &aCombinedMatrix, &aRenderMatrix, &aViewMatrix);
135 
136  if( viewState.Clip.is() )
137  {
138  SAL_INFO( "canvas.cairo", "view clip");
139 
140  aViewMatrix.x0 = basegfx::fround( aViewMatrix.x0 );
141  aViewMatrix.y0 = basegfx::fround( aViewMatrix.y0 );
142  cairo_set_matrix( mpCairo.get(), &aViewMatrix );
143  doPolyPolygonPath( viewState.Clip, Clip );
144  }
145 
146  aCombinedMatrix.x0 = basegfx::fround( aCombinedMatrix.x0 );
147  aCombinedMatrix.y0 = basegfx::fround( aCombinedMatrix.y0 );
148  cairo_set_matrix( mpCairo.get(), &aCombinedMatrix );
149 
150  if( renderState.Clip.is() )
151  {
152  SAL_INFO( "canvas.cairo", "render clip BEGIN");
153 
154  doPolyPolygonPath( renderState.Clip, Clip );
155  SAL_INFO( "canvas.cairo", "render clip END");
156  }
157 
158  if( bSetColor )
159  setColor(mpCairo.get(),renderState.DeviceColor);
160 
161  cairo_operator_t compositingMode( CAIRO_OPERATOR_OVER );
162  switch( renderState.CompositeOperation )
163  {
164  case rendering::CompositeOperation::CLEAR:
165  compositingMode = CAIRO_OPERATOR_CLEAR;
166  break;
167  case rendering::CompositeOperation::SOURCE:
168  compositingMode = CAIRO_OPERATOR_SOURCE;
169  break;
170  case rendering::CompositeOperation::DESTINATION:
171  compositingMode = CAIRO_OPERATOR_DEST;
172  break;
173  case rendering::CompositeOperation::OVER:
174  compositingMode = CAIRO_OPERATOR_OVER;
175  break;
176  case rendering::CompositeOperation::UNDER:
177  compositingMode = CAIRO_OPERATOR_DEST;
178  break;
179  case rendering::CompositeOperation::INSIDE:
180  compositingMode = CAIRO_OPERATOR_IN;
181  break;
182  case rendering::CompositeOperation::INSIDE_REVERSE:
183  compositingMode = CAIRO_OPERATOR_OUT;
184  break;
185  case rendering::CompositeOperation::OUTSIDE:
186  compositingMode = CAIRO_OPERATOR_DEST_OVER;
187  break;
188  case rendering::CompositeOperation::OUTSIDE_REVERSE:
189  compositingMode = CAIRO_OPERATOR_DEST_OUT;
190  break;
191  case rendering::CompositeOperation::ATOP:
192  compositingMode = CAIRO_OPERATOR_ATOP;
193  break;
194  case rendering::CompositeOperation::ATOP_REVERSE:
195  compositingMode = CAIRO_OPERATOR_DEST_ATOP;
196  break;
197  case rendering::CompositeOperation::XOR:
198  compositingMode = CAIRO_OPERATOR_XOR;
199  break;
200  case rendering::CompositeOperation::ADD:
201  compositingMode = CAIRO_OPERATOR_ADD;
202  break;
203  case rendering::CompositeOperation::SATURATE:
204  compositingMode = CAIRO_OPERATOR_SATURATE;
205  break;
206  }
207  cairo_set_operator( mpCairo.get(), compositingMode );
208  }
209 
211  {
212  SAL_INFO( "canvas.cairo", "clear whole area: " << maSize.getX() << " x " << maSize.getY() );
213 
214  if( !mpCairo )
215  return;
216 
217  cairo_save( mpCairo.get() );
218 
219  cairo_identity_matrix( mpCairo.get() );
220  // this does not really differ from all-zero, as cairo
221  // internally converts to premultiplied alpha. but anyway,
222  // this keeps it consistent with the other canvas impls
223  if( mbHaveAlpha )
224  cairo_set_source_rgba( mpCairo.get(), 1.0, 1.0, 1.0, 0.0 );
225  else
226  cairo_set_source_rgb( mpCairo.get(), 1.0, 1.0, 1.0 );
227  cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
228 
229  cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
230  cairo_fill( mpCairo.get() );
231 
232  cairo_restore( mpCairo.get() );
233  }
234 
235  void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
236  const geometry::RealPoint2D& aStartPoint,
237  const geometry::RealPoint2D& aEndPoint,
238  const rendering::ViewState& viewState,
239  const rendering::RenderState& renderState )
240  {
241  if( !mpCairo )
242  return;
243 
244  cairo_save( mpCairo.get() );
245 
246  cairo_set_line_width( mpCairo.get(), 1 );
247 
248  useStates( viewState, renderState, true );
249 
250  cairo_move_to( mpCairo.get(), aStartPoint.X + 0.5, aStartPoint.Y + 0.5 );
251  cairo_line_to( mpCairo.get(), aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
252  cairo_stroke( mpCairo.get() );
253 
254  cairo_restore( mpCairo.get() );
255  }
256 
257  void CanvasHelper::drawBezier( const rendering::XCanvas* ,
258  const geometry::RealBezierSegment2D& aBezierSegment,
259  const geometry::RealPoint2D& aEndPoint,
260  const rendering::ViewState& viewState,
261  const rendering::RenderState& renderState )
262  {
263  if( !mpCairo )
264  return;
265 
266  cairo_save( mpCairo.get() );
267 
268  cairo_set_line_width( mpCairo.get(), 1 );
269 
270  useStates( viewState, renderState, true );
271 
272  cairo_move_to( mpCairo.get(), aBezierSegment.Px + 0.5, aBezierSegment.Py + 0.5 );
273  // tdf#99165 correction of control points not needed here, only hairlines drawn
274  // (see cairo_set_line_width above)
275  cairo_curve_to( mpCairo.get(),
276  aBezierSegment.C1x + 0.5, aBezierSegment.C1y + 0.5,
277  aBezierSegment.C2x + 0.5, aBezierSegment.C2y + 0.5,
278  aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
279  cairo_stroke( mpCairo.get() );
280 
281  cairo_restore( mpCairo.get() );
282  }
283 
284 constexpr OUStringLiteral PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME = u"Canvas::ParametricPolyPolygon";
285 
295  static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
296  {
297  CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
298  if( pBitmapImpl )
299  return pBitmapImpl->getSurface();
300 
301  SurfaceProvider* pSurfaceProvider = dynamic_cast<SurfaceProvider*>( xBitmap.get() );
302  if( pSurfaceProvider )
303  return pSurfaceProvider->getSurface();
304 
305  return SurfaceSharedPtr();
306  }
307 
308  static ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
309  {
310  // TODO(F1): Add support for floating point bitmap formats
311  uno::Reference<rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap,
312  uno::UNO_QUERY_THROW);
314  if( !aBmpEx.IsEmpty() )
315  return aBmpEx;
316 
317  // TODO(F1): extract pixel from XBitmap interface
318  ENSURE_OR_THROW( false,
319  "bitmapExFromXBitmap(): could not extract BitmapEx" );
320 
321  return ::BitmapEx();
322  }
323 
335  static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, const SurfaceProviderRef& rSurfaceProvider, unsigned char*& data, bool& bHasAlpha )
336  {
337  bHasAlpha = xBitmap->hasAlpha();
338  SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap );
339  if( pSurface )
340  data = nullptr;
341  else
342  {
343  ::BitmapEx aBmpEx = bitmapExFromXBitmap(xBitmap);
344  ::Bitmap aBitmap = aBmpEx.GetBitmap();
345 
346  // there's no pixmap for alpha bitmap. we might still
347  // use rgb pixmap and only access alpha pixels the
348  // slow way. now we just speedup rgb bitmaps
349  if( !aBmpEx.IsAlpha() )
350  {
351  pSurface = rSurfaceProvider->createSurface( aBitmap );
352  data = nullptr;
353  bHasAlpha = false;
354  }
355 
356  if( !pSurface )
357  {
358  tools::Long nWidth;
359  tools::Long nHeight;
360  vcl::bitmap::CanvasCairoExtractBitmapData(aBmpEx, aBitmap, data, bHasAlpha, nWidth, nHeight);
361 
362  pSurface = rSurfaceProvider->getOutputDevice()->CreateSurface(
364  cairo_image_surface_create_for_data(
365  data,
366  bHasAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
367  nWidth, nHeight, nWidth*4 ),
368  &cairo_surface_destroy) );
369 
370  SAL_INFO( "canvas.cairo","image: " << nWidth << " x " << nHeight << " alpha: " << bHasAlpha);
371  }
372  }
373 
374  return pSurface;
375  }
376 
377  static void addColorStops( cairo_pattern_t* pPattern, const uno::Sequence< uno::Sequence< double > >& rColors, const uno::Sequence< double >& rStops, bool bReverseStops )
378  {
379  int i;
380 
381  OSL_ASSERT( rColors.getLength() == rStops.getLength() );
382 
383  for( i = 0; i < rColors.getLength(); i++ )
384  {
385  const uno::Sequence< double >& rColor( rColors[i] );
386  float stop = bReverseStops ? 1 - rStops[i] : rStops[i];
387  if( rColor.getLength() == 3 )
388  cairo_pattern_add_color_stop_rgb( pPattern, stop, rColor[0], rColor[1], rColor[2] );
389  else if( rColor.getLength() == 4 )
390  {
391  double alpha = rColor[3];
392  // cairo expects premultiplied alpha
393  cairo_pattern_add_color_stop_rgba( pPattern, stop, rColor[0]*alpha, rColor[1]*alpha, rColor[2]*alpha, alpha );
394  }
395  }
396  }
397 
398  static uno::Sequence<double> lerp(const uno::Sequence<double>& rLeft, const uno::Sequence<double>& rRight, double fAlpha)
399  {
400  if( rLeft.getLength() == 3 )
401  {
402  return
403  {
404  basegfx::utils::lerp(rLeft[0],rRight[0],fAlpha),
405  basegfx::utils::lerp(rLeft[1],rRight[1],fAlpha),
406  basegfx::utils::lerp(rLeft[2],rRight[2],fAlpha)
407  };
408  }
409  else if( rLeft.getLength() == 4 )
410  {
411  return
412  {
413  basegfx::utils::lerp(rLeft[0],rRight[0],fAlpha),
414  basegfx::utils::lerp(rLeft[1],rRight[1],fAlpha),
415  basegfx::utils::lerp(rLeft[2],rRight[2],fAlpha),
416  basegfx::utils::lerp(rLeft[3],rRight[3],fAlpha)
417  };
418  }
419 
420  return {};
421  }
422 
423  static cairo_pattern_t* patternFromParametricPolyPolygon( ::canvas::ParametricPolyPolygon const & rPolygon )
424  {
425  cairo_pattern_t* pPattern = nullptr;
426  const ::canvas::ParametricPolyPolygon::Values aValues = rPolygon.getValues();
427  double x0, x1, y0, y1, cx, cy, r0, r1;
428 
429  switch( aValues.meType )
430  {
431  case ::canvas::ParametricPolyPolygon::GradientType::Linear:
432  x0 = 0;
433  y0 = 0;
434  x1 = 1;
435  y1 = 0;
436  pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 );
437  addColorStops( pPattern, aValues.maColors, aValues.maStops, false );
438  break;
439 
440  case ::canvas::ParametricPolyPolygon::GradientType::Elliptical:
441  cx = 0;
442  cy = 0;
443  r0 = 0;
444  r1 = 1;
445 
446  pPattern = cairo_pattern_create_radial( cx, cy, r0, cy, cy, r1 );
447  addColorStops( pPattern, aValues.maColors, aValues.maStops, true );
448  break;
449  default:
450  break;
451  }
452 
453  return pPattern;
454  }
455 
456  static void doOperation( Operation aOperation,
457  cairo_t* pCairo,
458  const uno::Sequence< rendering::Texture >* pTextures,
459  const SurfaceProviderRef& pDevice,
460  const basegfx::B2DRange& rBounds )
461  {
462  switch( aOperation )
463  {
464  case Fill:
465  /* TODO: multitexturing */
466  if( pTextures )
467  {
468  const css::rendering::Texture& aTexture ( (*pTextures)[0] );
469  if( aTexture.Bitmap.is() )
470  {
471  unsigned char* data = nullptr;
472  bool bHasAlpha = false;
473  SurfaceSharedPtr pSurface = surfaceFromXBitmap( (*pTextures)[0].Bitmap, pDevice, data, bHasAlpha );
474 
475  if( pSurface )
476  {
477  cairo_pattern_t* pPattern;
478 
479  cairo_save( pCairo );
480 
481  css::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
482  cairo_matrix_t aScaleMatrix, aTextureMatrix, aScaledTextureMatrix;
483 
484  cairo_matrix_init( &aTextureMatrix,
485  aTransform.m00, aTransform.m10, aTransform.m01,
486  aTransform.m11, aTransform.m02, aTransform.m12);
487 
488  geometry::IntegerSize2D aSize = aTexture.Bitmap->getSize();
489 
490  cairo_matrix_init_scale( &aScaleMatrix, 1.0/aSize.Width, 1.0/aSize.Height );
491  cairo_matrix_multiply( &aScaledTextureMatrix, &aTextureMatrix, &aScaleMatrix );
492  cairo_matrix_invert( &aScaledTextureMatrix );
493 
494  // we don't care about repeat mode yet, so the workaround is disabled for now
495  pPattern = cairo_pattern_create_for_surface( pSurface->getCairoSurface().get() );
496 
497  if( aTexture.RepeatModeX == rendering::TexturingMode::REPEAT &&
498  aTexture.RepeatModeY == rendering::TexturingMode::REPEAT )
499  {
500  cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_REPEAT );
501  }
502  else if ( aTexture.RepeatModeX == rendering::TexturingMode::NONE &&
503  aTexture.RepeatModeY == rendering::TexturingMode::NONE )
504  {
505  cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_NONE );
506  }
507  else if ( aTexture.RepeatModeX == rendering::TexturingMode::CLAMP &&
508  aTexture.RepeatModeY == rendering::TexturingMode::CLAMP )
509  {
510  cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_PAD );
511  }
512 
513  aScaledTextureMatrix.x0 = basegfx::fround( aScaledTextureMatrix.x0 );
514  aScaledTextureMatrix.y0 = basegfx::fround( aScaledTextureMatrix.y0 );
515 
516  double x1, y1, x2, y2;
517  cairo_path_extents(pCairo, &x1, &y1, &x2, &y2);
518  aScaledTextureMatrix.x0 -= (x1 * aScaledTextureMatrix.xx);
519  aScaledTextureMatrix.y0 -= (y1 * aScaledTextureMatrix.yy);
520 
521  cairo_pattern_set_matrix( pPattern, &aScaledTextureMatrix );
522 
523  cairo_set_source( pCairo, pPattern );
524  if( !bHasAlpha )
525  cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
526  cairo_fill( pCairo );
527 
528  cairo_restore( pCairo );
529 
530  cairo_pattern_destroy( pPattern );
531  }
532 
533  if( data )
534  free( data );
535  }
536  else if( aTexture.Gradient.is() )
537  {
538  uno::Reference< lang::XServiceInfo > xRef( aTexture.Gradient, uno::UNO_QUERY );
539 
540  SAL_INFO( "canvas.cairo", "gradient fill" );
541  if( xRef.is() && xRef->getImplementationName() == PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME )
542  {
543  // TODO(Q1): Maybe use dynamic_cast here
544 
545  // TODO(E1): Return value
546  // TODO(F1): FillRule
547  SAL_INFO( "canvas.cairo", "known implementation" );
548 
549  ::canvas::ParametricPolyPolygon* pPolyImpl = static_cast< ::canvas::ParametricPolyPolygon* >( aTexture.Gradient.get() );
550  css::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
551  cairo_matrix_t aTextureMatrix;
552 
553  cairo_matrix_init( &aTextureMatrix,
554  aTransform.m00, aTransform.m10, aTransform.m01,
555  aTransform.m11, aTransform.m02, aTransform.m12);
557  {
558  // no general path gradient yet in cairo; emulate then
559  cairo_save( pCairo );
560  cairo_clip( pCairo );
561 
562  // fill bound rect with start color
563  cairo_rectangle( pCairo, rBounds.getMinX(), rBounds.getMinY(),
564  rBounds.getWidth(), rBounds.getHeight() );
565  setColor(pCairo,pPolyImpl->getValues().maColors[0]);
566  cairo_fill(pCairo);
567 
568  cairo_transform( pCairo, &aTextureMatrix );
569 
570  // longest line in gradient bound rect
571  const unsigned int nGradientSize(
572  static_cast<unsigned int>(
573  ::basegfx::B2DVector(rBounds.getMinimum() - rBounds.getMaximum()).getLength() + 1.0 ) );
574 
575  // typical number for pixel of the same color (strip size)
576  const unsigned int nStripSize( nGradientSize < 50 ? 2 : 4 );
577 
578  // use at least three steps, and at utmost the number of color
579  // steps
580  const unsigned int nStepCount(
581  std::max(
582  3U,
583  std::min(
584  nGradientSize / nStripSize,
585  128U )) + 1 );
586 
587  const uno::Sequence<double>* pColors=&pPolyImpl->getValues().maColors[0];
588  basegfx::utils::KeyStopLerp aLerper(pPolyImpl->getValues().maStops);
589  for( unsigned int i=1; i<nStepCount; ++i )
590  {
591  const double fT( i/double(nStepCount) );
592 
593  std::ptrdiff_t nIndex;
594  double fAlpha;
595  std::tie(nIndex,fAlpha)=aLerper.lerp(fT);
596 
597  setColor(pCairo, lerp(pColors[nIndex], pColors[nIndex+1], fAlpha));
598  cairo_rectangle( pCairo, -1+fT, -1+fT, 2-2*fT, 2-2*fT );
599  cairo_fill(pCairo);
600  }
601 
602  cairo_restore( pCairo );
603  }
604  else
605  {
606  cairo_pattern_t* pPattern = patternFromParametricPolyPolygon( *pPolyImpl );
607 
608  if( pPattern )
609  {
610  SAL_INFO( "canvas.cairo", "filling with pattern" );
611 
612  cairo_save( pCairo );
613 
614  cairo_transform( pCairo, &aTextureMatrix );
615  cairo_set_source( pCairo, pPattern );
616  cairo_fill( pCairo );
617  cairo_restore( pCairo );
618 
619  cairo_pattern_destroy( pPattern );
620  }
621  }
622  }
623  }
624  }
625  else
626  cairo_fill( pCairo );
627  SAL_INFO( "canvas.cairo", "fill");
628  break;
629  case Stroke:
630  cairo_stroke( pCairo );
631  SAL_INFO( "canvas.cairo", "stroke");
632  break;
633  case Clip:
634  cairo_clip( pCairo );
635  SAL_INFO( "canvas.cairo", "clip");
636  break;
637  }
638  }
639 
640  static void clipNULL( cairo_t *pCairo )
641  {
642  SAL_INFO( "canvas.cairo", "clipNULL");
643  cairo_matrix_t aOrigMatrix, aIdentityMatrix;
644 
645  /* we set identity matrix here to overcome bug in cairo 0.9.2
646  where XCreatePixmap is called with zero width and height.
647 
648  it also reaches faster path in cairo clipping code.
649  */
650  cairo_matrix_init_identity( &aIdentityMatrix );
651  cairo_get_matrix( pCairo, &aOrigMatrix );
652  cairo_set_matrix( pCairo, &aIdentityMatrix );
653 
654  cairo_reset_clip( pCairo );
655  cairo_rectangle( pCairo, 0, 0, 1, 1 );
656  cairo_clip( pCairo );
657  cairo_rectangle( pCairo, 2, 0, 1, 1 );
658  cairo_clip( pCairo );
659 
660  /* restore the original matrix */
661  cairo_set_matrix( pCairo, &aOrigMatrix );
662  }
663 
664  void doPolyPolygonImplementation( const ::basegfx::B2DPolyPolygon& aPolyPolygon,
665  Operation aOperation,
666  cairo_t* pCairo,
667  const uno::Sequence< rendering::Texture >* pTextures,
668  const SurfaceProviderRef& pDevice,
669  rendering::FillRule eFillrule )
670  {
671  if( pTextures )
672  ENSURE_ARG_OR_THROW( pTextures->hasElements(),
673  "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
674 
675  bool bOpToDo = false;
676  cairo_matrix_t aOrigMatrix, aIdentityMatrix;
677  double nX, nY, nBX, nBY, nAX, nAY, nLastX(0.0), nLastY(0.0);
678 
679  cairo_get_matrix( pCairo, &aOrigMatrix );
680  cairo_matrix_init_identity( &aIdentityMatrix );
681  cairo_set_matrix( pCairo, &aIdentityMatrix );
682 
683  cairo_set_fill_rule( pCairo,
684  eFillrule == rendering::FillRule_EVEN_ODD ?
685  CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING );
686 
687  for( sal_uInt32 nPolygonIndex = 0; nPolygonIndex < aPolyPolygon.count(); nPolygonIndex++ )
688  {
689  const ::basegfx::B2DPolygon& aPolygon( aPolyPolygon.getB2DPolygon( nPolygonIndex ) );
690  const sal_uInt32 nPointCount( aPolygon.count() );
691  // to correctly render closed curves, need to output first
692  // point twice (so output one additional point)
693  const sal_uInt32 nExtendedPointCount( nPointCount +
694  int(aPolygon.isClosed() && aPolygon.areControlPointsUsed()) );
695 
696  if( nPointCount > 1)
697  {
698  bool bIsBezier = aPolygon.areControlPointsUsed();
699  ::basegfx::B2DPoint aA, aB, aP;
700 
701  for( sal_uInt32 j=0; j < nExtendedPointCount; j++ )
702  {
703  aP = aPolygon.getB2DPoint( j % nPointCount );
704 
705  nX = aP.getX();
706  nY = aP.getY();
707  cairo_matrix_transform_point( &aOrigMatrix, &nX, &nY );
708 
709  if (!bIsBezier && aOperation == Clip)
710  {
711  nX = basegfx::fround( nX );
712  nY = basegfx::fround( nY );
713  }
714 
715  if( aOperation == Stroke )
716  {
717  nX += 0.5;
718  nY += 0.5;
719  }
720 
721  if( j==0 )
722  {
723  cairo_move_to( pCairo, nX, nY );
724  SAL_INFO( "canvas.cairo", "move to " << nX << "," << nY );
725  }
726  else
727  {
728  if( bIsBezier )
729  {
730  aA = aPolygon.getNextControlPoint( (j-1) % nPointCount );
731  aB = aPolygon.getPrevControlPoint( j % nPointCount );
732 
733  nAX = aA.getX();
734  nAY = aA.getY();
735  nBX = aB.getX();
736  nBY = aB.getY();
737 
738  cairo_matrix_transform_point( &aOrigMatrix, &nAX, &nAY );
739  cairo_matrix_transform_point( &aOrigMatrix, &nBX, &nBY );
740 
741  if( aOperation == Stroke )
742  {
743  nAX += 0.5;
744  nAY += 0.5;
745  nBX += 0.5;
746  nBY += 0.5;
747  }
748 
749  // tdf#99165 if the control points are 'empty', create the mathematical
750  // correct replacement ones to avoid problems with the graphical sub-system
751  // tdf#101026 The 1st attempt to create a mathematically correct replacement control
752  // vector was wrong. Best alternative is one as close as possible which means short.
753  if (basegfx::fTools::equal(nAX, nLastX) && basegfx::fTools::equal(nAY, nLastY))
754  {
755  nAX = nLastX + ((nBX - nLastX) * 0.0005);
756  nAY = nLastY + ((nBY - nLastY) * 0.0005);
757  }
758 
759  if(basegfx::fTools::equal(nBX, nX) && basegfx::fTools::equal(nBY, nY))
760  {
761  nBX = nX + ((nAX - nX) * 0.0005);
762  nBY = nY + ((nAY - nY) * 0.0005);
763  }
764 
765  cairo_curve_to( pCairo, nAX, nAY, nBX, nBY, nX, nY );
766  }
767  else
768  {
769  cairo_line_to( pCairo, nX, nY );
770  SAL_INFO( "canvas.cairo", "line to " << nX << "," << nY );
771  }
772  bOpToDo = true;
773  }
774 
775  nLastX = nX;
776  nLastY = nY;
777  }
778 
779  if( aPolygon.isClosed() )
780  cairo_close_path( pCairo );
781 
782  }
783  else
784  {
785  SAL_INFO( "canvas.cairo", "empty polygon for op: " << aOperation );
786  if( aOperation == Clip )
787  {
788  clipNULL( pCairo );
789 
790  return;
791  }
792  }
793  }
794 
795  if( aOperation == Fill && pTextures )
796  {
797  cairo_set_matrix( pCairo, &aOrigMatrix );
798  doOperation( aOperation, pCairo, pTextures, pDevice, aPolyPolygon.getB2DRange() );
799  cairo_set_matrix( pCairo, &aIdentityMatrix );
800  }
801 
802  if( bOpToDo && ( aOperation != Fill || !pTextures ) )
803  doOperation( aOperation, pCairo, pTextures, pDevice, aPolyPolygon.getB2DRange() );
804 
805  cairo_set_matrix( pCairo, &aOrigMatrix );
806 
807  if( aPolyPolygon.count() == 0 && aOperation == Clip )
808  clipNULL( pCairo );
809  }
810 
811  void CanvasHelper::doPolyPolygonPath( const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
812  Operation aOperation,
813  bool bNoLineJoin,
814  const uno::Sequence< rendering::Texture >* pTextures ) const
815  {
816  const ::basegfx::B2DPolyPolygon& rPolyPoly(
818 
819  cairo_t* pCairo = mpCairo.get();
820 
821  if(bNoLineJoin && aOperation == Stroke)
822  {
823  // emulate rendering::PathJoinType::NONE by painting single edges
824  for(sal_uInt32 a(0); a < rPolyPoly.count(); a++)
825  {
826  const basegfx::B2DPolygon& aCandidate(rPolyPoly.getB2DPolygon(a));
827  const sal_uInt32 nPointCount(aCandidate.count());
828 
829  if(nPointCount)
830  {
831  const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount: nPointCount - 1);
832  basegfx::B2DPolygon aEdge;
833  aEdge.append(aCandidate.getB2DPoint(0));
834  aEdge.append(basegfx::B2DPoint(0.0, 0.0));
835 
836  for(sal_uInt32 b(0); b < nEdgeCount; b++)
837  {
838  const sal_uInt32 nNextIndex((b + 1) % nPointCount);
839  aEdge.setB2DPoint(1, aCandidate.getB2DPoint(nNextIndex));
840  aEdge.setNextControlPoint(0, aCandidate.getNextControlPoint(b % nPointCount));
841  aEdge.setPrevControlPoint(1, aCandidate.getPrevControlPoint(nNextIndex));
842 
844  aOperation,
845  pCairo, pTextures,
847  xPolyPolygon->getFillRule() );
848 
849  // prepare next step
850  aEdge.setB2DPoint(0, aEdge.getB2DPoint(1));
851  }
852  }
853  }
854  }
855  else
856  {
857  doPolyPolygonImplementation( rPolyPoly, aOperation,
858  pCairo, pTextures,
860  xPolyPolygon->getFillRule() );
861  }
862  }
863 
864  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* ,
865  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
866  const rendering::ViewState& viewState,
867  const rendering::RenderState& renderState )
868  {
869 #ifdef CAIRO_CANVAS_PERF_TRACE
870  struct timespec aTimer;
871  mxDevice->startPerfTrace( &aTimer );
872 #endif
873 
874  if( mpCairo )
875  {
876  cairo_save( mpCairo.get() );
877 
878  cairo_set_line_width( mpCairo.get(), 1 );
879 
880  useStates( viewState, renderState, true );
881  doPolyPolygonPath( xPolyPolygon, Stroke );
882 
883  cairo_restore( mpCairo.get() );
884  }
885  else
886  SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
887 
888 #ifdef CAIRO_CANVAS_PERF_TRACE
889  mxDevice->stopPerfTrace( &aTimer, "drawPolyPolygon" );
890 #endif
891 
892  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
893  }
894 
895  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* ,
896  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
897  const rendering::ViewState& viewState,
898  const rendering::RenderState& renderState,
899  const rendering::StrokeAttributes& strokeAttributes )
900  {
901 #ifdef CAIRO_CANVAS_PERF_TRACE
902  struct timespec aTimer;
903  mxDevice->startPerfTrace( &aTimer );
904 #endif
905 
906  if( mpCairo )
907  {
908  cairo_save( mpCairo.get() );
909 
910  useStates( viewState, renderState, true );
911 
912  cairo_matrix_t aMatrix;
913  cairo_get_matrix( mpCairo.get(), &aMatrix );
914  double scaleFactorX = 1;
915  double scaleFactorY = 0;
916  cairo_matrix_transform_distance( &aMatrix, &scaleFactorX, &scaleFactorY );
917  double scaleFactor = basegfx::B2DVector( scaleFactorX, scaleFactorY ).getLength();
918  cairo_set_line_width( mpCairo.get(), strokeAttributes.StrokeWidth * scaleFactor );
919 
920  cairo_set_miter_limit( mpCairo.get(), strokeAttributes.MiterLimit );
921 
922  // FIXME: cairo doesn't handle end cap so far (rodo)
923  switch( strokeAttributes.StartCapType )
924  {
925  case rendering::PathCapType::BUTT:
926  cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_BUTT );
927  break;
928  case rendering::PathCapType::ROUND:
929  cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_ROUND );
930  break;
931  case rendering::PathCapType::SQUARE:
932  cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_SQUARE );
933  break;
934  }
935 
936  bool bNoLineJoin(false);
937 
938  switch( strokeAttributes.JoinType )
939  {
940  case rendering::PathJoinType::NONE:
941  bNoLineJoin = true;
942  [[fallthrough]]; // cairo doesn't have join type NONE so we use MITER as it's pretty close
943  case rendering::PathJoinType::MITER:
944  cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_MITER );
945  break;
946  case rendering::PathJoinType::ROUND:
947  cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_ROUND );
948  break;
949  case rendering::PathJoinType::BEVEL:
950  cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_BEVEL );
951  break;
952  }
953 
954  //tdf#103026 If the scaling is 0, then all dashes become zero so
955  //cairo will set the cairo_t status to CAIRO_STATUS_INVALID_DASH
956  //and no further drawing will occur
957  if (strokeAttributes.DashArray.hasElements() && scaleFactor > 0.0)
958  {
959  auto aDashArray(comphelper::sequenceToContainer<std::vector<double>>(strokeAttributes.DashArray));
960  for (auto& rDash : aDashArray)
961  rDash *= scaleFactor;
962  cairo_set_dash(mpCairo.get(), aDashArray.data(), aDashArray.size(), 0);
963  }
964 
965  // TODO(rodo) use LineArray of strokeAttributes
966 
967  doPolyPolygonPath( xPolyPolygon, Stroke, bNoLineJoin );
968 
969  cairo_restore( mpCairo.get() );
970  }
971  else
972  SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
973 
974 #ifdef CAIRO_CANVAS_PERF_TRACE
975  mxDevice->stopPerfTrace( &aTimer, "strokePolyPolygon" );
976 #endif
977 
978  // TODO(P1): Provide caching here.
979  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
980  }
981 
982  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* ,
983  const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
984  const rendering::ViewState& /*viewState*/,
985  const rendering::RenderState& /*renderState*/,
986  const uno::Sequence< rendering::Texture >& /*textures*/,
987  const rendering::StrokeAttributes& /*strokeAttributes*/ )
988  {
989  // TODO
990  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
991  }
992 
993  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* ,
994  const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
995  const rendering::ViewState& /*viewState*/,
996  const rendering::RenderState& /*renderState*/,
997  const uno::Sequence< rendering::Texture >& /*textures*/,
998  const uno::Reference< geometry::XMapping2D >& /*xMapping*/,
999  const rendering::StrokeAttributes& /*strokeAttributes*/ )
1000  {
1001  // TODO
1002  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
1003  }
1004 
1005  uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* ,
1006  const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
1007  const rendering::ViewState& /*viewState*/,
1008  const rendering::RenderState& /*renderState*/,
1009  const rendering::StrokeAttributes& /*strokeAttributes*/ )
1010  {
1011  // TODO
1012  return uno::Reference< rendering::XPolyPolygon2D >(nullptr);
1013  }
1014 
1015  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* ,
1016  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
1017  const rendering::ViewState& viewState,
1018  const rendering::RenderState& renderState )
1019  {
1020 #ifdef CAIRO_CANVAS_PERF_TRACE
1021  struct timespec aTimer;
1022  mxDevice->startPerfTrace( &aTimer );
1023 #endif
1024 
1025  if( mpCairo )
1026  {
1027  cairo_save( mpCairo.get() );
1028 
1029  useStates( viewState, renderState, true );
1030  doPolyPolygonPath( xPolyPolygon, Fill );
1031 
1032  cairo_restore( mpCairo.get() );
1033  }
1034  else
1035  SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1036 
1037 #ifdef CAIRO_CANVAS_PERF_TRACE
1038  mxDevice->stopPerfTrace( &aTimer, "fillPolyPolygon" );
1039 #endif
1040 
1041  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
1042  }
1043 
1044  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* ,
1045  const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
1046  const rendering::ViewState& viewState,
1047  const rendering::RenderState& renderState,
1048  const uno::Sequence< rendering::Texture >& textures )
1049  {
1050  if( mpCairo )
1051  {
1052  cairo_save( mpCairo.get() );
1053 
1054  useStates( viewState, renderState, true );
1055  doPolyPolygonPath( xPolyPolygon, Fill, false, &textures );
1056 
1057  cairo_restore( mpCairo.get() );
1058  }
1059 
1060  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
1061  }
1062 
1063  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* ,
1064  const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
1065  const rendering::ViewState& /*viewState*/,
1066  const rendering::RenderState& /*renderState*/,
1067  const uno::Sequence< rendering::Texture >& /*textures*/,
1068  const uno::Reference< geometry::XMapping2D >& /*xMapping*/ )
1069  {
1070  // TODO
1071  return uno::Reference< rendering::XCachedPrimitive >(nullptr);
1072  }
1073 
1074  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmapSurface( const rendering::XCanvas* pCanvas,
1075  const SurfaceSharedPtr& pInputSurface,
1076  const rendering::ViewState& viewState,
1077  const rendering::RenderState& renderState,
1078  const geometry::IntegerSize2D& rSize,
1079  bool bModulateColors,
1080  bool bHasAlpha )
1081  {
1082  SurfaceSharedPtr pSurface=pInputSurface;
1083  uno::Reference< rendering::XCachedPrimitive > rv;
1084  geometry::IntegerSize2D aBitmapSize = rSize;
1085 
1086  if( mpCairo )
1087  {
1088  cairo_save( mpCairo.get() );
1089 
1090  cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
1091  cairo_clip( mpCairo.get() );
1092 
1093  useStates( viewState, renderState, true );
1094 
1095  cairo_matrix_t aMatrix;
1096 
1097  cairo_get_matrix( mpCairo.get(), &aMatrix );
1098  if( ! ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
1099  ! ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
1100  ::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
1101  ::rtl::math::approxEqual( aMatrix.y0, 0 ) &&
1102  basegfx::fround( rSize.Width * aMatrix.xx ) > 8 &&
1103  basegfx::fround( rSize.Height* aMatrix.yy ) > 8 )
1104  {
1105  double dWidth, dHeight;
1106 
1107  dWidth = basegfx::fround( rSize.Width * aMatrix.xx );
1108  dHeight = basegfx::fround( rSize.Height* aMatrix.yy );
1109  aBitmapSize.Width = static_cast<sal_Int32>( dWidth );
1110  aBitmapSize.Height = static_cast<sal_Int32>( dHeight );
1111 
1113  ::basegfx::B2ISize( aBitmapSize.Width, aBitmapSize.Height ),
1114  bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
1115  CairoSharedPtr pCairo = pScaledSurface->getCairo();
1116 
1117  cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE );
1118  // add 0.5px to size to avoid rounding errors in cairo, leading sometimes to random data on the image right/bottom borders
1119  cairo_scale( pCairo.get(), (dWidth+0.5)/rSize.Width, (dHeight+0.5)/rSize.Height );
1120  cairo_set_source_surface( pCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
1121  cairo_paint( pCairo.get() );
1122 
1123  pSurface = pScaledSurface;
1124 
1125  aMatrix.xx = aMatrix.yy = 1;
1126  cairo_set_matrix( mpCairo.get(), &aMatrix );
1127 
1128  rv.set(
1129  new CachedBitmap( pSurface, viewState, renderState,
1130  // cast away const, need to
1131  // change refcount (as this is
1132  // ~invisible to client code,
1133  // still logically const)
1134  const_cast< rendering::XCanvas* >(pCanvas)) );
1135  }
1136 
1137  if( !bHasAlpha && mbHaveAlpha )
1138  {
1139  double x, y, width, height;
1140 
1141  x = y = 0;
1142  width = aBitmapSize.Width;
1143  height = aBitmapSize.Height;
1144  cairo_matrix_transform_point( &aMatrix, &x, &y );
1145  cairo_matrix_transform_distance( &aMatrix, &width, &height );
1146 
1147  // in case the bitmap doesn't have alpha and covers whole area
1148  // we try to change surface to plain rgb
1149  SAL_INFO( "canvas.cairo","chance to change surface to rgb, " << x << ", " << y << ", " << width << " x " << height << " (" << maSize.getX() << " x " << maSize.getY() << ")" );
1150  if( x <= 0 && y <= 0 && x + width >= maSize.getX() && y + height >= maSize.getY() )
1151  {
1152  SAL_INFO( "canvas.cairo","trying to change surface to rgb");
1153  if( mpSurfaceProvider ) {
1155 
1156  if( pNewSurface )
1157  setSurface( pNewSurface, false );
1158 
1159  // set state to new mpCairo.get()
1160  useStates( viewState, renderState, true );
1161  // use the possibly modified matrix
1162  cairo_set_matrix( mpCairo.get(), &aMatrix );
1163  }
1164  }
1165  }
1166 
1167  cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
1168  if( !bHasAlpha &&
1169  ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
1170  ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
1171  ::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
1172  ::rtl::math::approxEqual( aMatrix.y0, 0 ) )
1173  cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
1174  cairo_pattern_set_extend( cairo_get_source(mpCairo.get()), CAIRO_EXTEND_PAD );
1175  cairo_rectangle( mpCairo.get(), 0, 0, aBitmapSize.Width, aBitmapSize.Height );
1176  cairo_clip( mpCairo.get() );
1177 
1178  int nPixelWidth = std::round(rSize.Width * aMatrix.xx);
1179  int nPixelHeight = std::round(rSize.Height * aMatrix.yy);
1180  if (std::abs(nPixelWidth) > 0 && std::abs(nPixelHeight) > 0)
1181  {
1182  // Only render the image if it's at least 1x1 px sized.
1183  if (bModulateColors)
1184  cairo_paint_with_alpha(mpCairo.get(), renderState.DeviceColor[3]);
1185  else
1186  {
1187  cairo_paint(mpCairo.get());
1188  if (cairo_status(mpCairo.get()) != CAIRO_STATUS_SUCCESS)
1189  {
1190  SAL_WARN("canvas.cairo", "cairo_paint() failed: " << cairo_status_to_string(
1191  cairo_status(mpCairo.get())));
1192  }
1193  }
1194  }
1195  cairo_restore( mpCairo.get() );
1196  }
1197  else
1198  SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1199 
1200  return rv; // uno::Reference< rendering::XCachedPrimitive >(NULL);
1201  }
1202 
1203  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* pCanvas,
1204  const uno::Reference< rendering::XBitmap >& xBitmap,
1205  const rendering::ViewState& viewState,
1206  const rendering::RenderState& renderState )
1207  {
1208 #ifdef CAIRO_CANVAS_PERF_TRACE
1209  struct timespec aTimer;
1210  mxDevice->startPerfTrace( &aTimer );
1211 #endif
1212 
1213  uno::Reference< rendering::XCachedPrimitive > rv;
1214  unsigned char* data = nullptr;
1215  bool bHasAlpha = false;
1216  SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
1217  geometry::IntegerSize2D aSize = xBitmap->getSize();
1218 
1219  if( pSurface )
1220  {
1221  rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, false, bHasAlpha );
1222 
1223  if( data )
1224  free( data );
1225  }
1226  else
1227  rv.set(nullptr);
1228 
1229 #ifdef CAIRO_CANVAS_PERF_TRACE
1230  mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1231 #endif
1232 
1233  return rv;
1234  }
1235 
1236  uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
1237  const uno::Reference< rendering::XBitmap >& xBitmap,
1238  const rendering::ViewState& viewState,
1239  const rendering::RenderState& renderState )
1240  {
1241 #ifdef CAIRO_CANVAS_PERF_TRACE
1242  struct timespec aTimer;
1243  mxDevice->startPerfTrace( &aTimer );
1244 #endif
1245 
1246  uno::Reference< rendering::XCachedPrimitive > rv;
1247  unsigned char* data = nullptr;
1248  bool bHasAlpha = false;
1249  SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
1250  geometry::IntegerSize2D aSize = xBitmap->getSize();
1251 
1252  if( pSurface )
1253  {
1254  rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, true, bHasAlpha );
1255 
1256  if( data )
1257  free( data );
1258  }
1259  else
1260  rv.set(nullptr);
1261 
1262 #ifdef CAIRO_CANVAS_PERF_TRACE
1263  mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1264 #endif
1265 
1266  return rv;
1267  }
1268 
1269 
1270  geometry::IntegerSize2D CanvasHelper::getSize() const
1271  {
1272  if( !mpSurfaceProvider )
1273  return geometry::IntegerSize2D(1, 1); // we're disposed
1274 
1275  return ::basegfx::unotools::integerSize2DFromB2ISize( maSize );
1276  }
1277 
1278  uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
1279  bool /*beFast*/ )
1280  {
1281 #ifdef CAIRO_CANVAS_PERF_TRACE
1282  struct timespec aTimer;
1283  mxDevice->startPerfTrace( &aTimer );
1284 #endif
1285 
1286  if( mpCairo )
1287  {
1288  return uno::Reference< rendering::XBitmap >( new CanvasBitmap( ::basegfx::B2ISize( ::canvas::tools::roundUp( newSize.Width ),
1289  ::canvas::tools::roundUp( newSize.Height ) ),
1290  mpSurfaceProvider, mpDevice, false ) );
1291  }
1292  else
1293  SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1294 
1295 #ifdef CAIRO_CANVAS_PERF_TRACE
1296  mxDevice->stopPerfTrace( &aTimer, "getScaledBitmap" );
1297 #endif
1298 
1299  return uno::Reference< rendering::XBitmap >();
1300  }
1301 
1302  uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& aLayout,
1303  const geometry::IntegerRectangle2D& rect )
1304  {
1305  if( mpCairo )
1306  {
1307  const sal_Int32 nWidth( rect.X2 - rect.X1 );
1308  const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
1309  const cairo_format_t eFormat( mbHaveAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24 );
1310  uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
1311  sal_Int8* pData = aRes.getArray();
1312  cairo_surface_t* pImageSurface = cairo_image_surface_create_for_data( reinterpret_cast<unsigned char *>(pData),
1313  eFormat,
1314  nWidth, nHeight, 4*nWidth );
1315  cairo_t* pCairo = cairo_create( pImageSurface );
1316  cairo_set_source_surface( pCairo, mpSurface->getCairoSurface().get(), -rect.X1, -rect.Y1);
1317  cairo_paint( pCairo );
1318  cairo_destroy( pCairo );
1319  cairo_surface_destroy( pImageSurface );
1320 
1321  aLayout = impl_getMemoryLayout( nWidth, nHeight );
1322 
1323  return aRes;
1324  }
1325 
1326  return uno::Sequence< sal_Int8 >();
1327  }
1328 
1329  uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
1330  const geometry::IntegerPoint2D& /*pos*/ )
1331  {
1332  return uno::Sequence< sal_Int8 >();
1333  }
1334 
1335  namespace
1336  {
1337  class CairoColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
1338  {
1339  private:
1340  uno::Sequence< sal_Int8 > maComponentTags;
1341  uno::Sequence< sal_Int32 > maBitCounts;
1342 
1343  virtual ::sal_Int8 SAL_CALL getType( ) override
1344  {
1345  return rendering::ColorSpaceType::RGB;
1346  }
1347  virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
1348  {
1349  return maComponentTags;
1350  }
1351  virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
1352  {
1353  return rendering::RenderingIntent::PERCEPTUAL;
1354  }
1355  virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
1356  {
1357  return uno::Sequence< beans::PropertyValue >();
1358  }
1359  virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
1360  const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1361  {
1362  // TODO(P3): if we know anything about target
1363  // colorspace, this can be greatly sped up
1364  uno::Sequence<rendering::ARGBColor> aIntermediate(
1365  convertToARGB(deviceColor));
1366  return targetColorSpace->convertFromARGB(aIntermediate);
1367  }
1368  virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
1369  {
1370  const double* pIn( deviceColor.getConstArray() );
1371  const std::size_t nLen( deviceColor.getLength() );
1372  ENSURE_ARG_OR_THROW2(nLen%4==0,
1373  "number of channels no multiple of 4",
1374  static_cast<rendering::XColorSpace*>(this), 0);
1375 
1376  uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1377  rendering::RGBColor* pOut( aRes.getArray() );
1378  for( std::size_t i=0; i<nLen; i+=4 )
1379  {
1380  const double fAlpha(pIn[3]);
1381  if( fAlpha == 0.0 )
1382  *pOut++ = rendering::RGBColor(0.0, 0.0, 0.0);
1383  else
1384  *pOut++ = rendering::RGBColor(pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1385  pIn += 4;
1386  }
1387  return aRes;
1388  }
1389  virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
1390  {
1391  const double* pIn( deviceColor.getConstArray() );
1392  const std::size_t nLen( deviceColor.getLength() );
1393  ENSURE_ARG_OR_THROW2(nLen%4==0,
1394  "number of channels no multiple of 4",
1395  static_cast<rendering::XColorSpace*>(this), 0);
1396 
1397  uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1398  rendering::ARGBColor* pOut( aRes.getArray() );
1399  for( std::size_t i=0; i<nLen; i+=4 )
1400  {
1401  const double fAlpha(pIn[3]);
1402  if( fAlpha == 0.0 )
1403  *pOut++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
1404  else
1405  *pOut++ = rendering::ARGBColor(fAlpha,pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1406  pIn += 4;
1407  }
1408  return aRes;
1409  }
1410  virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
1411  {
1412  const double* pIn( deviceColor.getConstArray() );
1413  const std::size_t nLen( deviceColor.getLength() );
1414  ENSURE_ARG_OR_THROW2(nLen%4==0,
1415  "number of channels no multiple of 4",
1416  static_cast<rendering::XColorSpace*>(this), 0);
1417 
1418  uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1419  rendering::ARGBColor* pOut( aRes.getArray() );
1420  for( std::size_t i=0; i<nLen; i+=4 )
1421  {
1422  *pOut++ = rendering::ARGBColor(pIn[3],pIn[2],pIn[1],pIn[1]);
1423  pIn += 4;
1424  }
1425  return aRes;
1426  }
1427  virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1428  {
1429  const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1430  const std::size_t nLen( rgbColor.getLength() );
1431 
1432  uno::Sequence< double > aRes(nLen*4);
1433  double* pColors=aRes.getArray();
1434  for( std::size_t i=0; i<nLen; ++i )
1435  {
1436  *pColors++ = pIn->Blue;
1437  *pColors++ = pIn->Green;
1438  *pColors++ = pIn->Red;
1439  *pColors++ = 1.0;
1440  ++pIn;
1441  }
1442  return aRes;
1443  }
1444  virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1445  {
1446  const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1447  const std::size_t nLen( rgbColor.getLength() );
1448 
1449  uno::Sequence< double > aRes(nLen*4);
1450  double* pColors=aRes.getArray();
1451  for( std::size_t i=0; i<nLen; ++i )
1452  {
1453  *pColors++ = pIn->Alpha*pIn->Blue;
1454  *pColors++ = pIn->Alpha*pIn->Green;
1455  *pColors++ = pIn->Alpha*pIn->Red;
1456  *pColors++ = pIn->Alpha;
1457  ++pIn;
1458  }
1459  return aRes;
1460  }
1461  virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1462  {
1463  const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1464  const std::size_t nLen( rgbColor.getLength() );
1465 
1466  uno::Sequence< double > aRes(nLen*4);
1467  double* pColors=aRes.getArray();
1468  for( std::size_t i=0; i<nLen; ++i )
1469  {
1470  *pColors++ = pIn->Blue;
1471  *pColors++ = pIn->Green;
1472  *pColors++ = pIn->Red;
1473  *pColors++ = pIn->Alpha;
1474  ++pIn;
1475  }
1476  return aRes;
1477  }
1478 
1479  // XIntegerBitmapColorSpace
1480  virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
1481  {
1482  return 32;
1483  }
1484  virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
1485  {
1486  return maBitCounts;
1487  }
1488  virtual ::sal_Int8 SAL_CALL getEndianness( ) override
1489  {
1490  return util::Endianness::LITTLE;
1491  }
1492  virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1493  const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1494  {
1495  if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1496  {
1497  const sal_Int8* pIn( deviceColor.getConstArray() );
1498  const std::size_t nLen( deviceColor.getLength() );
1499  ENSURE_ARG_OR_THROW2(nLen%4==0,
1500  "number of channels no multiple of 4",
1501  static_cast<rendering::XColorSpace*>(this), 0);
1502 
1503  uno::Sequence<double> aRes(nLen);
1504  double* pOut( aRes.getArray() );
1505  for( std::size_t i=0; i<nLen; i+=4 )
1506  {
1507  *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1508  *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1509  *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1510  *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1511  }
1512  return aRes;
1513  }
1514  else
1515  {
1516  // TODO(P3): if we know anything about target
1517  // colorspace, this can be greatly sped up
1518  uno::Sequence<rendering::ARGBColor> aIntermediate(
1519  convertIntegerToARGB(deviceColor));
1520  return targetColorSpace->convertFromARGB(aIntermediate);
1521  }
1522  }
1523  virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1524  const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
1525  {
1526  if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1527  {
1528  // it's us, so simply pass-through the data
1529  return deviceColor;
1530  }
1531  else
1532  {
1533  // TODO(P3): if we know anything about target
1534  // colorspace, this can be greatly sped up
1535  uno::Sequence<rendering::ARGBColor> aIntermediate(
1536  convertIntegerToARGB(deviceColor));
1537  return targetColorSpace->convertIntegerFromARGB(aIntermediate);
1538  }
1539  }
1540  virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1541  {
1542  const sal_Int8* pIn( deviceColor.getConstArray() );
1543  const std::size_t nLen( deviceColor.getLength() );
1544  ENSURE_ARG_OR_THROW2(nLen%4==0,
1545  "number of channels no multiple of 4",
1546  static_cast<rendering::XColorSpace*>(this), 0);
1547 
1548  uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1549  rendering::RGBColor* pOut( aRes.getArray() );
1550  for( std::size_t i=0; i<nLen; i+=4 )
1551  {
1552  const double fAlpha(static_cast<sal_uInt8>(pIn[3]));
1553  if( fAlpha )
1554  *pOut++ = rendering::RGBColor(
1555  pIn[2]/fAlpha,
1556  pIn[1]/fAlpha,
1557  pIn[0]/fAlpha);
1558  else
1559  *pOut++ = rendering::RGBColor(0,0,0);
1560  pIn += 4;
1561  }
1562  return aRes;
1563  }
1564 
1565  virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1566  {
1567  const sal_Int8* pIn( deviceColor.getConstArray() );
1568  const std::size_t nLen( deviceColor.getLength() );
1569  ENSURE_ARG_OR_THROW2(nLen%4==0,
1570  "number of channels no multiple of 4",
1571  static_cast<rendering::XColorSpace*>(this), 0);
1572 
1573  uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1574  rendering::ARGBColor* pOut( aRes.getArray() );
1575  for( std::size_t i=0; i<nLen; i+=4 )
1576  {
1577  const double fAlpha(static_cast<sal_uInt8>(pIn[3]));
1578  if( fAlpha )
1579  *pOut++ = rendering::ARGBColor(
1580  fAlpha/255.0,
1581  pIn[2]/fAlpha,
1582  pIn[1]/fAlpha,
1583  pIn[0]/fAlpha);
1584  else
1585  *pOut++ = rendering::ARGBColor(0,0,0,0);
1586  pIn += 4;
1587  }
1588  return aRes;
1589  }
1590  virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1591  {
1592  const sal_Int8* pIn( deviceColor.getConstArray() );
1593  const std::size_t nLen( deviceColor.getLength() );
1594  ENSURE_ARG_OR_THROW2(nLen%4==0,
1595  "number of channels no multiple of 4",
1596  static_cast<rendering::XColorSpace*>(this), 0);
1597 
1598  uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1599  rendering::ARGBColor* pOut( aRes.getArray() );
1600  for( std::size_t i=0; i<nLen; i+=4 )
1601  {
1602  *pOut++ = rendering::ARGBColor(
1607  pIn += 4;
1608  }
1609  return aRes;
1610  }
1611 
1612  virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1613  {
1614  const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1615  const std::size_t nLen( rgbColor.getLength() );
1616 
1617  uno::Sequence< sal_Int8 > aRes(nLen*4);
1618  sal_Int8* pColors=aRes.getArray();
1619  for( std::size_t i=0; i<nLen; ++i )
1620  {
1621  *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1622  *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1623  *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1624  *pColors++ = -1;
1625  ++pIn;
1626  }
1627  return aRes;
1628  }
1629 
1630  virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1631  {
1632  const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1633  const std::size_t nLen( rgbColor.getLength() );
1634 
1635  uno::Sequence< sal_Int8 > aRes(nLen*4);
1636  sal_Int8* pColors=aRes.getArray();
1637  for( std::size_t i=0; i<nLen; ++i )
1638  {
1639  const double fAlpha(pIn->Alpha);
1640  *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Blue);
1641  *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Green);
1642  *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Red);
1643  *pColors++ = vcl::unotools::toByteColor(fAlpha);
1644  ++pIn;
1645  }
1646  return aRes;
1647  }
1648  virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1649  {
1650  const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1651  const std::size_t nLen( rgbColor.getLength() );
1652 
1653  uno::Sequence< sal_Int8 > aRes(nLen*4);
1654  sal_Int8* pColors=aRes.getArray();
1655  for( std::size_t i=0; i<nLen; ++i )
1656  {
1657  *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1658  *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1659  *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1660  *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
1661  ++pIn;
1662  }
1663  return aRes;
1664  }
1665 
1666  public:
1667  CairoColorSpace() :
1668  maComponentTags(4),
1669  maBitCounts(4)
1670  {
1671  sal_Int8* pTags = maComponentTags.getArray();
1672  sal_Int32* pBitCounts = maBitCounts.getArray();
1673  pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
1674  pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
1675  pTags[2] = rendering::ColorComponentTag::RGB_RED;
1676  pTags[3] = rendering::ColorComponentTag::PREMULTIPLIED_ALPHA;
1677 
1678  pBitCounts[0] =
1679  pBitCounts[1] =
1680  pBitCounts[2] =
1681  pBitCounts[3] = 8;
1682  }
1683  };
1684 
1685  class CairoNoAlphaColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
1686  {
1687  private:
1688  uno::Sequence< sal_Int8 > maComponentTags;
1689  uno::Sequence< sal_Int32 > maBitCounts;
1690 
1691  virtual ::sal_Int8 SAL_CALL getType( ) override
1692  {
1693  return rendering::ColorSpaceType::RGB;
1694  }
1695  virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
1696  {
1697  return maComponentTags;
1698  }
1699  virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
1700  {
1701  return rendering::RenderingIntent::PERCEPTUAL;
1702  }
1703  virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
1704  {
1705  return uno::Sequence< beans::PropertyValue >();
1706  }
1707  virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
1708  const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1709  {
1710  // TODO(P3): if we know anything about target
1711  // colorspace, this can be greatly sped up
1712  uno::Sequence<rendering::ARGBColor> aIntermediate(
1713  convertToARGB(deviceColor));
1714  return targetColorSpace->convertFromARGB(aIntermediate);
1715  }
1716  virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
1717  {
1718  const double* pIn( deviceColor.getConstArray() );
1719  const std::size_t nLen( deviceColor.getLength() );
1720  ENSURE_ARG_OR_THROW2(nLen%4==0,
1721  "number of channels no multiple of 4",
1722  static_cast<rendering::XColorSpace*>(this), 0);
1723 
1724  uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1725  rendering::RGBColor* pOut( aRes.getArray() );
1726  for( std::size_t i=0; i<nLen; i+=4 )
1727  {
1728  *pOut++ = rendering::RGBColor(pIn[2], pIn[1], pIn[0]);
1729  pIn += 4;
1730  }
1731  return aRes;
1732  }
1733  uno::Sequence< rendering::ARGBColor > impl_convertToARGB( const uno::Sequence< double >& deviceColor )
1734  {
1735  const double* pIn( deviceColor.getConstArray() );
1736  const std::size_t nLen( deviceColor.getLength() );
1737  ENSURE_ARG_OR_THROW2(nLen%4==0,
1738  "number of channels no multiple of 4",
1739  static_cast<rendering::XColorSpace*>(this), 0);
1740 
1741  uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1742  rendering::ARGBColor* pOut( aRes.getArray() );
1743  for( std::size_t i=0; i<nLen; i+=4 )
1744  {
1745  *pOut++ = rendering::ARGBColor(1.0, pIn[2], pIn[1], pIn[0]);
1746  pIn += 4;
1747  }
1748  return aRes;
1749  }
1750  virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
1751  {
1752  return impl_convertToARGB( deviceColor );
1753  }
1754  virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
1755  {
1756  return impl_convertToARGB( deviceColor );
1757  }
1758  virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1759  {
1760  const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1761  const std::size_t nLen( rgbColor.getLength() );
1762 
1763  uno::Sequence< double > aRes(nLen*4);
1764  double* pColors=aRes.getArray();
1765  for( std::size_t i=0; i<nLen; ++i )
1766  {
1767  *pColors++ = pIn->Blue;
1768  *pColors++ = pIn->Green;
1769  *pColors++ = pIn->Red;
1770  *pColors++ = 1.0; // the value does not matter
1771  ++pIn;
1772  }
1773  return aRes;
1774  }
1775  uno::Sequence< double > impl_convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor )
1776  {
1777  const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1778  const std::size_t nLen( rgbColor.getLength() );
1779 
1780  uno::Sequence< double > aRes(nLen*4);
1781  double* pColors=aRes.getArray();
1782  for( std::size_t i=0; i<nLen; ++i )
1783  {
1784  *pColors++ = pIn->Blue;
1785  *pColors++ = pIn->Green;
1786  *pColors++ = pIn->Red;
1787  *pColors++ = 1.0; // the value does not matter
1788  ++pIn;
1789  }
1790  return aRes;
1791  }
1792  virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1793  {
1794  return impl_convertFromARGB( rgbColor );
1795  }
1796  virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1797  {
1798  return impl_convertFromARGB( rgbColor );
1799  }
1800 
1801  // XIntegerBitmapColorSpace
1802  virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
1803  {
1804  return 32;
1805  }
1806  virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
1807  {
1808  return maBitCounts;
1809  }
1810  virtual ::sal_Int8 SAL_CALL getEndianness( ) override
1811  {
1812  return util::Endianness::LITTLE;
1813  }
1814  virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1815  const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1816  {
1817  if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1818  {
1819  const sal_Int8* pIn( deviceColor.getConstArray() );
1820  const std::size_t nLen( deviceColor.getLength() );
1821  ENSURE_ARG_OR_THROW2(nLen%4==0,
1822  "number of channels no multiple of 4",
1823  static_cast<rendering::XColorSpace*>(this), 0);
1824 
1825  uno::Sequence<double> aRes(nLen);
1826  double* pOut( aRes.getArray() );
1827  for( std::size_t i=0; i<nLen; i+=4 )
1828  {
1829  *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1830  *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1831  *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
1832  *pOut++ = 1.0; pIn++; // the value does not matter
1833  }
1834  return aRes;
1835  }
1836  else
1837  {
1838  // TODO(P3): if we know anything about target
1839  // colorspace, this can be greatly sped up
1840  uno::Sequence<rendering::ARGBColor> aIntermediate(
1841  convertIntegerToARGB(deviceColor));
1842  return targetColorSpace->convertFromARGB(aIntermediate);
1843  }
1844  }
1845  virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1846  const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
1847  {
1848  if( dynamic_cast<CairoNoAlphaColorSpace*>(targetColorSpace.get()) )
1849  {
1850  // it's us, so simply pass-through the data
1851  return deviceColor;
1852  }
1853  else
1854  {
1855  // TODO(P3): if we know anything about target
1856  // colorspace, this can be greatly sped up
1857  uno::Sequence<rendering::ARGBColor> aIntermediate(
1858  convertIntegerToARGB(deviceColor));
1859  return targetColorSpace->convertIntegerFromARGB(aIntermediate);
1860  }
1861  }
1862  virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1863  {
1864  const sal_Int8* pIn( deviceColor.getConstArray() );
1865  const std::size_t nLen( deviceColor.getLength() );
1866  ENSURE_ARG_OR_THROW2(nLen%4==0,
1867  "number of channels no multiple of 4",
1868  static_cast<rendering::XColorSpace*>(this), 0);
1869 
1870  uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1871  rendering::RGBColor* pOut( aRes.getArray() );
1872  for( std::size_t i=0; i<nLen; i+=4 )
1873  {
1874  *pOut++ = rendering::RGBColor( pIn[2], pIn[1], pIn[0] );
1875  pIn += 4;
1876  }
1877  return aRes;
1878  }
1879 
1880  virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1881  {
1882  return impl_convertIntegerToARGB( deviceColor );
1883  }
1884  virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1885  {
1886  return impl_convertIntegerToARGB( deviceColor );
1887  }
1888  uno::Sequence< rendering::ARGBColor > impl_convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor )
1889  {
1890  const sal_Int8* pIn( deviceColor.getConstArray() );
1891  const std::size_t nLen( deviceColor.getLength() );
1892  ENSURE_ARG_OR_THROW2(nLen%4==0,
1893  "number of channels no multiple of 4",
1894  static_cast<rendering::XColorSpace*>(this), 0);
1895 
1896  uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1897  rendering::ARGBColor* pOut( aRes.getArray() );
1898  for( std::size_t i=0; i<nLen; i+=4 )
1899  {
1900  *pOut++ = rendering::ARGBColor(
1901  1.0,
1905  pIn += 4;
1906  }
1907  return aRes;
1908  }
1909 
1910  virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1911  {
1912  const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1913  const std::size_t nLen( rgbColor.getLength() );
1914 
1915  uno::Sequence< sal_Int8 > aRes(nLen*4);
1916  sal_Int8* pColors=aRes.getArray();
1917  for( std::size_t i=0; i<nLen; ++i )
1918  {
1919  *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1920  *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1921  *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1922  *pColors++ = -1; // the value does not matter
1923  ++pIn;
1924  }
1925  return aRes;
1926  }
1927 
1928  virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1929  {
1930  return impl_convertIntegerFromARGB( rgbColor );
1931  }
1932  virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1933  {
1934  return impl_convertIntegerFromARGB( rgbColor );
1935  }
1936  uno::Sequence< ::sal_Int8 > impl_convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor )
1937  {
1938  const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1939  const std::size_t nLen( rgbColor.getLength() );
1940 
1941  uno::Sequence< sal_Int8 > aRes(nLen*4);
1942  sal_Int8* pColors=aRes.getArray();
1943  for( std::size_t i=0; i<nLen; ++i )
1944  {
1945  *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1946  *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1947  *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1948  *pColors++ = -1; // the value does not matter
1949  ++pIn;
1950  }
1951  return aRes;
1952  }
1953 
1954  public:
1955  CairoNoAlphaColorSpace() :
1956  maComponentTags(3),
1957  maBitCounts(3)
1958  {
1959  sal_Int8* pTags = maComponentTags.getArray();
1960  sal_Int32* pBitCounts = maBitCounts.getArray();
1961  pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
1962  pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
1963  pTags[2] = rendering::ColorComponentTag::RGB_RED;
1964 
1965  pBitCounts[0] =
1966  pBitCounts[1] =
1967  pBitCounts[2] = 8;
1968  }
1969  };
1970 
1971  uno::Reference<rendering::XIntegerBitmapColorSpace>& GetCairoNoAlphaColorSpace()
1972  {
1973  static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new CairoNoAlphaColorSpace();
1974  return SPACE;
1975  };
1976 
1977  uno::Reference<rendering::XIntegerBitmapColorSpace>& GetCairoColorSpace()
1978  {
1979  static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new CairoColorSpace();
1980  return SPACE;
1981  };
1982 
1983  }
1984 
1985  rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
1986  {
1987  if( !mpCairo )
1988  return rendering::IntegerBitmapLayout(); // we're disposed
1989 
1990  const geometry::IntegerSize2D aSize(getSize());
1991 
1992  return impl_getMemoryLayout( aSize.Width, aSize.Height );
1993  }
1994 
1995  rendering::IntegerBitmapLayout
1996  CanvasHelper::impl_getMemoryLayout( const sal_Int32 nWidth, const sal_Int32 nHeight )
1997  {
1998  rendering::IntegerBitmapLayout aLayout;
1999 
2000  aLayout.ScanLines = nHeight;
2001  aLayout.ScanLineBytes = nWidth*4;
2002  aLayout.ScanLineStride = aLayout.ScanLineBytes;
2003  aLayout.PlaneStride = 0;
2004  aLayout.ColorSpace = mbHaveAlpha ? GetCairoColorSpace() : GetCairoNoAlphaColorSpace();
2005  aLayout.Palette.clear();
2006  aLayout.IsMsbFirst = false;
2007 
2008  return aLayout;
2009  }
2010 
2011 
2012  bool CanvasHelper::repaint( const SurfaceSharedPtr& pSurface,
2013  const rendering::ViewState& viewState,
2014  const rendering::RenderState& renderState )
2015  {
2016  SAL_INFO( "canvas.cairo", "CanvasHelper::repaint");
2017 
2018  if( mpCairo )
2019  {
2020  cairo_save( mpCairo.get() );
2021 
2022  cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
2023  cairo_clip( mpCairo.get() );
2024 
2025  useStates( viewState, renderState, true );
2026 
2027  cairo_matrix_t aMatrix;
2028 
2029  cairo_get_matrix( mpCairo.get(), &aMatrix );
2030  aMatrix.xx = aMatrix.yy = 1;
2031  cairo_set_matrix( mpCairo.get(), &aMatrix );
2032 
2033  cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
2034  cairo_paint( mpCairo.get() );
2035  cairo_restore( mpCairo.get() );
2036  }
2037 
2038  return true;
2039  }
2040 }
2041 
2042 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 getY() const
css::rendering::IntegerBitmapLayout impl_getMemoryLayout(sal_Int32 nWidth, sal_Int32 nHeight)
css::uno::Reference< css::rendering::XCachedPrimitive > implDrawBitmapSurface(const css::rendering::XCanvas *pCanvas, const ::cairo::SurfaceSharedPtr &pSurface, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, const css::geometry::IntegerSize2D &rSize, bool bModulateColors, bool bHasAlpha)
sal_Int32 nIndex
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
Target interface for XCachedPrimitive implementations.
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)
double getHeight() const
::cairo::CairoSharedPtr mpCairo
bool getType(BSTR name, Type &type)
std::unique_ptr< ContentProperties > pData
const GradientType meType
Type of gradient to render (as e.g. linear grads are not represented by maGradientPoly) ...
virtual ::cairo::SurfaceSharedPtr createSurface(const ::basegfx::B2ISize &rSize, int aContent)=0
create new surface in given size
signed char sal_Int8
const css::uno::Sequence< css::uno::Sequence< double > > maColors
Gradient colors.
static uno::Sequence< double > lerp(const uno::Sequence< double > &rLeft, const uno::Sequence< double > &rRight, double fAlpha)
long Long
void setSize(const ::basegfx::B2ISize &rSize)
#define max(a, b)
Definition: dx_winstuff.hxx:43
static void addColorStops(cairo_pattern_t *pPattern, const uno::Sequence< uno::Sequence< double > > &rColors, const uno::Sequence< double > &rStops, bool bReverseStops)
::BitmapEx bitmapExFromXBitmap(const uno::Reference< rendering::XBitmap > &xBitmap)
css::uno::Sequence< sal_Int8 > getPixel(css::rendering::IntegerBitmapLayout &bitmapLayout, const css::geometry::IntegerPoint2D &pos)
B2DPoint getMaximum() const
::BitmapEx bitmapExFromXBitmap(const uno::Reference< rendering::XIntegerReadOnlyBitmap > &xInputBitmap)
float x
Definition: dx_9rm.cxx:190
constexpr OUStringLiteral PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME
#define SPACE
void disposing()
Release all references.
std::shared_ptr< cairo_t > CairoSharedPtr
DstType sequenceToContainer(const css::uno::Sequence< SrcType > &i_Sequence)
bool IsAlpha() const
#define min(a, b)
Definition: dx_winstuff.hxx:46
double getWidth() const
struct _cairo_surface cairo_surface_t
virtual ::cairo::SurfaceSharedPtr getSurface() override
oslFileHandle & pOut
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)
static cairo_pattern_t * patternFromParametricPolyPolygon(::canvas::ParametricPolyPolygon const &rPolygon)
B2IRange fround(const B2DRange &rRange)
sal_Int8 toByteColor(double val)
float y
Definition: dx_9rm.cxx:190
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)
std::shared_ptr< Surface > SurfaceSharedPtr
const css::uno::Sequence< double > maStops
Gradient color stops.
uno::Sequence< sal_Int8 > maComponentTags
static void clipNULL(cairo_t *pCairo)
bool IsEmpty() const
int i
uno_Any a
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)
bool equal(T const &rfValA, T const &rfValB)
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)
void doPolyPolygonImplementation(const ::basegfx::B2DPolyPolygon &aPolyPolygon, Operation aOperation, cairo_t *pCairo, const uno::Sequence< rendering::Texture > *pTextures, const SurfaceProviderRef &pDevice, rendering::FillRule eFillrule)
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 > 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)
sal::systools::COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:169
void CanvasCairoExtractBitmapData(BitmapEx const &rBmpEx, Bitmap &rBitmap, unsigned char *&data, bool &bHasAlpha, tools::Long &rnWidth, tools::Long &rnHeight)
#define ENSURE_ARG_OR_THROW2(c, m, ifc, arg)
float u
Definition: dx_9rm.cxx:192
::basegfx::B2DPolyPolygon b2DPolyPolygonFromXPolyPolygon2D(const uno::Reference< rendering::XPolyPolygon2D > &xPoly)
def stop
std::shared_ptr< cairo_surface_t > CairoSurfaceSharedPtr
css::uno::Sequence< sal_Int8 > getData(css::rendering::IntegerBitmapLayout &bitmapLayout, const css::geometry::IntegerRectangle2D &rect)
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)
void useStates(const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, bool setColor)
struct _cairo cairo_t
#define ENSURE_ARG_OR_THROW(c, m)
bool mbHaveAlpha
When true, content is able to represent alpha.
css::rendering::IntegerBitmapLayout getMemoryLayout()
exports com.sun.star.chart2. data
static void doOperation(Operation aOperation, cairo_t *pCairo, const uno::Sequence< rendering::Texture > *pTextures, const SurfaceProviderRef &pDevice, const basegfx::B2DRange &rBounds)
::cairo::SurfaceSharedPtr mpSurface
Bitmap GetBitmap(Color aTransparentReplaceColor) const
virtual ::cairo::SurfaceSharedPtr changeSurface()=0
convert surface from alpha to non-alpha, does not copy content channel.
css::geometry::IntegerSize2D getSize() const
SurfaceProvider * mpSurfaceProvider
Surface provider.
#define ENSURE_OR_THROW(c, m)
sal_Int32 roundUp(const double &rVal)
Round given floating point value up to next integer.
virtual ::cairo::SurfaceSharedPtr getSurface()=0
Query surface from this provider.
void setSurface(const ::cairo::SurfaceSharedPtr &pSurface, bool bHasAlpha)
void init(const ::basegfx::B2ISize &rSizePixel, SurfaceProvider &rSurfaceProvider, css::rendering::XGraphicDevice *pDevice)
Initialize canvas helper.
double getMinY() const
double toDoubleColor(sal_uInt8 val)
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)
void doPolyPolygonPath(const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, Operation aOperation, bool bNoLineJoin=false, const css::uno::Sequence< css::rendering::Texture > *pTextures=nullptr) const
#define SAL_INFO(area, stream)
static SurfaceSharedPtr surfaceFromXBitmap(const uno::Reference< rendering::XBitmap > &xBitmap)
surfaceFromXBitmap Create a surface from XBitmap
B2DPoint getMinimum() const
ValueType lerp(const ValueType &rFrom, const ValueType &rTo, double t)
css::rendering::XGraphicDevice * mpDevice
Phyical output device.
double getLength(const B2DPolygon &rCandidate)
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)
static void setColor(cairo_t *pCairo, const uno::Sequence< double > &rColor)
double getLength() const
uno::Sequence< sal_Int32 > maBitCounts
#define SAL_WARN(area, stream)
sal_Int32 getX() const
double getMinX() const
css::uno::Reference< css::rendering::XCachedPrimitive > fillTexturedPolyPolygon(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)
Values getValues() const
Query all defining values of this object atomically.
bool repaint(const ::cairo::SurfaceSharedPtr &pSurface, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Reference< css::rendering::XBitmap > getScaledBitmap(const css::geometry::RealSize2D &newSize, bool beFast)
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)
VclPtr< VirtualDevice > mpVirtualDevice