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
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>
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
52#include <parametricpolypolygon.hxx>
53#include <cairo.h>
54
58
59using namespace ::cairo;
60using namespace ::com::sun::star;
61
62namespace 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
284constexpr 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(
817 ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
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);
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 {
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 {
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 return true;
2020
2021 cairo_save( mpCairo.get() );
2022
2023 cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
2024 cairo_clip( mpCairo.get() );
2025
2026 useStates( viewState, renderState, true );
2027
2028 cairo_matrix_t aMatrix;
2029
2030 cairo_get_matrix( mpCairo.get(), &aMatrix );
2031 aMatrix.xx = aMatrix.yy = 1;
2032 cairo_set_matrix( mpCairo.get(), &aMatrix );
2033
2034 cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
2035 cairo_paint( mpCairo.get() );
2036 cairo_restore( mpCairo.get() );
2037
2038 return true;
2039 }
2040}
2041
2042/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
struct _cairo_surface cairo_surface_t
struct _cairo cairo_t
uno::Sequence< sal_Int32 > maBitCounts
uno::Sequence< sal_Int8 > maComponentTags
bool IsAlpha() const
bool IsEmpty() const
Bitmap GetBitmap(Color aTransparentReplaceColor) const
void disposeAndClear()
void setB2DPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
void setPrevControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
bool isClosed() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
void setNextControlPoint(sal_uInt32 nIndex, const basegfx::B2DPoint &rValue)
basegfx::B2DPoint getPrevControlPoint(sal_uInt32 nIndex) const
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
sal_uInt32 count() const
basegfx::B2DPoint getNextControlPoint(sal_uInt32 nIndex) const
B2DPoint getMaximum() const
B2DPoint getMinimum() const
double getLength() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getHeight() const
TYPE getX() const
TYPE getY() const
ResultType lerp(double fAlpha) const
virtual ::cairo::SurfaceSharedPtr getSurface() override
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)
void disposing()
Release all references.
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
::cairo::CairoSharedPtr mpCairo
::cairo::SurfaceSharedPtr mpSurface
void drawLine(const css::rendering::XCanvas *pCanvas, const css::geometry::RealPoint2D &aStartPoint, const css::geometry::RealPoint2D &aEndPoint, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::rendering::IntegerBitmapLayout impl_getMemoryLayout(sal_Int32 nWidth, sal_Int32 nHeight)
void useStates(const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState, bool setColor)
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)
css::uno::Reference< css::rendering::XBitmap > getScaledBitmap(const css::geometry::RealSize2D &newSize, bool beFast)
css::uno::Reference< css::rendering::XCachedPrimitive > fillPolyPolygon(const css::rendering::XCanvas *pCanvas, const css::uno::Reference< css::rendering::XPolyPolygon2D > &xPolyPolygon, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Reference< css::rendering::XCachedPrimitive > 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)
void init(const ::basegfx::B2ISize &rSizePixel, SurfaceProvider &rSurfaceProvider, css::rendering::XGraphicDevice *pDevice)
Initialize canvas helper.
bool repaint(const ::cairo::SurfaceSharedPtr &pSurface, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::geometry::IntegerSize2D getSize() const
SurfaceProvider * mpSurfaceProvider
Surface provider.
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::rendering::XGraphicDevice * mpDevice
Phyical output device.
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 drawBezier(const css::rendering::XCanvas *pCanvas, const css::geometry::RealBezierSegment2D &aBezierSegment, const css::geometry::RealPoint2D &aEndPoint, const css::rendering::ViewState &viewState, const css::rendering::RenderState &renderState)
css::uno::Sequence< sal_Int8 > getData(css::rendering::IntegerBitmapLayout &bitmapLayout, const css::geometry::IntegerRectangle2D &rect)
css::uno::Sequence< sal_Int8 > getPixel(css::rendering::IntegerBitmapLayout &bitmapLayout, const css::geometry::IntegerPoint2D &pos)
VclPtr< VirtualDevice > mpVirtualDevice
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)
bool mbHaveAlpha
When true, content is able to represent alpha.
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 > 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 setSize(const ::basegfx::B2ISize &rSize)
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::rendering::IntegerBitmapLayout getMemoryLayout()
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)
void setSurface(const ::cairo::SurfaceSharedPtr &pSurface, bool bHasAlpha)
Target interface for XCachedPrimitive implementations.
virtual ::cairo::SurfaceSharedPtr getSurface()=0
Query surface from this provider.
virtual ::cairo::SurfaceSharedPtr createSurface(const ::basegfx::B2ISize &rSize, int aContent)=0
create new surface in given size
virtual ::cairo::SurfaceSharedPtr changeSurface()=0
convert surface from alpha to non-alpha, does not copy content channel.
Values getValues() const
Query all defining values of this object atomically.
#define SPACE
#define ENSURE_ARG_OR_THROW2(c, m, ifc, arg)
#define ENSURE_OR_THROW(c, m)
#define ENSURE_ARG_OR_THROW(c, m)
sal::systools::COMReference< IDirect3DDevice9 > mpDevice
Definition: dx_9rm.cxx:169
float u
Definition: dx_9rm.cxx:192
float y
Definition: dx_9rm.cxx:190
float x
Definition: dx_9rm.cxx:190
#define max(a, b)
Definition: dx_winstuff.hxx:43
sal_Int32 nIndex
uno_Any a
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
std::unique_ptr< sal_Int32[]> pData
def stop(arg=None)
bool equal(T const &rfValA, T const &rfValB)
ValueType lerp(const ValueType &rFrom, const ValueType &rTo, double t)
B2IRange fround(const B2DRange &rRange)
std::shared_ptr< Surface > SurfaceSharedPtr
std::shared_ptr< cairo_t > CairoSharedPtr
std::shared_ptr< cairo_surface_t > CairoSurfaceSharedPtr
::BitmapEx bitmapExFromXBitmap(const uno::Reference< rendering::XBitmap > &xBitmap)
void doPolyPolygonImplementation(const ::basegfx::B2DPolyPolygon &aPolyPolygon, Operation aOperation, cairo_t *pCairo, const uno::Sequence< rendering::Texture > *pTextures, const SurfaceProviderRef &pDevice, rendering::FillRule eFillrule)
static void addColorStops(cairo_pattern_t *pPattern, const uno::Sequence< uno::Sequence< double > > &rColors, const uno::Sequence< double > &rStops, bool bReverseStops)
static void doOperation(Operation aOperation, cairo_t *pCairo, const uno::Sequence< rendering::Texture > *pTextures, const SurfaceProviderRef &pDevice, const basegfx::B2DRange &rBounds)
static SurfaceSharedPtr surfaceFromXBitmap(const uno::Reference< rendering::XBitmap > &xBitmap)
surfaceFromXBitmap Create a surface from XBitmap
static void setColor(cairo_t *pCairo, const uno::Sequence< double > &rColor)
static cairo_pattern_t * patternFromParametricPolyPolygon(::canvas::ParametricPolyPolygon const &rPolygon)
constexpr OUStringLiteral PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME
static uno::Sequence< double > lerp(const uno::Sequence< double > &rLeft, const uno::Sequence< double > &rRight, double fAlpha)
static void clipNULL(cairo_t *pCairo)
DstType sequenceToContainer(const css::uno::Sequence< SrcType > &i_Sequence)
int i
long Long
void CanvasCairoExtractBitmapData(BitmapEx const &aBmpEx, Bitmap &aBitmap, unsigned char *&data, bool &bHasAlpha, tools::Long &rnWidth, tools::Long &rnHeight)
sal_Int8 toByteColor(double val)
double toDoubleColor(sal_uInt8 val)
::BitmapEx bitmapExFromXBitmap(const uno::Reference< rendering::XIntegerReadOnlyBitmap > &xInputBitmap)
bool getType(BSTR name, Type &type)
const css::uno::Sequence< css::uno::Sequence< double > > maColors
Gradient colors.
const GradientType meType
Type of gradient to render (as e.g. linear grads are not represented by maGradientPoly)
const css::uno::Sequence< double > maStops
Gradient color stops.
unsigned char sal_uInt8
signed char sal_Int8
oslFileHandle & pOut