forked from amazingfate/loongoffice
2219 lines
105 KiB
C++
2219 lines
105 KiB
C++
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2000, 2010 Oracle and/or its affiliates.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_drawinglayer.hxx"
|
|
|
|
#include <drawinglayer/processor2d/canvasprocessor.hxx>
|
|
#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
|
|
#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
|
|
#include <com/sun/star/rendering/XCanvas.hpp>
|
|
#include <vcl/canvastools.hxx>
|
|
#include <basegfx/tools/canvastools.hxx>
|
|
#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
|
|
#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
|
|
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
|
|
#include <canvas/canvastools.hxx>
|
|
#include <svl/ctloptions.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
|
|
#include <basegfx/polygon/b2dpolygonclipper.hxx>
|
|
#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
|
|
#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
|
|
#include <cppcanvas/basegfxfactory.hxx>
|
|
#include <com/sun/star/rendering/XBitmapCanvas.hpp>
|
|
#include <cppcanvas/vclfactory.hxx>
|
|
#include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
|
|
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
|
|
#include <com/sun/star/rendering/TextDirection.hpp>
|
|
#include <vclhelperbitmaptransform.hxx>
|
|
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
|
|
#include <basegfx/polygon/b2dpolygontools.hxx>
|
|
#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
|
|
#include <basegfx/tuple/b2i64tuple.hxx>
|
|
#include <basegfx/range/b2irange.hxx>
|
|
#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
|
|
#include <com/sun/star/rendering/CompositeOperation.hpp>
|
|
#include <com/sun/star/rendering/StrokeAttributes.hpp>
|
|
#include <com/sun/star/rendering/PathJoinType.hpp>
|
|
#include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx>
|
|
#include <com/sun/star/rendering/TexturingMode.hpp>
|
|
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
|
|
#include <vclhelperbufferdevice.hxx>
|
|
#include <drawinglayer/primitive2d/chartprimitive2d.hxx>
|
|
#include <helperchartrenderer.hxx>
|
|
#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
|
|
#include <helperwrongspellrenderer.hxx>
|
|
#include <basegfx/matrix/b2dhommatrixtools.hxx>
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
using namespace com::sun::star;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// AW: Adding the canvas example from THB here to extract stuff later
|
|
/*
|
|
// TODO(Q3): share impCreateEmptyBitmapWithPattern() and other
|
|
// helper methods with vclprocessor.cxx
|
|
Bitmap impCreateEmptyBitmapWithPattern(Bitmap aSource, const Size& aTargetSizePixel)
|
|
{
|
|
Bitmap aRetval;
|
|
BitmapReadAccess* pReadAccess = aSource.AcquireReadAccess();
|
|
|
|
if(pReadAccess)
|
|
{
|
|
if(aSource.GetBitCount() <= 8)
|
|
{
|
|
BitmapPalette aPalette(pReadAccess->GetPalette());
|
|
aRetval = Bitmap(aTargetSizePixel, aSource.GetBitCount(), &aPalette);
|
|
}
|
|
else
|
|
{
|
|
aRetval = Bitmap(aTargetSizePixel, aSource.GetBitCount());
|
|
}
|
|
|
|
delete pReadAccess;
|
|
}
|
|
|
|
return aRetval;
|
|
}
|
|
|
|
Bitmap impModifyBitmap(const basegfx::BColorModifier& rModifier, const Bitmap& rSource)
|
|
{
|
|
Bitmap aRetval(rSource);
|
|
|
|
switch(rModifier.getMode())
|
|
{
|
|
case basegfx::BCOLORMODIFYMODE_REPLACE :
|
|
{
|
|
aRetval = impCreateEmptyBitmapWithPattern(aRetval, Size(1L, 1L));
|
|
aRetval.Erase(Color(rModifier.getBColor()));
|
|
break;
|
|
}
|
|
|
|
default : // BCOLORMODIFYMODE_INTERPOLATE, BCOLORMODIFYMODE_GRAY, BCOLORMODIFYMODE_BLACKANDWHITE
|
|
{
|
|
BitmapWriteAccess* pContent = aRetval.AcquireWriteAccess();
|
|
|
|
if(pContent)
|
|
{
|
|
for(sal_uInt32 y(0L); y < (sal_uInt32)pContent->Height(); y++)
|
|
{
|
|
for(sal_uInt32 x(0L); x < (sal_uInt32)pContent->Width(); x++)
|
|
{
|
|
const Color aColor = pContent->GetPixel(y, x);
|
|
const basegfx::BColor aBColor(rModifier.getModifiedColor(aColor.getBColor()));
|
|
pContent->SetPixel(y, x, BitmapColor(Color(aBColor)));
|
|
}
|
|
}
|
|
|
|
delete pContent;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return aRetval;
|
|
}
|
|
|
|
Bitmap impModifyBitmap(const basegfx::BColorModifierStack& rBColorModifierStack, const Bitmap& rSource)
|
|
{
|
|
Bitmap aRetval(rSource);
|
|
|
|
for(sal_uInt32 a(rBColorModifierStack.count()); a; )
|
|
{
|
|
const basegfx::BColorModifier& rModifier = rBColorModifierStack.getBColorModifier(--a);
|
|
aRetval = impModifyBitmap(rModifier, aRetval);
|
|
}
|
|
|
|
return aRetval;
|
|
}
|
|
|
|
sal_uInt32 impCalcGradientSteps(sal_uInt32 nSteps, const basegfx::B2DRange& rRange, sal_uInt32 nMaxDist)
|
|
{
|
|
if(nSteps == 0L)
|
|
nSteps = (sal_uInt32)(rRange.getWidth() + rRange.getHeight()) / 8;
|
|
|
|
if(nSteps < 2L)
|
|
{
|
|
nSteps = 2L;
|
|
}
|
|
|
|
if(nSteps > nMaxDist)
|
|
{
|
|
nSteps = nMaxDist;
|
|
}
|
|
|
|
return nSteps;
|
|
}
|
|
|
|
void canvasProcessor::impDrawGradientSimple(
|
|
const basegfx::B2DPolyPolygon& rTargetForm,
|
|
const ::std::vector< basegfx::B2DHomMatrix >& rMatrices,
|
|
const ::std::vector< basegfx::BColor >& rColors,
|
|
const basegfx::B2DPolygon& rUnitPolygon)
|
|
{
|
|
uno::Reference< rendering::XPolyPolygon2D > xPoly(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolygon(
|
|
mxCanvas->getDevice(),
|
|
rUnitPolygon));
|
|
uno::Reference< rendering::XPolyPolygon2D > xTargetPoly(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
rTargetForm));
|
|
|
|
for(sal_uInt32 a(0L); a < rColors.size(); a++)
|
|
{
|
|
// set correct color
|
|
const basegfx::BColor aFillColor(rColors[a]);
|
|
|
|
maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence(
|
|
mxCanvas->getDevice(),
|
|
aFillColor);
|
|
|
|
if(a)
|
|
{
|
|
if(a - 1L < rMatrices.size())
|
|
{
|
|
canvas::tools::setRenderStateTransform( maRenderState,
|
|
rMatrices[a - 1L] );
|
|
mxCanvas->fillPolyPolygon(xPoly,maViewState,maRenderState);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
canvas::tools::setRenderStateTransform( maRenderState,
|
|
basegfx::B2DHomMatrix() );
|
|
mxCanvas->fillPolyPolygon(xTargetPoly,maViewState,maRenderState);
|
|
}
|
|
}
|
|
}
|
|
|
|
void canvasProcessor::impDrawGradientComplex(
|
|
const basegfx::B2DPolyPolygon& rTargetForm,
|
|
const ::std::vector< basegfx::B2DHomMatrix >& rMatrices,
|
|
const ::std::vector< basegfx::BColor >& rColors,
|
|
const basegfx::B2DPolygon& rUnitPolygon)
|
|
{
|
|
uno::Reference< rendering::XPolyPolygon2D > xPoly(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolygon(
|
|
mxCanvas->getDevice(),
|
|
rUnitPolygon));
|
|
uno::Reference< rendering::XPolyPolygon2D > xTargetPoly(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
rTargetForm));
|
|
|
|
maRenderState.Clip = xTargetPoly;
|
|
|
|
// draw gradient PolyPolygons
|
|
for(std::size_t a = 0L; a < rMatrices.size(); a++)
|
|
{
|
|
// set correct color
|
|
if(rColors.size() > a)
|
|
{
|
|
const basegfx::BColor aFillColor(rColors[a]);
|
|
|
|
maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence(
|
|
mxCanvas->getDevice(),
|
|
aFillColor);
|
|
}
|
|
|
|
canvas::tools::setRenderStateTransform( maRenderState,
|
|
rMatrices[a] );
|
|
|
|
if(a)
|
|
mxCanvas->fillPolyPolygon(xPoly,maViewState,maRenderState);
|
|
else
|
|
mxCanvas->fillPolyPolygon(xTargetPoly,maViewState,maRenderState);
|
|
}
|
|
|
|
maRenderState.Clip.clear();
|
|
}
|
|
|
|
void canvasProcessor::impDrawGradient(
|
|
const basegfx::B2DPolyPolygon& rTargetForm,
|
|
::drawinglayer::primitive::GradientStyle eGradientStyle,
|
|
sal_uInt32 nSteps,
|
|
const basegfx::BColor& rStart,
|
|
const basegfx::BColor& rEnd,
|
|
double fBorder, double fAngle, double fOffsetX, double fOffsetY, bool bSimple)
|
|
{
|
|
fprintf(stderr,"impDrawGradient\n");
|
|
|
|
basegfx::B2DPolyPolygon aTmp(rTargetForm);
|
|
aTmp.transform( maWorldToView );
|
|
const basegfx::B2DRange aOutlineRangePixel(basegfx::tools::getRange(aTmp));
|
|
const basegfx::B2DRange aOutlineRange(basegfx::tools::getRange(rTargetForm));
|
|
|
|
fprintf(stderr,"impDrawGradient: #%d\n",nSteps);
|
|
|
|
if( // step count is infinite, can use native canvas
|
|
// gradients here
|
|
nSteps == 0 ||
|
|
// step count is sufficiently high, such that no
|
|
// discernible difference should be visible.
|
|
nSteps > 64 )
|
|
{
|
|
uno::Reference< rendering::XParametricPolyPolygon2DFactory > xFactory(
|
|
mxCanvas->getDevice()->getParametricPolyPolygonFactory() );
|
|
|
|
if( xFactory.is() )
|
|
{
|
|
fprintf(stderr,"native gradient #1\n");
|
|
|
|
basegfx::B2DHomMatrix aTextureTransformation;
|
|
rendering::Texture aTexture;
|
|
|
|
aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
|
|
aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
|
|
aTexture.Alpha = 1.0;
|
|
|
|
|
|
// setup start/end color values
|
|
// ----------------------------
|
|
|
|
const uno::Sequence< double > aStartColor(
|
|
basegfx::unotools::colorToDoubleSequence( mxCanvas->getDevice(),
|
|
rStart ));
|
|
const uno::Sequence< double > aEndColor(
|
|
basegfx::unotools::colorToDoubleSequence( mxCanvas->getDevice(),
|
|
rEnd ));
|
|
|
|
// Setup texture transformation
|
|
// ----------------------------
|
|
|
|
const basegfx::B2DRange& rBounds(
|
|
basegfx::tools::getRange( rTargetForm ));
|
|
|
|
// setup rotation angle. VCL rotates
|
|
// counter-clockwise, while canvas transformation
|
|
// rotates clockwise
|
|
//fAngle = -fAngle;
|
|
|
|
switch(eGradientStyle)
|
|
{
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_LINEAR:
|
|
// FALLTHROUGH intended
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_AXIAL:
|
|
{
|
|
// standard orientation for VCL linear
|
|
// gradient is vertical, thus, rotate 90
|
|
// degrees
|
|
fAngle += M_PI/2.0;
|
|
|
|
// shrink texture, to account for border
|
|
// (only in x direction, linear gradient
|
|
// is constant in y direction, anyway)
|
|
aTextureTransformation.scale(
|
|
basegfx::pruneScaleValue(1.0 - fBorder),
|
|
1.0 );
|
|
|
|
double fBorderX(0.0);
|
|
|
|
// determine type of gradient (and necessary
|
|
// transformation matrix, should it be emulated by a
|
|
// generic gradient)
|
|
switch(eGradientStyle)
|
|
{
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_LINEAR:
|
|
// linear gradients don't respect
|
|
// offsets (they are implicitely
|
|
// assumed to be 50%). linear
|
|
// gradients don't have border on
|
|
// both sides, only on the
|
|
// startColor side. Gradient is
|
|
// invariant in y direction: leave
|
|
// y offset alone.
|
|
fBorderX = fBorder;
|
|
aTexture.Gradient = xFactory->createLinearHorizontalGradient( aStartColor,
|
|
aEndColor );
|
|
break;
|
|
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_AXIAL:
|
|
// axial gradients have border on
|
|
// both sides. Gradient is
|
|
// invariant in y direction: leave
|
|
// y offset alone.
|
|
fBorderX = fBorder * .5;
|
|
aTexture.Gradient = xFactory->createAxialHorizontalGradient( aStartColor,
|
|
aEndColor );
|
|
break;
|
|
}
|
|
|
|
// apply border offset values
|
|
aTextureTransformation.translate( fBorderX,
|
|
0.0 );
|
|
|
|
// rotate texture according to gradient rotation
|
|
aTextureTransformation.translate( -0.5, -0.5 );
|
|
aTextureTransformation.rotate( fAngle );
|
|
|
|
// to let the first strip of a rotated
|
|
// gradient start at the _edge_ of the
|
|
// bound rect (and not, due to rotation,
|
|
// slightly inside), slightly enlarge the
|
|
// gradient:
|
|
//
|
|
// y/2 sin(transparence) + x/2 cos(transparence)
|
|
//
|
|
// (values to change are not actual
|
|
// gradient scales, but original bound
|
|
// rect dimensions. Since we still want
|
|
// the border setting to apply after that,
|
|
// we multiply with that as above for
|
|
// nScaleX)
|
|
const double nScale(
|
|
basegfx::pruneScaleValue(
|
|
fabs( rBounds.getHeight()*sin(fAngle) ) +
|
|
fabs( rBounds.getWidth()*cos(fAngle) )));
|
|
|
|
aTextureTransformation.scale( nScale, nScale );
|
|
|
|
// translate back origin to center of
|
|
// primitive
|
|
aTextureTransformation.translate( 0.5*rBounds.getWidth(),
|
|
0.5*rBounds.getHeight() );
|
|
break;
|
|
}
|
|
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_RADIAL:
|
|
// FALLTHROUGH intended
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_ELLIPTICAL:
|
|
// FALLTHROUGH intended
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_SQUARE:
|
|
// FALLTHROUGH intended
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_RECT:
|
|
{
|
|
fprintf(stderr,"native gradient #2\n");
|
|
|
|
// determine scale factors for the gradient (must
|
|
// be scaled up from [0,1]x[0,1] rect to object
|
|
// bounds). Will potentially changed in switch
|
|
// statement below.
|
|
// Respect border value, while doing so, the VCL
|
|
// gradient's border will effectively shrink the
|
|
// resulting gradient.
|
|
double nScaleX( rBounds.getWidth() * (1.0 - fBorder) );
|
|
double nScaleY( rBounds.getHeight()* (1.0 - fBorder) );
|
|
|
|
// determine offset values. Since the
|
|
// border is divided half-by-half to both
|
|
// sides of the gradient, divide
|
|
// translation offset by an additional
|
|
// factor of 2. Also respect offset here,
|
|
// but since VCL gradients have their
|
|
// center at [0,0] for zero offset, but
|
|
// canvas gradients have their top, left
|
|
// edge aligned with the primitive, and
|
|
// offset of 50% effectively must yield
|
|
// zero shift. Both values will
|
|
// potentially be adapted in switch
|
|
// statement below.
|
|
double nOffsetX( rBounds.getWidth() *
|
|
(2.0 * fOffsetX - 1.0 + fBorder)*.5 );
|
|
double nOffsetY( rBounds.getHeight() *
|
|
(2.0 * fOffsetY - 1.0 + fBorder)*.5 );
|
|
|
|
// determine type of gradient (and necessary
|
|
// transformation matrix, should it be emulated by a
|
|
// generic gradient)
|
|
switch(eGradientStyle)
|
|
{
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_RADIAL:
|
|
{
|
|
// create isotrophic scaling
|
|
if( nScaleX > nScaleY )
|
|
{
|
|
nOffsetY -= (nScaleX - nScaleY) * 0.5;
|
|
nScaleY = nScaleX;
|
|
}
|
|
else
|
|
{
|
|
nOffsetX -= (nScaleY - nScaleX) * 0.5;
|
|
nScaleX = nScaleY;
|
|
}
|
|
|
|
// enlarge gradient to match bound rect diagonal
|
|
aTextureTransformation.translate( -0.5, -0.5 );
|
|
const double nScale( hypot(rBounds.getWidth(),
|
|
rBounds.getHeight()) / nScaleX );
|
|
aTextureTransformation.scale( nScale, nScale );
|
|
aTextureTransformation.translate( 0.5, 0.5 );
|
|
|
|
aTexture.Gradient = xFactory->createEllipticalGradient(
|
|
aEndColor,
|
|
aStartColor,
|
|
cssgeom::RealRectangle2D(0.0,0.0,
|
|
1.0,1.0) );
|
|
}
|
|
break;
|
|
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_ELLIPTICAL:
|
|
{
|
|
// enlarge gradient slightly
|
|
aTextureTransformation.translate( -0.5, -0.5 );
|
|
const double nSqrt2( sqrt(2.0) );
|
|
aTextureTransformation.scale( nSqrt2,nSqrt2 );
|
|
aTextureTransformation.translate( 0.5, 0.5 );
|
|
|
|
aTexture.Gradient = xFactory->createEllipticalGradient(
|
|
aEndColor,
|
|
aStartColor,
|
|
cssgeom::RealRectangle2D( rBounds.getMinX(),
|
|
rBounds.getMinY(),
|
|
rBounds.getMaxX(),
|
|
rBounds.getMaxY() ));
|
|
}
|
|
break;
|
|
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_SQUARE:
|
|
{
|
|
// create isotrophic scaling
|
|
if( nScaleX > nScaleY )
|
|
{
|
|
nOffsetY -= (nScaleX - nScaleY) * 0.5;
|
|
nScaleY = nScaleX;
|
|
}
|
|
else
|
|
{
|
|
nOffsetX -= (nScaleY - nScaleX) * 0.5;
|
|
nScaleX = nScaleY;
|
|
}
|
|
|
|
aTexture.Gradient = xFactory->createRectangularGradient(
|
|
aEndColor,
|
|
aStartColor,
|
|
cssgeom::RealRectangle2D(0.0,0.0,
|
|
1.0,1.0));
|
|
}
|
|
break;
|
|
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_RECT:
|
|
{
|
|
aTexture.Gradient = xFactory->createRectangularGradient(
|
|
aEndColor,
|
|
aStartColor,
|
|
cssgeom::RealRectangle2D( rBounds.getMinX(),
|
|
rBounds.getMinY(),
|
|
rBounds.getMaxX(),
|
|
rBounds.getMaxY() ));
|
|
}
|
|
break;
|
|
}
|
|
|
|
nScaleX = basegfx::pruneScaleValue( nScaleX );
|
|
nScaleY = basegfx::pruneScaleValue( nScaleY );
|
|
|
|
aTextureTransformation.scale( nScaleX, nScaleY );
|
|
|
|
// rotate texture according to gradient rotation
|
|
aTextureTransformation.translate( -0.5*nScaleX, -0.5*nScaleY );
|
|
aTextureTransformation.rotate( fAngle );
|
|
aTextureTransformation.translate( 0.5*nScaleX, 0.5*nScaleY );
|
|
|
|
aTextureTransformation.translate( nOffsetX, nOffsetY );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
OSL_ENSURE( false,
|
|
"canvasProcessor::impDrawGradient(): Unexpected gradient type" );
|
|
break;
|
|
}
|
|
|
|
// As the texture coordinate space is relative to
|
|
// the polygon coordinate space (NOT to the
|
|
// polygon itself), move gradient to the start of
|
|
// the actual polygon. If we skip this, the
|
|
// gradient will always display at the origin, and
|
|
// not within the polygon bound (which might be
|
|
// miles away from the origin).
|
|
aTextureTransformation.translate( rBounds.getMinX(),
|
|
rBounds.getMinY() );
|
|
|
|
basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
|
|
aTextureTransformation );
|
|
uno::Sequence< rendering::Texture > aSeq(1);
|
|
aSeq[0] = aTexture;
|
|
|
|
mxCanvas->fillTexturedPolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
rTargetForm),
|
|
maViewState,
|
|
maRenderState,
|
|
aSeq );
|
|
|
|
// done, using native gradients
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// make sure steps is not too high/low
|
|
nSteps = impCalcGradientSteps(nSteps,
|
|
aOutlineRangePixel,
|
|
sal_uInt32((rStart.getMaximumDistance(rEnd) * 127.5) + 0.5));
|
|
|
|
|
|
::std::vector< basegfx::B2DHomMatrix > aMatrices;
|
|
::std::vector< basegfx::BColor > aColors;
|
|
basegfx::B2DPolygon aUnitPolygon;
|
|
|
|
if( drawinglayer::primitive::GRADIENTSTYLE_RADIAL == eGradientStyle ||
|
|
drawinglayer::primitive::GRADIENTSTYLE_ELLIPTICAL == eGradientStyle)
|
|
{
|
|
const basegfx::B2DPoint aCircleCenter(0.5, 0.5);
|
|
aUnitPolygon = basegfx::tools::createPolygonFromEllipse(aCircleCenter, 0.5, 0.5);
|
|
aUnitPolygon = basegfx::tools::adaptiveSubdivideByAngle(aUnitPolygon);
|
|
}
|
|
else
|
|
{
|
|
aUnitPolygon = basegfx::tools::createUnitPolygon();
|
|
}
|
|
|
|
// create geometries
|
|
switch(eGradientStyle)
|
|
{
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_LINEAR:
|
|
{
|
|
::drawinglayer::primitive::geoTexSvxGradientLinear aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fAngle);
|
|
aGradient.appendTransformations(aMatrices);
|
|
aGradient.appendColors(aColors);
|
|
break;
|
|
}
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_AXIAL:
|
|
{
|
|
::drawinglayer::primitive::geoTexSvxGradientAxial aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fAngle);
|
|
aGradient.appendTransformations(aMatrices);
|
|
aGradient.appendColors(aColors);
|
|
break;
|
|
}
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_RADIAL:
|
|
{
|
|
::drawinglayer::primitive::geoTexSvxGradientRadial aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetY);
|
|
aGradient.appendTransformations(aMatrices);
|
|
aGradient.appendColors(aColors);
|
|
break;
|
|
}
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_ELLIPTICAL:
|
|
{
|
|
::drawinglayer::primitive::geoTexSvxGradientElliptical aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle);
|
|
aGradient.appendTransformations(aMatrices);
|
|
aGradient.appendColors(aColors);
|
|
break;
|
|
}
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_SQUARE:
|
|
{
|
|
::drawinglayer::primitive::geoTexSvxGradientSquare aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle);
|
|
aGradient.appendTransformations(aMatrices);
|
|
aGradient.appendColors(aColors);
|
|
break;
|
|
}
|
|
case ::drawinglayer::primitive::GRADIENTSTYLE_RECT:
|
|
{
|
|
::drawinglayer::primitive::geoTexSvxGradientRect aGradient(aOutlineRange, rStart, rEnd, nSteps, fBorder, fOffsetX, fOffsetX, fAngle);
|
|
aGradient.appendTransformations(aMatrices);
|
|
aGradient.appendColors(aColors);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// paint them with mask using the XOR method
|
|
if(aMatrices.size())
|
|
{
|
|
if(bSimple)
|
|
{
|
|
impDrawGradientSimple(rTargetForm, aMatrices, aColors, aUnitPolygon);
|
|
}
|
|
else
|
|
{
|
|
impDrawGradientComplex(rTargetForm, aMatrices, aColors, aUnitPolygon);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// rendering support
|
|
|
|
// directdraw of text simple portion
|
|
void canvasProcessor::impRender_STXP(const textSimplePortionPrimitive& rTextCandidate)
|
|
{
|
|
const fontAttributes& rFontAttrs( rTextCandidate.getFontAttribute() );
|
|
rendering::FontRequest aFontRequest;
|
|
|
|
aFontRequest.FontDescription.FamilyName = rFontAttrs.maFamilyName;
|
|
aFontRequest.FontDescription.StyleName = rFontAttrs.maStyleName;
|
|
aFontRequest.FontDescription.IsSymbolFont = rFontAttrs.mbSymbol ? util::TriState_YES : util::TriState_NO;
|
|
aFontRequest.FontDescription.IsVertical = rFontAttrs.mbVertical ? util::TriState_YES : util::TriState_NO;
|
|
|
|
// TODO(F2): improve vclenum->panose conversion
|
|
aFontRequest.FontDescription.FontDescription.Weight =
|
|
rFontAttrs.mnWeight;
|
|
aFontRequest.FontDescription.FontDescription.Letterform =
|
|
rFontAttrs.mbItalic ? 9 : 0;
|
|
|
|
// font matrix should only be used for glyph rotations etc.
|
|
css::geometry::Matrix2D aFontMatrix;
|
|
canvas::tools::setIdentityMatrix2D( aFontMatrix );
|
|
|
|
uno::Reference<rendering::XCanvasFont> xFont(
|
|
mxCanvas->createFont( aFontRequest,
|
|
uno::Sequence< beans::PropertyValue >(),
|
|
aFontMatrix ));
|
|
|
|
if( !xFont.is() )
|
|
return;
|
|
|
|
uno::Reference<rendering::XTextLayout> xLayout(
|
|
xFont->createTextLayout(
|
|
rendering::StringContext( rTextCandidate.getText(),
|
|
0,
|
|
rTextCandidate.getText().Len() ),
|
|
// TODO(F3): Is this sufficient?
|
|
rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
|
|
0 ));
|
|
if( !xLayout.is() )
|
|
return;
|
|
|
|
xLayout->applyLogicalAdvancements(
|
|
uno::Sequence<double>(&rTextCandidate.getDXArray()[0],
|
|
rTextCandidate.getDXArray().size() ));
|
|
|
|
const basegfx::BColor aRGBColor(
|
|
maBColorModifierStack.getModifiedColor(
|
|
rTextCandidate.getFontColor()));
|
|
|
|
maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence(
|
|
mxCanvas->getDevice(),
|
|
aRGBColor);
|
|
|
|
// get render parameters and paint
|
|
mxCanvas->drawTextLayout( xLayout,
|
|
maViewState,
|
|
maRenderState );
|
|
}
|
|
|
|
// direct draw of hairline
|
|
void canvasProcessor::impRender_POHL(const polygonHairlinePrimitive& rPolygonCandidate)
|
|
{
|
|
const basegfx::BColor aRGBColor(
|
|
maBColorModifierStack.getModifiedColor(
|
|
rPolygonCandidate.getBColor()));
|
|
|
|
maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence(
|
|
mxCanvas->getDevice(),
|
|
aRGBColor);
|
|
|
|
mxCanvas->drawPolyPolygon( basegfx::unotools::xPolyPolygonFromB2DPolygon(
|
|
mxCanvas->getDevice(),
|
|
rPolygonCandidate.getB2DPolygon()),
|
|
maViewState,
|
|
maRenderState );
|
|
}
|
|
|
|
// direct draw of transformed BitmapEx primitive
|
|
void canvasProcessor::impRender_BMPR(const bitmapPrimitive& rBitmapCandidate)
|
|
{
|
|
BitmapEx aBitmapEx(rBitmapCandidate.getBitmapEx());
|
|
|
|
if(maBColorModifierStack.count())
|
|
{
|
|
// TODO(Q3): Share common bmp modification code with
|
|
// vclprocessor.cxx
|
|
Bitmap aChangedBitmap(impModifyBitmap(maBColorModifierStack, aBitmapEx.GetBitmap()));
|
|
|
|
if(aBitmapEx.IsTransparent())
|
|
{
|
|
if(aBitmapEx.IsAlpha())
|
|
aBitmapEx = BitmapEx(aChangedBitmap, aBitmapEx.GetAlpha());
|
|
else
|
|
aBitmapEx = BitmapEx(aChangedBitmap, aBitmapEx.GetMask());
|
|
}
|
|
else
|
|
aBitmapEx = BitmapEx(aChangedBitmap);
|
|
}
|
|
|
|
mxCanvas->drawBitmap(
|
|
vcl::unotools::xBitmapFromBitmapEx( mxCanvas->getDevice(),
|
|
aBitmapEx ),
|
|
maViewState,
|
|
maRenderState);
|
|
}
|
|
|
|
void canvasProcessor::impRender_PPLB(const polyPolygonBitmapPrimitive& rPolyBitmapCandidate )
|
|
{
|
|
const fillBitmapAttribute& rFillBmpAttr( rPolyBitmapCandidate.getFillBitmap() );
|
|
const basegfx::B2DPolyPolygon& rPoly( rPolyBitmapCandidate.getB2DPolyPolygon() );
|
|
|
|
// TODO(Q3): Share common bmp modification code with
|
|
// vclprocessor.cxx
|
|
Bitmap aChangedBitmap(
|
|
impModifyBitmap(maBColorModifierStack,
|
|
rFillBmpAttr.getBitmap()));
|
|
|
|
rendering::Texture aTexture;
|
|
const basegfx::B2DVector aBmpSize( rFillBmpAttr.getSize() );
|
|
|
|
const basegfx::B2DRange& rBounds(
|
|
basegfx::tools::getRange( rPoly ));
|
|
|
|
basegfx::B2DHomMatrix aScale;
|
|
aScale.scale( aBmpSize.getX() * rBounds.getWidth(),
|
|
aBmpSize.getY() * rBounds.getHeight() );
|
|
|
|
basegfx::unotools::affineMatrixFromHomMatrix(
|
|
aTexture.AffineTransform,
|
|
aScale );
|
|
|
|
aTexture.Alpha = 1.0;
|
|
aTexture.Bitmap =
|
|
::vcl::unotools::xBitmapFromBitmapEx(
|
|
mxCanvas->getDevice(),
|
|
aChangedBitmap );
|
|
aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
|
|
aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
|
|
|
|
uno::Sequence< rendering::Texture > aSeq(1);
|
|
aSeq[0] = aTexture;
|
|
|
|
mxCanvas->fillTexturedPolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
rPoly),
|
|
maViewState,
|
|
maRenderState,
|
|
aSeq );
|
|
}
|
|
|
|
// direct draw of gradient
|
|
void canvasProcessor::impRender_PPLG(const polyPolygonGradientPrimitive& rPolygonCandidate)
|
|
{
|
|
const fillGradientAttribute& rGradient(rPolygonCandidate.getFillGradient());
|
|
basegfx::BColor aStartColor(maBColorModifierStack.getModifiedColor(rGradient.getStartColor()));
|
|
basegfx::BColor aEndColor(maBColorModifierStack.getModifiedColor(rGradient.getEndColor()));
|
|
basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
|
|
|
|
if(aStartColor == aEndColor)
|
|
{
|
|
// no gradient at all, draw as polygon
|
|
|
|
maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence(
|
|
mxCanvas->getDevice(),
|
|
aStartColor);
|
|
|
|
mxCanvas->drawPolyPolygon( basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
aLocalPolyPolygon),
|
|
maViewState,
|
|
maRenderState );
|
|
}
|
|
else
|
|
{
|
|
// TODO(F3): if rGradient.getSteps() > 0, render
|
|
// gradient manually!
|
|
impDrawGradient(
|
|
aLocalPolyPolygon, rGradient.getStyle(), rGradient.getSteps(),
|
|
aStartColor, aEndColor, rGradient.getBorder(),
|
|
-rGradient.getAngle(), rGradient.getOffsetX(), rGradient.getOffsetY(), false);
|
|
}
|
|
}
|
|
|
|
// direct draw of PolyPolygon with color
|
|
void canvasProcessor::impRender_PPLC(const polyPolygonColorPrimitive& rPolygonCandidate)
|
|
{
|
|
const basegfx::BColor aRGBColor(
|
|
maBColorModifierStack.getModifiedColor(
|
|
rPolygonCandidate.getBColor()));
|
|
|
|
maRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence(
|
|
mxCanvas->getDevice(),
|
|
aRGBColor);
|
|
|
|
mxCanvas->fillPolyPolygon( basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
rPolygonCandidate.getB2DPolyPolygon()),
|
|
maViewState,
|
|
maRenderState );
|
|
}
|
|
|
|
// direct draw of MetaFile
|
|
void canvasProcessor::impRender_META(const metafilePrimitive& rMetaCandidate)
|
|
{
|
|
// get metafile (copy it)
|
|
GDIMetaFile aMetaFile;
|
|
|
|
// TODO(Q3): Share common metafile modification code with
|
|
// vclprocessor.cxx
|
|
if(maBColorModifierStack.count())
|
|
{
|
|
const basegfx::BColor aRGBBaseColor(0, 0, 0);
|
|
const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(aRGBBaseColor));
|
|
aMetaFile = rMetaCandidate.getMetaFile().GetMonochromeMtf(Color(aRGBColor));
|
|
}
|
|
else
|
|
{
|
|
aMetaFile = rMetaCandidate.getMetaFile();
|
|
}
|
|
|
|
cppcanvas::BitmapCanvasSharedPtr pCanvas(
|
|
cppcanvas::VCLFactory::getInstance().createCanvas(
|
|
uno::Reference<rendering::XBitmapCanvas>(
|
|
mxCanvas,
|
|
uno::UNO_QUERY_THROW) ));
|
|
cppcanvas::RendererSharedPtr pMtfRenderer(
|
|
cppcanvas::VCLFactory::getInstance().createRenderer(
|
|
pCanvas,
|
|
aMetaFile,
|
|
cppcanvas::Renderer::Parameters() ));
|
|
if( pMtfRenderer )
|
|
{
|
|
pCanvas->setTransformation(maWorldToView);
|
|
pMtfRenderer->setTransformation(rMetaCandidate.getTransform());
|
|
pMtfRenderer->draw();
|
|
}
|
|
}
|
|
|
|
// mask group. Set mask polygon as clip
|
|
void canvasProcessor::impRender_MASK(const maskPrimitive& rMaskCandidate)
|
|
{
|
|
const primitiveVector& rSubList = rMaskCandidate.getPrimitiveVector();
|
|
|
|
if(!rSubList.empty())
|
|
{
|
|
// TODO(F3): cannot use state-global renderstate, when recursing!
|
|
maRenderState.Clip =
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
rMaskCandidate.getMask());
|
|
|
|
// paint to it
|
|
process(rSubList);
|
|
|
|
maRenderState.Clip.clear();
|
|
}
|
|
}
|
|
|
|
// modified color group. Force output to unified color.
|
|
void canvasProcessor::impRender_MCOL(const modifiedColorPrimitive& rModifiedCandidate)
|
|
{
|
|
const primitiveVector& rSubList = rModifiedCandidate.getPrimitiveVector();
|
|
|
|
if(!rSubList.empty())
|
|
{
|
|
maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
|
|
process(rModifiedCandidate.getPrimitiveVector());
|
|
maBColorModifierStack.pop();
|
|
}
|
|
}
|
|
|
|
// sub-transparence group. Draw to bitmap device first.
|
|
void canvasProcessor::impRender_TRPR(const transparencePrimitive& rTransCandidate)
|
|
{
|
|
const primitiveVector& rSubList = rTransCandidate.getPrimitiveVector();
|
|
|
|
if(!rSubList.empty())
|
|
{
|
|
basegfx::B2DRange aRange(
|
|
get2DRangeFromVector(rSubList,
|
|
getViewInformation()));
|
|
aRange.transform(maWorldToView);
|
|
const basegfx::B2I64Tuple& rSize(
|
|
canvas::tools::spritePixelAreaFromB2DRange(aRange).getRange());
|
|
uno::Reference< rendering::XCanvas > xBitmap(
|
|
mxCanvas->getDevice()->createCompatibleAlphaBitmap(
|
|
css::geometry::IntegerSize2D(rSize.getX(),
|
|
rSize.getY())),
|
|
uno::UNO_QUERY_THROW);
|
|
|
|
// remember last worldToView and add pixel offset
|
|
basegfx::B2DHomMatrix aLastWorldToView(maWorldToView);
|
|
basegfx::B2DHomMatrix aPixelOffset;
|
|
aPixelOffset.translate(aRange.getMinX(),
|
|
aRange.getMinY());
|
|
setWorldToView(aPixelOffset * maWorldToView);
|
|
|
|
// remember last canvas, set bitmap as target
|
|
uno::Reference< rendering::XCanvas > xLastCanvas( mxCanvas );
|
|
mxCanvas = xBitmap;
|
|
|
|
// paint content to it
|
|
process(rSubList);
|
|
|
|
// TODO(F3): render transparent list to transparence
|
|
// channel. Note that the OutDev implementation has a
|
|
// shortcoming, in that nested transparency groups
|
|
// don't work - transparence is not combined properly.
|
|
|
|
// process(rTransCandidate.getTransparenceList());
|
|
|
|
// back to old OutDev and worldToView
|
|
mxCanvas = xLastCanvas;
|
|
setWorldToView(aLastWorldToView);
|
|
|
|
// DUMMY: add transparence modulation value to DeviceColor
|
|
// TODO(F3): color management
|
|
canvas::tools::setDeviceColor( maRenderState,
|
|
1.0, 1.0, 1.0, 0.5 );
|
|
// finally, draw bitmap
|
|
mxCanvas->drawBitmapModulated(
|
|
uno::Reference< rendering::XBitmap >(
|
|
xBitmap,
|
|
uno::UNO_QUERY_THROW),
|
|
maViewState,
|
|
maRenderState );
|
|
}
|
|
}
|
|
|
|
// transform group.
|
|
void canvasProcessor::impRender_TRN2(const transformPrimitive& rTransformCandidate)
|
|
{
|
|
// remember current transformation
|
|
basegfx::B2DHomMatrix aLastWorldToView(maWorldToView);
|
|
|
|
// create new transformations
|
|
setWorldToView(maWorldToView * rTransformCandidate.getTransformation());
|
|
|
|
// let break down
|
|
process(rTransformCandidate.getPrimitiveVector());
|
|
|
|
// restore transformations
|
|
setWorldToView(aLastWorldToView);
|
|
}
|
|
|
|
// marker
|
|
void canvasProcessor::impRender_MARK(const markerPrimitive& rMarkCandidate)
|
|
{
|
|
const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rMarkCandidate.getRGBColor()));
|
|
|
|
canvas::tools::initRenderState(maMarkerRenderState);
|
|
maMarkerRenderState.DeviceColor = basegfx::unotools::colorToDoubleSequence(
|
|
mxCanvas->getDevice(),
|
|
aRGBColor);
|
|
|
|
// Markers are special objects - their position is
|
|
// determined by the view transformation, but their size
|
|
// is always the same
|
|
const basegfx::B2DPoint aViewPos(maWorldToView * rMarkCandidate.getPosition());
|
|
|
|
uno::Reference< rendering::XPolyPolygon2D > xMarkerPoly;
|
|
uno::Reference< rendering::XPolyPolygon2D > xHighlightMarkerPoly;
|
|
switch(rMarkCandidate.getStyle())
|
|
{
|
|
default:
|
|
case MARKERSTYLE_POINT:
|
|
mxCanvas->drawPoint( basegfx::unotools::point2DFromB2DPoint(aViewPos),
|
|
maMarkerViewState,
|
|
maMarkerRenderState );
|
|
return;
|
|
|
|
case MARKERSTYLE_CROSS:
|
|
if( !mxCrossMarkerPoly.is() )
|
|
{
|
|
basegfx::B2DPolyPolygon aPoly;
|
|
basegfx::tools::importFromSvgD(
|
|
aPoly,
|
|
rtl::OUString::createFromAscii(
|
|
"m-1 0 h2 m0 -1 v2" ));
|
|
mxCrossMarkerPoly =
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
aPoly );
|
|
}
|
|
xMarkerPoly = mxCrossMarkerPoly;
|
|
break;
|
|
|
|
case MARKERSTYLE_GLUEPOINT :
|
|
if( !mxGluePointPoly.is() )
|
|
{
|
|
basegfx::B2DPolyPolygon aPoly;
|
|
basegfx::tools::importFromSvgD(
|
|
aPoly,
|
|
rtl::OUString::createFromAscii(
|
|
"m-2 -3 l5 5 m-3 -2 l5 5 m-3 2 l5 -5 m-2 3 l5 -5" ));
|
|
mxGluePointPoly =
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
aPoly );
|
|
}
|
|
if( !mxGluePointHighlightPoly.is() )
|
|
{
|
|
basegfx::B2DPolyPolygon aPoly;
|
|
basegfx::tools::importFromSvgD(
|
|
aPoly,
|
|
rtl::OUString::createFromAscii(
|
|
"m-2 -2 l4 4 m-2 2 l4 -4" ));
|
|
mxGluePointHighlightPoly =
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(),
|
|
aPoly );
|
|
}
|
|
xMarkerPoly = mxGluePointPoly;
|
|
xHighlightMarkerPoly = mxGluePointHighlightPoly;
|
|
break;
|
|
}
|
|
|
|
basegfx::B2DRange aRange;
|
|
rMarkCandidate.getRealtiveViewRange(aRange);
|
|
const basegfx::B2DPoint aCenter(aRange.getCenter());
|
|
|
|
basegfx::B2DHomMatrix aTranslate;
|
|
aTranslate.translate(aViewPos.getX()+aCenter.getX(),
|
|
aViewPos.getY()+aCenter.getY());
|
|
|
|
canvas::tools::setRenderStateTransform( maMarkerRenderState,
|
|
aTranslate );
|
|
|
|
|
|
mxCanvas->drawPolyPolygon( xMarkerPoly,
|
|
maMarkerViewState,
|
|
maMarkerRenderState );
|
|
if( xHighlightMarkerPoly.is() )
|
|
{
|
|
// TODO(F3): color management
|
|
canvas::tools::setDeviceColor(maMarkerRenderState,
|
|
0.0, 0.0, 1.0, 1.0);
|
|
mxCanvas->drawPolyPolygon( xMarkerPoly,
|
|
maMarkerViewState,
|
|
maMarkerRenderState );
|
|
}
|
|
}
|
|
|
|
void canvasProcessor::setWorldToView(const basegfx::B2DHomMatrix& rMat)
|
|
{
|
|
maWorldToView = rMat;
|
|
canvas::tools::setViewStateTransform(maViewState,
|
|
maWorldToView);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// internal processing support
|
|
|
|
void canvasProcessor::process(const primitiveVector& rSource)
|
|
{
|
|
primitiveVector::const_iterator aCurr = rSource.begin();
|
|
const primitiveVector::const_iterator aEnd = rSource.end();
|
|
while( aCurr != aEnd )
|
|
{
|
|
const referencedPrimitive& rCandidate = *aCurr;
|
|
|
|
switch(rCandidate.getID())
|
|
{
|
|
case CreatePrimitiveID('S', 'T', 'X', 'P'):
|
|
{
|
|
// directdraw of text simple portion
|
|
impRender_STXP(static_cast< const textSimplePortionPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('P', 'O', 'H', 'L'):
|
|
{
|
|
// direct draw of hairline
|
|
impRender_POHL(static_cast< const polygonHairlinePrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('B', 'M', 'P', 'R'):
|
|
{
|
|
// direct draw of transformed BitmapEx primitive
|
|
impRender_BMPR(static_cast< const bitmapPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('F', 'B', 'M', 'P'):
|
|
{
|
|
OSL_ENSURE(false,"fillBitmapPrimitive not yet implemented");
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('P', 'P', 'L', 'B'):
|
|
{
|
|
// direct draw of polygon with bitmap fill
|
|
impRender_PPLB(static_cast< const polyPolygonBitmapPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('P', 'P', 'L', 'G'):
|
|
{
|
|
// direct draw of gradient
|
|
impRender_PPLG(static_cast< const polyPolygonGradientPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('P', 'P', 'L', 'C'):
|
|
{
|
|
// direct draw of PolyPolygon with color
|
|
impRender_PPLC(static_cast< const polyPolygonColorPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('M', 'E', 'T', 'A'):
|
|
{
|
|
// direct draw of MetaFile
|
|
impRender_META(static_cast< const metafilePrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('M', 'A', 'S', 'K'):
|
|
{
|
|
// mask group. Force output to VDev and create mask from given mask
|
|
impRender_MASK(static_cast< const maskPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('M', 'C', 'O', 'L'):
|
|
{
|
|
// modified color group. Force output to unified color.
|
|
impRender_MCOL(static_cast< const modifiedColorPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('T', 'R', 'P', 'R'):
|
|
{
|
|
// sub-transparence group. Draw to VDev first.
|
|
impRender_TRPR(static_cast< const transparencePrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('T', 'R', 'N', '2'):
|
|
{
|
|
// transform group.
|
|
impRender_TRN2(static_cast< const transformPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('M', 'A', 'R', 'K'):
|
|
{
|
|
// marker
|
|
impRender_MARK(static_cast< const markerPrimitive& >(rCandidate.getBasePrimitive()));
|
|
break;
|
|
}
|
|
|
|
case CreatePrimitiveID('A', 'N', 'S', 'W'):
|
|
case CreatePrimitiveID('A', 'N', 'B', 'L'):
|
|
case CreatePrimitiveID('A', 'N', 'I', 'N'):
|
|
{
|
|
// check timing, but do not accept
|
|
const animatedSwitchPrimitive& rAnimatedCandidate(static_cast< const animatedSwitchPrimitive& >(rCandidate.getBasePrimitive()));
|
|
const ::drawinglayer::animation::animationEntryList& rAnimationList = rAnimatedCandidate.getAnimationList();
|
|
const double fNewTime(rAnimationList.getNextEventTime(getViewInformation().getViewTime()));
|
|
|
|
// let break down
|
|
process(rAnimatedCandidate.getDecomposition(getViewInformation()));
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// let break down
|
|
process(rCandidate.getBasePrimitive().getDecomposition(getViewInformation()));
|
|
}
|
|
}
|
|
|
|
++aCurr;
|
|
}
|
|
}
|
|
|
|
canvasProcessor::canvasProcessor( const ::drawinglayer::geometry::viewInformation& rViewInformation,
|
|
const uno::Reference<rendering::XCanvas>& rCanvas ) :
|
|
processor(rViewInformation),
|
|
mxCanvas( rCanvas ),
|
|
mxCrossMarkerPoly(),
|
|
mxGluePointPoly(),
|
|
mxGluePointHighlightPoly(),
|
|
maBColorModifierStack(),
|
|
maWorldToView(),
|
|
maViewState(),
|
|
maRenderState(),
|
|
maMarkerViewState(),
|
|
maMarkerRenderState()
|
|
{
|
|
canvas::tools::initViewState(maViewState);
|
|
canvas::tools::initRenderState(maRenderState);
|
|
canvas::tools::initViewState(maMarkerViewState);
|
|
canvas::tools::initRenderState(maMarkerRenderState);
|
|
|
|
maWorldToView = maViewInformation.getViewTransformation();
|
|
|
|
canvas::tools::setViewStateTransform(maViewState,
|
|
maWorldToView);
|
|
}
|
|
|
|
canvasProcessor::~canvasProcessor()
|
|
{}
|
|
*/
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace drawinglayer
|
|
{
|
|
namespace processor2d
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// single primitive renderers
|
|
|
|
void canvasProcessor2D::impRenderMaskPrimitive2D(const primitive2d::MaskPrimitive2D& rMaskCandidate)
|
|
{
|
|
const primitive2d::Primitive2DSequence& rChildren = rMaskCandidate.getChildren();
|
|
static bool bUseMaskBitmapMethod(true);
|
|
|
|
if(rChildren.hasElements())
|
|
{
|
|
basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
|
|
|
|
if(!aMask.count())
|
|
{
|
|
// no mask, no clipping. recursively paint content
|
|
process(rChildren);
|
|
}
|
|
else
|
|
{
|
|
// there are principally two methods for implementing the mask primitive. One
|
|
// is to set a clip polygon at the canvas, the other is to create and use a
|
|
// transparence-using XBitmap for content and draw the mask as transparence. Both have their
|
|
// advantages and disadvantages, so here are both with a bool allowing simple
|
|
// change
|
|
if(bUseMaskBitmapMethod)
|
|
{
|
|
// get logic range of transparent part, clip with ViewRange
|
|
basegfx::B2DRange aLogicRange(aMask.getB2DRange());
|
|
|
|
if(!getViewInformation2D().getViewport().isEmpty())
|
|
{
|
|
aLogicRange.intersect(getViewInformation2D().getViewport());
|
|
}
|
|
|
|
if(!aLogicRange.isEmpty())
|
|
{
|
|
// get discrete range of transparent part
|
|
basegfx::B2DRange aDiscreteRange(aLogicRange);
|
|
aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation());
|
|
|
|
// expand to next covering discrete values (pixel bounds)
|
|
aDiscreteRange.expand(basegfx::B2DTuple(floor(aDiscreteRange.getMinX()), floor(aDiscreteRange.getMinY())));
|
|
aDiscreteRange.expand(basegfx::B2DTuple(ceil(aDiscreteRange.getMaxX()), ceil(aDiscreteRange.getMaxY())));
|
|
|
|
// use VCL-based buffer device
|
|
impBufferDevice aBufferDevice(*mpOutputDevice, aDiscreteRange, false);
|
|
|
|
if(aBufferDevice.isVisible())
|
|
{
|
|
// remember current OutDev, Canvas and ViewInformation
|
|
OutputDevice* pLastOutputDevice = mpOutputDevice;
|
|
uno::Reference< rendering::XCanvas > xLastCanvas(mxCanvas);
|
|
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
|
|
|
|
// prepare discrete offset for XBitmap, do not forget that the buffer bitmap
|
|
// may be truncated to discrete visible pixels
|
|
const basegfx::B2DHomMatrix aDiscreteOffset(basegfx::tools::createTranslateB2DHomMatrix(
|
|
aDiscreteRange.getMinX() > 0.0 ? -aDiscreteRange.getMinX() : 0.0,
|
|
aDiscreteRange.getMinY() > 0.0 ? -aDiscreteRange.getMinY() : 0.0));
|
|
|
|
// create new local ViewInformation2D with new transformation
|
|
const geometry::ViewInformation2D aViewInformation2D(
|
|
getViewInformation2D().getObjectTransformation(),
|
|
aDiscreteOffset * getViewInformation2D().getViewTransformation(),
|
|
getViewInformation2D().getViewport(),
|
|
getViewInformation2D().getVisualizedPage(),
|
|
getViewInformation2D().getViewTime(),
|
|
getViewInformation2D().getExtendedInformationSequence());
|
|
updateViewInformation(aViewInformation2D);
|
|
|
|
// set OutDev and Canvas to content target
|
|
mpOutputDevice = &aBufferDevice.getContent();
|
|
mxCanvas = mpOutputDevice->GetCanvas();
|
|
canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation());
|
|
|
|
// if ViewState transform is changed, the clipping polygon needs to be adapted, too
|
|
const basegfx::B2DPolyPolygon aOldClipPolyPolygon(maClipPolyPolygon);
|
|
|
|
if(maClipPolyPolygon.count())
|
|
{
|
|
maClipPolyPolygon.transform(aDiscreteOffset);
|
|
maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon);
|
|
}
|
|
|
|
// paint content
|
|
process(rChildren);
|
|
|
|
// draw mask
|
|
const basegfx::BColor aBlack(0.0, 0.0, 0.0);
|
|
maRenderState.DeviceColor = aBlack.colorToDoubleSequence(mxCanvas->getDevice());
|
|
|
|
if(getOptionsDrawinglayer().IsAntiAliasing())
|
|
{
|
|
// with AA, use 8bit AlphaMask to get nice borders
|
|
VirtualDevice& rTransparence = aBufferDevice.getTransparence();
|
|
rTransparence.GetCanvas()->fillPolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), aMask),
|
|
maViewState, maRenderState);
|
|
}
|
|
else
|
|
{
|
|
// No AA, use 1bit mask
|
|
VirtualDevice& rMask = aBufferDevice.getMask();
|
|
rMask.GetCanvas()->fillPolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), aMask),
|
|
maViewState, maRenderState);
|
|
}
|
|
|
|
// back to old color stack, OutDev, Canvas and ViewTransform
|
|
mpOutputDevice = pLastOutputDevice;
|
|
mxCanvas = xLastCanvas;
|
|
updateViewInformation(aLastViewInformation2D);
|
|
canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation());
|
|
|
|
// restore clipping polygon
|
|
maClipPolyPolygon = aOldClipPolyPolygon;
|
|
|
|
if(maClipPolyPolygon.count())
|
|
{
|
|
maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon);
|
|
}
|
|
|
|
// dump buffer to outdev
|
|
aBufferDevice.paint();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// transform new mask polygon to view coordinates for processing. All masks
|
|
// are processed in view coordinates and clipped against each other evtl. to
|
|
// create multi-clips
|
|
aMask.transform(getViewInformation2D().getObjectTransformation());
|
|
|
|
// remember last current clip polygon
|
|
const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon);
|
|
|
|
if(maClipPolyPolygon.count())
|
|
{
|
|
// there is already a clip polygon set; build clipped union of
|
|
// current mask polygon and new one
|
|
maClipPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(aMask, maClipPolyPolygon, false, false);
|
|
}
|
|
else
|
|
{
|
|
// use mask directly
|
|
maClipPolyPolygon = aMask;
|
|
}
|
|
|
|
// set at ViewState
|
|
if(maClipPolyPolygon.count())
|
|
{
|
|
// set new as clip polygon
|
|
maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon);
|
|
}
|
|
else
|
|
{
|
|
// empty, reset
|
|
maViewState.Clip.clear();
|
|
}
|
|
|
|
// paint content
|
|
process(rChildren);
|
|
|
|
// restore local current to rescued clip polygon
|
|
maClipPolyPolygon = aLastClipPolyPolygon;
|
|
|
|
// set at ViewState
|
|
if(maClipPolyPolygon.count())
|
|
{
|
|
// set new as clip polygon
|
|
maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon);
|
|
}
|
|
else
|
|
{
|
|
// empty, reset
|
|
maViewState.Clip.clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void canvasProcessor2D::impRenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D& rMetaCandidate)
|
|
{
|
|
GDIMetaFile aMetaFile;
|
|
|
|
if(maBColorModifierStack.count())
|
|
{
|
|
const basegfx::BColor aRGBBaseColor(0, 0, 0);
|
|
const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(aRGBBaseColor));
|
|
aMetaFile = rMetaCandidate.getMetaFile().GetMonochromeMtf(Color(aRGBColor));
|
|
}
|
|
else
|
|
{
|
|
aMetaFile = rMetaCandidate.getMetaFile();
|
|
}
|
|
|
|
cppcanvas::BitmapCanvasSharedPtr pCanvas(cppcanvas::VCLFactory::getInstance().createCanvas(
|
|
uno::Reference<rendering::XBitmapCanvas>(mxCanvas, uno::UNO_QUERY_THROW)));
|
|
cppcanvas::RendererSharedPtr pMtfRenderer(cppcanvas::VCLFactory::getInstance().createRenderer(
|
|
pCanvas, aMetaFile, cppcanvas::Renderer::Parameters()));
|
|
|
|
if(pMtfRenderer)
|
|
{
|
|
pCanvas->setTransformation(getViewInformation2D().getObjectToViewTransformation());
|
|
pMtfRenderer->setTransformation(rMetaCandidate.getTransform());
|
|
pMtfRenderer->draw();
|
|
}
|
|
}
|
|
|
|
void canvasProcessor2D::impRenderTextSimplePortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate)
|
|
{
|
|
if(rTextCandidate.getTextLength())
|
|
{
|
|
double fShearX(0.0);
|
|
{
|
|
const basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectToViewTransformation() * rTextCandidate.getTextTransform());
|
|
basegfx::B2DVector aScale, aTranslate;
|
|
double fRotate;
|
|
aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
|
|
}
|
|
|
|
if(!basegfx::fTools::equalZero(fShearX))
|
|
{
|
|
// text is sheared. As long as the canvas renderers do not support this,
|
|
// use the decomposed primitive
|
|
process(rTextCandidate.get2DDecomposition(getViewInformation2D()));
|
|
}
|
|
else
|
|
{
|
|
const attribute::FontAttribute& rFontAttr(rTextCandidate.getFontAttribute());
|
|
rendering::FontRequest aFontRequest;
|
|
|
|
aFontRequest.FontDescription.FamilyName = rFontAttr.getFamilyName();
|
|
aFontRequest.FontDescription.StyleName = rFontAttr.getStyleName();
|
|
aFontRequest.FontDescription.IsSymbolFont = rFontAttr.getSymbol() ? util::TriState_YES : util::TriState_NO;
|
|
aFontRequest.FontDescription.IsVertical = rFontAttr.getVertical() ? util::TriState_YES : util::TriState_NO;
|
|
// TODO(F2): improve vclenum->panose conversion
|
|
aFontRequest.FontDescription.FontDescription.Weight = static_cast< sal_uInt8 >(rFontAttr.getWeight());
|
|
aFontRequest.FontDescription.FontDescription.Letterform = rFontAttr.getItalic() ? 9 : 0;
|
|
|
|
// init CellSize to 1.0, else a default font height will be used
|
|
aFontRequest.CellSize = 1.0;
|
|
aFontRequest.Locale = rTextCandidate.getLocale();
|
|
|
|
// font matrix should only be used for glyph rotations etc.
|
|
com::sun::star::geometry::Matrix2D aFontMatrix;
|
|
canvas::tools::setIdentityMatrix2D(aFontMatrix);
|
|
|
|
uno::Reference<rendering::XCanvasFont> xFont(mxCanvas->createFont(
|
|
aFontRequest, uno::Sequence< beans::PropertyValue >(), aFontMatrix));
|
|
|
|
if(xFont.is())
|
|
{
|
|
// got a font, now try to get a TextLayout
|
|
const rendering::StringContext aStringContext(
|
|
rTextCandidate.getText(), rTextCandidate.getTextPosition(), rTextCandidate.getTextLength());
|
|
uno::Reference<rendering::XTextLayout> xLayout(xFont->createTextLayout(
|
|
aStringContext, com::sun::star::rendering::TextDirection::WEAK_LEFT_TO_RIGHT, 0));
|
|
|
|
if(xLayout.is())
|
|
{
|
|
// got a text layout, apply DXArray if given
|
|
const ::std::vector< double >& rDXArray = rTextCandidate.getDXArray();
|
|
const sal_uInt32 nDXCount(rDXArray.size());
|
|
|
|
if(nDXCount)
|
|
{
|
|
// DXArray does not need to be adapted to getTextPosition/getTextLength,
|
|
// it is already provided correctly
|
|
const uno::Sequence< double > aDXSequence(&rDXArray[0], nDXCount);
|
|
xLayout->applyLogicalAdvancements(aDXSequence);
|
|
}
|
|
|
|
// set text color
|
|
const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
|
|
maRenderState.DeviceColor = aRGBColor.colorToDoubleSequence(mxCanvas->getDevice());
|
|
|
|
// set text transformation
|
|
canvas::tools::setRenderStateTransform(maRenderState,
|
|
getViewInformation2D().getObjectTransformation() * rTextCandidate.getTextTransform());
|
|
|
|
// paint
|
|
mxCanvas->drawTextLayout(xLayout, maViewState, maRenderState);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void canvasProcessor2D::impRenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
|
|
{
|
|
// apply possible color modification to BitmapEx
|
|
BitmapEx aModifiedBitmapEx(impModifyBitmapEx(maBColorModifierStack, rBitmapCandidate.getBitmapEx()));
|
|
|
|
if(aModifiedBitmapEx.IsEmpty())
|
|
{
|
|
// replace with color filled polygon
|
|
const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor()));
|
|
const basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon());
|
|
|
|
maRenderState.DeviceColor = aModifiedColor.colorToDoubleSequence(mxCanvas->getDevice());
|
|
canvas::tools::setRenderStateTransform(maRenderState,
|
|
getViewInformation2D().getObjectTransformation() * rBitmapCandidate.getTransform());
|
|
|
|
mxCanvas->fillPolyPolygon(basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(), basegfx::B2DPolyPolygon(aPolygon)), maViewState, maRenderState);
|
|
}
|
|
else
|
|
{
|
|
// adapt object's transformation to the correct scale
|
|
basegfx::B2DVector aScale, aTranslate;
|
|
double fRotate, fShearX;
|
|
const Size aSizePixel(aModifiedBitmapEx.GetSizePixel());
|
|
|
|
if(0 != aSizePixel.Width() && 0 != aSizePixel.Height())
|
|
{
|
|
rBitmapCandidate.getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
|
|
const basegfx::B2DHomMatrix aNewMatrix(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
|
|
aScale.getX() / aSizePixel.Width(), aScale.getY() / aSizePixel.Height(),
|
|
fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
|
|
|
|
canvas::tools::setRenderStateTransform(maRenderState,
|
|
getViewInformation2D().getObjectTransformation() * aNewMatrix);
|
|
|
|
mxCanvas->drawBitmap(
|
|
vcl::unotools::xBitmapFromBitmapEx(mxCanvas->getDevice(), aModifiedBitmapEx),
|
|
maViewState, maRenderState);
|
|
}
|
|
}
|
|
}
|
|
|
|
void canvasProcessor2D::impRenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransparenceCandidate)
|
|
{
|
|
const primitive2d::Primitive2DSequence& rChildren = rTransparenceCandidate.getChildren();
|
|
const primitive2d::Primitive2DSequence& rTransparence = rTransparenceCandidate.getTransparence();
|
|
|
|
if(rChildren.hasElements() && rTransparence.hasElements())
|
|
{
|
|
// get logic range of transparent part and clip with ViewRange
|
|
basegfx::B2DRange aLogicRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rChildren, getViewInformation2D()));
|
|
|
|
if(!getViewInformation2D().getViewport().isEmpty())
|
|
{
|
|
aLogicRange.intersect(getViewInformation2D().getViewport());
|
|
}
|
|
|
|
if(!aLogicRange.isEmpty())
|
|
{
|
|
// get discrete range of transparent part
|
|
basegfx::B2DRange aDiscreteRange(aLogicRange);
|
|
aDiscreteRange.transform(getViewInformation2D().getObjectToViewTransformation());
|
|
|
|
// expand to next covering discrete values (pixel bounds)
|
|
aDiscreteRange.expand(basegfx::B2DTuple(floor(aDiscreteRange.getMinX()), floor(aDiscreteRange.getMinY())));
|
|
aDiscreteRange.expand(basegfx::B2DTuple(ceil(aDiscreteRange.getMaxX()), ceil(aDiscreteRange.getMaxY())));
|
|
|
|
// use VCL-based buffer device
|
|
impBufferDevice aBufferDevice(*mpOutputDevice, aDiscreteRange, false);
|
|
|
|
if(aBufferDevice.isVisible())
|
|
{
|
|
// remember current OutDev, Canvas and ViewInformation
|
|
OutputDevice* pLastOutputDevice = mpOutputDevice;
|
|
uno::Reference< rendering::XCanvas > xLastCanvas(mxCanvas);
|
|
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
|
|
|
|
// prepare discrete offset for XBitmap, do not forget that the buffer bitmap
|
|
// may be truncated to discrete visible pixels
|
|
const basegfx::B2DHomMatrix aDiscreteOffset(basegfx::tools::createTranslateB2DHomMatrix(
|
|
aDiscreteRange.getMinX() > 0.0 ? -aDiscreteRange.getMinX() : 0.0,
|
|
aDiscreteRange.getMinY() > 0.0 ? -aDiscreteRange.getMinY() : 0.0));
|
|
|
|
// create new local ViewInformation2D with new transformation
|
|
const geometry::ViewInformation2D aViewInformation2D(
|
|
getViewInformation2D().getObjectTransformation(),
|
|
aDiscreteOffset * getViewInformation2D().getViewTransformation(),
|
|
getViewInformation2D().getViewport(),
|
|
getViewInformation2D().getVisualizedPage(),
|
|
getViewInformation2D().getViewTime(),
|
|
getViewInformation2D().getExtendedInformationSequence());
|
|
updateViewInformation(aViewInformation2D);
|
|
|
|
// set OutDev and Canvas to content target
|
|
mpOutputDevice = &aBufferDevice.getContent();
|
|
mxCanvas = mpOutputDevice->GetCanvas();
|
|
canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation());
|
|
|
|
// if ViewState transform is changed, the clipping polygon needs to be adapted, too
|
|
const basegfx::B2DPolyPolygon aOldClipPolyPolygon(maClipPolyPolygon);
|
|
|
|
if(maClipPolyPolygon.count())
|
|
{
|
|
maClipPolyPolygon.transform(aDiscreteOffset);
|
|
maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon);
|
|
}
|
|
|
|
// paint content
|
|
process(rChildren);
|
|
|
|
// set to mask
|
|
mpOutputDevice = &aBufferDevice.getTransparence();
|
|
mxCanvas = mpOutputDevice->GetCanvas();
|
|
canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation());
|
|
|
|
// when painting transparence masks, reset the color stack
|
|
basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack);
|
|
maBColorModifierStack = basegfx::BColorModifierStack();
|
|
|
|
// paint mask to it (always with transparence intensities, evtl. with AA)
|
|
process(rTransparence);
|
|
|
|
// back to old color stack, OutDev, Canvas and ViewTransform
|
|
maBColorModifierStack = aLastBColorModifierStack;
|
|
mpOutputDevice = pLastOutputDevice;
|
|
mxCanvas = xLastCanvas;
|
|
updateViewInformation(aLastViewInformation2D);
|
|
canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation());
|
|
|
|
// restore clipping polygon
|
|
maClipPolyPolygon = aOldClipPolyPolygon;
|
|
|
|
if(maClipPolyPolygon.count())
|
|
{
|
|
maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon);
|
|
}
|
|
|
|
// dump buffer to outdev
|
|
aBufferDevice.paint();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void canvasProcessor2D::impRenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokePrimitive)
|
|
{
|
|
// support direct fat line geometry. This moves the decomposition to the canvas.
|
|
// As long as our canvases are used (which also use basegfx tooling) this makes
|
|
// no difference, but potentially canvases may better support this
|
|
static bool bSupportFatLineDirectly(true);
|
|
bool bOutputDone(false);
|
|
|
|
if(bSupportFatLineDirectly)
|
|
{
|
|
const attribute::LineAttribute& rLineAttribute = rPolygonStrokePrimitive.getLineAttribute();
|
|
const attribute::StrokeAttribute& rStrokeAttribute = rPolygonStrokePrimitive.getStrokeAttribute();
|
|
|
|
if(0.0 < rLineAttribute.getWidth() || 0 != rStrokeAttribute.getDotDashArray().size())
|
|
{
|
|
rendering::StrokeAttributes aStrokeAttribute;
|
|
|
|
aStrokeAttribute.StrokeWidth = rLineAttribute.getWidth();
|
|
aStrokeAttribute.MiterLimit = 15.0; // degrees; maybe here (15.0 * F_PI180) is needed, not clear in the documentation
|
|
const ::std::vector< double >& rDotDashArray = rStrokeAttribute.getDotDashArray();
|
|
|
|
if(rDotDashArray.size())
|
|
{
|
|
aStrokeAttribute.DashArray = uno::Sequence< double >(&rDotDashArray[0], rDotDashArray.size());
|
|
}
|
|
|
|
switch(rLineAttribute.getLineJoin())
|
|
{
|
|
default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
|
|
aStrokeAttribute.JoinType = rendering::PathJoinType::NONE;
|
|
break;
|
|
case basegfx::B2DLINEJOIN_BEVEL:
|
|
aStrokeAttribute.JoinType = rendering::PathJoinType::BEVEL;
|
|
break;
|
|
case basegfx::B2DLINEJOIN_MITER:
|
|
aStrokeAttribute.JoinType = rendering::PathJoinType::MITER;
|
|
break;
|
|
case basegfx::B2DLINEJOIN_ROUND:
|
|
aStrokeAttribute.JoinType = rendering::PathJoinType::ROUND;
|
|
break;
|
|
}
|
|
|
|
const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor()));
|
|
maRenderState.DeviceColor = aHairlineColor.colorToDoubleSequence(mxCanvas->getDevice());
|
|
canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
|
|
|
|
mxCanvas->strokePolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolygon(mxCanvas->getDevice(), rPolygonStrokePrimitive.getB2DPolygon()),
|
|
maViewState, maRenderState, aStrokeAttribute);
|
|
|
|
bOutputDone = true;
|
|
}
|
|
}
|
|
|
|
if(!bOutputDone)
|
|
{
|
|
// process decomposition
|
|
process(rPolygonStrokePrimitive.get2DDecomposition(getViewInformation2D()));
|
|
}
|
|
}
|
|
|
|
void canvasProcessor2D::impRenderFillBitmapPrimitive2D(const primitive2d::FillBitmapPrimitive2D& rFillBitmapPrimitive2D)
|
|
{
|
|
// support tiled fills directly when tiling is on
|
|
static bool bSupportFillBitmapDirectly(true);
|
|
bool bOutputDone(false);
|
|
|
|
if(bSupportFillBitmapDirectly)
|
|
{
|
|
const attribute::FillBitmapAttribute& rFillBitmapAttribute = rFillBitmapPrimitive2D.getFillBitmap();
|
|
|
|
if(rFillBitmapAttribute.getTiling())
|
|
{
|
|
// apply possible color modification to Bitmap
|
|
const BitmapEx aChangedBitmapEx(impModifyBitmapEx(maBColorModifierStack, rFillBitmapAttribute.getBitmapEx()));
|
|
|
|
if(aChangedBitmapEx.IsEmpty())
|
|
{
|
|
// replace with color filled polygon
|
|
const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor()));
|
|
const basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon());
|
|
|
|
maRenderState.DeviceColor = aModifiedColor.colorToDoubleSequence(mxCanvas->getDevice());
|
|
canvas::tools::setRenderStateTransform(maRenderState,
|
|
getViewInformation2D().getObjectTransformation() * rFillBitmapPrimitive2D.getTransformation());
|
|
|
|
mxCanvas->fillPolyPolygon(basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
|
|
mxCanvas->getDevice(), basegfx::B2DPolyPolygon(aPolygon)), maViewState, maRenderState);
|
|
}
|
|
else
|
|
{
|
|
const Size aSizePixel(aChangedBitmapEx.GetSizePixel());
|
|
|
|
if(0 != aSizePixel.Width() && 0 != aSizePixel.Height())
|
|
{
|
|
// create texture matrix from texture to object (where object is unit square here),
|
|
// so use values directly
|
|
const basegfx::B2DHomMatrix aTextureMatrix(basegfx::tools::createScaleTranslateB2DHomMatrix(
|
|
rFillBitmapAttribute.getSize().getX(), rFillBitmapAttribute.getSize().getY(),
|
|
rFillBitmapAttribute.getTopLeft().getX(), rFillBitmapAttribute.getTopLeft().getY()));
|
|
|
|
// create and fill texture
|
|
rendering::Texture aTexture;
|
|
|
|
basegfx::unotools::affineMatrixFromHomMatrix(aTexture.AffineTransform, aTextureMatrix);
|
|
aTexture.Alpha = 1.0;
|
|
aTexture.Bitmap = vcl::unotools::xBitmapFromBitmapEx(mxCanvas->getDevice(), aChangedBitmapEx);
|
|
aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
|
|
aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
|
|
|
|
// canvas needs a polygon to fill, create unit rectangle polygon
|
|
const basegfx::B2DPolygon aOutlineRectangle(basegfx::tools::createUnitPolygon());
|
|
|
|
// set primitive's transformation as render state transform
|
|
canvas::tools::setRenderStateTransform(maRenderState,
|
|
getViewInformation2D().getObjectTransformation() * rFillBitmapPrimitive2D.getTransformation());
|
|
|
|
// put texture into a uno sequence for handover
|
|
uno::Sequence< rendering::Texture > aSeq(1);
|
|
aSeq[0] = aTexture;
|
|
|
|
// draw textured rectangle
|
|
mxCanvas->fillTexturedPolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), basegfx::B2DPolyPolygon(aOutlineRectangle)),
|
|
maViewState, maRenderState, aSeq);
|
|
}
|
|
}
|
|
|
|
bOutputDone = true;
|
|
}
|
|
}
|
|
|
|
if(!bOutputDone)
|
|
{
|
|
// process decomposition
|
|
process(rFillBitmapPrimitive2D.get2DDecomposition(getViewInformation2D()));
|
|
}
|
|
}
|
|
|
|
void canvasProcessor2D::impRenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate)
|
|
{
|
|
if(0.0 == rUniTransparenceCandidate.getTransparence())
|
|
{
|
|
// not transparent at all, directly use content
|
|
process(rUniTransparenceCandidate.getChildren());
|
|
}
|
|
else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0)
|
|
{
|
|
const primitive2d::Primitive2DSequence rChildren = rUniTransparenceCandidate.getChildren();
|
|
|
|
if(rChildren.hasElements())
|
|
{
|
|
bool bOutputDone(false);
|
|
|
|
// Detect if a single PolyPolygonColorPrimitive2D is contained; in that case,
|
|
// use the fillPolyPolygon method with correctly set transparence. This is a often used
|
|
// case, so detectiong it is valuable
|
|
if(1 == rChildren.getLength())
|
|
{
|
|
const primitive2d::Primitive2DReference xReference(rChildren[0]);
|
|
const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get());
|
|
|
|
if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID())
|
|
{
|
|
// direct draw of PolyPolygon with color and transparence
|
|
const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor()));
|
|
|
|
// add transparence modulation value to DeviceColor
|
|
uno::Sequence< double > aColor(4);
|
|
|
|
aColor[0] = aPolygonColor.getRed();
|
|
aColor[1] = aPolygonColor.getGreen();
|
|
aColor[2] = aPolygonColor.getBlue();
|
|
aColor[3] = 1.0 - rUniTransparenceCandidate.getTransparence();
|
|
maRenderState.DeviceColor = aColor;
|
|
|
|
canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
|
|
mxCanvas->fillPolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), pPoPoColor->getB2DPolyPolygon()),
|
|
maViewState, maRenderState);
|
|
bOutputDone = true;
|
|
}
|
|
}
|
|
|
|
if(!bOutputDone)
|
|
{
|
|
// process decomposition. This will be decomposed to an TransparencePrimitive2D
|
|
// with the same child context and a single polygon for transparent context. This could be
|
|
// directly handled here with known VCL-buffer technology, but would only
|
|
// make a small difference compared to directly rendering the TransparencePrimitive2D
|
|
// using impRenderTransparencePrimitive2D above.
|
|
process(rUniTransparenceCandidate.get2DDecomposition(getViewInformation2D()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// internal processing support
|
|
|
|
void canvasProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
|
|
{
|
|
switch(rCandidate.getPrimitive2DID())
|
|
{
|
|
case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
|
|
{
|
|
// direct draw of hairline
|
|
const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate = static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate);
|
|
const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
|
|
|
|
maRenderState.DeviceColor = aHairlineColor.colorToDoubleSequence(mxCanvas->getDevice());
|
|
canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
|
|
mxCanvas->drawPolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolygon(mxCanvas->getDevice(), rPolygonCandidate.getB2DPolygon()),
|
|
maViewState, maRenderState);
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
|
|
{
|
|
// direct draw of PolyPolygon with color
|
|
const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate = static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate);
|
|
const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
|
|
|
|
maRenderState.DeviceColor = aPolygonColor.colorToDoubleSequence(mxCanvas->getDevice());
|
|
canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
|
|
mxCanvas->fillPolyPolygon(
|
|
basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), rPolygonCandidate.getB2DPolyPolygon()),
|
|
maViewState, maRenderState);
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D :
|
|
{
|
|
// modified color group. Force output to unified color.
|
|
const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate = static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate);
|
|
|
|
if(rModifiedCandidate.getChildren().hasElements())
|
|
{
|
|
maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
|
|
process(rModifiedCandidate.getChildren());
|
|
maBColorModifierStack.pop();
|
|
}
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
|
|
{
|
|
// mask group
|
|
impRenderMaskPrimitive2D(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate));
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
|
|
{
|
|
// transform group. Remember current ViewInformation2D
|
|
const primitive2d::TransformPrimitive2D& rTransformCandidate = static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate);
|
|
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
|
|
|
|
// create new local ViewInformation2D with new transformation
|
|
const geometry::ViewInformation2D aViewInformation2D(
|
|
getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
|
|
getViewInformation2D().getViewTransformation(),
|
|
getViewInformation2D().getViewport(),
|
|
getViewInformation2D().getVisualizedPage(),
|
|
getViewInformation2D().getViewTime(),
|
|
getViewInformation2D().getExtendedInformationSequence());
|
|
updateViewInformation(aViewInformation2D);
|
|
|
|
// set at canvas
|
|
canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
|
|
|
|
// proccess content
|
|
process(rTransformCandidate.getChildren());
|
|
|
|
// restore transformations
|
|
updateViewInformation(aLastViewInformation2D);
|
|
|
|
// restore at canvas
|
|
canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D :
|
|
{
|
|
// new XDrawPage for ViewInformation2D
|
|
const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate = static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate);
|
|
|
|
// remember current transformation and ViewInformation
|
|
const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
|
|
|
|
// create new local ViewInformation2D
|
|
const geometry::ViewInformation2D aViewInformation2D(
|
|
getViewInformation2D().getObjectTransformation(),
|
|
getViewInformation2D().getViewTransformation(),
|
|
getViewInformation2D().getViewport(),
|
|
rPagePreviewCandidate.getXDrawPage(),
|
|
getViewInformation2D().getViewTime(),
|
|
getViewInformation2D().getExtendedInformationSequence());
|
|
updateViewInformation(aViewInformation2D);
|
|
|
|
// proccess decomposed content
|
|
process(rPagePreviewCandidate.get2DDecomposition(getViewInformation2D()));
|
|
|
|
// restore transformations
|
|
updateViewInformation(aLastViewInformation2D);
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
|
|
{
|
|
// MetaFile primitive
|
|
impRenderMetafilePrimitive2D(static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate));
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
|
|
{
|
|
// PointArray primitive
|
|
const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate = static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate);
|
|
|
|
// set point color
|
|
const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
|
|
maRenderState.DeviceColor = aRGBColor.colorToDoubleSequence(mxCanvas->getDevice());
|
|
canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
|
|
|
|
const std::vector< basegfx::B2DPoint >& rPointVector = rPointArrayCandidate.getPositions();
|
|
const sal_uInt32 nPointCount(rPointVector.size());
|
|
|
|
for(sal_uInt32 a(0); a < nPointCount; a++)
|
|
{
|
|
const basegfx::B2DPoint& rPoint = rPointVector[a];
|
|
mxCanvas->drawPoint(basegfx::unotools::point2DFromB2DPoint(rPoint), maViewState, maRenderState);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
|
|
{
|
|
// TextSimplePortion primitive
|
|
impRenderTextSimplePortionPrimitive2D(static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate));
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
|
|
{
|
|
// Bitmap primitive
|
|
impRenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
|
|
{
|
|
// Transparence primitive
|
|
impRenderTransparencePrimitive2D(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate));
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
|
|
{
|
|
// PolygonStrokePrimitive
|
|
impRenderPolygonStrokePrimitive2D(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate));
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_FILLBITMAPPRIMITIVE2D :
|
|
{
|
|
// FillBitmapPrimitive2D
|
|
impRenderFillBitmapPrimitive2D(static_cast< const primitive2d::FillBitmapPrimitive2D& >(rCandidate));
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D :
|
|
{
|
|
// UnifiedTransparencePrimitive2D
|
|
impRenderUnifiedTransparencePrimitive2D(static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate));
|
|
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_CHARTPRIMITIVE2D :
|
|
{
|
|
// chart primitive in canvas renderer; restore original DrawMode during call
|
|
// since the evtl. used ChartPrettyPainter will use the MapMode
|
|
const primitive2d::ChartPrimitive2D& rChartPrimitive = static_cast< const primitive2d::ChartPrimitive2D& >(rCandidate);
|
|
mpOutputDevice->Push(PUSH_MAPMODE);
|
|
mpOutputDevice->SetMapMode(maOriginalMapMode);
|
|
|
|
if(!renderChartPrimitive2D(
|
|
rChartPrimitive,
|
|
*mpOutputDevice,
|
|
getViewInformation2D()))
|
|
{
|
|
// fallback to decomposition (MetaFile)
|
|
process(rChartPrimitive.get2DDecomposition(getViewInformation2D()));
|
|
}
|
|
|
|
mpOutputDevice->Pop();
|
|
break;
|
|
}
|
|
case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
|
|
{
|
|
// wrong spell primitive. Handled directly here using VCL since VCL has a nice and
|
|
// very direct waveline painting which is needed for this. If VCL is to be avoided,
|
|
// this can be removed anytime and the decomposition may be used
|
|
const primitive2d::WrongSpellPrimitive2D& rWrongSpellPrimitive = static_cast< const primitive2d::WrongSpellPrimitive2D& >(rCandidate);
|
|
|
|
if(!renderWrongSpellPrimitive2D(
|
|
rWrongSpellPrimitive,
|
|
*mpOutputDevice,
|
|
getViewInformation2D().getObjectToViewTransformation(),
|
|
maBColorModifierStack))
|
|
{
|
|
// fallback to decomposition (MetaFile)
|
|
process(rWrongSpellPrimitive.get2DDecomposition(getViewInformation2D()));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// nice to have:
|
|
//
|
|
// case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
|
|
// - support FormControls more direct eventually, not sure if this is needed
|
|
// with the canvas renderer. The decomposition provides a bitmap representation
|
|
// of the control which will work
|
|
//
|
|
|
|
default :
|
|
{
|
|
// process recursively
|
|
process(rCandidate.get2DDecomposition(getViewInformation2D()));
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// process support
|
|
|
|
canvasProcessor2D::canvasProcessor2D(
|
|
const geometry::ViewInformation2D& rViewInformation,
|
|
OutputDevice& rOutDev)
|
|
: BaseProcessor2D(rViewInformation),
|
|
maOriginalMapMode(rOutDev.GetMapMode()),
|
|
mpOutputDevice(&rOutDev),
|
|
mxCanvas(rOutDev.GetCanvas()),
|
|
maViewState(),
|
|
maRenderState(),
|
|
maBColorModifierStack(),
|
|
maDrawinglayerOpt(),
|
|
maClipPolyPolygon(),
|
|
meLang(LANGUAGE_SYSTEM)
|
|
{
|
|
const SvtCTLOptions aSvtCTLOptions;
|
|
|
|
canvas::tools::initViewState(maViewState);
|
|
canvas::tools::initRenderState(maRenderState);
|
|
canvas::tools::setViewStateTransform(maViewState, getViewInformation2D().getViewTransformation());
|
|
|
|
// set digit language, derived from SvtCTLOptions to have the correct
|
|
// number display for arabic/hindi numerals
|
|
if(SvtCTLOptions::NUMERALS_HINDI == aSvtCTLOptions.GetCTLTextNumerals())
|
|
{
|
|
meLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
|
|
}
|
|
else if(SvtCTLOptions::NUMERALS_ARABIC == aSvtCTLOptions.GetCTLTextNumerals())
|
|
{
|
|
meLang = LANGUAGE_ENGLISH;
|
|
}
|
|
else
|
|
{
|
|
meLang = (LanguageType)Application::GetSettings().GetLanguage();
|
|
}
|
|
|
|
rOutDev.SetDigitLanguage(meLang);
|
|
|
|
// prepare output directly to pixels
|
|
mpOutputDevice->Push(PUSH_MAPMODE);
|
|
mpOutputDevice->SetMapMode();
|
|
|
|
// react on AntiAliasing settings
|
|
if(getOptionsDrawinglayer().IsAntiAliasing())
|
|
{
|
|
mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() | ANTIALIASING_ENABLE_B2DDRAW);
|
|
}
|
|
else
|
|
{
|
|
mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW);
|
|
}
|
|
}
|
|
|
|
canvasProcessor2D::~canvasProcessor2D()
|
|
{
|
|
// restore MapMode
|
|
mpOutputDevice->Pop();
|
|
|
|
// restore AntiAliasing
|
|
mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW);
|
|
}
|
|
} // end of namespace processor2d
|
|
} // end of namespace drawinglayer
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// eof
|