| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 <basegfx/tools/keystoplerp.hxx> |
| #include <basegfx/tools/lerp.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 <boost/tuple/tuple.hpp> |
| #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(); |
| } |
| |
| static void setColor( Cairo* pCairo, |
| const uno::Sequence<double>& rColor ) |
| { |
| if( rColor.getLength() > 3 ) |
| { |
| const double alpha = rColor[3]; |
| |
| cairo_set_source_rgba( pCairo, |
| alpha*rColor[0], |
| alpha*rColor[1], |
| alpha*rColor[2], |
| alpha ); |
| } |
| else if( rColor.getLength() == 3 ) |
| cairo_set_source_rgb( pCairo, |
| rColor[0], |
| rColor[1], |
| rColor[2] ); |
| } |
| |
| void CanvasHelper::useStates( const rendering::ViewState& viewState, |
| const rendering::RenderState& renderState, |
| bool bSetColor ) |
| { |
| 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( bSetColor ) |
| setColor(mpCairo.get(),renderState.DeviceColor); |
| |
| 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 ); |
| } |
| } |
| } |
| |
| static uno::Sequence<double> lerp(const uno::Sequence<double>& rLeft, const uno::Sequence<double>& rRight, double fAlpha) |
| { |
| if( rLeft.getLength() == 3 ) |
| { |
| uno::Sequence<double> aRes(3); |
| aRes[0] = basegfx::tools::lerp(rLeft[0],rRight[0],fAlpha); |
| aRes[1] = basegfx::tools::lerp(rLeft[1],rRight[1],fAlpha); |
| aRes[2] = basegfx::tools::lerp(rLeft[2],rRight[2],fAlpha); |
| return aRes; |
| } |
| else if( rLeft.getLength() == 4 ) |
| { |
| uno::Sequence<double> aRes(4); |
| aRes[0] = basegfx::tools::lerp(rLeft[0],rRight[0],fAlpha); |
| aRes[1] = basegfx::tools::lerp(rLeft[1],rRight[1],fAlpha); |
| aRes[2] = basegfx::tools::lerp(rLeft[2],rRight[2],fAlpha); |
| aRes[3] = basegfx::tools::lerp(rLeft[3],rRight[3],fAlpha); |
| return aRes; |
| } |
| |
| return uno::Sequence<double>(); |
| } |
| |
| 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_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; |
| |
| case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL: |
| cx = 0; |
| cy = 0; |
| r0 = 0; |
| r1 = 1; |
| |
| pPattern = cairo_pattern_create_radial( cx, cy, r0, cy, cy, r1 ); |
| addColorStops( pPattern, aValues.maColors, aValues.maStops, true ); |
| break; |
| default: |
| break; |
| } |
| |
| return pPattern; |
| } |
| |
| static void doOperation( Operation aOperation, |
| Cairo* pCairo, |
| const uno::Sequence< rendering::Texture >* pTextures, |
| const SurfaceProviderRef& pDevice, |
| const basegfx::B2DRange& rBounds ) |
| { |
| 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 ); |
| } |
| else if ( aTexture.RepeatModeX == rendering::TexturingMode::NONE && |
| aTexture.RepeatModeY == rendering::TexturingMode::NONE ) |
| { |
| cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_NONE ); |
| } |
| else if ( aTexture.RepeatModeX == rendering::TexturingMode::CLAMP && |
| aTexture.RepeatModeY == rendering::TexturingMode::CLAMP ) |
| { |
| cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_PAD ); |
| } |
| |
| 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); |
| if( pPolyImpl->getValues().meType == canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR ) |
| { |
| // no general path gradient yet in cairo; emulate then |
| cairo_save( pCairo ); |
| cairo_clip( pCairo ); |
| |
| // fill bound rect with start color |
| cairo_rectangle( pCairo, rBounds.getMinX(), rBounds.getMinY(), |
| rBounds.getWidth(), rBounds.getHeight() ); |
| setColor(pCairo,pPolyImpl->getValues().maColors[0]); |
| cairo_fill(pCairo); |
| |
| cairo_transform( pCairo, &aTextureMatrix ); |
| |
| // longest line in gradient bound rect |
| const unsigned int nGradientSize( |
| static_cast<unsigned int>( |
| ::basegfx::B2DVector(rBounds.getMinimum() - rBounds.getMaximum()).getLength() + 1.0 ) ); |
| |
| // typical number for pixel of the same color (strip size) |
| const unsigned int nStripSize( nGradientSize < 50 ? 2 : 4 ); |
| |
| // use at least three steps, and at utmost the number of color |
| // steps |
| const unsigned int nStepCount( |
| ::std::max( |
| 3U, |
| ::std::min( |
| nGradientSize / nStripSize, |
| 128U )) + 1 ); |
| |
| const uno::Sequence<double>* pColors=&pPolyImpl->getValues().maColors[0]; |
| basegfx::tools::KeyStopLerp aLerper(pPolyImpl->getValues().maStops); |
| for( unsigned int i=1; i<nStepCount; ++i ) |
| { |
| const double fT( i/double(nStepCount) ); |
| |
| std::ptrdiff_t nIndex; |
| double fAlpha; |
| boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT); |
| |
| setColor(pCairo, lerp(pColors[nIndex], pColors[nIndex+1], fAlpha)); |
| cairo_rectangle( pCairo, -1+fT, -1+fT, 2-2*fT, 2-2*fT ); |
| cairo_fill(pCairo); |
| } |
| |
| cairo_restore( pCairo ); |
| } |
| else |
| { |
| 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, aPolyPolygon.getB2DRange() ); |
| 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, aPolyPolygon.getB2DRange() ); |
| |
| cairo_set_matrix( pCairo, &aOrigMatrix ); |
| |
| if( aPolyPolygon.count() == 0 && aOperation == Clip ) |
| clipNULL( pCairo ); |
| } |
| |
| void CanvasHelper::doPolyPolygonPath( const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, |
| Operation aOperation, |
| bool bNoLineJoin, |
| const uno::Sequence< rendering::Texture >* pTextures, |
| Cairo* pCairo ) const |
| { |
| const ::basegfx::B2DPolyPolygon& rPolyPoly( |
| ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(xPolyPolygon) ); |
| |
| if( !pCairo ) |
| pCairo = mpCairo.get(); |
| |
| if(bNoLineJoin && Stroke == aOperation) |
| { |
| // emulate rendering::PathJoinType::NONE by painting single edges |
| for(sal_uInt32 a(0); a < rPolyPoly.count(); a++) |
| { |
| const basegfx::B2DPolygon aCandidate(rPolyPoly.getB2DPolygon(a)); |
| const sal_uInt32 nPointCount(aCandidate.count()); |
| |
| if(nPointCount) |
| { |
| const sal_uInt32 nEdgeCount(aCandidate.isClosed() ? nPointCount + 1: nPointCount); |
| basegfx::B2DPolygon aEdge; |
| aEdge.append(aCandidate.getB2DPoint(0)); |
| aEdge.append(basegfx::B2DPoint(0.0, 0.0)); |
| |
| for(sal_uInt32 b(0); b < nEdgeCount; b++) |
| { |
| const sal_uInt32 nNextIndex((b + 1) % nPointCount); |
| aEdge.setB2DPoint(1, aCandidate.getB2DPoint(nNextIndex)); |
| aEdge.setNextControlPoint(0, aCandidate.getNextControlPoint(b)); |
| aEdge.setPrevControlPoint(1, aCandidate.getPrevControlPoint(nNextIndex)); |
| |
| doPolyPolygonImplementation( basegfx::B2DPolyPolygon(aEdge), |
| aOperation, |
| pCairo, pTextures, |
| mpSurfaceProvider, |
| xPolyPolygon->getFillRule() ); |
| |
| // prepare next step |
| aEdge.setB2DPoint(0, aEdge.getB2DPoint(1)); |
| } |
| } |
| } |
| } |
| else |
| { |
| 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; |
| } |
| |
| bool bNoLineJoin(false); |
| |
| switch( strokeAttributes.JoinType ) { |
| // cairo doesn't have join type NONE so we use MITER as it's pretty close |
| case rendering::PathJoinType::NONE: |
| bNoLineJoin = true; |
| 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, bNoLineJoin ); |
| |
| 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, false, &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_pattern_set_extend( cairo_get_source(mpCairo.get()), CAIRO_EXTEND_PAD ); |
| cairo_rectangle( mpCairo.get(), 0, 0, aBitmapSize.Width, aBitmapSize.Height ); |
| cairo_clip( mpCairo.get() ); |
| |
| if( bModulateColors ) |
| cairo_paint_with_alpha( mpCairo.get(), renderState.DeviceColor[3] ); |
| else |
| 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* 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, true, 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::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 double fAlpha((sal_uInt8)pIn[3]); |
| if( fAlpha ) |
| *pOut++ = rendering::RGBColor( |
| pIn[2]/fAlpha, |
| pIn[1]/fAlpha, |
| pIn[0]/fAlpha); |
| 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 double fAlpha((sal_uInt8)pIn[3]); |
| if( fAlpha ) |
| *pOut++ = rendering::ARGBColor( |
| fAlpha/255.0, |
| pIn[2]/fAlpha, |
| pIn[1]/fAlpha, |
| pIn[0]/fAlpha); |
| 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; |
| } |
| } |