summaryrefslogtreecommitdiff
path: root/canvas/source/cairo/cairo_canvashelper.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'canvas/source/cairo/cairo_canvashelper.cxx')
-rw-r--r--canvas/source/cairo/cairo_canvashelper.cxx1844
1 files changed, 1844 insertions, 0 deletions
diff --git a/canvas/source/cairo/cairo_canvashelper.cxx b/canvas/source/cairo/cairo_canvashelper.cxx
new file mode 100644
index 000000000000..9cf2dd978759
--- /dev/null
+++ b/canvas/source/cairo/cairo_canvashelper.cxx
@@ -0,0 +1,1844 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: cairo_canvashelper.cxx,v $
+ * $Revision: 1.17 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_canvas.hxx"
+
+#include <canvas/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <rtl/logfile.hxx>
+#include <rtl/math.hxx>
+#include <rtl/instance.hxx>
+
+#include <com/sun/star/util/Endianness.hpp>
+#include <com/sun/star/rendering/TexturingMode.hpp>
+#include <com/sun/star/rendering/CompositeOperation.hpp>
+#include <com/sun/star/rendering/RepaintResult.hpp>
+#include <com/sun/star/rendering/PathCapType.hpp>
+#include <com/sun/star/rendering/PathJoinType.hpp>
+#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp>
+#include <com/sun/star/rendering/IntegerBitmapLayout.hpp>
+#include <com/sun/star/rendering/ColorSpaceType.hpp>
+#include <com/sun/star/rendering/ColorComponentTag.hpp>
+#include <com/sun/star/rendering/RenderingIntent.hpp>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/tools/canvastools.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/compbase1.hxx>
+
+#include <canvas/canvastools.hxx>
+#include <canvas/parametricpolypolygon.hxx>
+
+#include <vcl/canvastools.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/bmpacc.hxx>
+#include <vcl/virdev.hxx>
+
+#include "cairo_spritecanvas.hxx"
+#include "cairo_cachedbitmap.hxx"
+#include "cairo_canvashelper.hxx"
+#include "cairo_canvasbitmap.hxx"
+
+#include <algorithm>
+
+using namespace ::cairo;
+using namespace ::com::sun::star;
+
+namespace cairocanvas
+{
+ CanvasHelper::CanvasHelper() :
+ mpSurfaceProvider(NULL),
+ mpDevice(NULL),
+ mpVirtualDevice(),
+ mbHaveAlpha(),
+ mpCairo(),
+ mpSurface(),
+ maSize()
+ {
+ }
+
+ void CanvasHelper::disposing()
+ {
+ mpSurface.reset();
+ mpCairo.reset();
+ mpVirtualDevice.reset();
+ mpDevice = NULL;
+ mpSurfaceProvider = NULL;
+ }
+
+ void CanvasHelper::init( const ::basegfx::B2ISize& rSizePixel,
+ SurfaceProvider& rSurfaceProvider,
+ rendering::XGraphicDevice* pDevice )
+ {
+ maSize = rSizePixel;
+ mpSurfaceProvider = &rSurfaceProvider;
+ mpDevice = pDevice;
+ }
+
+ void CanvasHelper::setSize( const ::basegfx::B2ISize& rSize )
+ {
+ maSize = rSize;
+ }
+
+ void CanvasHelper::setSurface( const SurfaceSharedPtr& pSurface, bool bHasAlpha )
+ {
+ mbHaveAlpha = bHasAlpha;
+ mpVirtualDevice.reset();
+ mpSurface = pSurface;
+ mpCairo = pSurface->getCairo();
+ }
+
+ void CanvasHelper::useStates( const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ bool setColor )
+ {
+ Matrix aViewMatrix;
+ Matrix aRenderMatrix;
+ Matrix aCombinedMatrix;
+
+ cairo_matrix_init( &aViewMatrix,
+ viewState.AffineTransform.m00, viewState.AffineTransform.m10, viewState.AffineTransform.m01,
+ viewState.AffineTransform.m11, viewState.AffineTransform.m02, viewState.AffineTransform.m12);
+ cairo_matrix_init( &aRenderMatrix,
+ renderState.AffineTransform.m00, renderState.AffineTransform.m10, renderState.AffineTransform.m01,
+ renderState.AffineTransform.m11, renderState.AffineTransform.m02, renderState.AffineTransform.m12);
+ cairo_matrix_multiply( &aCombinedMatrix, &aRenderMatrix, &aViewMatrix);
+
+ if( viewState.Clip.is() ) {
+ OSL_TRACE ("view clip");
+
+ aViewMatrix.x0 = basegfx::fround( aViewMatrix.x0 );
+ aViewMatrix.y0 = basegfx::fround( aViewMatrix.y0 );
+ cairo_set_matrix( mpCairo.get(), &aViewMatrix );
+ doPolyPolygonPath( viewState.Clip, Clip );
+ }
+
+ aCombinedMatrix.x0 = basegfx::fround( aCombinedMatrix.x0 );
+ aCombinedMatrix.y0 = basegfx::fround( aCombinedMatrix.y0 );
+ cairo_set_matrix( mpCairo.get(), &aCombinedMatrix );
+
+ if( renderState.Clip.is() ) {
+ OSL_TRACE ("render clip BEGIN");
+
+ doPolyPolygonPath( renderState.Clip, Clip );
+ OSL_TRACE ("render clip END");
+ }
+
+ if( setColor ) {
+ if( renderState.DeviceColor.getLength() > 3 )
+ cairo_set_source_rgba( mpCairo.get(),
+ renderState.DeviceColor [0],
+ renderState.DeviceColor [1],
+ renderState.DeviceColor [2],
+ renderState.DeviceColor [3] );
+ else if (renderState.DeviceColor.getLength() == 3)
+ cairo_set_source_rgb( mpCairo.get(),
+ renderState.DeviceColor [0],
+ renderState.DeviceColor [1],
+ renderState.DeviceColor [2] );
+ }
+
+ cairo_operator_t compositingMode( CAIRO_OPERATOR_OVER );
+ switch( renderState.CompositeOperation )
+ {
+ case rendering::CompositeOperation::CLEAR:
+ compositingMode = CAIRO_OPERATOR_CLEAR;
+ break;
+ case rendering::CompositeOperation::SOURCE:
+ compositingMode = CAIRO_OPERATOR_SOURCE;
+ break;
+ case rendering::CompositeOperation::DESTINATION:
+ compositingMode = CAIRO_OPERATOR_DEST;
+ break;
+ case rendering::CompositeOperation::OVER:
+ compositingMode = CAIRO_OPERATOR_OVER;
+ break;
+ case rendering::CompositeOperation::UNDER:
+ compositingMode = CAIRO_OPERATOR_DEST;
+ break;
+ case rendering::CompositeOperation::INSIDE:
+ compositingMode = CAIRO_OPERATOR_IN;
+ break;
+ case rendering::CompositeOperation::INSIDE_REVERSE:
+ compositingMode = CAIRO_OPERATOR_OUT;
+ break;
+ case rendering::CompositeOperation::OUTSIDE:
+ compositingMode = CAIRO_OPERATOR_DEST_OVER;
+ break;
+ case rendering::CompositeOperation::OUTSIDE_REVERSE:
+ compositingMode = CAIRO_OPERATOR_DEST_OUT;
+ break;
+ case rendering::CompositeOperation::ATOP:
+ compositingMode = CAIRO_OPERATOR_ATOP;
+ break;
+ case rendering::CompositeOperation::ATOP_REVERSE:
+ compositingMode = CAIRO_OPERATOR_DEST_ATOP;
+ break;
+ case rendering::CompositeOperation::XOR:
+ compositingMode = CAIRO_OPERATOR_XOR;
+ break;
+ case rendering::CompositeOperation::ADD:
+ compositingMode = CAIRO_OPERATOR_ADD;
+ break;
+ case rendering::CompositeOperation::SATURATE:
+ compositingMode = CAIRO_OPERATOR_SATURATE;
+ break;
+ }
+ cairo_set_operator( mpCairo.get(), compositingMode );
+ }
+
+ void CanvasHelper::clear()
+ {
+ OSL_TRACE ("clear whole area: %d x %d", maSize.getX(), maSize.getY() );
+
+ if( mpCairo )
+ {
+ cairo_save( mpCairo.get() );
+
+ cairo_identity_matrix( mpCairo.get() );
+ // this does not really differ from all-zero, as cairo
+ // internally converts to premultiplied alpha. but anyway,
+ // this keeps it consistent with the other canvas impls
+ if( mbHaveAlpha )
+ cairo_set_source_rgba( mpCairo.get(), 1.0, 1.0, 1.0, 0.0 );
+ else
+ cairo_set_source_rgb( mpCairo.get(), 1.0, 1.0, 1.0 );
+ cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
+
+ cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
+ cairo_fill( mpCairo.get() );
+
+ cairo_restore( mpCairo.get() );
+ }
+ }
+
+ void CanvasHelper::drawPoint( const rendering::XCanvas* ,
+ const geometry::RealPoint2D& ,
+ const rendering::ViewState& ,
+ const rendering::RenderState& )
+ {
+ }
+
+ void CanvasHelper::drawLine( const rendering::XCanvas* /*pCanvas*/,
+ const geometry::RealPoint2D& aStartPoint,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( mpCairo ) {
+ cairo_save( mpCairo.get() );
+
+ cairo_set_line_width( mpCairo.get(), 1 );
+
+ useStates( viewState, renderState, true );
+
+ cairo_move_to( mpCairo.get(), aStartPoint.X + 0.5, aStartPoint.Y + 0.5 );
+ cairo_line_to( mpCairo.get(), aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
+ cairo_stroke( mpCairo.get() );
+
+ cairo_restore( mpCairo.get() );
+ }
+ }
+
+ void CanvasHelper::drawBezier( const rendering::XCanvas* ,
+ const geometry::RealBezierSegment2D& aBezierSegment,
+ const geometry::RealPoint2D& aEndPoint,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ if( mpCairo ) {
+ cairo_save( mpCairo.get() );
+
+ cairo_set_line_width( mpCairo.get(), 1 );
+
+ useStates( viewState, renderState, true );
+
+ cairo_move_to( mpCairo.get(), aBezierSegment.Px + 0.5, aBezierSegment.Py + 0.5 );
+ cairo_curve_to( mpCairo.get(),
+ aBezierSegment.C1x + 0.5, aBezierSegment.C1y + 0.5,
+ aBezierSegment.C2x + 0.5, aBezierSegment.C2y + 0.5,
+ aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
+ cairo_stroke( mpCairo.get() );
+
+ cairo_restore( mpCairo.get() );
+ }
+ }
+
+#define CANVASBITMAP_IMPLEMENTATION_NAME "CairoCanvas::CanvasBitmap"
+#define PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME "Canvas::ParametricPolyPolygon"
+
+
+ /** surfaceFromXBitmap Create a surface from XBitmap
+ * @param xBitmap bitmap image that will be used for the surface
+ * @param bHasAlpha will be set to true if resulting surface has alpha
+ *
+ * This is a helper function for the other surfaceFromXBitmap().
+ * This function tries to create surface from xBitmap by checking if xBitmap is CanvasBitmap or SpriteCanvas.
+ *
+ * @return created surface or NULL
+ **/
+ static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
+ {
+ CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
+ if( pBitmapImpl )
+ return pBitmapImpl->getSurface();
+
+ SurfaceProvider* pSurfaceProvider = dynamic_cast<SurfaceProvider*>( xBitmap.get() );
+ if( pSurfaceProvider )
+ return pSurfaceProvider->getSurface();
+
+ return SurfaceSharedPtr();
+ }
+
+ static ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
+ {
+ // TODO(F1): Add support for floating point bitmap formats
+ uno::Reference<rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap,
+ uno::UNO_QUERY_THROW);
+ ::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap(xIntBmp);
+ if( !!aBmpEx )
+ return aBmpEx;
+
+ // TODO(F1): extract pixel from XBitmap interface
+ ENSURE_OR_THROW( false,
+ "bitmapExFromXBitmap(): could not extract BitmapEx" );
+
+ return ::BitmapEx();
+ }
+
+ static bool readAlpha( BitmapReadAccess* pAlphaReadAcc, long nY, const long nWidth, unsigned char* data, long nOff )
+ {
+ bool bIsAlpha = false;
+ long nX;
+ int nAlpha;
+ Scanline pReadScan;
+
+ nOff += 3;
+
+ switch( pAlphaReadAcc->GetScanlineFormat() ) {
+ case BMP_FORMAT_8BIT_TC_MASK:
+ pReadScan = pAlphaReadAcc->GetScanline( nY );
+ for( nX = 0; nX < nWidth; nX++ ) {
+ nAlpha = data[ nOff ] = 255 - ( *pReadScan++ );
+ if( nAlpha != 255 )
+ bIsAlpha = true;
+ nOff += 4;
+ }
+ break;
+ case BMP_FORMAT_8BIT_PAL:
+ pReadScan = pAlphaReadAcc->GetScanline( nY );
+ for( nX = 0; nX < nWidth; nX++ ) {
+ nAlpha = data[ nOff ] = 255 - ( pAlphaReadAcc->GetPaletteColor( *pReadScan++ ).GetBlue() );
+ if( nAlpha != 255 )
+ bIsAlpha = true;
+ nOff += 4;
+ }
+ break;
+ default:
+ OSL_TRACE( "fallback to GetColor for alpha - slow, format: %d", pAlphaReadAcc->GetScanlineFormat() );
+ for( nX = 0; nX < nWidth; nX++ ) {
+ nAlpha = data[ nOff ] = 255 - pAlphaReadAcc->GetColor( nY, nX ).GetBlue();
+ if( nAlpha != 255 )
+ bIsAlpha = true;
+ nOff += 4;
+ }
+ }
+
+ return bIsAlpha;
+ }
+
+
+ /** surfaceFromXBitmap Create a surface from XBitmap
+ * @param xBitmap bitmap image that will be used for the surface
+ * @param rDevice reference to the device into which we want to draw
+ * @param data will be filled with alpha data, if xBitmap is alpha/transparent image
+ * @param bHasAlpha will be set to true if resulting surface has alpha
+ *
+ * This function tries various methods for creating a surface from xBitmap. It also uses
+ * the helper function surfaceFromXBitmap( xBitmap, bHasAlpha )
+ *
+ * @return created surface or NULL
+ **/
+ static SurfaceSharedPtr surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, const SurfaceProviderRef& rSurfaceProvider, unsigned char*& data, bool& bHasAlpha )
+ {
+ bHasAlpha = xBitmap->hasAlpha();
+ SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap );
+ if( pSurface )
+ data = NULL;
+ else
+ {
+ ::BitmapEx aBmpEx = bitmapExFromXBitmap(xBitmap);
+ ::Bitmap aBitmap = aBmpEx.GetBitmap();
+
+ // there's no pixmap for alpha bitmap. we might still
+ // use rgb pixmap and only access alpha pixels the
+ // slow way. now we just speedup rgb bitmaps
+ if( !aBmpEx.IsTransparent() && !aBmpEx.IsAlpha() ) {
+ pSurface = rSurfaceProvider->createSurface( aBitmap );
+ data = NULL;
+ bHasAlpha = false;
+ }
+
+ if( !pSurface ) {
+ AlphaMask aAlpha = aBmpEx.GetAlpha();
+
+ ::BitmapReadAccess* pBitmapReadAcc = aBitmap.AcquireReadAccess();
+ ::BitmapReadAccess* pAlphaReadAcc = NULL;
+ const long nWidth = pBitmapReadAcc->Width();
+ const long nHeight = pBitmapReadAcc->Height();
+ long nX, nY;
+ bool bIsAlpha = false;
+
+ if( aBmpEx.IsTransparent() || aBmpEx.IsAlpha() )
+ pAlphaReadAcc = aAlpha.AcquireReadAccess();
+
+ data = (unsigned char*) malloc( nWidth*nHeight*4 );
+
+ long nOff = 0;
+ ::Color aColor;
+ unsigned int nAlpha = 255;
+
+ for( nY = 0; nY < nHeight; nY++ ) {
+ ::Scanline pReadScan;
+
+ switch( pBitmapReadAcc->GetScanlineFormat() ) {
+ case BMP_FORMAT_8BIT_PAL:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ ) {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff++ ];
+ else
+ nAlpha = data[ nOff++ ] = 255;
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+#endif
+ aColor = pBitmapReadAcc->GetPaletteColor( *pReadScan++ );
+
+#ifdef OSL_BIGENDIAN
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetRed() ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetGreen() ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetBlue() ) )/255 );
+#else
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetBlue() ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetGreen() ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( aColor.GetRed() ) )/255 );
+ nOff++;
+#endif
+ }
+ break;
+ case BMP_FORMAT_24BIT_TC_BGR:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ ) {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff ];
+ else
+ nAlpha = data[ nOff ] = 255;
+ data[ nOff + 3 ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff + 2 ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff + 1 ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ nOff += 4;
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ nOff++;
+#endif
+ }
+ break;
+ case BMP_FORMAT_24BIT_TC_RGB:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ ) {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff++ ];
+ else
+ nAlpha = data[ nOff++ ] = 255;
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 2 ] ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 1 ] ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 0 ] ) )/255 );
+ pReadScan += 3;
+ nOff++;
+#endif
+ }
+ break;
+ case BMP_FORMAT_32BIT_TC_BGRA:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ ) {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff++ ];
+ else
+ nAlpha = data[ nOff++ ] = pReadScan[ 3 ];
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 2 ] ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 1 ] ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 0 ] ) )/255 );
+ pReadScan += 4;
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = pReadScan[ 3 ];
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ pReadScan++;
+ nOff++;
+#endif
+ }
+ break;
+ case BMP_FORMAT_32BIT_TC_RGBA:
+ pReadScan = pBitmapReadAcc->GetScanline( nY );
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ ) {
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff ++ ];
+ else
+ nAlpha = data[ nOff ++ ] = 255;
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( *pReadScan++ ) )/255 );
+ pReadScan++;
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 2 ] ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 1 ] ) )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*( pReadScan[ 0 ] ) )/255 );
+ pReadScan += 4;
+ nOff++;
+#endif
+ }
+ break;
+ default:
+ OSL_TRACE( "fallback to GetColor - slow, format: %d", pBitmapReadAcc->GetScanlineFormat() );
+
+ if( pAlphaReadAcc )
+ if( readAlpha( pAlphaReadAcc, nY, nWidth, data, nOff ) )
+ bIsAlpha = true;
+
+ for( nX = 0; nX < nWidth; nX++ ) {
+ aColor = pBitmapReadAcc->GetColor( nY, nX );
+
+ // cairo need premultiplied color values
+ // TODO(rodo) handle endianess
+#ifdef OSL_BIGENDIAN
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff++ ];
+ else
+ nAlpha = data[ nOff++ ] = 255;
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetRed() )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetGreen() )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetBlue() )/255 );
+#else
+ if( pAlphaReadAcc )
+ nAlpha = data[ nOff + 3 ];
+ else
+ nAlpha = data[ nOff + 3 ] = 255;
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetBlue() )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetGreen() )/255 );
+ data[ nOff++ ] = sal::static_int_cast<unsigned char>(( nAlpha*aColor.GetRed() )/255 );
+ nOff ++;
+#endif
+ }
+ }
+ }
+
+ aBitmap.ReleaseAccess( pBitmapReadAcc );
+ if( pAlphaReadAcc )
+ aAlpha.ReleaseAccess( pAlphaReadAcc );
+
+ SurfaceSharedPtr pImageSurface = createSurface(
+ CairoSurfaceSharedPtr(
+ cairo_image_surface_create_for_data(
+ data,
+ bIsAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
+ nWidth, nHeight, nWidth*4 ),
+ &cairo_surface_destroy) );
+
+ // pSurface = rSurfaceProvider->getSurface( ::basegfx::B2ISize( nWidth, nHeight ), bIsAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
+ // Cairo* pTargetCairo = cairo_create( pSurface );
+ // cairo_set_source_surface( pTargetCairo, pImageSurface, 0, 0 );
+
+ // //if( !bIsAlpha )
+ // //cairo_set_operator( pTargetCairo, CAIRO_OPERATOR_SOURCE );
+
+ // cairo_paint( pTargetCairo );
+ // cairo_destroy( pTargetCairo );
+ // cairo_surface_destroy( pImageSurface );
+ pSurface = pImageSurface;
+
+ bHasAlpha = bIsAlpha;
+
+ OSL_TRACE("image: %d x %d alpha: %d alphaRead %p", nWidth, nHeight, bIsAlpha, pAlphaReadAcc);
+ }
+ }
+
+ return pSurface;
+ }
+
+ static void addColorStops( Pattern* pPattern, const uno::Sequence< uno::Sequence< double > >& rColors, const uno::Sequence< double >& rStops, bool bReverseStops = false )
+ {
+ float stop;
+ int i;
+
+ OSL_ASSERT( rColors.getLength() == rStops.getLength() );
+
+ for( i = 0; i < rColors.getLength(); i++ ) {
+ const uno::Sequence< double >& rColor( rColors[i] );
+ stop = bReverseStops ? 1 - rStops[i] : rStops[i];
+ if( rColor.getLength() == 3 )
+ cairo_pattern_add_color_stop_rgb( pPattern, stop, rColor[0], rColor[1], rColor[2] );
+ else if( rColor.getLength() == 4 ) {
+ double alpha = rColor[3];
+ // cairo expects premultiplied alpha
+ cairo_pattern_add_color_stop_rgba( pPattern, stop, rColor[0]*alpha, rColor[1]*alpha, rColor[2]*alpha, alpha );
+ //cairo_pattern_add_color_stop_rgba( pPattern, stop, rColor[0], rColor[1], rColor[2], alpha );
+ }
+ }
+ }
+
+ static Pattern* patternFromParametricPolyPolygon( ::canvas::ParametricPolyPolygon& rPolygon )
+ {
+ Pattern* pPattern = NULL;
+ const ::canvas::ParametricPolyPolygon::Values aValues = rPolygon.getValues();
+ double x0, x1, y0, y1, cx, cy, r0, r1;
+
+// undef macros from vclenum.hxx which conflicts with GradientType enum values
+#undef GRADIENT_LINEAR
+#undef GRADIENT_AXIAL
+#undef GRADIENT_ELLIPTICAL
+
+ switch( aValues.meType ) {
+ case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR:
+ x0 = 0;
+ y0 = 0;
+ x1 = 1;
+ y1 = 0;
+ pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 );
+ addColorStops( pPattern, aValues.maColors, aValues.maStops );
+ break;
+
+ // FIXME: NYI
+ case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR:
+ case ::canvas::ParametricPolyPolygon::GRADIENT_AXIAL:
+ x0 = 0;
+ y0 = 0;
+ x1 = 1;
+ y1 = 0;
+ pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 );
+ addColorStops( pPattern, aValues.maColors, aValues.maStops );
+ break;
+
+ case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL:
+ cx = 0.5;
+ cy = 0.5;
+ r0 = 0;
+ r1 = 0.5;
+
+ pPattern = cairo_pattern_create_radial( cx, cy, r0, cx, cy, r1 );
+ addColorStops( pPattern, aValues.maColors, aValues.maStops, true );
+ break;
+ }
+
+ return pPattern;
+ }
+
+ static void doOperation( Operation aOperation,
+ Cairo* pCairo,
+ const uno::Sequence< rendering::Texture >* pTextures,
+ const SurfaceProviderRef& pDevice )
+ {
+ switch( aOperation ) {
+ case Fill:
+ /* TODO: multitexturing */
+ if( pTextures ) {
+ const ::com::sun::star::rendering::Texture& aTexture ( (*pTextures)[0] );
+ if( aTexture.Bitmap.is() ) {
+ unsigned char* data = NULL;
+ bool bHasAlpha = false;
+ SurfaceSharedPtr pSurface = surfaceFromXBitmap( (*pTextures)[0].Bitmap, pDevice, data, bHasAlpha );
+
+ if( pSurface ) {
+ cairo_pattern_t* pPattern;
+
+ cairo_save( pCairo );
+
+ ::com::sun::star::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
+ Matrix aScaleMatrix, aTextureMatrix, aScaledTextureMatrix;
+
+ cairo_matrix_init( &aTextureMatrix,
+ aTransform.m00, aTransform.m10, aTransform.m01,
+ aTransform.m11, aTransform.m02, aTransform.m12);
+
+ geometry::IntegerSize2D aSize = aTexture.Bitmap->getSize();
+
+ cairo_matrix_init_scale( &aScaleMatrix, 1.0/aSize.Width, 1.0/aSize.Height );
+ cairo_matrix_multiply( &aScaledTextureMatrix, &aTextureMatrix, &aScaleMatrix );
+ cairo_matrix_invert( &aScaledTextureMatrix );
+
+ // we don't care about repeat mode yet, so the workaround is disabled for now
+ pPattern = cairo_pattern_create_for_surface( pSurface->getCairoSurface().get() );
+
+ if( aTexture.RepeatModeX == rendering::TexturingMode::REPEAT &&
+ aTexture.RepeatModeY == rendering::TexturingMode::REPEAT )
+ cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_REPEAT );
+ aScaledTextureMatrix.x0 = basegfx::fround( aScaledTextureMatrix.x0 );
+ aScaledTextureMatrix.y0 = basegfx::fround( aScaledTextureMatrix.y0 );
+ cairo_pattern_set_matrix( pPattern, &aScaledTextureMatrix );
+
+ cairo_set_source( pCairo, pPattern );
+ if( !bHasAlpha )
+ cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
+ cairo_fill( pCairo );
+
+ cairo_restore( pCairo );
+
+ cairo_pattern_destroy( pPattern );
+ }
+
+ if( data )
+ free( data );
+ } else if( aTexture.Gradient.is() ) {
+ uno::Reference< lang::XServiceInfo > xRef( aTexture.Gradient, uno::UNO_QUERY );
+
+ OSL_TRACE( "gradient fill" );
+ if( xRef.is() &&
+ xRef->getImplementationName().equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME ) ) ) ) {
+ // TODO(Q1): Maybe use dynamic_cast here
+
+ // TODO(E1): Return value
+ // TODO(F1): FillRule
+ OSL_TRACE( "known implementation" );
+
+ ::canvas::ParametricPolyPolygon* pPolyImpl = static_cast< ::canvas::ParametricPolyPolygon* >( aTexture.Gradient.get() );
+ ::com::sun::star::geometry::AffineMatrix2D aTransform( aTexture.AffineTransform );
+ Matrix aTextureMatrix;
+
+ cairo_matrix_init( &aTextureMatrix,
+ aTransform.m00, aTransform.m10, aTransform.m01,
+ aTransform.m11, aTransform.m02, aTransform.m12);
+ Pattern* pPattern = patternFromParametricPolyPolygon( *pPolyImpl );
+
+ if( pPattern ) {
+ OSL_TRACE( "filling with pattern" );
+
+ cairo_save( pCairo );
+
+ cairo_transform( pCairo, &aTextureMatrix );
+ cairo_set_source( pCairo, pPattern );
+ cairo_fill( pCairo );
+ cairo_restore( pCairo );
+
+ cairo_pattern_destroy( pPattern );
+ }
+ }
+ }
+ } else
+ cairo_fill( pCairo );
+ OSL_TRACE("fill");
+ break;
+ case Stroke:
+ cairo_stroke( pCairo );
+ OSL_TRACE("stroke");
+ break;
+ case Clip:
+ cairo_clip( pCairo );
+ OSL_TRACE("clip");
+ break;
+ }
+ }
+
+ static void clipNULL( Cairo *pCairo )
+ {
+ OSL_TRACE("clipNULL");
+ Matrix aOrigMatrix, aIdentityMatrix;
+
+ /* we set identity matrix here to overcome bug in cairo 0.9.2
+ where XCreatePixmap is called with zero width and height.
+
+ it also reaches faster path in cairo clipping code.
+ */
+ cairo_matrix_init_identity( &aIdentityMatrix );
+ cairo_get_matrix( pCairo, &aOrigMatrix );
+ cairo_set_matrix( pCairo, &aIdentityMatrix );
+
+ cairo_reset_clip( pCairo );
+ cairo_rectangle( pCairo, 0, 0, 1, 1 );
+ cairo_clip( pCairo );
+ cairo_rectangle( pCairo, 2, 0, 1, 1 );
+ cairo_clip( pCairo );
+
+ /* restore the original matrix */
+ cairo_set_matrix( pCairo, &aOrigMatrix );
+ }
+
+ void doPolyPolygonImplementation( ::basegfx::B2DPolyPolygon aPolyPolygon,
+ Operation aOperation,
+ Cairo* pCairo,
+ const uno::Sequence< rendering::Texture >* pTextures,
+ const SurfaceProviderRef& pDevice,
+ rendering::FillRule eFillrule )
+ {
+ if( pTextures )
+ ENSURE_ARG_OR_THROW( pTextures->getLength(),
+ "CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
+
+ bool bOpToDo = false;
+ Matrix aOrigMatrix, aIdentityMatrix;
+ double nX, nY, nBX, nBY, nAX, nAY;
+
+ cairo_get_matrix( pCairo, &aOrigMatrix );
+ cairo_matrix_init_identity( &aIdentityMatrix );
+ cairo_set_matrix( pCairo, &aIdentityMatrix );
+
+ cairo_set_fill_rule( pCairo,
+ eFillrule == rendering::FillRule_EVEN_ODD ?
+ CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING );
+
+ for( sal_uInt32 nPolygonIndex = 0; nPolygonIndex < aPolyPolygon.count(); nPolygonIndex++ ) {
+ ::basegfx::B2DPolygon aPolygon( aPolyPolygon.getB2DPolygon( nPolygonIndex ) );
+ const sal_uInt32 nPointCount( aPolygon.count() );
+ // to correctly render closed curves, need to output first
+ // point twice (so output one additional point)
+ const sal_uInt32 nExtendedPointCount( nPointCount +
+ aPolygon.isClosed()*aPolygon.areControlPointsUsed() );
+
+ if( nPointCount > 1) {
+ bool bIsBezier = aPolygon.areControlPointsUsed();
+ bool bIsRectangle = ::basegfx::tools::isRectangle( aPolygon );
+ ::basegfx::B2DPoint aA, aB, aP;
+
+ for( sal_uInt32 j=0; j < nExtendedPointCount; j++ ) {
+ aP = aPolygon.getB2DPoint( j % nPointCount );
+
+ nX = aP.getX();
+ nY = aP.getY();
+ cairo_matrix_transform_point( &aOrigMatrix, &nX, &nY );
+
+ if( ! bIsBezier && (bIsRectangle || aOperation == Clip) ) {
+ nX = basegfx::fround( nX );
+ nY = basegfx::fround( nY );
+ }
+
+ if( aOperation == Stroke ) {
+ nX += 0.5;
+ nY += 0.5;
+ }
+
+ if( j==0 )
+ {
+ cairo_move_to( pCairo, nX, nY );
+ OSL_TRACE( "move to %f,%f", nX, nY );
+ }
+ else {
+ if( bIsBezier ) {
+ aA = aPolygon.getNextControlPoint( (j-1) % nPointCount );
+ aB = aPolygon.getPrevControlPoint( j % nPointCount );
+
+ nAX = aA.getX();
+ nAY = aA.getY();
+ nBX = aB.getX();
+ nBY = aB.getY();
+
+ if( aOperation == Stroke ) {
+ nAX += 0.5;
+ nAY += 0.5;
+ nBX += 0.5;
+ nBY += 0.5;
+ }
+ cairo_matrix_transform_point( &aOrigMatrix, &nAX, &nAY );
+ cairo_matrix_transform_point( &aOrigMatrix, &nBX, &nBY );
+ cairo_curve_to( pCairo, nAX, nAY, nBX, nBY, nX, nY );
+ } else {
+ cairo_line_to( pCairo, nX, nY );
+ OSL_TRACE( "line to %f,%f", nX, nY );
+ }
+ bOpToDo = true;
+ }
+ }
+
+ if( aPolygon.isClosed() )
+ cairo_close_path( pCairo );
+
+ if( aOperation == Fill && pTextures ) {
+ cairo_set_matrix( pCairo, &aOrigMatrix );
+ doOperation( aOperation, pCairo, pTextures, pDevice );
+ cairo_set_matrix( pCairo, &aIdentityMatrix );
+ }
+ } else {
+ OSL_TRACE( "empty polygon for op: %d\n", aOperation );
+ if( aOperation == Clip ) {
+ clipNULL( pCairo );
+
+ return;
+ }
+ }
+ }
+ if( bOpToDo && ( aOperation != Fill || !pTextures ) )
+ doOperation( aOperation, pCairo, pTextures, pDevice );
+
+ cairo_set_matrix( pCairo, &aOrigMatrix );
+
+ if( aPolyPolygon.count() == 0 && aOperation == Clip )
+ clipNULL( pCairo );
+ }
+
+ void CanvasHelper::doPolyPolygonPath( const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ Operation aOperation,
+ const uno::Sequence< rendering::Texture >* pTextures,
+ Cairo* pCairo ) const
+ {
+ const ::basegfx::B2DPolyPolygon& rPolyPoly(
+ ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) );
+
+ if( !pCairo )
+ pCairo = mpCairo.get();
+
+ doPolyPolygonImplementation( rPolyPoly, aOperation,
+ pCairo, pTextures,
+ mpSurfaceProvider,
+ xPolyPolygon->getFillRule() );
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ if( mpCairo ) {
+ cairo_save( mpCairo.get() );
+
+ cairo_set_line_width( mpCairo.get(), 1 );
+
+ useStates( viewState, renderState, true );
+ doPolyPolygonPath( xPolyPolygon, Stroke );
+
+ cairo_restore( mpCairo.get() );
+ } else
+ OSL_TRACE ("CanvasHelper called after it was disposed");
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "drawPolyPolygon" );
+#endif
+
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokePolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const rendering::StrokeAttributes& strokeAttributes )
+ {
+ #ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+ #endif
+
+ if( mpCairo ) {
+ cairo_save( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+
+ Matrix aMatrix;
+ double w = strokeAttributes.StrokeWidth, h = 0;
+ cairo_get_matrix( mpCairo.get(), &aMatrix );
+ cairo_matrix_transform_distance( &aMatrix, &w, &h );
+ cairo_set_line_width( mpCairo.get(), w );
+
+ cairo_set_miter_limit( mpCairo.get(), strokeAttributes.MiterLimit );
+
+ // FIXME: cairo doesn't handle end cap so far (rodo)
+ switch( strokeAttributes.StartCapType ) {
+ case rendering::PathCapType::BUTT:
+ cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_BUTT );
+ break;
+ case rendering::PathCapType::ROUND:
+ cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_ROUND );
+ break;
+ case rendering::PathCapType::SQUARE:
+ cairo_set_line_cap( mpCairo.get(), CAIRO_LINE_CAP_SQUARE );
+ break;
+ }
+
+ switch( strokeAttributes.JoinType ) {
+ // cairo doesn't have join type NONE so we use MITER as it's pretty close
+ case rendering::PathJoinType::NONE:
+ case rendering::PathJoinType::MITER:
+ cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_MITER );
+ break;
+ case rendering::PathJoinType::ROUND:
+ cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_ROUND );
+ break;
+ case rendering::PathJoinType::BEVEL:
+ cairo_set_line_join( mpCairo.get(), CAIRO_LINE_JOIN_BEVEL );
+ break;
+ }
+
+ if( strokeAttributes.DashArray.getLength() > 0 ) {
+ double* pDashArray = new double[ strokeAttributes.DashArray.getLength() ];
+ for( sal_Int32 i=0; i<strokeAttributes.DashArray.getLength(); i++ )
+ pDashArray[i]=strokeAttributes.DashArray[i];
+ cairo_set_dash( mpCairo.get(), pDashArray, strokeAttributes.DashArray.getLength(), 0 );
+ delete[] pDashArray;
+ }
+
+ // TODO(rodo) use LineArray of strokeAttributes
+
+ doPolyPolygonPath( xPolyPolygon, Stroke );
+
+ cairo_restore( mpCairo.get() );
+ } else
+ OSL_TRACE ("CanvasHelper called after it was disposed");
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "strokePolyPolygon" );
+#endif
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTexturedPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::strokeTextureMappedPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const uno::Reference< geometry::XMapping2D >& /*xMapping*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XPolyPolygon2D > CanvasHelper::queryStrokeShapes( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const rendering::StrokeAttributes& /*strokeAttributes*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XPolyPolygon2D >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ #ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+ #endif
+
+ if( mpCairo ) {
+ cairo_save( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+ doPolyPolygonPath( xPolyPolygon, Fill );
+
+ cairo_restore( mpCairo.get() );
+ } else
+ OSL_TRACE ("CanvasHelper called after it was disposed");
+
+ #ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "fillPolyPolygon" );
+ #endif
+
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTexturedPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const uno::Sequence< rendering::Texture >& textures )
+ {
+ if( mpCairo ) {
+ cairo_save( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+ doPolyPolygonPath( xPolyPolygon, Fill, &textures );
+
+ cairo_restore( mpCairo.get() );
+ }
+
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/,
+ const uno::Sequence< rendering::Texture >& /*textures*/,
+ const uno::Reference< geometry::XMapping2D >& /*xMapping*/ )
+ {
+ // TODO
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::implDrawBitmapSurface( const rendering::XCanvas* pCanvas,
+ const SurfaceSharedPtr& pInputSurface,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState,
+ const geometry::IntegerSize2D& rSize,
+ bool /*bModulateColors*/,
+ bool bHasAlpha )
+ {
+ SurfaceSharedPtr pSurface=pInputSurface;
+ uno::Reference< rendering::XCachedPrimitive > rv = uno::Reference< rendering::XCachedPrimitive >(NULL);
+ geometry::IntegerSize2D aBitmapSize = rSize;
+
+ if( mpCairo ) {
+ cairo_save( mpCairo.get() );
+
+ cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
+ cairo_clip( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+
+ // if( !bHasAlpha )
+ // cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
+
+ Matrix aMatrix;
+
+ cairo_get_matrix( mpCairo.get(), &aMatrix );
+ if( ! ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
+ ! ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
+ ::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
+ ::rtl::math::approxEqual( aMatrix.y0, 0 ) &&
+ basegfx::fround( rSize.Width * aMatrix.xx ) > 8 &&
+ basegfx::fround( rSize.Height* aMatrix.yy ) > 8 )
+ {
+ double dWidth, dHeight;
+
+ dWidth = basegfx::fround( rSize.Width * aMatrix.xx );
+ dHeight = basegfx::fround( rSize.Height* aMatrix.yy );
+ aBitmapSize.Width = static_cast<sal_Int32>( dWidth );
+ aBitmapSize.Height = static_cast<sal_Int32>( dHeight );
+
+ SurfaceSharedPtr pScaledSurface = mpSurfaceProvider->createSurface(
+ ::basegfx::B2ISize( aBitmapSize.Width, aBitmapSize.Height ),
+ bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
+ CairoSharedPtr pCairo = pScaledSurface->getCairo();
+
+ cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE );
+ // add 0.5px to size to avoid rounding errors in cairo, leading sometimes to random data on the image right/bottom borders
+ cairo_scale( pCairo.get(), (dWidth+0.5)/rSize.Width, (dHeight+0.5)/rSize.Height );
+ cairo_set_source_surface( pCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
+ cairo_paint( pCairo.get() );
+
+ pSurface = pScaledSurface;
+
+ aMatrix.xx = aMatrix.yy = 1;
+ cairo_set_matrix( mpCairo.get(), &aMatrix );
+
+ rv = uno::Reference< rendering::XCachedPrimitive >(
+ new CachedBitmap( pSurface, viewState, renderState,
+ // cast away const, need to
+ // change refcount (as this is
+ // ~invisible to client code,
+ // still logically const)
+ const_cast< rendering::XCanvas* >(pCanvas)) );
+ }
+
+ if( !bHasAlpha && mbHaveAlpha )
+ {
+ double x, y, width, height;
+
+ x = y = 0;
+ width = aBitmapSize.Width;
+ height = aBitmapSize.Height;
+ cairo_matrix_transform_point( &aMatrix, &x, &y );
+ cairo_matrix_transform_distance( &aMatrix, &width, &height );
+
+ // in case the bitmap doesn't have alpha and covers whole area
+ // we try to change surface to plain rgb
+ OSL_TRACE ("chance to change surface to rgb, %f, %f, %f x %f (%d x %d)", x, y, width, height, maSize.getX(), maSize.getY() );
+ if( x <= 0 && y <= 0 && x + width >= maSize.getX() && y + height >= maSize.getY() )
+ {
+ OSL_TRACE ("trying to change surface to rgb");
+ if( mpSurfaceProvider ) {
+ SurfaceSharedPtr pNewSurface = mpSurfaceProvider->changeSurface( false, false );
+
+ if( pNewSurface )
+ setSurface( pNewSurface, false );
+
+ // set state to new mpCairo.get()
+ useStates( viewState, renderState, true );
+ // use the possibly modified matrix
+ cairo_set_matrix( mpCairo.get(), &aMatrix );
+ }
+ }
+ }
+
+ cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
+ if( !bHasAlpha &&
+ ::rtl::math::approxEqual( aMatrix.xx, 1 ) &&
+ ::rtl::math::approxEqual( aMatrix.yy, 1 ) &&
+ ::rtl::math::approxEqual( aMatrix.x0, 0 ) &&
+ ::rtl::math::approxEqual( aMatrix.y0, 0 ) )
+ cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
+ cairo_rectangle( mpCairo.get(), 0, 0, aBitmapSize.Width, aBitmapSize.Height );
+ cairo_clip( mpCairo.get() );
+ cairo_paint( mpCairo.get() );
+ cairo_restore( mpCairo.get() );
+ } else
+ OSL_TRACE ("CanvasHelper called after it was disposed");
+
+ return rv; // uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmap( const rendering::XCanvas* pCanvas,
+ const uno::Reference< rendering::XBitmap >& xBitmap,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ #ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+ #endif
+
+ uno::Reference< rendering::XCachedPrimitive > rv;
+ unsigned char* data = NULL;
+ bool bHasAlpha = false;
+ SurfaceSharedPtr pSurface = surfaceFromXBitmap( xBitmap, mpSurfaceProvider, data, bHasAlpha );
+ geometry::IntegerSize2D aSize = xBitmap->getSize();
+
+ if( pSurface ) {
+ rv = implDrawBitmapSurface( pCanvas, pSurface, viewState, renderState, aSize, false, bHasAlpha );
+
+ if( data )
+ free( data );
+ } else
+ rv = uno::Reference< rendering::XCachedPrimitive >(NULL);
+
+ #ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "drawBitmap" );
+ #endif
+
+ return rv;
+ }
+
+ uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawBitmapModulated( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XBitmap >& /*xBitmap*/,
+ const rendering::ViewState& /*viewState*/,
+ const rendering::RenderState& /*renderState*/ )
+ {
+ // TODO(F3): Implement modulated bitmap!
+
+ // TODO(P1): Provide caching here.
+ return uno::Reference< rendering::XCachedPrimitive >(NULL);
+ }
+
+ uno::Reference< rendering::XGraphicDevice > CanvasHelper::getDevice()
+ {
+ return uno::Reference< rendering::XGraphicDevice >(mpDevice);
+ }
+
+ void CanvasHelper::copyRect( const rendering::XCanvas* ,
+ const uno::Reference< rendering::XBitmapCanvas >& /*sourceCanvas*/,
+ const geometry::RealRectangle2D& /*sourceRect*/,
+ const rendering::ViewState& /*sourceViewState*/,
+ const rendering::RenderState& /*sourceRenderState*/,
+ const geometry::RealRectangle2D& /*destRect*/,
+ const rendering::ViewState& /*destViewState*/,
+ const rendering::RenderState& /*destRenderState*/ )
+ {
+ // TODO(F2): copyRect NYI
+ }
+
+ geometry::IntegerSize2D CanvasHelper::getSize()
+ {
+ if( !mpSurfaceProvider )
+ geometry::IntegerSize2D(1, 1); // we're disposed
+
+ return ::basegfx::unotools::integerSize2DFromB2ISize( maSize );
+ }
+
+ uno::Reference< rendering::XBitmap > CanvasHelper::getScaledBitmap( const geometry::RealSize2D& newSize,
+ sal_Bool /*beFast*/ )
+ {
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ struct timespec aTimer;
+ mxDevice->startPerfTrace( &aTimer );
+#endif
+
+ if( mpCairo ) {
+ return uno::Reference< rendering::XBitmap >( new CanvasBitmap( ::basegfx::B2ISize( ::canvas::tools::roundUp( newSize.Width ),
+ ::canvas::tools::roundUp( newSize.Height ) ),
+ mpSurfaceProvider, mpDevice, false ) );
+ } else
+ OSL_TRACE ("CanvasHelper called after it was disposed");
+
+#ifdef CAIRO_CANVAS_PERF_TRACE
+ mxDevice->stopPerfTrace( &aTimer, "getScaledBitmap" );
+#endif
+
+ return uno::Reference< rendering::XBitmap >();
+ }
+
+ uno::Sequence< sal_Int8 > CanvasHelper::getData( rendering::IntegerBitmapLayout& aLayout,
+ const geometry::IntegerRectangle2D& rect )
+ {
+ if( mpCairo ) {
+ aLayout = getMemoryLayout();
+
+ const sal_Int32 nWidth( rect.X2 - rect.X1 );
+ const sal_Int32 nHeight( rect.Y2 - rect.Y1 );
+ uno::Sequence< sal_Int8 > aRes( 4*nWidth*nHeight );
+ sal_Int8* pData = aRes.getArray();
+ cairo_surface_t* pImageSurface = cairo_image_surface_create_for_data( (unsigned char *) pData,
+ CAIRO_FORMAT_ARGB32,
+ nWidth, nHeight, 4*nWidth );
+ cairo_t* pCairo = cairo_create( pImageSurface );
+ cairo_set_source_surface( pCairo, mpSurface->getCairoSurface().get(), -rect.X1, -rect.Y1);
+ cairo_paint( pCairo );
+ cairo_destroy( pCairo );
+ cairo_surface_destroy( pImageSurface );
+
+ aLayout.ScanLines = nHeight;
+ aLayout.ScanLineBytes = nWidth*4;
+ aLayout.ScanLineStride = aLayout.ScanLineBytes;
+
+ return aRes;
+ }
+
+ return uno::Sequence< sal_Int8 >();
+ }
+
+ void CanvasHelper::setData( const uno::Sequence< sal_Int8 >& /*data*/,
+ const rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerRectangle2D& /*rect*/ )
+ {
+ }
+
+ void CanvasHelper::setPixel( const uno::Sequence< sal_Int8 >& /*color*/,
+ const rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& /*pos*/ )
+ {
+ }
+
+ uno::Sequence< sal_Int8 > CanvasHelper::getPixel( rendering::IntegerBitmapLayout& /*bitmapLayout*/,
+ const geometry::IntegerPoint2D& /*pos*/ )
+ {
+ return uno::Sequence< sal_Int8 >();
+ }
+
+ uno::Reference< rendering::XBitmapPalette > CanvasHelper::getPalette()
+ {
+ // TODO(F1): Palette bitmaps NYI
+ return uno::Reference< rendering::XBitmapPalette >();
+ }
+
+ namespace
+ {
+ class CairoColorSpace : public cppu::WeakImplHelper1< com::sun::star::rendering::XIntegerBitmapColorSpace >
+ {
+ private:
+ uno::Sequence< sal_Int8 > maComponentTags;
+ uno::Sequence< sal_Int32 > maBitCounts;
+
+ virtual ::sal_Int8 SAL_CALL getType( ) throw (uno::RuntimeException)
+ {
+ return rendering::ColorSpaceType::RGB;
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) throw (uno::RuntimeException)
+ {
+ return maComponentTags;
+ }
+ virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) throw (uno::RuntimeException)
+ {
+ return rendering::RenderingIntent::PERCEPTUAL;
+ }
+ virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) throw (uno::RuntimeException)
+ {
+ return uno::Sequence< beans::PropertyValue >();
+ }
+ virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,
+ uno::RuntimeException)
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const sal_Size nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( sal_Size i=0; i<nLen; i+=4 )
+ {
+ const double fAlpha(pIn[3]);
+ if( fAlpha == 0.0 )
+ *pOut++ = rendering::RGBColor(0.0, 0.0, 0.0);
+ else
+ *pOut++ = rendering::RGBColor(pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const sal_Size nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( sal_Size i=0; i<nLen; i+=4 )
+ {
+ const double fAlpha(pIn[3]);
+ if( fAlpha == 0.0 )
+ *pOut++ = rendering::ARGBColor(0.0, 0.0, 0.0, 0.0);
+ else
+ *pOut++ = rendering::ARGBColor(fAlpha,pIn[2]/fAlpha,pIn[1]/fAlpha,pIn[0]/fAlpha);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const double* pIn( deviceColor.getConstArray() );
+ const sal_Size nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( sal_Size i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(pIn[3],pIn[2],pIn[1],pIn[1]);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const sal_Size nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( sal_Size i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Blue;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Red;
+ *pColors++ = 1.0;
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const sal_Size nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( sal_Size i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Alpha*pIn->Blue;
+ *pColors++ = pIn->Alpha*pIn->Green;
+ *pColors++ = pIn->Alpha*pIn->Red;
+ *pColors++ = pIn->Alpha;
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const sal_Size nLen( rgbColor.getLength() );
+
+ uno::Sequence< double > aRes(nLen*4);
+ double* pColors=aRes.getArray();
+ for( sal_Size i=0; i<nLen; ++i )
+ {
+ *pColors++ = pIn->Blue;
+ *pColors++ = pIn->Green;
+ *pColors++ = pIn->Red;
+ *pColors++ = pIn->Alpha;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ // XIntegerBitmapColorSpace
+ virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) throw (uno::RuntimeException)
+ {
+ return 32;
+ }
+ virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) throw (uno::RuntimeException)
+ {
+ return maBitCounts;
+ }
+ virtual ::sal_Int8 SAL_CALL getEndianness( ) throw (uno::RuntimeException)
+ {
+ return util::Endianness::LITTLE;
+ }
+ virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,
+ uno::RuntimeException)
+ {
+ if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const sal_Size nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence<double> aRes(nLen);
+ double* pOut( aRes.getArray() );
+ for( sal_Size i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ *pOut++ = vcl::unotools::toDoubleColor(*pIn++);
+ }
+ return aRes;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor,
+ const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,
+ uno::RuntimeException)
+ {
+ if( dynamic_cast<CairoColorSpace*>(targetColorSpace.get()) )
+ {
+ // it's us, so simply pass-through the data
+ return deviceColor;
+ }
+ else
+ {
+ // TODO(P3): if we know anything about target
+ // colorspace, this can be greatly sped up
+ uno::Sequence<rendering::ARGBColor> aIntermediate(
+ convertIntegerToARGB(deviceColor));
+ return targetColorSpace->convertIntegerFromARGB(aIntermediate);
+ }
+ }
+ virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const sal_Size nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::RGBColor > aRes(nLen/4);
+ rendering::RGBColor* pOut( aRes.getArray() );
+ for( sal_Size i=0; i<nLen; i+=4 )
+ {
+ const sal_Int8 nAlpha(pIn[3]);
+ if( nAlpha )
+ *pOut++ = rendering::RGBColor(
+ vcl::unotools::toDoubleColor(pIn[2]/nAlpha),
+ vcl::unotools::toDoubleColor(pIn[1]/nAlpha),
+ vcl::unotools::toDoubleColor(pIn[0]/nAlpha));
+ else
+ *pOut++ = rendering::RGBColor(0,0,0);
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const sal_Size nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( sal_Size i=0; i<nLen; i+=4 )
+ {
+ const sal_Int8 nAlpha(pIn[3]);
+ if( nAlpha )
+ *pOut++ = rendering::ARGBColor(
+ vcl::unotools::toDoubleColor(nAlpha),
+ vcl::unotools::toDoubleColor(pIn[2]/nAlpha),
+ vcl::unotools::toDoubleColor(pIn[1]/nAlpha),
+ vcl::unotools::toDoubleColor(pIn[0]/nAlpha));
+ else
+ *pOut++ = rendering::ARGBColor(0,0,0,0);
+ pIn += 4;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const sal_Int8* pIn( deviceColor.getConstArray() );
+ const sal_Size nLen( deviceColor.getLength() );
+ ENSURE_ARG_OR_THROW2(nLen%4==0,
+ "number of channels no multiple of 4",
+ static_cast<rendering::XColorSpace*>(this), 0);
+
+ uno::Sequence< rendering::ARGBColor > aRes(nLen/4);
+ rendering::ARGBColor* pOut( aRes.getArray() );
+ for( sal_Size i=0; i<nLen; i+=4 )
+ {
+ *pOut++ = rendering::ARGBColor(
+ vcl::unotools::toDoubleColor(pIn[3]),
+ vcl::unotools::toDoubleColor(pIn[2]),
+ vcl::unotools::toDoubleColor(pIn[1]),
+ vcl::unotools::toDoubleColor(pIn[0]));
+ pIn += 4;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const rendering::RGBColor* pIn( rgbColor.getConstArray() );
+ const sal_Size nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( sal_Size i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = 255;
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const sal_Size nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( sal_Size i=0; i<nLen; ++i )
+ {
+ const double fAlpha(pIn->Alpha);
+ *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(fAlpha*pIn->Red);
+ *pColors++ = vcl::unotools::toByteColor(fAlpha);
+ ++pIn;
+ }
+ return aRes;
+ }
+ virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException)
+ {
+ const rendering::ARGBColor* pIn( rgbColor.getConstArray() );
+ const sal_Size nLen( rgbColor.getLength() );
+
+ uno::Sequence< sal_Int8 > aRes(nLen*4);
+ sal_Int8* pColors=aRes.getArray();
+ for( sal_Size i=0; i<nLen; ++i )
+ {
+ *pColors++ = vcl::unotools::toByteColor(pIn->Blue);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Green);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Red);
+ *pColors++ = vcl::unotools::toByteColor(pIn->Alpha);
+ ++pIn;
+ }
+ return aRes;
+ }
+
+ public:
+ CairoColorSpace() :
+ maComponentTags(4),
+ maBitCounts(4)
+ {
+ sal_Int8* pTags = maComponentTags.getArray();
+ sal_Int32* pBitCounts = maBitCounts.getArray();
+ pTags[0] = rendering::ColorComponentTag::RGB_BLUE;
+ pTags[1] = rendering::ColorComponentTag::RGB_GREEN;
+ pTags[2] = rendering::ColorComponentTag::RGB_RED;
+ pTags[3] = rendering::ColorComponentTag::PREMULTIPLIED_ALPHA;
+
+ pBitCounts[0] =
+ pBitCounts[1] =
+ pBitCounts[2] =
+ pBitCounts[3] = 8;
+ }
+ };
+
+ struct CairoColorSpaceHolder : public rtl::StaticWithInit<uno::Reference<rendering::XIntegerBitmapColorSpace>,
+ CairoColorSpaceHolder>
+ {
+ uno::Reference<rendering::XIntegerBitmapColorSpace> operator()()
+ {
+ return new CairoColorSpace();
+ }
+ };
+ }
+
+ rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
+ {
+ if( !mpCairo )
+ return rendering::IntegerBitmapLayout(); // we're disposed
+
+ const geometry::IntegerSize2D aSize(getSize());
+ rendering::IntegerBitmapLayout aLayout;
+
+ aLayout.ScanLines = aSize.Height;
+ aLayout.ScanLineBytes = aSize.Width*4;
+ aLayout.ScanLineStride = aLayout.ScanLineBytes;
+ aLayout.PlaneStride = 0;
+ aLayout.ColorSpace = CairoColorSpaceHolder::get();
+ aLayout.Palette.clear();
+ aLayout.IsMsbFirst = sal_False;
+
+ return aLayout;
+ }
+
+ void CanvasHelper::flush() const
+ {
+ }
+
+ bool CanvasHelper::hasAlpha() const
+ {
+ return mbHaveAlpha;
+ }
+
+ bool CanvasHelper::repaint( const SurfaceSharedPtr& pSurface,
+ const rendering::ViewState& viewState,
+ const rendering::RenderState& renderState )
+ {
+ OSL_TRACE("CanvasHelper::repaint");
+
+ if( mpCairo ) {
+ cairo_save( mpCairo.get() );
+
+ cairo_rectangle( mpCairo.get(), 0, 0, maSize.getX(), maSize.getY() );
+ cairo_clip( mpCairo.get() );
+
+ useStates( viewState, renderState, true );
+
+ Matrix aMatrix;
+
+ cairo_get_matrix( mpCairo.get(), &aMatrix );
+ aMatrix.xx = aMatrix.yy = 1;
+ cairo_set_matrix( mpCairo.get(), &aMatrix );
+
+ // if( !bHasAlpha )
+ // cairo_set_operator( mpCairo.get(), CAIRO_OPERATOR_SOURCE );
+
+ cairo_set_source_surface( mpCairo.get(), pSurface->getCairoSurface().get(), 0, 0 );
+ cairo_paint( mpCairo.get() );
+ cairo_restore( mpCairo.get() );
+ }
+
+ return true;
+ }
+}