2006/10/09 16:30:39 radekdoulik 1.3.16.3: RESYNC: (1.3-1.4); FILE MERGED 2006/10/09 15:57:10 radekdoulik 1.3.16.2: Issue number: 69325 Submitted by: radekdoulik Reviewed by: radekdoulik 2006-10-09 Radek Doulik <rodo@novell.com> * patches/src680/apply: added fix from Caolan, move path coordinates only when doing stroke. Before they were moved for fills as well. It happened because of unclear method name, which was indicating just strokes, but was used for clips and fills as well. I changed the method names as well now. 2006/06/30 13:58:13 radekdoulik 1.3.16.1: #i65589# workaround for cairo bug, fixes bitmap drawing problem
1330 lines
54 KiB
C++
1330 lines
54 KiB
C++
/*************************************************************************
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* $RCSfile: cairo_canvashelper.cxx,v $
|
|
*
|
|
* $Revision: 1.5 $
|
|
*
|
|
* last change: $Author: vg $ $Date: 2006-11-01 14:45:42 $
|
|
*
|
|
* The Contents of this file are made available subject to
|
|
* the terms of GNU Lesser General Public License Version 2.1.
|
|
*
|
|
*
|
|
* GNU Lesser General Public License Version 2.1
|
|
* =============================================
|
|
* Copyright 2005 by Sun Microsystems, Inc.
|
|
* 901 San Antonio Road, Palo Alto, CA 94303, USA
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License version 2.1, as published by the Free Software Foundation.
|
|
*
|
|
* This library 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 for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_canvas.hxx"
|
|
|
|
#include <canvas/debug.hxx>
|
|
|
|
#include <rtl/logfile.hxx>
|
|
#include <rtl/math.hxx>
|
|
|
|
#include <com/sun/star/lang/XUnoTunnel.hpp>
|
|
|
|
#include <com/sun/star/rendering/IntegerBitmapFormat.hpp>
|
|
#include <com/sun/star/rendering/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 <basegfx/matrix/b2dhommatrix.hxx>
|
|
#include <basegfx/point/b2dpoint.hxx>
|
|
#include <basegfx/polygon/b2dpolygon.hxx>
|
|
#include <basegfx/polygon/b2dpolypolygon.hxx>
|
|
#include <basegfx/tools/canvastools.hxx>
|
|
|
|
#include <comphelper/sequence.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() :
|
|
mpDevice( NULL ),
|
|
mbHaveAlpha(),
|
|
mpCairo( NULL ),
|
|
mpSurface( NULL ),
|
|
mpVirtualDevice( NULL )
|
|
{
|
|
}
|
|
|
|
void CanvasHelper::disposing()
|
|
{
|
|
mpDevice = NULL;
|
|
|
|
if( mpVirtualDevice ) {
|
|
delete mpVirtualDevice;
|
|
mpVirtualDevice = NULL;
|
|
}
|
|
|
|
if( mpCairo )
|
|
{
|
|
cairo_destroy( mpCairo );
|
|
mpCairo = NULL;
|
|
}
|
|
|
|
if( mpSurface ) {
|
|
mpSurface->Unref();
|
|
mpSurface = NULL;
|
|
}
|
|
}
|
|
|
|
void CanvasHelper::init( const ::basegfx::B2ISize& rSize,
|
|
SpriteCanvas& rDevice )
|
|
{
|
|
mpDevice = &rDevice;
|
|
}
|
|
|
|
void CanvasHelper::setSurface( Surface* pSurface, bool bHasAlpha, SurfaceProvider* pSurfaceProvider )
|
|
{
|
|
mbHaveAlpha = bHasAlpha;
|
|
mpSurfaceProvider = pSurfaceProvider;
|
|
|
|
if( mpSurface ) {
|
|
mpSurface->Unref();
|
|
}
|
|
|
|
if( mpVirtualDevice ) {
|
|
delete mpVirtualDevice;
|
|
mpVirtualDevice = NULL;
|
|
}
|
|
|
|
mpSurface = pSurface;
|
|
mpSurface->Ref();
|
|
|
|
if( mpCairo )
|
|
{
|
|
cairo_destroy( mpCairo );
|
|
}
|
|
|
|
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\n");
|
|
|
|
aViewMatrix.x0 = round( aViewMatrix.x0 );
|
|
aViewMatrix.y0 = round( aViewMatrix.y0 );
|
|
cairo_set_matrix( mpCairo, &aViewMatrix );
|
|
doPolyPolygonPath( viewState.Clip, Clip );
|
|
}
|
|
|
|
aCombinedMatrix.x0 = round( aCombinedMatrix.x0 );
|
|
aCombinedMatrix.y0 = round( aCombinedMatrix.y0 );
|
|
cairo_set_matrix( mpCairo, &aCombinedMatrix );
|
|
|
|
if( renderState.Clip.is() ) {
|
|
OSL_TRACE ("render clip BEGIN\n");
|
|
|
|
doPolyPolygonPath( renderState.Clip, Clip );
|
|
OSL_TRACE ("render clip END\n");
|
|
}
|
|
|
|
if( setColor ) {
|
|
if( renderState.DeviceColor.getLength() > 3 )
|
|
cairo_set_source_rgba( mpCairo,
|
|
renderState.DeviceColor [0],
|
|
renderState.DeviceColor [1],
|
|
renderState.DeviceColor [2],
|
|
renderState.DeviceColor [3] );
|
|
else if (renderState.DeviceColor.getLength() == 3)
|
|
cairo_set_source_rgb( mpCairo,
|
|
renderState.DeviceColor [0],
|
|
renderState.DeviceColor [1],
|
|
renderState.DeviceColor [2] );
|
|
}
|
|
}
|
|
|
|
void CanvasHelper::drawPoint( const rendering::XCanvas* pCanvas,
|
|
const geometry::RealPoint2D& aPoint,
|
|
const rendering::ViewState& viewState,
|
|
const rendering::RenderState& 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 );
|
|
|
|
cairo_set_line_width( mpCairo, 1 );
|
|
|
|
useStates( viewState, renderState, true );
|
|
|
|
cairo_move_to( mpCairo, aStartPoint.X + 0.5, aStartPoint.Y + 0.5 );
|
|
cairo_line_to( mpCairo, aEndPoint.X + 0.5, aEndPoint.Y + 0.5 );
|
|
cairo_stroke( mpCairo );
|
|
|
|
cairo_restore( mpCairo );
|
|
}
|
|
}
|
|
|
|
void CanvasHelper::drawBezier( const rendering::XCanvas* pCanvas,
|
|
const geometry::RealBezierSegment2D& aBezierSegment,
|
|
const geometry::RealPoint2D& aEndPoint,
|
|
const rendering::ViewState& viewState,
|
|
const rendering::RenderState& renderState )
|
|
{
|
|
}
|
|
|
|
#define CANVASBITMAP_IMPLEMENTATION_NAME "CairoCanvas::CanvasBitmap"
|
|
#define PARAMETRICPOLYPOLYGON_IMPLEMENTATION_NAME "Canvas::ParametricPolyPolygon"
|
|
|
|
static Surface* surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, bool &bHasAlpha )
|
|
{
|
|
CanvasBitmap* pBitmapImpl = dynamic_cast< CanvasBitmap* >( xBitmap.get() );
|
|
|
|
if( pBitmapImpl ) {
|
|
// TODO(Q1): Maybe use dynamic_cast here
|
|
bHasAlpha = pBitmapImpl->hasAlpha();
|
|
|
|
return pBitmapImpl->getSurface();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static ::BitmapEx bitmapExFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap )
|
|
{
|
|
uno::Reference< lang::XUnoTunnel > xTunnel( xBitmap, uno::UNO_QUERY );
|
|
if( xTunnel.is() )
|
|
{
|
|
sal_Int64 nPtr = xTunnel->getSomething( vcl::unotools::getTunnelIdentifier( vcl::unotools::Id_BitmapEx ) );
|
|
if( nPtr )
|
|
return BitmapEx( *(BitmapEx*)nPtr );
|
|
}
|
|
|
|
// TODO(F1): extract pixel from XBitmap interface
|
|
ENSURE_AND_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\n", 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;
|
|
}
|
|
|
|
static Surface* surfaceFromXBitmap( const uno::Reference< rendering::XBitmap >& xBitmap, const DeviceRef& rDevice, unsigned char*& data, bool& bHasAlpha )
|
|
{
|
|
Surface* pSurface = surfaceFromXBitmap( xBitmap, bHasAlpha );
|
|
if( pSurface ) {
|
|
pSurface->Ref();
|
|
data = NULL;
|
|
} else {
|
|
BitmapEx aBmpEx = bitmapExFromXBitmap(xBitmap);
|
|
Bitmap aBitmap = aBmpEx.GetBitmap();
|
|
AlphaMask aAlpha = aBmpEx.GetAlpha();
|
|
|
|
// 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 = rDevice->getSurface( aBitmap );
|
|
data = NULL;
|
|
bHasAlpha = false;
|
|
}
|
|
|
|
if( !pSurface ) {
|
|
|
|
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++ ) {
|
|
if( pAlphaReadAcc )
|
|
nAlpha = data[ nOff + 3 ];
|
|
else
|
|
nAlpha = data[ nOff + 3 ] = 255;
|
|
aColor = pBitmapReadAcc->GetPaletteColor( *pReadScan++ );
|
|
|
|
data[ nOff++ ] = ( nAlpha*( aColor.GetBlue() ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( aColor.GetGreen() ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( aColor.GetRed() ) )/255;
|
|
|
|
nOff++;
|
|
}
|
|
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++ ) {
|
|
if( pAlphaReadAcc )
|
|
nAlpha = data[ nOff + 3 ];
|
|
else
|
|
nAlpha = data[ nOff + 3 ] = 255;
|
|
data[ nOff++ ] = ( nAlpha*( *pReadScan++ ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( *pReadScan++ ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( *pReadScan++ ) )/255;
|
|
|
|
nOff++;
|
|
}
|
|
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++ ) {
|
|
if( pAlphaReadAcc )
|
|
nAlpha = data[ nOff + 3 ];
|
|
else
|
|
nAlpha = data[ nOff + 3 ] = 255;
|
|
data[ nOff++ ] = ( nAlpha*( pReadScan[ 2 ] ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( pReadScan[ 1 ] ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( pReadScan[ 0 ] ) )/255;
|
|
|
|
nOff++;
|
|
pReadScan += 3;
|
|
}
|
|
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++ ) {
|
|
if( pAlphaReadAcc )
|
|
nAlpha = data[ nOff + 3 ];
|
|
else
|
|
nAlpha = data[ nOff + 3 ] = pReadScan[ 3 ];
|
|
data[ nOff++ ] = ( nAlpha*( *pReadScan++ ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( *pReadScan++ ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( *pReadScan++ ) )/255;
|
|
|
|
nOff++;
|
|
pReadScan++;
|
|
}
|
|
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++ ) {
|
|
if( pAlphaReadAcc )
|
|
nAlpha = data[ nOff + 3 ];
|
|
else
|
|
nAlpha = data[ nOff + 3 ] = 255;
|
|
data[ nOff++ ] = ( nAlpha*( pReadScan[ 2 ] ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( pReadScan[ 1 ] ) )/255;
|
|
data[ nOff++ ] = ( nAlpha*( pReadScan[ 0 ] ) )/255;
|
|
|
|
nOff++;
|
|
pReadScan += 4;
|
|
}
|
|
break;
|
|
default:
|
|
OSL_TRACE( "fallback to GetColor - slow, format: %d\n", 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
|
|
if( pAlphaReadAcc )
|
|
nAlpha = data[ nOff + 3 ];
|
|
else
|
|
nAlpha = data[ nOff + 3 ] = 255;
|
|
|
|
data[ nOff++ ] = ( nAlpha*aColor.GetBlue() )/255;
|
|
data[ nOff++ ] = ( nAlpha*aColor.GetGreen() )/255;
|
|
data[ nOff++ ] = ( nAlpha*aColor.GetRed() )/255;
|
|
|
|
nOff ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
aBitmap.ReleaseAccess( pBitmapReadAcc );
|
|
if( pAlphaReadAcc )
|
|
aAlpha.ReleaseAccess( pAlphaReadAcc );
|
|
|
|
Surface* pImageSurface = new Surface( cairo_image_surface_create_for_data( data,
|
|
bIsAlpha ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
|
|
nWidth, nHeight, nWidth*4 ) );
|
|
|
|
// pSurface = rDevice->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 addColorStop( Pattern* pPattern, double nOffset, const uno::Sequence< double >& rColor )
|
|
{
|
|
if( rColor.getLength() == 3 )
|
|
cairo_pattern_add_color_stop_rgb( pPattern, nOffset, rColor[0], rColor[1], rColor[2] );
|
|
else if( rColor.getLength() == 4 )
|
|
cairo_pattern_add_color_stop_rgba( pPattern, nOffset, rColor[0], rColor[1], rColor[2], rColor[3] );
|
|
}
|
|
|
|
static Pattern* patternFromParametricPolyPolygon( ::canvas::ParametricPolyPolygon& rPolygon, Matrix& rMatrix )
|
|
{
|
|
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;
|
|
cairo_matrix_transform_point( &rMatrix, &x0, &y0 );
|
|
cairo_matrix_transform_point( &rMatrix, &x1, &y1 );
|
|
pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 );
|
|
addColorStop( pPattern, 0, aValues.maColor1 );
|
|
addColorStop( pPattern, 1, aValues.maColor2 );
|
|
break;
|
|
|
|
// FIXME: NYI
|
|
case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR:
|
|
case ::canvas::ParametricPolyPolygon::GRADIENT_AXIAL:
|
|
x0 = 0;
|
|
y0 = 0;
|
|
x1 = 1;
|
|
y1 = 0;
|
|
cairo_matrix_transform_point( &rMatrix, &x0, &y0 );
|
|
cairo_matrix_transform_point( &rMatrix, &x1, &y1 );
|
|
pPattern = cairo_pattern_create_linear( x0, y0, x1, y1 );
|
|
addColorStop( pPattern, 0, aValues.maColor1 );
|
|
addColorStop( pPattern, 0.5, aValues.maColor2 );
|
|
addColorStop( pPattern, 1, aValues.maColor1 );
|
|
break;
|
|
|
|
case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL:
|
|
cx = 0.5;
|
|
cy = 0.5;
|
|
r0 = 0;
|
|
r1 = 0.5;
|
|
Matrix* pMatrix = &rMatrix;
|
|
Matrix aScaledMatrix, aScaleMatrix;
|
|
|
|
cairo_matrix_transform_point( &rMatrix, &cx, &cy );
|
|
cairo_matrix_transform_distance( &rMatrix, &r0, &r1 );
|
|
pPattern = cairo_pattern_create_radial( cx, cy, r0, cx, cy, r1 );
|
|
addColorStop( pPattern, 0, aValues.maColor1 );
|
|
addColorStop( pPattern, 1, aValues.maColor2 );
|
|
|
|
if( ! ::rtl::math::approxEqual( aValues.mnAspectRatio, 1 ) ) {
|
|
cairo_matrix_init_scale( &aScaleMatrix, 1, aValues.mnAspectRatio );
|
|
cairo_pattern_set_matrix( pPattern, &aScaleMatrix );
|
|
}
|
|
break;
|
|
}
|
|
|
|
return pPattern;
|
|
}
|
|
|
|
void doOperation( Operation aOperation,
|
|
Cairo* pCairo,
|
|
sal_uInt32 nPolygonIndex,
|
|
const uno::Sequence< rendering::Texture >* pTextures,
|
|
SpriteCanvas* 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;
|
|
bool bHasAlpha;
|
|
Surface* 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, aOrigMatrix, aNewMatrix;
|
|
|
|
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 );
|
|
|
|
#if 0
|
|
OSL_TRACE("slow workaround");
|
|
// workaround for X/glitz and/or cairo bug
|
|
// we create big enough temporary surface, copy texture bitmap there and use it for the pattern
|
|
// it only happens on enlargening matrices with REPEAT mode enabled
|
|
Surface* pTmpSurface = pDevice->getSurface();
|
|
Cairo* pTmpCairo = cairo_create( pTmpSurface );
|
|
cairo_set_source_surface( pTmpCairo, pSurface, 0, 0 );
|
|
cairo_paint( pTmpCairo );
|
|
pPattern = cairo_pattern_create_for_surface( pTmpSurface );
|
|
#else
|
|
|
|
// we don't care about repeat mode yet, so the workaround is disabled for now
|
|
pPattern = cairo_pattern_create_for_surface( pSurface->mpSurface );
|
|
#endif
|
|
if( aTexture.RepeatModeX == rendering::TexturingMode::REPEAT &&
|
|
aTexture.RepeatModeY == rendering::TexturingMode::REPEAT )
|
|
cairo_pattern_set_extend( pPattern, CAIRO_EXTEND_REPEAT );
|
|
aScaledTextureMatrix.x0 = round( aScaledTextureMatrix.x0 );
|
|
aScaledTextureMatrix.y0 = round( 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 );
|
|
pSurface->Unref();
|
|
|
|
#if 0
|
|
cairo_destroy( pTmpCairo );
|
|
cairo_surface_destroy( pTmpSurface );
|
|
#endif
|
|
}
|
|
|
|
if( data )
|
|
free( data );
|
|
} else if( aTexture.Gradient.is() ) {
|
|
uno::Reference< lang::XServiceInfo > xRef( aTexture.Gradient, uno::UNO_QUERY );
|
|
|
|
OSL_TRACE( "gradient fill\n" );
|
|
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\n" );
|
|
|
|
::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, aTextureMatrix );
|
|
|
|
if( pPattern ) {
|
|
OSL_TRACE( "filling with pattern\n" );
|
|
|
|
cairo_save( pCairo );
|
|
|
|
cairo_set_source( pCairo, pPattern );
|
|
cairo_fill( pCairo );
|
|
cairo_restore( pCairo );
|
|
|
|
cairo_pattern_destroy( pPattern );
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
cairo_fill( pCairo );
|
|
OSL_TRACE("fill\n");
|
|
break;
|
|
case Stroke:
|
|
cairo_stroke( pCairo );
|
|
OSL_TRACE("stroke\n");
|
|
break;
|
|
case Clip:
|
|
cairo_clip( pCairo );
|
|
OSL_TRACE("clip\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void clipNULL( Cairo *pCairo )
|
|
{
|
|
OSL_TRACE("clipNULL\n");
|
|
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,
|
|
SpriteCanvas* pDevice )
|
|
{
|
|
if( pTextures )
|
|
CHECK_AND_THROW( pTextures->getLength(),
|
|
"CanvasHelper::fillTexturedPolyPolygon: empty texture sequence");
|
|
|
|
bool bOpToDo = false;
|
|
Matrix aOrigMatrix, aIdentityMatrix;
|
|
double nX, nY, nBX, nBY, nPX, nPY, nAX, nAY;
|
|
|
|
cairo_get_matrix( pCairo, &aOrigMatrix );
|
|
cairo_matrix_init_identity( &aIdentityMatrix );
|
|
cairo_set_matrix( pCairo, &aIdentityMatrix );
|
|
|
|
for( sal_uInt32 nPolygonIndex = 0; nPolygonIndex < aPolyPolygon.count(); nPolygonIndex++ ) {
|
|
::basegfx::B2DPolygon aPolygon = aPolyPolygon.getB2DPolygon( nPolygonIndex );
|
|
|
|
if( aPolygon.count() > 1) {
|
|
bool bIsBezier = aPolygon.areControlPointsUsed();
|
|
::basegfx::B2DPoint aA, aB, aP;
|
|
|
|
aP = aPolygon.getB2DPoint( 0 );
|
|
nX = aP.getX();
|
|
nY = aP.getY();
|
|
|
|
cairo_matrix_transform_point( &aOrigMatrix, &nX, &nY );
|
|
|
|
if( ! bIsBezier ) {
|
|
nX = round( nX );
|
|
nY = round( nY );
|
|
}
|
|
|
|
if( aOperation == Stroke ) {
|
|
nX += 0.5;
|
|
nY += 0.5;
|
|
}
|
|
|
|
cairo_move_to( pCairo, nX, nY );
|
|
OSL_TRACE( "move to %f,%f\n", nX, nY );
|
|
|
|
if( bIsBezier ) {
|
|
aA = aPolygon.getControlPointA( 0 );
|
|
aB = aPolygon.getControlPointB( 0 );
|
|
}
|
|
|
|
for( sal_uInt32 j = 1; j < aPolygon.count(); j++ ) {
|
|
aP = aPolygon.getB2DPoint( j );
|
|
|
|
nX = aP.getX();
|
|
nY = aP.getY();
|
|
cairo_matrix_transform_point( &aOrigMatrix, &nX, &nY );
|
|
|
|
if( ! bIsBezier ) {
|
|
nX = round( nX );
|
|
nY = round( nY );
|
|
}
|
|
|
|
if( aOperation == Stroke ) {
|
|
nX += 0.5;
|
|
nY += 0.5;
|
|
}
|
|
|
|
if( bIsBezier ) {
|
|
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 );
|
|
|
|
aA = aPolygon.getControlPointA( j );
|
|
aB = aPolygon.getControlPointB( j );
|
|
} else {
|
|
cairo_line_to( pCairo, nX, nY );
|
|
OSL_TRACE( "line to %f,%f\n", nX, nY );
|
|
}
|
|
bOpToDo = true;
|
|
}
|
|
|
|
if( aPolygon.isClosed() )
|
|
cairo_close_path( pCairo );
|
|
|
|
if( aOperation == Fill && pTextures ) {
|
|
cairo_set_matrix( pCairo, &aOrigMatrix );
|
|
doOperation( aOperation, pCairo, nPolygonIndex, pTextures, pDevice );
|
|
cairo_set_matrix( pCairo, &aIdentityMatrix );
|
|
}
|
|
} else {
|
|
OSL_TRACE( "empty polygon for op: %d\n\n", aOperation );
|
|
if( aOperation == Clip ) {
|
|
clipNULL( pCairo );
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if( bOpToDo && ( aOperation != Fill || !pTextures ) )
|
|
doOperation( aOperation, pCairo );
|
|
|
|
cairo_set_matrix( pCairo, &aOrigMatrix );
|
|
|
|
// fixme, spec says even clipping polypolygon with zero polygons means NULL clip, but it breaks animations with sprites
|
|
// 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
|
|
{
|
|
::basegfx::B2DPolyPolygon aPoly = ::canvas::tools::polyPolygonFromXPolyPolygon2D( xPolyPolygon );
|
|
|
|
if( !pCairo )
|
|
pCairo = mpCairo;
|
|
|
|
doPolyPolygonImplementation( aPoly, aOperation, pCairo, pTextures, mpDevice );
|
|
}
|
|
|
|
uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawPolyPolygon( const rendering::XCanvas* pCanvas,
|
|
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 );
|
|
|
|
cairo_set_line_width( mpCairo, 1 );
|
|
|
|
useStates( viewState, renderState, true );
|
|
doPolyPolygonPath( xPolyPolygon, Stroke );
|
|
|
|
cairo_restore( mpCairo );
|
|
} 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* pCanvas,
|
|
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 );
|
|
|
|
useStates( viewState, renderState, true );
|
|
|
|
cairo_set_line_width( mpCairo, strokeAttributes.StrokeWidth );
|
|
cairo_set_miter_limit( mpCairo, strokeAttributes.MiterLimit );
|
|
|
|
// FIXME: cairo doesn't handle end cap so far (rodo)
|
|
switch( strokeAttributes.StartCapType ) {
|
|
case rendering::PathCapType::BUTT:
|
|
cairo_set_line_cap( mpCairo, CAIRO_LINE_CAP_BUTT );
|
|
break;
|
|
case rendering::PathCapType::ROUND:
|
|
cairo_set_line_cap( mpCairo, CAIRO_LINE_CAP_ROUND );
|
|
break;
|
|
case rendering::PathCapType::SQUARE:
|
|
cairo_set_line_cap( mpCairo, CAIRO_LINE_CAP_SQUARE );
|
|
break;
|
|
}
|
|
|
|
switch( strokeAttributes.JoinType ) {
|
|
// cairo doesn't have join type NONE so we use MITTER as it's pretty close
|
|
case rendering::PathJoinType::NONE:
|
|
case rendering::PathJoinType::MITER:
|
|
cairo_set_line_join( mpCairo, CAIRO_LINE_JOIN_MITER );
|
|
break;
|
|
case rendering::PathJoinType::ROUND:
|
|
cairo_set_line_join( mpCairo, CAIRO_LINE_JOIN_ROUND );
|
|
break;
|
|
case rendering::PathJoinType::BEVEL:
|
|
cairo_set_line_join( mpCairo, 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, pDashArray, strokeAttributes.DashArray.getLength(), 0 );
|
|
delete[] pDashArray;
|
|
}
|
|
|
|
// TODO(rodo) use LineArray of strokeAttributes
|
|
|
|
doPolyPolygonPath( xPolyPolygon, Stroke );
|
|
|
|
cairo_restore( mpCairo );
|
|
} 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* pCanvas,
|
|
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* pCanvas,
|
|
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* pCanvas,
|
|
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* pCanvas,
|
|
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 );
|
|
|
|
useStates( viewState, renderState, true );
|
|
doPolyPolygonPath( xPolyPolygon, Fill );
|
|
|
|
cairo_restore( mpCairo );
|
|
} 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* pCanvas,
|
|
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 );
|
|
|
|
useStates( viewState, renderState, true );
|
|
doPolyPolygonPath( xPolyPolygon, Fill, &textures );
|
|
|
|
cairo_restore( mpCairo );
|
|
}
|
|
|
|
return uno::Reference< rendering::XCachedPrimitive >(NULL);
|
|
}
|
|
|
|
uno::Reference< rendering::XCachedPrimitive > CanvasHelper::fillTextureMappedPolyPolygon( const rendering::XCanvas* pCanvas,
|
|
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( Surface* pSurface,
|
|
const rendering::ViewState& viewState,
|
|
const rendering::RenderState& renderState,
|
|
const geometry::IntegerSize2D& rSize,
|
|
bool bModulateColors,
|
|
bool bHasAlpha )
|
|
{
|
|
uno::Reference< rendering::XCachedPrimitive > rv = uno::Reference< rendering::XCachedPrimitive >(NULL);
|
|
geometry::IntegerSize2D aBitmapSize = rSize;
|
|
|
|
if( mpCairo ) {
|
|
const ::basegfx::B2ISize& aSize = mpDevice->getSizePixel();
|
|
|
|
cairo_save( mpCairo );
|
|
|
|
cairo_rectangle( mpCairo, 0, 0, aSize.getX(), aSize.getY() );
|
|
cairo_clip( mpCairo );
|
|
|
|
useStates( viewState, renderState, true );
|
|
|
|
// if( !bHasAlpha )
|
|
// cairo_set_operator( mpCairo, CAIRO_OPERATOR_SOURCE );
|
|
|
|
Matrix aMatrix;
|
|
|
|
cairo_get_matrix( mpCairo, &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 ) &&
|
|
round( rSize.Width * aMatrix.xx ) > 8 &&
|
|
round( rSize.Height* aMatrix.yy ) > 8 )
|
|
{
|
|
double dWidth, dHeight;
|
|
|
|
dWidth = round( rSize.Width * aMatrix.xx );
|
|
dHeight = round( rSize.Height* aMatrix.yy );
|
|
aBitmapSize.Width = static_cast<sal_Int32>( dWidth );
|
|
aBitmapSize.Height = static_cast<sal_Int32>( dHeight );
|
|
|
|
Surface* pScaledSurface = mpDevice->getSurface( ::basegfx::B2ISize( aBitmapSize.Width, aBitmapSize.Height ),
|
|
bHasAlpha ? CAIRO_CONTENT_COLOR_ALPHA : CAIRO_CONTENT_COLOR );
|
|
Cairo* pCairo = pScaledSurface->getCairo();
|
|
|
|
// cairo_set_operator( pCairo, CAIRO_OPERATOR_SOURCE );
|
|
cairo_scale( pCairo, dWidth/rSize.Width, dHeight/rSize.Height );
|
|
cairo_set_source_surface( pCairo, pSurface->mpSurface, 0, 0 );
|
|
cairo_paint( pCairo );
|
|
|
|
cairo_destroy( pCairo );
|
|
|
|
pSurface = pScaledSurface;
|
|
|
|
aMatrix.xx = aMatrix.yy = 1;
|
|
cairo_set_matrix( mpCairo, &aMatrix );
|
|
|
|
rv = uno::Reference< rendering::XCachedPrimitive >( new CachedBitmap( pSurface, viewState, renderState, mpDevice ) );
|
|
|
|
pSurface->Unref();
|
|
}
|
|
|
|
if( !bHasAlpha && mbHaveAlpha /* && mpSurfaceProvider */ )
|
|
{
|
|
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, aSize.getX(), aSize.getY() );
|
|
if( x <= 0 && y <= 0 && x + width >= aSize.getX() && y + height >= aSize.getY() )
|
|
{
|
|
OSL_TRACE ("trying to change surface to rgb");
|
|
if( mpSurfaceProvider ) {
|
|
Surface* pNewSurface = mpSurfaceProvider->changeSurface( false, false );
|
|
|
|
if( pNewSurface )
|
|
setSurface( pNewSurface, false, mpSurfaceProvider );
|
|
|
|
// set state to new mpCairo
|
|
useStates( viewState, renderState, true );
|
|
// use the possibly modified matrix
|
|
cairo_set_matrix( mpCairo, &aMatrix );
|
|
}
|
|
}
|
|
}
|
|
|
|
cairo_set_source_surface( mpCairo, pSurface->mpSurface, 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, CAIRO_OPERATOR_SOURCE );
|
|
cairo_rectangle( mpCairo, 0, 0, aBitmapSize.Width, aBitmapSize.Height );
|
|
cairo_clip( mpCairo );
|
|
cairo_paint( mpCairo );
|
|
cairo_restore( mpCairo );
|
|
} 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;
|
|
bool bHasAlpha;
|
|
Surface* pSurface = surfaceFromXBitmap( xBitmap, mpDevice, data, bHasAlpha );
|
|
geometry::IntegerSize2D aSize = xBitmap->getSize();
|
|
|
|
if( pSurface ) {
|
|
rv = implDrawBitmapSurface( pSurface, viewState, renderState, aSize, false, bHasAlpha );
|
|
|
|
pSurface->Unref();
|
|
|
|
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 )
|
|
{
|
|
// 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* pCanvas,
|
|
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( !mpDevice )
|
|
geometry::IntegerSize2D(1, 1); // we're disposed
|
|
|
|
return ::basegfx::unotools::integerSize2DFromB2ISize( mpDevice->getSizePixel() );
|
|
}
|
|
|
|
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 ) ),
|
|
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& bitmapLayout,
|
|
const geometry::IntegerRectangle2D& rect )
|
|
{
|
|
// TODO
|
|
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 >();
|
|
}
|
|
|
|
rendering::IntegerBitmapLayout CanvasHelper::getMemoryLayout()
|
|
{
|
|
// TODO(F1): finish memory layout initialization
|
|
rendering::IntegerBitmapLayout aLayout;
|
|
|
|
const geometry::IntegerSize2D& rBmpSize( getSize() );
|
|
|
|
aLayout.ScanLines = rBmpSize.Width;
|
|
aLayout.ScanLineBytes = rBmpSize.Height * 4;
|
|
aLayout.ScanLineStride = aLayout.ScanLineBytes;
|
|
aLayout.PlaneStride = 0;
|
|
aLayout.ColorSpace.set( mpDevice );
|
|
aLayout.NumComponents = 4;
|
|
aLayout.ComponentMasks.realloc(4);
|
|
aLayout.ComponentMasks[0] = 0x00FF0000;
|
|
aLayout.ComponentMasks[1] = 0x0000FF00;
|
|
aLayout.ComponentMasks[2] = 0x000000FF;
|
|
aLayout.ComponentMasks[3] = 0xFF000000;
|
|
aLayout.Palette.clear();
|
|
aLayout.Endianness = rendering::Endianness::LITTLE;
|
|
aLayout.Format = rendering::IntegerBitmapFormat::CHUNKY_32BIT;
|
|
aLayout.IsMsbFirst = sal_False;
|
|
|
|
return aLayout;
|
|
}
|
|
|
|
void CanvasHelper::flush() const
|
|
{
|
|
}
|
|
|
|
bool CanvasHelper::hasAlpha() const
|
|
{
|
|
return mbHaveAlpha;
|
|
}
|
|
|
|
bool CanvasHelper::repaint( Surface* pSurface,
|
|
const rendering::ViewState& viewState,
|
|
const rendering::RenderState& renderState )
|
|
{
|
|
OSL_TRACE("CanvasHelper::repaint");
|
|
|
|
if( mpCairo ) {
|
|
const ::basegfx::B2ISize& aSize = mpDevice->getSizePixel();
|
|
|
|
cairo_save( mpCairo );
|
|
|
|
cairo_rectangle( mpCairo, 0, 0, aSize.getX(), aSize.getY() );
|
|
cairo_clip( mpCairo );
|
|
|
|
useStates( viewState, renderState, true );
|
|
|
|
Matrix aMatrix;
|
|
|
|
cairo_get_matrix( mpCairo, &aMatrix );
|
|
aMatrix.xx = aMatrix.yy = 1;
|
|
cairo_set_matrix( mpCairo, &aMatrix );
|
|
|
|
// if( !bHasAlpha )
|
|
// cairo_set_operator( mpCairo, CAIRO_OPERATOR_SOURCE );
|
|
|
|
cairo_set_source_surface( mpCairo, pSurface->mpSurface, 0, 0 );
|
|
cairo_paint( mpCairo );
|
|
cairo_restore( mpCairo );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|