diff options
author | RĂ¼diger Timm <rt@openoffice.org> | 2004-11-26 19:54:32 +0000 |
---|---|---|
committer | RĂ¼diger Timm <rt@openoffice.org> | 2004-11-26 19:54:32 +0000 |
commit | 3ccf0dffc2c5dc668d47eb1e5e565a9e3f6db222 (patch) | |
tree | 8acd8d43e3c960c90d8ea58499cd0702090c772a /cppcanvas/source/mtfrenderer/implrenderer.cxx | |
parent | fe2666e381ae24bd533e77a8daa6c495f5764f74 (diff) |
INTEGRATION: CWS presentationengine01 (1.2.2); FILE MERGED
2004/11/21 22:05:09 thb 1.2.2.23: #110496# After merge, correctOrientations now returns result, no longer modifies passed parameter
2004/11/19 23:57:04 thb 1.2.2.22: #110496# Adapted to AW clipper changes
2004/11/08 05:56:07 thb 1.2.2.21: #i36093# Added native canvas gradients
2004/11/01 22:21:48 thb 1.2.2.20: #110496# Performance optimizations: added profiling traces
2004/10/26 12:39:17 thb 1.2.2.19: #110496# Setup default text color (in contrast to the line/fill color, OutDev inits that to default black)
2004/10/12 14:14:40 thb 1.2.2.18: #i34997# Added support for FLOATTRANSPARENCY meta action
2004/10/01 17:24:52 thb 1.2.2.17: #110496# Normalizing len attribute on text metaactions (might well be magic STRING_LEN, which is handled on VCL's OutputDevice, but not for our DX canvas, and means: simply take the length of the passed string argument)
2004/09/30 21:45:41 thb 1.2.2.16: #110496# Now handling clip meta actions correctly (were mostly ignored before)
2004/09/01 22:02:40 thb 1.2.2.15: #110496# Cosmetical changes, removed extraneous include; added two TODO comments
2004/08/31 15:06:53 hdu 1.2.2.14: #116716# enable text outline effect
2004/08/31 14:14:11 hdu 1.2.2.13: #116716# add text outline effect
2004/08/27 11:33:38 hdu 1.2.2.12: #116716# make gcc happy by replacing an unbalanced ternary expression
2004/08/23 19:15:49 thb 1.2.2.11: #110496# Added more overridable attributes (needed for slideshow attribute animations
2004/07/28 13:08:47 thb 1.2.2.10: #110496# Adapted to reversed template argument order in containerToSequence
2004/07/20 19:08:58 thb 1.2.2.9: #110496# Unified include statements; removed external prefix from boost includes
2004/07/09 09:50:51 hdu 1.2.2.8: #116716# more text effects and text lines
2004/06/03 14:18:46 thb 1.2.2.7: #110496# Fixed crash occuring when no font was set before text rendering
2004/05/27 20:51:28 thb 1.2.2.6: #110496#
Added classification code to all TODO/HACK/FIXME comments.
There are four categories:
- code quality (C)
- performance (P)
- missing functionality (F)
- and missing/incomplete error handling (E)
Furthermore, every category has a severity number between
1 and 3 associated, where 1 is lowest and 3 highest
severity
2004/05/11 15:19:31 hdu 1.2.2.5: #116716# add text effects and emphasis styles
2004/05/10 09:38:22 hdu 1.2.2.4: #116716# improve text handling
2004/04/21 18:49:39 thb 1.2.2.3: #110496# Added shape-global change functionality to Renderer interface
2004/04/12 15:13:20 thb 1.2.2.2: #110496# Adaptions after canvas01 merge
2004/04/05 15:58:47 thb 1.2.2.1: Resync with canvas01 changes
Diffstat (limited to 'cppcanvas/source/mtfrenderer/implrenderer.cxx')
-rw-r--r-- | cppcanvas/source/mtfrenderer/implrenderer.cxx | 1536 |
1 files changed, 1290 insertions, 246 deletions
diff --git a/cppcanvas/source/mtfrenderer/implrenderer.cxx b/cppcanvas/source/mtfrenderer/implrenderer.cxx index f3f50c2e9139..07656124a9c3 100644 --- a/cppcanvas/source/mtfrenderer/implrenderer.cxx +++ b/cppcanvas/source/mtfrenderer/implrenderer.cxx @@ -2,9 +2,9 @@ * * $RCSfile: implrenderer.cxx,v $ * - * $Revision: 1.2 $ + * $Revision: 1.3 $ * - * last change: $Author: thb $ $Date: 2004-03-18 10:41:04 $ + * last change: $Author: rt $ $Date: 2004-11-26 20:54:32 $ * * The Contents of this file are made available subject to the terms of * either of the following licenses @@ -59,6 +59,9 @@ * ************************************************************************/ +#include <canvas/debug.hxx> +#include <canvas/verbosetrace.hxx> + #ifndef _OSL_MUTEX_HXX_ #include <osl/mutex.hxx> #endif @@ -69,32 +72,59 @@ #include <vcl/svapp.hxx> #endif +#ifndef _RTL_LOGFILE_HXX_ +#include <rtl/logfile.hxx> +#endif + #ifndef _COMPHELPER_SEQUENCE_HXX_ #include <comphelper/sequence.hxx> #endif #include <cppcanvas/canvas.hxx> +#ifndef _DRAFTS_COM_SUN_STAR_RENDERING_XGRAPHICDEVICE_HPP_ +#include <drafts/com/sun/star/rendering/XGraphicDevice.hpp> +#endif +#ifndef _DRAFTS_COM_SUN_STAR_RENDERING_TEXTURINGMODE_HPP_ +#include <drafts/com/sun/star/rendering/TexturingMode.hpp> +#endif +#ifndef _DRAFTS_COM_SUN_STAR_RENDERING_XPARAMETRICPOLYPOLYGON2DFACTORY_HPP_ +#include <drafts/com/sun/star/rendering/XParametricPolyPolygon2DFactory.hpp> +#endif + #ifndef _VCL_CANVASTOOLS_HXX #include <vcl/canvastools.hxx> #endif #ifndef _BGFX_TOOLS_CANVASTOOLS_HXX #include <basegfx/tools/canvastools.hxx> #endif +#ifndef _BGFX_NUMERIC_FTOOLS_HXX +#include <basegfx/numeric/ftools.hxx> +#endif +#ifndef _BGFX_POLYGON_B2DPOLYPOLYGONTOOLS_HXX +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#endif +#ifndef _BGFX_POLYGON_B2DPOLYGONTOOLS_HXX +#include <basegfx/polygon/b2dpolygontools.hxx> +#endif #ifndef _CANVAS_CANVASTOOLS_HXX #include <canvas/canvastools.hxx> #endif +#ifndef _VCL_CANVASTOOLS_HXX +#include <vcl/canvastools.hxx> +#endif -#include "implrenderer.hxx" -#include "tools.hxx" -#include "outdevstate.hxx" +#include <implrenderer.hxx> +#include <tools.hxx> +#include <outdevstate.hxx> -#include "action.hxx" -#include "bitmapaction.hxx" -#include "lineaction.hxx" -#include "pointaction.hxx" -#include "polypolyaction.hxx" -#include "textaction.hxx" +#include <action.hxx> +#include <bitmapaction.hxx> +#include <lineaction.hxx> +#include <pointaction.hxx> +#include <polypolyaction.hxx> +#include <textaction.hxx> +#include <transparencygroupaction.hxx> #include <vector> #include <algorithm> @@ -125,6 +155,9 @@ #ifndef _BGFX_MATRIX_B2DHOMMATRIX_HXX #include <basegfx/matrix/b2dhommatrix.hxx> #endif +#ifndef _BGFX_TUPLE_B2DTUPLE_HXX +#include <basegfx/tuple/b2dtuple.hxx> +#endif #ifndef _SV_GDIMTF_HXX #include <vcl/gdimtf.hxx> @@ -138,11 +171,15 @@ #include <vcl/virdev.hxx> #endif +#ifndef _SV_METRIC_HXX +#include <vcl/metric.hxx> +#endif + #ifndef _TL_POLY_HXX #include <tools/poly.hxx> #endif -#include "outdevstate.hxx" +#include <outdevstate.hxx> using namespace ::drafts::com::sun::star; @@ -224,10 +261,17 @@ namespace cppcanvas int rActionIndex, VectorOfOutDevStates& rStates ) { + const OutDevState& rState( getState( rStates ) ); + if( rState.lineColor.getLength() == 0 && + rState.fillColor.getLength() == 0 ) + { + return false; + } + maActions.push_back( MtfAction( ActionSharedPtr( - new internal::PolyPolyAction( rPolyPoly, rCanvas, getState( rStates ) ) ), + new internal::PolyPolyAction( rPolyPoly, rCanvas, rState ) ), rActionIndex ) ); return true; @@ -251,60 +295,837 @@ namespace cppcanvas return; } - void ImplRenderer::createGradientAction( const Rectangle& rRect, - const Gradient& rGradient, - VirtualDevice& rVDev, + void ImplRenderer::createGradientAction( const ::PolyPolygon& rPoly, + const ::Gradient& rGradient, + ::VirtualDevice& rVDev, const CanvasSharedPtr& rCanvas, - VectorOfOutDevStates& rStates ) + VectorOfOutDevStates& rStates, + const Parameters& rParms, + int& io_rCurrActionIndex, + bool bIsPolygonRectangle ) { DBG_TESTSOLARMUTEX(); - // TODO: Use native canvas gradients here (saves a lot of UNO calls) - GDIMetaFile aTmpMtf; + ::PolyPolygon aDevicePoly( rVDev.LogicToPixel( rPoly ) ); + + // decide, whether this gradient can be rendered natively + // by the canvas, or must be emulated via VCL gradient + // action extraction. + const USHORT nSteps( rGradient.GetSteps() ); - rVDev.AddGradientActions( rRect, +#if 0 + 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( + rCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() ); + + if( xFactory.is() ) + { + ::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 + // ---------------------------- + + // scale color coefficients with gradient intensities + const USHORT nStartIntensity( rGradient.GetStartIntensity() ); + ::Color aVCLStartColor( rGradient.GetStartColor() ); + aVCLStartColor.SetRed( aVCLStartColor.GetRed() * nStartIntensity / 100 ); + aVCLStartColor.SetGreen( aVCLStartColor.GetGreen() * nStartIntensity / 100 ); + aVCLStartColor.SetBlue( aVCLStartColor.GetBlue() * nStartIntensity / 100 ); + + const USHORT nEndIntensity( rGradient.GetEndIntensity() ); + ::Color aVCLEndColor( rGradient.GetEndColor() ); + aVCLEndColor.SetRed( aVCLEndColor.GetRed() * nEndIntensity / 100 ); + aVCLEndColor.SetGreen( aVCLEndColor.GetGreen() * nEndIntensity / 100 ); + aVCLEndColor.SetBlue( aVCLEndColor.GetBlue() * nEndIntensity / 100 ); + + const uno::Sequence< double > aStartColor( + ::vcl::unotools::colorToDoubleSequence( rCanvas->getUNOCanvas()->getDevice(), + aVCLStartColor ) ); + const uno::Sequence< double > aEndColor( + ::vcl::unotools::colorToDoubleSequence( rCanvas->getUNOCanvas()->getDevice(), + aVCLEndColor ) ); + + // Setup texture transformation + // ---------------------------- + + const Rectangle aBounds( aDevicePoly.GetBoundRect() ); + + // setup rotation angle. VCL rotates + // counter-clockwise, while canvas transformation + // rotates clockwise + double nRotation( -rGradient.GetAngle() * M_PI / 1800.0 ); + + switch( rGradient.GetStyle() ) + { + case GRADIENT_LINEAR: + // FALLTHROUGH intended + case GRADIENT_AXIAL: + { + // standard orientation for VCL linear + // gradient is vertical, thus, rotate 90 + // degrees + nRotation += M_PI/2.0; + + const double nBorder( + ::basegfx::pruneScaleValue( + (1.0 - rGradient.GetBorder() / 100.0) ) ); + + // shrink texture, to account for border + // (only in x direction, linear gradient + // is constant in y direction, anyway) + aTextureTransformation.scale( nBorder, + 1.0 ); + + // 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, axial gradients have + // border on both sides. As both gradients + // are invariant in y direction: leave y + // offset alone. + double nOffsetX( rGradient.GetBorder() / 200.0 ); + + // determine type of gradient (and necessary + // transformation matrix, should it be emulated by a + // generic gradient) + switch( rGradient.GetStyle() ) + { + case GRADIENT_LINEAR: + nOffsetX = rGradient.GetBorder() / 100.0; + aTexture.Gradient = xFactory->createLinearHorizontalGradient( aEndColor, + aStartColor ); + break; + + case GRADIENT_AXIAL: + aTexture.Gradient = xFactory->createAxialHorizontalGradient( aStartColor, + aEndColor ); + break; + } + + // apply border offset values + aTextureTransformation.translate( nOffsetX, + 0.0 ); + + // rotate texture according to gradient rotation + aTextureTransformation.translate( -0.5, -0.5 ); + aTextureTransformation.rotate( nRotation ); + + // 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(alpha) + x/2 cos(alpha) + // + // (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( aBounds.GetHeight()*sin(nRotation) ) + + fabs( aBounds.GetWidth()*cos(nRotation) ))); + + aTextureTransformation.scale( nScale, nScale ); + + // translate back origin to center of + // primitive + aTextureTransformation.translate( 0.5*aBounds.GetWidth(), + 0.5*aBounds.GetHeight() ); + } + break; + + case GRADIENT_RADIAL: + // FALLTHROUGH intended + case GRADIENT_ELLIPTICAL: + // FALLTHROUGH intended + case GRADIENT_SQUARE: + // FALLTHROUGH intended + case GRADIENT_RECT: + { + // 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( aBounds.GetWidth() * (1.0 - rGradient.GetBorder() / 100.0) ); + double nScaleY( aBounds.GetHeight()* (1.0 - rGradient.GetBorder() / 100.0) ); + + // determine offset values. Since the border is + // divided half-by-half to both sides of the + // gradient, divide translation offset by an + // additional 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( aBounds.GetWidth() * + (2.0 * rGradient.GetOfsX() - 100.0 + rGradient.GetBorder()) / 200.0 ); + double nOffsetY( aBounds.GetHeight() * + (2.0 * rGradient.GetOfsY() - 100.0 + rGradient.GetBorder()) / 200.0 ); + + // determine type of gradient (and necessary + // transformation matrix, should it be emulated by a + // generic gradient) + switch( rGradient.GetStyle() ) + { + case GRADIENT_RADIAL: + { + // 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 ); + + // create isotrophic scaling + if( nScaleX > nScaleY ) + { + nOffsetY -= (nScaleX - nScaleY) * 0.5; + nScaleY = nScaleX; + } + else + { + nOffsetX -= (nScaleY - nScaleX) * 0.5; + nScaleX = nScaleY; + } + + aTexture.Gradient = xFactory->createCircularGradient( aEndColor, + aStartColor ); + } + break; + + case GRADIENT_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->createCircularGradient( aEndColor, + aStartColor ); + } + break; + + case GRADIENT_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, + geometry::RealRectangle2D(0.0,0.0, + 1.0,1.0) ); + break; + + case GRADIENT_RECT: + aTexture.Gradient = xFactory->createRectangularGradient( + aEndColor, + aStartColor, + geometry::RealRectangle2D( aBounds.Left(), + aBounds.Top(), + aBounds.Right(), + aBounds.Bottom() ) ); + 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( nRotation ); + aTextureTransformation.translate( 0.5*nScaleX, 0.5*nScaleY ); + + aTextureTransformation.translate( nOffsetX, nOffsetY ); + } + break; + + default: + ENSURE_AND_THROW( false, + "ImplRenderer::createGradientAction(): Unexpected gradient type" ); + break; + } + + ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform, + aTextureTransformation ); + + maActions.push_back( + MtfAction( + ActionSharedPtr( + new internal::PolyPolyAction( aDevicePoly, + rCanvas, + getState( rStates ), + aTexture ) ), + io_rCurrActionIndex ) ); + + // done, using native gradients + return; + } + } +#endif + + // cannot currently use native canvas gradients, as + // step size is given + pushState( rStates ); + + if( !bIsPolygonRectangle ) + { + // only clip, if given polygon is not a rectangle in + // the first place (the gradient is always limited to + // the given bound rect) + updateClipping( + rStates, + aDevicePoly.getB2DPolyPolygon(), + rCanvas, + true ); + } + + GDIMetaFile aTmpMtf; + rVDev.AddGradientActions( rPoly.GetBoundRect(), rGradient, aTmpMtf ); + createActions( rCanvas, rVDev, aTmpMtf, rStates, rParms, io_rCurrActionIndex ); + + popState( rStates ); + } + + uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( ::basegfx::B2DHomMatrix& o_rFontMatrix, + const ::Font& rFont, + const CanvasSharedPtr& rCanvas, + const ::VirtualDevice& rVDev, + const Parameters& rParms ) const + { + rendering::FontRequest aFontRequest; + + if( rParms.maFontName.isValid() ) + aFontRequest.FontDescription.FamilyName = rParms.maFontName.getValue(); + else + aFontRequest.FontDescription.FamilyName = rFont.GetName(); + + aFontRequest.FontDescription.StyleName = rFont.GetStyleName(); + + aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO; + aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO; + + // TODO(F2): improve vclenum->panose conversion + aFontRequest.FontDescription.FontDescription.Weight = + rParms.maFontWeight.isValid() ? + rParms.maFontWeight.getValue() : + ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) ); + aFontRequest.FontDescription.FontDescription.Letterform = + rParms.maFontLetterForm.isValid() ? + rParms.maFontLetterForm.getValue() : + (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9; + + // TODO(F2): use correct scale direction, font + // height might be width or anything else + aFontRequest.CellSize = rVDev.LogicToPixel( rFont.GetSize() ).Height(); + + // setup state-local text transformation, + // if the font be rotated + const short nFontAngle( rFont.GetOrientation() ); + if( nFontAngle != 0 ) + { + // set to unity transform rotated by font angle + const double rAngle( nFontAngle * (F_PI / 1800.0) ); + o_rFontMatrix.identity(); + o_rFontMatrix.rotate( -rAngle ); + } + + geometry::Matrix2D aFontMatrix; + ::canvas::tools::setIdentityMatrix2D( aFontMatrix ); + + // check if the font is stretched or squeezed + long nFontWidth = rFont.GetSize().Width(); + if( nFontWidth != 0 ) + { + ::Font aTestFont = rFont; + aTestFont.SetWidth( 0 ); + int nNormalWidth = rVDev.GetFontMetric( aTestFont ).GetWidth(); + if( nNormalWidth != nFontWidth ) + if( nNormalWidth ) + aFontMatrix.m00 = (double)nFontWidth / nNormalWidth; + } + + return rCanvas->getUNOCanvas()->createFont( aFontRequest, + uno::Sequence< beans::PropertyValue >(), + aFontMatrix ); + } + + // create text effects such as shadow/relief/embossed + void ImplRenderer::createTextWithEffectsAction( + const Point& rStartPoint, + const String rString, + int nIndex, + int nLength, + const long* pCharWidths, + VirtualDevice& rVDev, + const CanvasSharedPtr& rCanvas, + VectorOfOutDevStates& rStates, + const Parameters& rParms, + int nCurrActionIndex ) + { + ENSURE_AND_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex, + "ImplRenderer::createTextWithEffectsAction(): Invalid text index" ); + + ::cppcanvas::internal::OutDevState& rState = getState( rStates ); + + // TODO(F2): implement all text effects + if( rState.textAlignment ); // TODO(F2): NYI + + if( rState.isTextEffectShadowSet ) + { + // calculate relief offset (similar to outdev3.cxx) + // TODO(F3): better match with outdev3.cxx + long nShadowOffset = static_cast<long>(1.5 + ((rVDev.GetFont().GetHeight()-24.0)/24.0)); + if( nShadowOffset < 1 ) + nShadowOffset = 1; + Point aShadowPoint( nShadowOffset, nShadowOffset ); + aShadowPoint += rStartPoint; + + // determine shadow color (from outdev3.cxx) + ::Color aTextColor = ::vcl::unotools::sequenceToColor( + rCanvas->getUNOCanvas()->getDevice(), rState.textColor ); + bool bIsDark = (aTextColor.GetColor() == COL_BLACK) + || (aTextColor.GetLuminance() < 8); + ::Color aShadowColor( bIsDark ? COL_LIGHTGRAY : COL_BLACK ); + aShadowColor.SetTransparency( aTextColor.GetTransparency() ); + + // draw shadow text and restore original rState + // TODO(P2): just restore textColor instead of push/pop + pushState( rStates ); + // ::com::sun::star::uno::Sequence< double > origTextColor = rState.textColor; + getState( rStates ).textColor = ::vcl::unotools::colorToDoubleSequence( + rCanvas->getUNOCanvas()->getDevice(), aShadowColor ); + createTextWithLinesAction( + aShadowPoint, rString, nIndex, nLength, + pCharWidths, rVDev, rCanvas, rStates, rParms, nCurrActionIndex ); + popState( rStates ); + // rState.textColor = origTextColor; + } + + // draw the normal text + createTextWithLinesAction( + rStartPoint, rString, nIndex, nLength, + pCharWidths, rVDev, rCanvas, rStates, rParms, nCurrActionIndex ); + + if( rState.textReliefStyle ) + { + // calculate relief offset (similar to outdev3.cxx) + long nReliefOffset = rVDev.PixelToLogic( Size( 1, 1 ) ).Height(); + nReliefOffset += nReliefOffset/2; + if( nReliefOffset < 1 ) + nReliefOffset = 1; + + if( rState.textReliefStyle == RELIEF_ENGRAVED ) + nReliefOffset = -nReliefOffset; + Point aReliefPoint( nReliefOffset, nReliefOffset ); + aReliefPoint += rStartPoint; + + // determine relief color (from outdev3.cxx) + ::Color aTextColor = ::vcl::unotools::sequenceToColor( + rCanvas->getUNOCanvas()->getDevice(), rState.textColor ); + ::Color aReliefColor( COL_LIGHTGRAY ); + if( aTextColor.GetColor() == COL_BLACK ) + aReliefColor = ::Color( COL_WHITE ); + else if( aTextColor.GetColor() == COL_WHITE ) + aReliefColor = ::Color( COL_BLACK ); + aReliefColor.SetTransparency( aTextColor.GetTransparency() ); + + // draw relief text and restore original rState + // TODO(P2): just restore textColor instead of push/pop + pushState( rStates ); + // ::com::sun::star::uno::Sequence< double > origTextColor = rState.textColor; + getState( rStates ).textColor = ::vcl::unotools::colorToDoubleSequence( + rCanvas->getUNOCanvas()->getDevice(), aReliefColor ); + createTextWithLinesAction( + aReliefPoint, rString, nIndex, nLength, + pCharWidths, rVDev, rCanvas, rStates, rParms, nCurrActionIndex ); + popState( rStates ); + // rState.textColor = origTextColor; + } + } + + + // create draw actions for text and underlines/strikeouts/etc. + void ImplRenderer::createTextWithLinesAction( + const Point& rStartPoint, + const String rString, + int nIndex, + int nLength, + const long* pCharWidths, + VirtualDevice& rVDev, + const CanvasSharedPtr& rCanvas, + VectorOfOutDevStates& rStates, + const Parameters& rParms, + int nCurrActionIndex ) + { + ::cppcanvas::internal::OutDevState& rState = getState( rStates ); + + Point aStartPixel = rVDev.LogicToPixel( rStartPoint ); + internal::Action* pAction = NULL; + + if( rState.isTextOutlineModeSet ) + { + ::PolyPolygon aVclPolyPolygon; + if( rVDev.GetTextOutline( aVclPolyPolygon, rString, + nIndex, nIndex, nLength, TRUE, 0, pCharWidths ) ) + { + int nOutlineWidth = rVDev.GetFont().GetHeight() / 32; + if( nOutlineWidth <= 0 ) + nOutlineWidth = 1; + // TODO(F3): rState.strokeWith = nOutlineWidth; + // Path stroking not yet functional for all canvas + // implementations + aVclPolyPolygon.Translate( rStartPoint ); //####??? + aVclPolyPolygon = rVDev.LogicToPixel( aVclPolyPolygon ); + + // set outline color + rState.lineColor = rState.textColor; + rState.isLineColorSet = true; + // set the fill color to match the VCL fill color for outlined text + // TODO: in the current Canvas model setting a fixed color is beyond ugly + ColorSharedPtr pColor( rCanvas.get()->createColor() ); + rState.fillColor = pColor->getDeviceColor( 0xFFFFFFFF ); // solid white + rState.isFillColorSet = true; + // NOTE: rState.strokeWidth is already set by the caller + + // TODO(F3): use bezier polygon directly when it + // is implemented + ::PolyPolygon aNOTBEZIER; + aVclPolyPolygon.AdaptiveSubdivide( aNOTBEZIER ); + pAction = new internal::PolyPolyAction( aNOTBEZIER, rCanvas, rState ); + } + } + + // if no outline mode was requested or possible then draw the normal text + if( !pAction ) + { + if( !pCharWidths ) + pAction = new internal::TextAction( aStartPixel, rString, + nIndex, nLength, rCanvas, rState, + rParms.maTextTransformation ); + else + { + uno::Sequence< double > aCharWidthSeq( ::comphelper::arrayToSequence<double>( pCharWidths, nLength ) ); + // convert character widths from logical units + for( int i = 0; i < nLength; ++i ) + { + // TODO(F2): use correct scale direction + const Size aSize( ::basegfx::fround( pCharWidths[i] + .5 ), 0 ); + aCharWidthSeq[i] = rVDev.LogicToPixel( aSize ).Width(); + } + + pAction = new internal::TextAction( aStartPixel, rString, + nIndex, nLength, aCharWidthSeq, rCanvas, rState, + rParms.maTextTransformation ); + } + } + + maActions.push_back( MtfAction( ActionSharedPtr( pAction ), nCurrActionIndex ) ); + + if( rState.textUnderlineStyle || rState.textStrikeoutStyle ) + { + // TODO: split text lines on word breaks + if( rState.isTextWordUnderlineSet ); // TODO: NYI + + long nTextWidth = rVDev.GetTextWidth( rString, nIndex, nLength ); + createJustTextLinesAction( rStartPoint, nTextWidth, rVDev, + rCanvas, rStates, rParms, nCurrActionIndex ); + } + + // TODO: implement text emphasis mark styles + if( rState.textEmphasisMarkStyle ); // TODO: NYI + } + + + // create line actions for text such as underline and strikeout + void ImplRenderer::createJustTextLinesAction( + const Point& rLogicalStartPoint, + long nLineWidth, + VirtualDevice& rVDev, + const CanvasSharedPtr& rCanvas, + VectorOfOutDevStates& rStates, + const Parameters& rParms, + int nCurrActionIndex ) + { pushState( rStates ); - createActions( rCanvas, rVDev, aTmpMtf, rStates ); + ::cppcanvas::internal::OutDevState& rState = getState( rStates ); + + // initialize the color of the text lines + if( !rState.isTextOutlineModeSet ) + { + // normal text lines + rState.fillColor = rState.textColor; + // NOTE: strangely the text line color is ignored by vcl + // color = rState.isTextLineColorSet ? rState.textLineColor : rState.textColor; + rState.isFillColorSet = true; + rState.isLineColorSet = false; + } +#if 0 + // the line and fill colors are already set by the calling method + // when we are in outline mode, because the text rendering already needed it + else + { + // text lines for text using an outline font + rState.lineColor = rState.textColor; + rState.isLineColorSet = true; + // TODO: rState.fillColor = Color( COL_WHITE ); + // TODO: rState.isFillColorSet = true; + // NOTE: rState.strokeWidth is already set by the caller + } +#endif + + // prepare text line position and size + ::FontMetric aMetric = rVDev.GetFontMetric(); + long nLineHeight = (aMetric.GetDescent() + 2) / 4; + Point aUnderlineOffset( 0, aMetric.GetDescent() / 2); + + // fill the polypolygon with all text lines + ::PolyPolygon aTextLinePolyPoly; + + ::Polygon aPolygon(4); + switch( rState.textUnderlineStyle ) + { + case UNDERLINE_NONE: // nothing to do + case UNDERLINE_DONTKNOW: + break; + case UNDERLINE_DASHDOT: // TODO(F3): NYI + case UNDERLINE_DASHDOTDOT: // TODO(F3): NYI + case UNDERLINE_SMALLWAVE: // TODO(F3): NYI + case UNDERLINE_WAVE: // TODO(F3): NYI + case UNDERLINE_DOUBLEWAVE: // TODO(F3): NYI + case UNDERLINE_BOLDDOTTED: // TODO(F3): NYI + case UNDERLINE_BOLDDASH: // TODO(F3): NYI + case UNDERLINE_BOLDLONGDASH: // TODO(F3): NYI + case UNDERLINE_BOLDDASHDOT: // TODO(F3): NYI + case UNDERLINE_BOLDDASHDOTDOT:// TODO(F3): NYI + case UNDERLINE_BOLDWAVE: // TODO(F3): NYI + case UNDERLINE_SINGLE: + { + aPolygon = Polygon( Rectangle( aUnderlineOffset, Size( nLineWidth, nLineHeight ) ) ); + aTextLinePolyPoly.Insert( aPolygon ); + break; + } + case UNDERLINE_BOLD: + { + aPolygon = Polygon( Rectangle( aUnderlineOffset, Size( nLineWidth, 2*nLineHeight ) ) ); + aTextLinePolyPoly.Insert( aPolygon ); + break; + } + case UNDERLINE_DOUBLE: + { + aPolygon = Polygon( Rectangle( aUnderlineOffset, Size( nLineWidth, nLineHeight ) ) ); + aPolygon.Move( 0, -nLineHeight ); + aTextLinePolyPoly.Insert( aPolygon ); + aPolygon.Move( 0, +nLineHeight * 2 ); + aTextLinePolyPoly.Insert( aPolygon ); + break; + } + case UNDERLINE_DOTTED: + { + aPolygon = Polygon( Rectangle( aUnderlineOffset, Size( nLineHeight, nLineHeight ) ) ); + for( int x = 0; x < nLineWidth;) + { + aTextLinePolyPoly.Insert( aPolygon ); + aPolygon.Move( 2 * nLineHeight, 0 ); + x += 6 * nLineHeight ; + } + break; + } + case UNDERLINE_DASH: + { + aPolygon = Polygon( Rectangle( aUnderlineOffset, Size( 3*nLineHeight, nLineHeight ) ) ); + for( int x = 0; x < nLineWidth;) + { + aTextLinePolyPoly.Insert( aPolygon ); + aPolygon.Move( 6 * nLineHeight, 0 ); + x += 6 * nLineHeight ; + } + break; + } + case UNDERLINE_LONGDASH: + { + aPolygon = Polygon( Rectangle( aUnderlineOffset, Size( 6*nLineHeight, nLineHeight ) ) ); + for( int x = 0; x < nLineWidth;) + { + aTextLinePolyPoly.Insert( aPolygon ); + aPolygon.Move( 12 * nLineHeight, 0 ); + x += 12 * nLineHeight; + } + break; + } + } + + switch( rState.textStrikeoutStyle ) + { + case STRIKEOUT_NONE: // nothing to do + case STRIKEOUT_DONTKNOW: + break; + case STRIKEOUT_SLASH: // TODO: we should handle this in the text layer + case STRIKEOUT_X: + break; + case STRIKEOUT_SINGLE: + { + Point aOffset( 0, (aMetric.GetIntLeading() - aMetric.GetAscent()) / 3 ); + Rectangle aRect( aOffset, Size( nLineWidth, nLineHeight ) ); + aTextLinePolyPoly.Insert( ::Polygon( aRect ) ); + break; + } + case STRIKEOUT_BOLD: + { + Point aOffset( 0, (aMetric.GetIntLeading() - aMetric.GetAscent()) / 3 ); + Rectangle aRect( aOffset, Size( nLineWidth, 2 * nLineHeight ) ); + aTextLinePolyPoly.Insert( ::Polygon( aRect ) ); + break; + } + case STRIKEOUT_DOUBLE: + { + Point aOffset( 0, (aMetric.GetIntLeading() - aMetric.GetAscent()) / 3 ); + aOffset.Y() -= nLineHeight; + Rectangle aRect( aOffset, Size( nLineWidth, nLineHeight ) ); + aTextLinePolyPoly.Insert( ::Polygon( aRect ) ); + aRect.Move( 0, 2 * nLineHeight ); + aTextLinePolyPoly.Insert( ::Polygon( aRect ) ); + break; + } + } + + if( !aTextLinePolyPoly.Count() ) + return; + + // prepare text line orientation + basegfx::B2DTuple aTScale, aTTrans; + double fRotate, fShearX; + rState.fontTransform.decompose( aTScale, aTTrans, fRotate, fShearX ); + fRotate = 3600 - fRotate * (1800.0 / F_PI); + + // adjust to the target coordinate system + aTextLinePolyPoly.Rotate( Point(0,0), + ::canvas::tools::numeric_cast<USHORT>( ::basegfx::fround( fRotate ) ) ); + aTextLinePolyPoly.Translate( rLogicalStartPoint ); + aTextLinePolyPoly = rVDev.LogicToPixel( aTextLinePolyPoly ); + + // create the underline + strikethrough line actions + maActions.push_back( MtfAction( ActionSharedPtr( + new internal::PolyPolyAction( aTextLinePolyPoly, rCanvas, rState ) ), + nCurrActionIndex ) ); + popState( rStates ); } + void ImplRenderer::updateClipping( VectorOfOutDevStates& rStates, + const ::basegfx::B2DPolyPolygon& rClipPoly, + const CanvasSharedPtr& rCanvas, + bool bIntersect ) + { + ::cppcanvas::internal::OutDevState& rState( getState( rStates ) ); + ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly ); + + if( !bIntersect || + rState.clip.count() == 0 ) + { + rState.clip = rClipPoly; + } + else + { + rState.clip = ::basegfx::tools::correctOrientations( rState.clip ); + aClipPoly = ::basegfx::tools::correctOrientations( aClipPoly ); + + // intersect the two poly-polygons + rState.clip = ::basegfx::tools::removeAllIntersections(rState.clip); + rState.clip = ::basegfx::tools::removeNeutralPolygons(rState.clip, sal_True); + aClipPoly = ::basegfx::tools::removeAllIntersections(aClipPoly); + aClipPoly = ::basegfx::tools::removeNeutralPolygons(aClipPoly, sal_True); + rState.clip.append(aClipPoly); + rState.clip = ::basegfx::tools::removeAllIntersections(rState.clip); + rState.clip = ::basegfx::tools::removeNeutralPolygons(rState.clip, sal_False); + } + + if( rState.clip.count() == 0 ) + { + rState.xClipPoly.clear(); + } + else + { + rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + rCanvas->getUNOCanvas()->getDevice(), + rState.clip ); + } + } + bool ImplRenderer::createActions( const CanvasSharedPtr& rCanvas, VirtualDevice& rVDev, GDIMetaFile& rMtf, - VectorOfOutDevStates& rStates ) + VectorOfOutDevStates& rStates, + const Parameters& rParms, + int& io_rCurrActionIndex ) { - /* TODO: - ===== + /* TODO(P2): interpret mtf-comments + ================================ - - Float-Transparency (skipped for prototype + - Float-Transparency (skipped for prototype) - bitmap fillings (do that via comments) - gradient fillings (do that via comments) - think about mapping. _If_ we do everything in logical coordinates (which would solve the probs for stroke - widths and and text offsets), then we would have to + widths and text offsets), then we would have to recalc scaling for every drawing operation. This is because the outdev map mode might change at any time. + Also keep in mind, that, although we've double precision + float arithmetic now, different offsets might still + generate different roundings (aka + 'OutputDevice::SetPixelOffset()) */ // Loop over every metaaction // ========================== MetaAction* pCurrAct; - int nCurrActionIndex; - // TODO: think about caching - for( nCurrActionIndex=0, pCurrAct=rMtf.FirstAction(); + // TODO(P1): think about caching + for( pCurrAct=rMtf.FirstAction(); pCurrAct; - ++nCurrActionIndex, pCurrAct = rMtf.NextAction() ) + ++io_rCurrActionIndex, pCurrAct = rMtf.NextAction() ) { // execute every action, to keep VDev state up-to-date - // (currently used only for the map mode, and for - // line/fill color when processing a - // META_TRANSPARENT_ACTION) + // currently used only for + // - the map mode + // - the line/fill color when processing a META_TRANSPARENT_ACTION + // - SetFont to process font metric specific actions pCurrAct->Execute( &rVDev ); switch( pCurrAct->GetType() ) @@ -327,122 +1148,242 @@ namespace cppcanvas // monitor clip regions, to assemble clip polygon on our own case META_CLIPREGION_ACTION: + { + MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct); + + if( !pClipAction->IsClipping() ) + { + // clear clipping + getState( rStates ).clip.clear(); + } + else + { + if( !pClipAction->GetRegion().HasPolyPolygon() ) + { + VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " + "region encountered, falling back to bounding box!" ); + + Rectangle aClipRect( + rVDev.LogicToPixel( + pClipAction->GetRegion().GetBoundRect() ) ); + + // intersect current clip with given rect + updateClipping( + rStates, + ::basegfx::B2DPolyPolygon( + ::basegfx::tools::createPolygonFromRect( + ::basegfx::B2DRectangle( aClipRect.Left(), + aClipRect.Top(), + aClipRect.Right(), + aClipRect.Bottom() ) ) ), + rCanvas, + false ); + } + else + { + // set new clip polygon (don't intersect + // with old one, just set it) + updateClipping( + rStates, + rVDev.LogicToPixel( + pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(), + rCanvas, + false ); + } + } + + break; + } + case META_ISECTRECTCLIPREGION_ACTION: + { + MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct); + Rectangle aClipRect( + rVDev.LogicToPixel( pClipAction->GetRect() ) ); + + // intersect current clip with given rect + updateClipping( + rStates, + ::basegfx::B2DPolyPolygon( + ::basegfx::tools::createPolygonFromRect( + ::basegfx::B2DRectangle( aClipRect.Left(), + aClipRect.Top(), + aClipRect.Right(), + aClipRect.Bottom() ) ) ), + rCanvas, + true ); + + break; + } + case META_ISECTREGIONCLIPREGION_ACTION: + { + MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct); + + if( !pClipAction->GetRegion().HasPolyPolygon() ) + { + VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip " + "region encountered, falling back to bounding box!" ); + + Rectangle aClipRect( + rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) ); + + // intersect current clip with given rect + updateClipping( + rStates, + ::basegfx::B2DPolyPolygon( + ::basegfx::tools::createPolygonFromRect( + ::basegfx::B2DRectangle( aClipRect.Left(), + aClipRect.Top(), + aClipRect.Right(), + aClipRect.Bottom() ) ) ), + rCanvas, + true ); + } + else + { + // intersect current clip with given clip polygon + updateClipping( + rStates, + rVDev.LogicToPixel( + pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(), + rCanvas, + true ); + } + + break; + } + case META_MOVECLIPREGION_ACTION: - // TODO: NYI + // TODO(F2): NYI break; case META_LINECOLOR_ACTION: - setStateColor( static_cast<MetaLineColorAction*>(pCurrAct), - getState( rStates ).isLineColorSet, - getState( rStates ).lineColor, - rCanvas ); + if( !rParms.maLineColor.isValid() ) + { + setStateColor( static_cast<MetaLineColorAction*>(pCurrAct), + getState( rStates ).isLineColorSet, + getState( rStates ).lineColor, + rCanvas ); + } break; case META_FILLCOLOR_ACTION: - setStateColor( static_cast<MetaFillColorAction*>(pCurrAct), - getState( rStates ).isFillColorSet, - getState( rStates ).fillColor, - rCanvas ); + if( !rParms.maFillColor.isValid() ) + { + setStateColor( static_cast<MetaFillColorAction*>(pCurrAct), + getState( rStates ).isFillColorSet, + getState( rStates ).fillColor, + rCanvas ); + } break; case META_TEXTCOLOR_ACTION: { - // Text color is set unconditionally, thus, no - // use of setStateColor here - ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() ); - - // force alpha part of color to - // opaque. transparent painting is done - // explicitely via META_TRANSPARENT_ACTION - aColor.SetTransparency(0); - - getState( rStates ).textColor = - ::vcl::unotools::colorToDoubleSequence( rCanvas->getUNOCanvas()->getDevice(), - aColor ); + if( !rParms.maTextColor.isValid() ) + { + // Text color is set unconditionally, thus, no + // use of setStateColor here + ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() ); + + // force alpha part of color to + // opaque. transparent painting is done + // explicitely via META_TRANSPARENT_ACTION + aColor.SetTransparency(0); + + getState( rStates ).textColor = + ::vcl::unotools::colorToDoubleSequence( rCanvas->getUNOCanvas()->getDevice(), + aColor ); + } } break; case META_TEXTFILLCOLOR_ACTION: - setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct), - getState( rStates ).isTextFillColorSet, - getState( rStates ).textFillColor, - rCanvas ); + if( !rParms.maTextColor.isValid() ) + { + setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct), + getState( rStates ).isTextFillColorSet, + getState( rStates ).textFillColor, + rCanvas ); + } break; case META_TEXTLINECOLOR_ACTION: - setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct), - getState( rStates ).isTextLineColorSet, - getState( rStates ).textLineColor, - rCanvas ); + if( !rParms.maTextColor.isValid() ) + { + setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct), + getState( rStates ).isTextLineColorSet, + getState( rStates ).textLineColor, + rCanvas ); + } break; case META_TEXTALIGN_ACTION: - // TODO: NYI + // TODO(F2): NYI break; case META_FONT_ACTION: { - // TODO: For now, only dummy implementation - rendering::FontRequest aFontRequest; + ::cppcanvas::internal::OutDevState& rState = getState( rStates ); const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() ); - aFontRequest.FamilyName = rFont.GetName(); - aFontRequest.StyleName = rFont.GetStyleName(); - - // TODO: use correct scale direction, font - // height might be width or anything else - const Size aSize( 0, rFont.GetHeight() ); - aFontRequest.CellSize = rVDev.LogicToPixel( aSize ).Height(); - - const short nFontAngle( rFont.GetOrientation() ); - - // setup state-local text transformation, - // should the font be rotated - if( nFontAngle != 0 ) - { - // VCL font does not access system structs here - const double rAngle( nFontAngle * (F_PI / 1800.0) ); - - // reset transform - getState( rStates ).fontTransform.identity(); - - // rotate by given angle - getState( rStates ).fontTransform.rotate( -rAngle ); - } - - getState( rStates ).xFont = rCanvas->getUNOCanvas()->queryFont( aFontRequest ); + rState.xFont = createFont( rState.fontTransform, + rFont, + rCanvas, + rVDev, + rParms ); + + // TODO(Q2): define and use appropriate enumeration types + rState.textReliefStyle = (sal_Int8)rFont.GetRelief(); + rState.textUnderlineStyle = rParms.maFontUnderline.isValid() ? + (rParms.maFontUnderline.getValue() ? UNDERLINE_SINGLE : UNDERLINE_NONE) : + (sal_Int8)rFont.GetUnderline(); + rState.textStrikeoutStyle = (sal_Int8)rFont.GetStrikeout(); + rState.textEmphasisMarkStyle = (sal_Int8)rFont.GetEmphasisMark(); + rState.isTextEffectShadowSet = (rFont.IsShadow() != FALSE); + rState.isTextWordUnderlineSet = (rFont.IsWordLineMode() != FALSE); + rState.isTextOutlineModeSet = (rFont.IsOutline() != FALSE); } break; case META_RASTEROP_ACTION: - // TODO: NYI + // TODO(F2): NYI break; case META_REFPOINT_ACTION: - // TODO: NYI + // TODO(F2): NYI break; case META_LAYOUTMODE_ACTION: { - // TODO: A lot is missing here - switch( static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode() ) + // TODO(F2): A lot is missing here + int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode(); + ::cppcanvas::internal::OutDevState& rState = getState( rStates ); + switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) ) { + case TEXT_LAYOUT_BIDI_LTR: + rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT; + break; + + case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG): + rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT; + break; + case TEXT_LAYOUT_BIDI_RTL: - case TEXT_LAYOUT_TEXTORIGIN_RIGHT: - getState( rStates ).textDirection = rendering::TextDirection::RIGHT_TO_LEFT; + rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT; break; - case TEXT_LAYOUT_BIDI_LTR: - case TEXT_LAYOUT_BIDI_STRONG: - case TEXT_LAYOUT_TEXTORIGIN_LEFT: - case TEXT_LAYOUT_COMPLEX_DISABLED: - case TEXT_LAYOUT_ENABLE_LIGATURES: - case TEXT_LAYOUT_SUBSTITUTE_DIGITS: - getState( rStates ).textDirection = rendering::TextDirection::LEFT_TO_RIGHT; + case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG): + rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT; break; } + + rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED; + if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) ) + && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) ) + { + rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED; + } } break; @@ -458,23 +1399,26 @@ namespace cppcanvas case META_GRADIENT_ACTION: { MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct); - createGradientAction( pGradAct->GetRect(), + createGradientAction( ::Polygon( pGradAct->GetRect() ), pGradAct->GetGradient(), rVDev, rCanvas, - rStates ); + rStates, + rParms, + io_rCurrActionIndex, + true ); } break; case META_HATCH_ACTION: { - // TODO: use native Canvas hatches here + // TODO(F2): use native Canvas hatches here GDIMetaFile aTmpMtf; rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(), static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(), aTmpMtf ); - createActions( rCanvas, rVDev, aTmpMtf, rStates ); + createActions( rCanvas, rVDev, aTmpMtf, rStates, rParms, io_rCurrActionIndex ); } break; @@ -505,7 +1449,8 @@ namespace cppcanvas createActions( rCanvas, rVDev, const_cast<GDIMetaFile&>(pAct->GetSubstitute()), - rStates ); + rStates, rParms, + io_rCurrActionIndex ); popState( rStates ); } @@ -542,23 +1487,14 @@ namespace cppcanvas if( pGradAction ) { - pushState( rStates ); - - // TODO: Hack! If - // current state - // already contains a - // clipping, we'll - // overwrite it here! - getState( rStates ).xClipPoly = ::vcl::unotools::xPolyPolygonFromPolyPolygon( rCanvas->getUNOCanvas()->getDevice(), - rVDev.LogicToPixel( pGradAction->GetPolyPolygon() ) ); - - createGradientAction( pGradAction->GetPolyPolygon().GetBoundRect(), + createGradientAction( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), rVDev, rCanvas, - rStates ); - - popState( rStates ); + rStates, + rParms, + io_rCurrActionIndex, + false ); } } break; @@ -569,7 +1505,7 @@ namespace cppcanvas // Handle drawing layer strokes else if( pAct->GetComment().Equals( "XPATHSTROKE_SEQ_BEGIN" ) ) { - // TODO: Later + // TODO(F1): Later #if 0 const BYTE* pData = pAct->GetData(); if ( pData ) @@ -579,11 +1515,11 @@ namespace cppcanvas SvtGraphicStroke aStroke; aMemStm >> aStroke; - // TODO: respect exceptions, like + // TODO(F1): respect exceptions, like // start/end arrows and joins not // displayable via Canvas - // TODO: use correct scale direction, stroke + // TODO(F1): use correct scale direction, stroke // width might be height or anything else const Size aSize( aStroke.getStrokeWidth(), 0 ); @@ -608,7 +1544,7 @@ namespace cppcanvas // Handle drawing layer fills else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) ) { - // TODO: Later + // TODO(F1): Later #if 0 const BYTE* pData = pAct->GetData(); if ( pData ) @@ -695,49 +1631,61 @@ namespace cppcanvas case META_POINT_ACTION: { - maActions.push_back( - MtfAction( - ActionSharedPtr( - new internal::PointAction( - rVDev.LogicToPixel( static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ), - rCanvas, - getState( rStates ) ) ), - nCurrActionIndex ) ); + const OutDevState& rState( getState( rStates ) ); + if( rState.lineColor.getLength() ) + { + maActions.push_back( + MtfAction( + ActionSharedPtr( + new internal::PointAction( + rVDev.LogicToPixel( static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ), + rCanvas, + rState ) ), + io_rCurrActionIndex ) ); + } } break; case META_PIXEL_ACTION: { - maActions.push_back( - MtfAction( - ActionSharedPtr( - new internal::PointAction( - rVDev.LogicToPixel( - static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ), - rCanvas, - getState( rStates ), - static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) ), - nCurrActionIndex ) ); + const OutDevState& rState( getState( rStates ) ); + if( rState.lineColor.getLength() ) + { + maActions.push_back( + MtfAction( + ActionSharedPtr( + new internal::PointAction( + rVDev.LogicToPixel( + static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ), + rCanvas, + rState, + static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) ), + io_rCurrActionIndex ) ); + } } break; case META_LINE_ACTION: { - maActions.push_back( - MtfAction( - ActionSharedPtr( - new internal::LineAction( - rVDev.LogicToPixel( static_cast<MetaLineAction*>(pCurrAct)->GetStartPoint() ), - rVDev.LogicToPixel( static_cast<MetaLineAction*>(pCurrAct)->GetEndPoint() ), - rCanvas, - getState( rStates ) ) ), - nCurrActionIndex ) ); + const OutDevState& rState( getState( rStates ) ); + if( rState.lineColor.getLength() ) + { + maActions.push_back( + MtfAction( + ActionSharedPtr( + new internal::LineAction( + rVDev.LogicToPixel( static_cast<MetaLineAction*>(pCurrAct)->GetStartPoint() ), + rVDev.LogicToPixel( static_cast<MetaLineAction*>(pCurrAct)->GetEndPoint() ), + rCanvas, + rState ) ), + io_rCurrActionIndex ) ); + } } break; case META_RECT_ACTION: createFillAndStroke( ::PolyPolygon( ::Polygon( rVDev.LogicToPixel( static_cast<MetaRectAction*>(pCurrAct)->GetRect() ) ) ), - rCanvas, nCurrActionIndex, + rCanvas, io_rCurrActionIndex, rStates ); break; @@ -745,7 +1693,7 @@ namespace cppcanvas createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect(), static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(), static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ) ), - rCanvas, nCurrActionIndex, + rCanvas, io_rCurrActionIndex, rStates ); break; @@ -755,7 +1703,7 @@ namespace cppcanvas createFillAndStroke( rVDev.LogicToPixel( Polygon( rRect.Center(), rRect.GetWidth() >> 1, rRect.GetHeight() >> 1 ) ), - rCanvas, nCurrActionIndex, + rCanvas, io_rCurrActionIndex, rStates ); break; } @@ -764,7 +1712,7 @@ namespace cppcanvas createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast<MetaArcAction*>(pCurrAct)->GetRect(), static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(), static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC ) ), - rCanvas, nCurrActionIndex, + rCanvas, io_rCurrActionIndex, rStates ); break; @@ -772,7 +1720,7 @@ namespace cppcanvas createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast<MetaPieAction*>(pCurrAct)->GetRect(), static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(), static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE ) ), - rCanvas, nCurrActionIndex, + rCanvas, io_rCurrActionIndex, rStates ); break; @@ -780,33 +1728,38 @@ namespace cppcanvas createFillAndStroke( rVDev.LogicToPixel( Polygon( static_cast<MetaChordAction*>(pCurrAct)->GetRect(), static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(), static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD ) ), - rCanvas, nCurrActionIndex, + rCanvas, io_rCurrActionIndex, rStates ); break; case META_POLYLINE_ACTION: { - maActions.push_back( - MtfAction( - ActionSharedPtr( - new internal::PolyPolyAction( - rVDev.LogicToPixel( static_cast<MetaPolyLineAction*>(pCurrAct)->GetPolygon() ), - rCanvas, - getState( rStates ), - internal::PolyPolyAction::strokeOnly ) ), - nCurrActionIndex ) ); + const OutDevState& rState( getState( rStates ) ); + if( rState.lineColor.getLength() || + rState.fillColor.getLength() ) + { + maActions.push_back( + MtfAction( + ActionSharedPtr( + new internal::PolyPolyAction( + rVDev.LogicToPixel( static_cast<MetaPolyLineAction*>(pCurrAct)->GetPolygon() ), + rCanvas, + rState, + internal::PolyPolyAction::strokeOnly ) ), + io_rCurrActionIndex ) ); + } } break; case META_POLYGON_ACTION: createFillAndStroke( rVDev.LogicToPixel( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon() ), - rCanvas, nCurrActionIndex, + rCanvas, io_rCurrActionIndex, rStates ); break; case META_POLYPOLYGON_ACTION: createFillAndStroke( rVDev.LogicToPixel( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon() ), - rCanvas, nCurrActionIndex, + rCanvas, io_rCurrActionIndex, rStates ); break; @@ -822,7 +1775,7 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetPoint() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; @@ -839,7 +1792,7 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetSize() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; @@ -858,7 +1811,7 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetDestSize() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; @@ -874,7 +1827,7 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetPoint() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; @@ -891,7 +1844,7 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetSize() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; @@ -910,7 +1863,7 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetDestSize() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; @@ -918,7 +1871,7 @@ namespace cppcanvas { MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct); - // TODO: masking NYI. Further members: mask color + // TODO(F2): masking NYI. Further members: mask color maActions.push_back( MtfAction( ActionSharedPtr( @@ -927,7 +1880,7 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetPoint() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; @@ -935,7 +1888,7 @@ namespace cppcanvas { MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct); - // TODO: masking NYI. Further members: mask color + // TODO(F2): masking NYI. Further members: mask color maActions.push_back( MtfAction( ActionSharedPtr( @@ -945,7 +1898,7 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetSize() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; @@ -953,7 +1906,7 @@ namespace cppcanvas { MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct); - // TODO: masking NYI. Further members: mask color + // TODO(F2): masking NYI. Further members: mask color maActions.push_back( MtfAction( ActionSharedPtr( @@ -965,95 +1918,124 @@ namespace cppcanvas rVDev.LogicToPixel( pAct->GetDestSize() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); } break; case META_GRADIENTEX_ACTION: - // TODO: use native Canvas gradients here + // TODO(F1): use native Canvas gradients here // action is ignored here, because redundant to META_GRADIENT_ACTION break; case META_WALLPAPER_ACTION: - // TODO: NYI + // TODO(F2): NYI break; case META_TRANSPARENT_ACTION: { - MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct); - - maActions.push_back( - MtfAction( - ActionSharedPtr( - new internal::PolyPolyAction( - rVDev.LogicToPixel( pAct->GetPolyPolygon() ), - rCanvas, - getState( rStates ), - pAct->GetTransparence() ) ), - nCurrActionIndex ) ); - } + const OutDevState& rState( getState( rStates ) ); + if( rState.lineColor.getLength() || + rState.fillColor.getLength() ) + { + MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct); + + maActions.push_back( + MtfAction( + ActionSharedPtr( + new internal::PolyPolyAction( + rVDev.LogicToPixel( pAct->GetPolyPolygon() ), + rCanvas, + rState, + pAct->GetTransparence() ) ), + io_rCurrActionIndex ) ); + } + } break; case META_FLOATTRANSPARENT_ACTION: - // TODO: NYI. This has to be rendered into a separate bitmap canvas - break; - - case META_TEXT_ACTION: { - MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct); + MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct); + + internal::MtfAutoPtr pMtf( + new ::GDIMetaFile( pAct->GetGDIMetaFile() ) ); + + // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls) + internal::GradientAutoPtr pGradient( + new Gradient( pAct->GetGradient() ) ); + + DBG_TESTSOLARMUTEX(); maActions.push_back( MtfAction( ActionSharedPtr( - new internal::TextAction( - rVDev.LogicToPixel(pAct->GetPoint()), - pAct->GetText(), - pAct->GetIndex(), - pAct->GetLen(), + new internal::TransparencyGroupAction( + pMtf, + pGradient, + rParms, + rVDev.LogicToPixel( pAct->GetPoint() ), + rVDev.LogicToPixel( pAct->GetSize() ), rCanvas, getState( rStates ) ) ), - nCurrActionIndex ) ); + io_rCurrActionIndex ) ); + } + break; + + case META_TEXT_ACTION: + { + MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct); + createTextWithEffectsAction( + pAct->GetPoint(), + pAct->GetText(), + pAct->GetIndex(), + pAct->GetLen() == (USHORT)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), + NULL, + rVDev, + rCanvas, + rStates, + rParms, + io_rCurrActionIndex ); } break; case META_TEXTARRAY_ACTION: { MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct); + createTextWithEffectsAction( + pAct->GetPoint(), + pAct->GetText(), + pAct->GetIndex(), + pAct->GetLen() == (USHORT)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(), + pAct->GetDXArray(), + rVDev, + rCanvas, + rStates, + rParms, + io_rCurrActionIndex ); + } + break; - uno::Sequence< double > offsets( ::comphelper::arrayToSequence<long int, double>( pAct->GetDXArray(), - pAct->GetLen() ) ); - - // convert offsets to physical - - // TODO: use correct scale direction, text advancement - // might be horizontal, vertical, or anything else - int i; - for( i=0; i<offsets.getLength(); ++i ) - { - const Size aSize( static_cast<long>( offsets[i] + .5 ), 0 ); - offsets[i] = rVDev.LogicToPixel( aSize ).Width(); - } - - maActions.push_back( - MtfAction( - ActionSharedPtr( - new internal::TextAction( - rVDev.LogicToPixel(pAct->GetPoint()), - pAct->GetText(), - pAct->GetIndex(), - pAct->GetLen(), - offsets, - rCanvas, - getState( rStates ) ) ), - nCurrActionIndex ) ); + case META_TEXTLINE_ACTION: + { + MetaTextLineAction* pAct = static_cast<MetaTextLineAction*>(pCurrAct); + createJustTextLinesAction( + pAct->GetStartPoint(), + pAct->GetWidth(), + rVDev, + rCanvas, + rStates, + rParms, + io_rCurrActionIndex ); } break; case META_TEXTRECT_ACTION: + // TODO(F2): NYI + DBG_ERROR("META_TEXTRECT not yet supported"); + break; + case META_STRETCHTEXT_ACTION: - case META_TEXTLINE_ACTION: - // TODO: NYI - DBG_ERROR("META_TEXT* not yet supported"); + // TODO(F2): NYI + DBG_ERROR("META_STRETCHTEXT not yet supported"); break; default: @@ -1065,10 +2047,13 @@ namespace cppcanvas } ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, - const GDIMetaFile& rMtf ) : + const GDIMetaFile& rMtf, + const Parameters& rParams ) : CanvasGraphicHelper( rCanvas ), maActions() { + RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" ); + OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), "ImplRenderer::ImplRenderer(): Invalid canvas" ); OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), @@ -1094,7 +2079,8 @@ namespace cppcanvas aVDev.SetMapMode( rMtf.GetPrefMapMode() ); - const Size aMtfSizePix( aVDev.LogicToPixel( rMtf.GetPrefSize(), + const Size aMtfSize( rMtf.GetPrefSize() ); + const Size aMtfSizePix( aVDev.LogicToPixel( aMtfSize, rMtf.GetPrefMapMode() ) ); const Point aEmptyPt; const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) ); @@ -1113,9 +2099,50 @@ namespace cppcanvas getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(), 1.0 / aMtfSizePix.Height() ); + ColorSharedPtr pColor( getCanvas()->createColor() ); + + // setup default text color to black + getState( aStateStack ).textColor = + getState( aStateStack ).textFillColor = + getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF ); + + // apply overrides from the Parameters struct + if( rParams.maFillColor.isValid() ) + { + getState( aStateStack ).isFillColorSet = true; + getState( aStateStack ).fillColor = pColor->getDeviceColor( rParams.maFillColor.getValue() ); + } + if( rParams.maLineColor.isValid() ) + { + getState( aStateStack ).isLineColorSet = true; + getState( aStateStack ).lineColor = pColor->getDeviceColor( rParams.maLineColor.getValue() ); + } + if( rParams.maTextColor.isValid() ) + { + getState( aStateStack ).isTextFillColorSet = true; + getState( aStateStack ).isTextLineColorSet = true; + getState( aStateStack ).textColor = + getState( aStateStack ).textFillColor = + getState( aStateStack ).textLineColor = pColor->getDeviceColor( rParams.maTextColor.getValue() ); + } + if( rParams.maFontName.isValid() || + rParams.maFontWeight.isValid() || + rParams.maFontLetterForm.isValid() || + rParams.maFontUnderline.isValid() ) + { + ::cppcanvas::internal::OutDevState& rState = getState( aStateStack ); + + rState.xFont = createFont( rState.fontTransform, + ::Font(), // default font + rCanvas, + aVDev, + rParams ); + } + + int nCurrActions(0); createActions( rCanvas, aVDev, - const_cast<GDIMetaFile&>(rMtf), // HACK: + const_cast<GDIMetaFile&>(rMtf), // HACK(Q2): // we're // changing // the @@ -1123,15 +2150,20 @@ namespace cppcanvas // action // in // createActions! - aStateStack ); + aStateStack, + rParams, + nCurrActions ); } } ImplRenderer::ImplRenderer( const CanvasSharedPtr& rCanvas, - const BitmapEx& rBmpEx ) : + const BitmapEx& rBmpEx, + const Parameters& rParams ) : CanvasGraphicHelper( rCanvas ), maActions() { + RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" ); + OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(), "ImplRenderer::ImplRenderer(): Invalid canvas" ); OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(), @@ -1176,7 +2208,8 @@ namespace cppcanvas class ActionRenderer { public: - ActionRenderer() : + ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) : + maTransformation( rTransformation ), mbRet( true ) { } @@ -1190,17 +2223,20 @@ namespace cppcanvas { // ANDing the result. We want to fail if at least // one action failed. - mbRet &= rAction.mpAction->render(); + mbRet &= rAction.mpAction->render( maTransformation ); } private: - bool mbRet; + const ::basegfx::B2DHomMatrix maTransformation; + bool mbRet; }; } bool ImplRenderer::drawSubset( int startIndex, int endIndex ) const { + RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" ); + OSL_ENSURE( startIndex<=endIndex, "ImplRenderer::draw() invalid action range" ); @@ -1214,13 +2250,21 @@ namespace cppcanvas MtfAction( ActionSharedPtr(), endIndex ), ActionIndexComparator() ) ); + ::basegfx::B2DHomMatrix aMatrix; + ::canvas::tools::getRenderStateTransform( aMatrix, maRenderState ); + // render subset of actions - return ::std::for_each( aIterBegin, aIterEnd, ActionRenderer() ).result(); + return ::std::for_each( aIterBegin, aIterEnd, ActionRenderer( aMatrix ) ).result(); } bool ImplRenderer::draw() const { - return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer() ).result(); + RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" ); + + ::basegfx::B2DHomMatrix aMatrix; + ::canvas::tools::getRenderStateTransform( aMatrix, maRenderState ); + + return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result(); } } } |