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>
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.getWidth() << " x " << maSize.getHeight() );
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.getWidth(), maSize.getHeight() );
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.getWidth(), maSize.getHeight() );
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
1112 SurfaceSharedPtr pScaledSurface = mpSurfaceProvider->createSurface(
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.getWidth() << " x " << maSize.getHeight() << ")" );
1150 if( x <= 0 && y <= 0 && x + width >= maSize.getWidth() && y + height >= maSize.getHeight() )
1151 {
1152 SAL_INFO( "canvas.cairo","trying to change surface to rgb");
1153 if( mpSurfaceProvider ) {
1154 SurfaceSharedPtr pNewSurface = mpSurfaceProvider->changeSurface();
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 // Use cairo_matrix_transform_distance() to determine the scaling, as that works even if
1179 // the matrix also has rotation.
1180 double fPixelWidth = rSize.Width;
1181 double fPixelHeight = rSize.Height;
1182 cairo_matrix_transform_distance(&aMatrix, &fPixelWidth, &fPixelHeight);
1183 int nPixelWidth = std::round(fPixelWidth);
1184 int nPixelHeight = std::round(fPixelHeight);
1185 if (std::abs(nPixelWidth) > 0 && std::abs(nPixelHeight) > 0)
1186 {
1187 // Only render the image if it's at least 1x1 px sized.
1188 if (bModulateColors)
1189 cairo_paint_with_alpha(mpCairo.get(), renderState.DeviceColor[3]);
1190 else
1191 {
1192 cairo_paint(mpCairo.get());
1193 if (cairo_status(mpCairo.get()) != CAIRO_STATUS_SUCCESS)
1194 {
1195 SAL_WARN("canvas.cairo", "cairo_paint() failed: " << cairo_status_to_string(
1196 cairo_status(mpCairo.get())));
1197 }
1198 }
1199 }
1200 cairo_restore( mpCairo.get() );
1201 }
1202 else
1203 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1204
1205 return rv; // uno::Reference< rendering::XCachedPrimitive >(NULL);
1206 }
1207
1208 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* pCanvas,
1209 const uno::Reference< rendering::XBitmap >& xBitmap,
1210 const rendering::ViewState& viewState,
1211 const rendering::RenderState& renderState )
1212 {
1213#ifdef CAIRO_CANVAS_PERF_TRACE
1214 struct timespec aTimer;
1215 mxDevice->startPerfTrace( &aTimer );
1216#endif
1217
1218 uno::Reference< rendering::XCachedPrimitive > rv;
1219 unsigned char* data = nullptr;
1220 bool bHasAlpha = false;
1221 SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
1222 geometry::IntegerSize2D aSize = xBitmap->getSize();
1223
1224 if( pSurface )
1225 {
1226 rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, false, bHasAlpha );
1227
1228 if( data )
1229 free( data );
1230 }
1231 else
1232 rv.set(nullptr);
1233
1234#ifdef CAIRO_CANVAS_PERF_TRACE
1235 mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1236#endif
1237
1238 return rv;
1239 }
1240
1241 uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* pCanvas,
1242 const uno::Reference< rendering::XBitmap >& xBitmap,
1243 const rendering::ViewState& viewState,
1244 const rendering::RenderState& renderState )
1245 {
1246#ifdef CAIRO_CANVAS_PERF_TRACE
1247 struct timespec aTimer;
1248 mxDevice->startPerfTrace( &aTimer );
1249#endif
1250
1251 uno::Reference< rendering::XCachedPrimitive > rv;
1252 unsigned char* data = nullptr;
1253 bool bHasAlpha = false;
1254 SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
1255 geometry::IntegerSize2D aSize = xBitmap->getSize();
1256
1257 if( pSurface )
1258 {
1259 rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, true, bHasAlpha );
1260
1261 if( data )
1262 free( data );
1263 }
1264 else
1265 rv.set(nullptr);
1266
1267#ifdef CAIRO_CANVAS_PERF_TRACE
1268 mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
1269#endif
1270
1271 return rv;
1272 }
1273
1274
1275 geometry::IntegerSize2D CanvasHelper::getSize() const
1276 {
1277 if( !mpSurfaceProvider )
1278 return geometry::IntegerSize2D(1, 1); // we're disposed
1279
1280 return ::basegfx::unotools::integerSize2DFromB2ISize( maSize );
1281 }
1282
1283 uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
1284 bool /*beFast*/ )
1285 {
1286#ifdef CAIRO_CANVAS_PERF_TRACE
1287 struct timespec aTimer;
1288 mxDevice->startPerfTrace( &aTimer );
1289#endif
1290
1291 if( mpCairo )
1292 {
1293 return uno::Reference< rendering::XBitmap >( new CanvasBitmap( ::basegfx::B2ISize( ::canvas::tools::roundUp( newSize.Width ),
1294 ::canvas::tools::roundUp( newSize.Height ) ),
1295 mpSurfaceProvider, mpDevice, false ) );
1296 }
1297 else
1298 SAL_INFO( "canvas.cairo", "CanvasHelper called after it was disposed");
1299
1300#ifdef CAIRO_CANVAS_PERF_TRACE
1301 mxDevice->stopPerfTrace( &aTimer, "getScaledBitmap" );
1302#endif
1303
1304 return uno::Reference< rendering::XBitmap >();
1305 }
1306
1307 uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& aLayout,
1308 const geometry::IntegerRectangle2D& rect )
1309 {
1310 if( mpCairo )
1311 {
1312 const sal_Int32 nWidth( rect.X2 - rect.X1 );
1313 const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
1314 const cairo_format_t eFormat( mbHaveAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24 );
1315 uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
1316 sal_Int8* pData = aRes.getArray();
1317 cairo_surface_t* pImageSurface = cairo_image_surface_create_for_data( reinterpret_cast<unsigned char *>(pData),
1318 eFormat,
1319 nWidth, nHeight, 4*nWidth );
1320 cairo_t* pCairo = cairo_create( pImageSurface );
1321 cairo_set_source_surface( pCairo, mpSurface->getCairoSurface().get(), -rect.X1, -rect.Y1);
1322 cairo_paint( pCairo );
1323 cairo_destroy( pCairo );
1324 cairo_surface_destroy( pImageSurface );
1325
1326 aLayout = impl_getMemoryLayout( nWidth, nHeight );
1327
1328 return aRes;
1329 }
1330
1331 return uno::Sequence< sal_Int8 >();
1332 }
1333
1334 uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
1335 const geometry::IntegerPoint2D& /*pos*/ )
1336 {
1337 return uno::Sequence< sal_Int8 >();
1338 }
1339
1340 namespace
1341 {
1342 class CairoColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
1343 {
1344 private:
1345 uno::Sequence< sal_Int8 > maComponentTags;
1346 uno::Sequence< sal_Int32 > maBitCounts;
1347
1348 virtual ::sal_Int8 SAL_CALL getType( ) override
1349 {
1350 return rendering::ColorSpaceType::RGB;
1351 }
1352 virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
1353 {
1354 return maComponentTags;
1355 }
1356 virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
1357 {
1358 return rendering::RenderingIntent::PERCEPTUAL;
1359 }
1360 virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
1361 {
1362 return uno::Sequence< beans::PropertyValue >();
1363 }
1364 virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
1365 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1366 {
1367 // TODO(P3): if we know anything about target
1368 // colorspace, this can be greatly sped up
1369 uno::Sequence<rendering::ARGBColor> aIntermediate(
1370 convertToARGB(deviceColor));
1371 return targetColorSpace->convertFromARGB(aIntermediate);
1372 }
1373 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
1374 {
1375 const double* pIn( deviceColor.getConstArray() );
1376 const std::size_t nLen( deviceColor.getLength() );
1377 ENSURE_ARG_OR_THROW2(nLen%4==0,
1378 "number of channels no multiple of 4",
1379 static_cast<rendering::XColorSpace*>(this), 0);
1380
1381 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1382 rendering::RGBColor* pOut( aRes.getArray() );
1383 for( std::size_t i=0; i<nLen; i+=4 )
1384 {
1385 const double fAlpha(pIn[3]);
1386 if( fAlpha == 0.0 )
1387 *pOut++ = rendering::RGBColor(0.0, 0.0, 0.0);
1388 else
1389 *pOut++ = rendering::RGBColor(pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1390 pIn += 4;
1391 }
1392 return aRes;
1393 }
1394 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
1395 {
1396 const double* pIn( deviceColor.getConstArray() );
1397 const std::size_t nLen( deviceColor.getLength() );
1398 ENSURE_ARG_OR_THROW2(nLen%4==0,
1399 "number of channels no multiple of 4",
1400 static_cast<rendering::XColorSpace*>(this), 0);
1401
1402 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1403 rendering::ARGBColor* pOut( aRes.getArray() );
1404 for( std::size_t i=0; i<nLen; i+=4 )
1405 {
1406 const double fAlpha(pIn[3]);
1407 if( fAlpha == 0.0 )
1408 *pOut++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
1409 else
1410 *pOut++ = rendering::ARGBColor(fAlpha,pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
1411 pIn += 4;
1412 }
1413 return aRes;
1414 }
1415 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
1416 {
1417 const double* pIn( deviceColor.getConstArray() );
1418 const std::size_t nLen( deviceColor.getLength() );
1419 ENSURE_ARG_OR_THROW2(nLen%4==0,
1420 "number of channels no multiple of 4",
1421 static_cast<rendering::XColorSpace*>(this), 0);
1422
1423 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1424 rendering::ARGBColor* pOut( aRes.getArray() );
1425 for( std::size_t i=0; i<nLen; i+=4 )
1426 {
1427 *pOut++ = rendering::ARGBColor(pIn[3],pIn[2],pIn[1],pIn[1]);
1428 pIn += 4;
1429 }
1430 return aRes;
1431 }
1432 virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1433 {
1434 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1435 const std::size_t nLen( rgbColor.getLength() );
1436
1437 uno::Sequence< double > aRes(nLen*4);
1438 double* pColors=aRes.getArray();
1439 for( std::size_t i=0; i<nLen; ++i )
1440 {
1441 *pColors++ = pIn->Blue;
1442 *pColors++ = pIn->Green;
1443 *pColors++ = pIn->Red;
1444 *pColors++ = 1.0;
1445 ++pIn;
1446 }
1447 return aRes;
1448 }
1449 virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1450 {
1451 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1452 const std::size_t nLen( rgbColor.getLength() );
1453
1454 uno::Sequence< double > aRes(nLen*4);
1455 double* pColors=aRes.getArray();
1456 for( std::size_t i=0; i<nLen; ++i )
1457 {
1458 *pColors++ = pIn->Alpha*pIn->Blue;
1459 *pColors++ = pIn->Alpha*pIn->Green;
1460 *pColors++ = pIn->Alpha*pIn->Red;
1461 *pColors++ = pIn->Alpha;
1462 ++pIn;
1463 }
1464 return aRes;
1465 }
1466 virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1467 {
1468 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1469 const std::size_t nLen( rgbColor.getLength() );
1470
1471 uno::Sequence< double > aRes(nLen*4);
1472 double* pColors=aRes.getArray();
1473 for( std::size_t i=0; i<nLen; ++i )
1474 {
1475 *pColors++ = pIn->Blue;
1476 *pColors++ = pIn->Green;
1477 *pColors++ = pIn->Red;
1478 *pColors++ = pIn->Alpha;
1479 ++pIn;
1480 }
1481 return aRes;
1482 }
1483
1484 // XIntegerBitmapColorSpace
1485 virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
1486 {
1487 return 32;
1488 }
1489 virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
1490 {
1491 return maBitCounts;
1492 }
1493 virtual ::sal_Int8 SAL_CALL getEndianness( ) override
1494 {
1495 return util::Endianness::LITTLE;
1496 }
1497 virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1498 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1499 {
1500 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1501 {
1502 const sal_Int8* pIn( deviceColor.getConstArray() );
1503 const std::size_t nLen( deviceColor.getLength() );
1504 ENSURE_ARG_OR_THROW2(nLen%4==0,
1505 "number of channels no multiple of 4",
1506 static_cast<rendering::XColorSpace*>(this), 0);
1507
1508 uno::Sequence<double> aRes(nLen);
1509 double* pOut( aRes.getArray() );
1510 for( std::size_t i=0; i<nLen; i+=4 )
1511 {
1516 }
1517 return aRes;
1518 }
1519 else
1520 {
1521 // TODO(P3): if we know anything about target
1522 // colorspace, this can be greatly sped up
1523 uno::Sequence<rendering::ARGBColor> aIntermediate(
1524 convertIntegerToARGB(deviceColor));
1525 return targetColorSpace->convertFromARGB(aIntermediate);
1526 }
1527 }
1528 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1529 const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
1530 {
1531 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1532 {
1533 // it's us, so simply pass-through the data
1534 return deviceColor;
1535 }
1536 else
1537 {
1538 // TODO(P3): if we know anything about target
1539 // colorspace, this can be greatly sped up
1540 uno::Sequence<rendering::ARGBColor> aIntermediate(
1541 convertIntegerToARGB(deviceColor));
1542 return targetColorSpace->convertIntegerFromARGB(aIntermediate);
1543 }
1544 }
1545 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1546 {
1547 const sal_Int8* pIn( deviceColor.getConstArray() );
1548 const std::size_t nLen( deviceColor.getLength() );
1549 ENSURE_ARG_OR_THROW2(nLen%4==0,
1550 "number of channels no multiple of 4",
1551 static_cast<rendering::XColorSpace*>(this), 0);
1552
1553 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1554 rendering::RGBColor* pOut( aRes.getArray() );
1555 for( std::size_t i=0; i<nLen; i+=4 )
1556 {
1557 const double fAlpha(static_cast<sal_uInt8>(pIn[3]));
1558 if( fAlpha )
1559 *pOut++ = rendering::RGBColor(
1560 pIn[2]/fAlpha,
1561 pIn[1]/fAlpha,
1562 pIn[0]/fAlpha);
1563 else
1564 *pOut++ = rendering::RGBColor(0,0,0);
1565 pIn += 4;
1566 }
1567 return aRes;
1568 }
1569
1570 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1571 {
1572 const sal_Int8* pIn( deviceColor.getConstArray() );
1573 const std::size_t nLen( deviceColor.getLength() );
1574 ENSURE_ARG_OR_THROW2(nLen%4==0,
1575 "number of channels no multiple of 4",
1576 static_cast<rendering::XColorSpace*>(this), 0);
1577
1578 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1579 rendering::ARGBColor* pOut( aRes.getArray() );
1580 for( std::size_t i=0; i<nLen; i+=4 )
1581 {
1582 const double fAlpha(static_cast<sal_uInt8>(pIn[3]));
1583 if( fAlpha )
1584 *pOut++ = rendering::ARGBColor(
1585 fAlpha/255.0,
1586 pIn[2]/fAlpha,
1587 pIn[1]/fAlpha,
1588 pIn[0]/fAlpha);
1589 else
1590 *pOut++ = rendering::ARGBColor(0,0,0,0);
1591 pIn += 4;
1592 }
1593 return aRes;
1594 }
1595 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1596 {
1597 const sal_Int8* pIn( deviceColor.getConstArray() );
1598 const std::size_t nLen( deviceColor.getLength() );
1599 ENSURE_ARG_OR_THROW2(nLen%4==0,
1600 "number of channels no multiple of 4",
1601 static_cast<rendering::XColorSpace*>(this), 0);
1602
1603 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1604 rendering::ARGBColor* pOut( aRes.getArray() );
1605 for( std::size_t i=0; i<nLen; i+=4 )
1606 {
1607 *pOut++ = rendering::ARGBColor(
1612 pIn += 4;
1613 }
1614 return aRes;
1615 }
1616
1617 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1618 {
1619 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1620 const std::size_t nLen( rgbColor.getLength() );
1621
1622 uno::Sequence< sal_Int8 > aRes(nLen*4);
1623 sal_Int8* pColors=aRes.getArray();
1624 for( std::size_t i=0; i<nLen; ++i )
1625 {
1626 *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1627 *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1628 *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1629 *pColors++ = -1;
1630 ++pIn;
1631 }
1632 return aRes;
1633 }
1634
1635 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1636 {
1637 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1638 const std::size_t nLen( rgbColor.getLength() );
1639
1640 uno::Sequence< sal_Int8 > aRes(nLen*4);
1641 sal_Int8* pColors=aRes.getArray();
1642 for( std::size_t i=0; i<nLen; ++i )
1643 {
1644 const double fAlpha(pIn->Alpha);
1645 *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Blue);
1646 *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Green);
1647 *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Red);
1648 *pColors++ = vcl::unotools::toByteColor(fAlpha);
1649 ++pIn;
1650 }
1651 return aRes;
1652 }
1653 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1654 {
1655 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1656 const std::size_t nLen( rgbColor.getLength() );
1657
1658 uno::Sequence< sal_Int8 > aRes(nLen*4);
1659 sal_Int8* pColors=aRes.getArray();
1660 for( std::size_t i=0; i<nLen; ++i )
1661 {
1662 *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1663 *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1664 *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1665 *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
1666 ++pIn;
1667 }
1668 return aRes;
1669 }
1670
1671 public:
1672 CairoColorSpace() :
1673 maComponentTags(4),
1674 maBitCounts(4)
1675 {
1676 sal_Int8* pTags = maComponentTags.getArray();
1677 sal_Int32* pBitCounts = maBitCounts.getArray();
1678 pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
1679 pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
1680 pTags[2] = rendering::ColorComponentTag::RGB_RED;
1681 pTags[3] = rendering::ColorComponentTag::PREMULTIPLIED_ALPHA;
1682
1683 pBitCounts[0] =
1684 pBitCounts[1] =
1685 pBitCounts[2] =
1686 pBitCounts[3] = 8;
1687 }
1688 };
1689
1690 class CairoNoAlphaColorSpace : public cppu::WeakImplHelper< css::rendering::XIntegerBitmapColorSpace >
1691 {
1692 private:
1693 uno::Sequence< sal_Int8 > maComponentTags;
1694 uno::Sequence< sal_Int32 > maBitCounts;
1695
1696 virtual ::sal_Int8 SAL_CALL getType( ) override
1697 {
1698 return rendering::ColorSpaceType::RGB;
1699 }
1700 virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) override
1701 {
1702 return maComponentTags;
1703 }
1704 virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) override
1705 {
1706 return rendering::RenderingIntent::PERCEPTUAL;
1707 }
1708 virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) override
1709 {
1710 return uno::Sequence< beans::PropertyValue >();
1711 }
1712 virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
1713 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1714 {
1715 // TODO(P3): if we know anything about target
1716 // colorspace, this can be greatly sped up
1717 uno::Sequence<rendering::ARGBColor> aIntermediate(
1718 convertToARGB(deviceColor));
1719 return targetColorSpace->convertFromARGB(aIntermediate);
1720 }
1721 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) override
1722 {
1723 const double* pIn( deviceColor.getConstArray() );
1724 const std::size_t nLen( deviceColor.getLength() );
1725 ENSURE_ARG_OR_THROW2(nLen%4==0,
1726 "number of channels no multiple of 4",
1727 static_cast<rendering::XColorSpace*>(this), 0);
1728
1729 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1730 rendering::RGBColor* pOut( aRes.getArray() );
1731 for( std::size_t i=0; i<nLen; i+=4 )
1732 {
1733 *pOut++ = rendering::RGBColor(pIn[2], pIn[1], pIn[0]);
1734 pIn += 4;
1735 }
1736 return aRes;
1737 }
1738 uno::Sequence< rendering::ARGBColor > impl_convertToARGB( const uno::Sequence< double >& deviceColor )
1739 {
1740 const double* pIn( deviceColor.getConstArray() );
1741 const std::size_t nLen( deviceColor.getLength() );
1742 ENSURE_ARG_OR_THROW2(nLen%4==0,
1743 "number of channels no multiple of 4",
1744 static_cast<rendering::XColorSpace*>(this), 0);
1745
1746 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1747 rendering::ARGBColor* pOut( aRes.getArray() );
1748 for( std::size_t i=0; i<nLen; i+=4 )
1749 {
1750 *pOut++ = rendering::ARGBColor(1.0, pIn[2], pIn[1], pIn[0]);
1751 pIn += 4;
1752 }
1753 return aRes;
1754 }
1755 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) override
1756 {
1757 return impl_convertToARGB( deviceColor );
1758 }
1759 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) override
1760 {
1761 return impl_convertToARGB( deviceColor );
1762 }
1763 virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1764 {
1765 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1766 const std::size_t nLen( rgbColor.getLength() );
1767
1768 uno::Sequence< double > aRes(nLen*4);
1769 double* pColors=aRes.getArray();
1770 for( std::size_t i=0; i<nLen; ++i )
1771 {
1772 *pColors++ = pIn->Blue;
1773 *pColors++ = pIn->Green;
1774 *pColors++ = pIn->Red;
1775 *pColors++ = 1.0; // the value does not matter
1776 ++pIn;
1777 }
1778 return aRes;
1779 }
1780 uno::Sequence< double > impl_convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor )
1781 {
1782 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1783 const std::size_t nLen( rgbColor.getLength() );
1784
1785 uno::Sequence< double > aRes(nLen*4);
1786 double* pColors=aRes.getArray();
1787 for( std::size_t i=0; i<nLen; ++i )
1788 {
1789 *pColors++ = pIn->Blue;
1790 *pColors++ = pIn->Green;
1791 *pColors++ = pIn->Red;
1792 *pColors++ = 1.0; // the value does not matter
1793 ++pIn;
1794 }
1795 return aRes;
1796 }
1797 virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1798 {
1799 return impl_convertFromARGB( rgbColor );
1800 }
1801 virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1802 {
1803 return impl_convertFromARGB( rgbColor );
1804 }
1805
1806 // XIntegerBitmapColorSpace
1807 virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) override
1808 {
1809 return 32;
1810 }
1811 virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) override
1812 {
1813 return maBitCounts;
1814 }
1815 virtual ::sal_Int8 SAL_CALL getEndianness( ) override
1816 {
1817 return util::Endianness::LITTLE;
1818 }
1819 virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1820 const uno::Reference< rendering::XColorSpace >& targetColorSpace ) override
1821 {
1822 if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
1823 {
1824 const sal_Int8* pIn( deviceColor.getConstArray() );
1825 const std::size_t nLen( deviceColor.getLength() );
1826 ENSURE_ARG_OR_THROW2(nLen%4==0,
1827 "number of channels no multiple of 4",
1828 static_cast<rendering::XColorSpace*>(this), 0);
1829
1830 uno::Sequence<double> aRes(nLen);
1831 double* pOut( aRes.getArray() );
1832 for( std::size_t i=0; i<nLen; i+=4 )
1833 {
1837 *pOut++ = 1.0; pIn++; // the value does not matter
1838 }
1839 return aRes;
1840 }
1841 else
1842 {
1843 // TODO(P3): if we know anything about target
1844 // colorspace, this can be greatly sped up
1845 uno::Sequence<rendering::ARGBColor> aIntermediate(
1846 convertIntegerToARGB(deviceColor));
1847 return targetColorSpace->convertFromARGB(aIntermediate);
1848 }
1849 }
1850 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
1851 const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) override
1852 {
1853 if( dynamic_cast<CairoNoAlphaColorSpace*>(targetColorSpace.get()) )
1854 {
1855 // it's us, so simply pass-through the data
1856 return deviceColor;
1857 }
1858 else
1859 {
1860 // TODO(P3): if we know anything about target
1861 // colorspace, this can be greatly sped up
1862 uno::Sequence<rendering::ARGBColor> aIntermediate(
1863 convertIntegerToARGB(deviceColor));
1864 return targetColorSpace->convertIntegerFromARGB(aIntermediate);
1865 }
1866 }
1867 virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1868 {
1869 const sal_Int8* pIn( deviceColor.getConstArray() );
1870 const std::size_t nLen( deviceColor.getLength() );
1871 ENSURE_ARG_OR_THROW2(nLen%4==0,
1872 "number of channels no multiple of 4",
1873 static_cast<rendering::XColorSpace*>(this), 0);
1874
1875 uno::Sequence< rendering::RGBColor > aRes(nLen/4);
1876 rendering::RGBColor* pOut( aRes.getArray() );
1877 for( std::size_t i=0; i<nLen; i+=4 )
1878 {
1879 *pOut++ = rendering::RGBColor( pIn[2], pIn[1], pIn[0] );
1880 pIn += 4;
1881 }
1882 return aRes;
1883 }
1884
1885 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1886 {
1887 return impl_convertIntegerToARGB( deviceColor );
1888 }
1889 virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) override
1890 {
1891 return impl_convertIntegerToARGB( deviceColor );
1892 }
1893 uno::Sequence< rendering::ARGBColor > impl_convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor )
1894 {
1895 const sal_Int8* pIn( deviceColor.getConstArray() );
1896 const std::size_t nLen( deviceColor.getLength() );
1897 ENSURE_ARG_OR_THROW2(nLen%4==0,
1898 "number of channels no multiple of 4",
1899 static_cast<rendering::XColorSpace*>(this), 0);
1900
1901 uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
1902 rendering::ARGBColor* pOut( aRes.getArray() );
1903 for( std::size_t i=0; i<nLen; i+=4 )
1904 {
1905 *pOut++ = rendering::ARGBColor(
1906 1.0,
1910 pIn += 4;
1911 }
1912 return aRes;
1913 }
1914
1915 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) override
1916 {
1917 const rendering::RGBColor* pIn( rgbColor.getConstArray() );
1918 const std::size_t nLen( rgbColor.getLength() );
1919
1920 uno::Sequence< sal_Int8 > aRes(nLen*4);
1921 sal_Int8* pColors=aRes.getArray();
1922 for( std::size_t i=0; i<nLen; ++i )
1923 {
1924 *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1925 *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1926 *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1927 *pColors++ = -1; // the value does not matter
1928 ++pIn;
1929 }
1930 return aRes;
1931 }
1932
1933 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1934 {
1935 return impl_convertIntegerFromARGB( rgbColor );
1936 }
1937 virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) override
1938 {
1939 return impl_convertIntegerFromARGB( rgbColor );
1940 }
1941 uno::Sequence< ::sal_Int8 > impl_convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor )
1942 {
1943 const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
1944 const std::size_t nLen( rgbColor.getLength() );
1945
1946 uno::Sequence< sal_Int8 > aRes(nLen*4);
1947 sal_Int8* pColors=aRes.getArray();
1948 for( std::size_t i=0; i<nLen; ++i )
1949 {
1950 *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
1951 *pColors++ = vcl::unotools::toByteColor(pIn->Green);
1952 *pColors++ = vcl::unotools::toByteColor(pIn->Red);
1953 *pColors++ = -1; // the value does not matter
1954 ++pIn;
1955 }
1956 return aRes;
1957 }
1958
1959 public:
1960 CairoNoAlphaColorSpace() :
1961 maComponentTags(3),
1962 maBitCounts(3)
1963 {
1964 sal_Int8* pTags = maComponentTags.getArray();
1965 sal_Int32* pBitCounts = maBitCounts.getArray();
1966 pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
1967 pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
1968 pTags[2] = rendering::ColorComponentTag::RGB_RED;
1969
1970 pBitCounts[0] =
1971 pBitCounts[1] =
1972 pBitCounts[2] = 8;
1973 }
1974 };
1975
1976 uno::Reference<rendering::XIntegerBitmapColorSpace>& GetCairoNoAlphaColorSpace()
1977 {
1978 static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new CairoNoAlphaColorSpace();
1979 return SPACE;
1980 };
1981
1982 uno::Reference<rendering::XIntegerBitmapColorSpace>& GetCairoColorSpace()
1983 {
1984 static uno::Reference<rendering::XIntegerBitmapColorSpace> SPACE = new CairoColorSpace();
1985 return SPACE;
1986 };
1987
1988 }
1989
1990 rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
1991 {
1992 if( !mpCairo )
1993 return rendering::IntegerBitmapLayout(); // we're disposed
1994
1995 const geometry::IntegerSize2D aSize(getSize());
1996
1997 return impl_getMemoryLayout( aSize.Width, aSize.Height );
1998 }
1999
2000 rendering::IntegerBitmapLayout
2001 CanvasHelper::impl_getMemoryLayout( const sal_Int32 nWidth, const sal_Int32 nHeight )
2002 {
2003 rendering::IntegerBitmapLayout aLayout;
2004
2005 aLayout.ScanLines = nHeight;
2006 aLayout.ScanLineBytes = nWidth*4;
2007 aLayout.ScanLineStride = aLayout.ScanLineBytes;
2008 aLayout.PlaneStride = 0;
2009 aLayout.ColorSpace = mbHaveAlpha ? GetCairoColorSpace() : GetCairoNoAlphaColorSpace();
2010 aLayout.Palette.clear();
2011 aLayout.IsMsbFirst = false;
2012
2013 return aLayout;
2014 }
2015
2016
2017 bool CanvasHelper::repaint( const SurfaceSharedPtr& pSurface,
2018 const rendering::ViewState& viewState,
2019 const rendering::RenderState& renderState )
2020 {
2021 SAL_INFO( "canvas.cairo", "CanvasHelper::repaint");
2022
2023 if( !mpCairo )
2024 return true;
2025
2026 cairo_save( mpCairo.get() );
2027
2028 cairo_rectangle( mpCairo.get(), 0, 0, maSize.getWidth(), maSize.getHeight() );
2029 cairo_clip( mpCairo.get() );
2030
2031 useStates( viewState, renderState, true );
2032
2033 cairo_matrix_t aMatrix;
2034
2035 cairo_get_matrix( mpCairo.get(), &aMatrix );
2036 aMatrix.xx = aMatrix.yy = 1;
2037 cairo_set_matrix( mpCairo.get(), &aMatrix );
2038
2039 cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
2040 cairo_paint( mpCairo.get() );
2041 cairo_restore( mpCairo.get() );
2042
2043 return true;
2044 }
2045}
2046
2047/* 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 getWidth() 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)
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
class SAL_LOPLUGIN_ANNOTATE("crosscast") SurfaceProvider typedef ::rtl::Reference< SurfaceProvider > SurfaceProviderRef
Target interface for XCachedPrimitive implementations.
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)
constexpr double alpha[nDetails]
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