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