Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / drawinglayer / source / processor2d / vclprocessor2d.cxx
blobf113c723d69a30bfdf8d4731a7913af49128bb1e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <cmath>
21 #include <comphelper/string.hxx>
22 #include "vclprocessor2d.hxx"
23 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
25 #include <tools/debug.hxx>
26 #include <vcl/outdev.hxx>
27 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
29 #include <basegfx/polygon/b2dpolygontools.hxx>
30 #include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
31 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
33 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
35 #include <basegfx/polygon/b2dpolypolygontools.hxx>
36 #include <vclhelperbufferdevice.hxx>
37 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
43 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
44 #include <tools/diagnose_ex.h>
45 #include <rtl/ustrbuf.hxx>
46 #include <vcl/metric.hxx>
47 #include <drawinglayer/primitive2d/textenumsprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
49 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
50 #include <basegfx/color/bcolor.hxx>
51 #include <basegfx/matrix/b2dhommatrixtools.hxx>
52 #include <vcl/graph.hxx>
54 #include "getdigitlanguage.hxx"
56 // control support
58 #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
59 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
61 #include <basegfx/polygon/b2dpolygonclipper.hxx>
62 #include <basegfx/polygon/b2dtrapezoid.hxx>
64 using namespace com::sun::star;
66 namespace
68 sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit)
70 // use color distance, assume to do every color step
71 sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
73 if(nSteps)
75 // calc discrete length to change color each disctete unit (pixel)
76 const sal_uInt32 nDistSteps(basegfx::fround(fDelta / fDiscreteUnit));
78 nSteps = std::min(nSteps, nDistSteps);
81 // reduce quality to 3 discrete units or every 3rd color step for rendering
82 nSteps /= 2;
84 // roughly cut when too big or too small (not full quality, reduce complexity)
85 nSteps = std::min(nSteps, sal_uInt32(255));
86 nSteps = std::max(nSteps, sal_uInt32(1));
88 return nSteps;
92 namespace drawinglayer
94 namespace processor2d
96 // rendering support
98 // directdraw of text simple portion or decorated portion primitive. When decorated, all the extra
99 // information is translated to VCL parameters and set at the font.
100 // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring
101 // for VCL)
102 void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate)
104 // decompose matrix to have position and size of text
105 basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rTextCandidate.getTextTransform());
106 basegfx::B2DVector aFontScaling, aTranslate;
107 double fRotate, fShearX;
108 aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX);
109 bool bPrimitiveAccepted(false);
111 // tdf#95581: Assume tiny shears are rounding artefacts or whatever and can be ignored,
112 // especially if the effect is less than a pixel.
113 if(std::abs(aFontScaling.getY() * fShearX) < 1)
115 if(basegfx::fTools::less(aFontScaling.getX(), 0.0) && basegfx::fTools::less(aFontScaling.getY(), 0.0))
117 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
118 // be expressed as rotation by PI. Use this since the Font rendering will not
119 // apply the negative scales in any form
120 aFontScaling = basegfx::absolute(aFontScaling);
121 fRotate += F_PI;
124 if(basegfx::fTools::more(aFontScaling.getX(), 0.0) && basegfx::fTools::more(aFontScaling.getY(), 0.0))
126 // Get the VCL font (use FontHeight as FontWidth)
127 vcl::Font aFont(primitive2d::getVclFontFromFontAttribute(
128 rTextCandidate.getFontAttribute(),
129 aFontScaling.getX(),
130 aFontScaling.getY(),
131 fRotate,
132 rTextCandidate.getLocale()));
134 // set FillColor Attribute
135 const Color aFillColor( rTextCandidate.getTextFillColor() );
136 if( aFillColor != COL_TRANSPARENT )
138 aFont.SetFillColor(aFillColor);
139 aFont.SetTransparent(false);
142 // Don't draw fonts without height
143 if( aFont.GetFontHeight() <= 0 )
144 return;
146 // handle additional font attributes
147 const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP =
148 dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>( &rTextCandidate );
150 if( pTCPP != nullptr )
153 // set the color of text decorations
154 const basegfx::BColor aTextlineColor = maBColorModifierStack.getModifiedColor(pTCPP->getTextlineColor());
155 mpOutputDevice->SetTextLineColor( Color(aTextlineColor) );
157 // set Overline attribute
158 const FontLineStyle eFontOverline(primitive2d::mapTextLineToFontLineStyle( pTCPP->getFontOverline() ));
159 if( eFontOverline != LINESTYLE_NONE )
161 aFont.SetOverline( eFontOverline );
162 const basegfx::BColor aOverlineColor = maBColorModifierStack.getModifiedColor(pTCPP->getOverlineColor());
163 mpOutputDevice->SetOverlineColor( Color(aOverlineColor) );
164 if( pTCPP->getWordLineMode() )
165 aFont.SetWordLineMode( true );
168 // set Underline attribute
169 const FontLineStyle eFontLineStyle(primitive2d::mapTextLineToFontLineStyle( pTCPP->getFontUnderline() ));
170 if( eFontLineStyle != LINESTYLE_NONE )
172 aFont.SetUnderline( eFontLineStyle );
173 if( pTCPP->getWordLineMode() )
174 aFont.SetWordLineMode( true );
177 // set Strikeout attribute
178 const FontStrikeout eFontStrikeout(primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP->getTextStrikeout()));
180 if( eFontStrikeout != STRIKEOUT_NONE )
181 aFont.SetStrikeout( eFontStrikeout );
184 // set EmphasisMark attribute
185 FontEmphasisMark eFontEmphasisMark = FontEmphasisMark::NONE;
186 switch( pTCPP->getTextEmphasisMark() )
188 default:
189 SAL_WARN("drawinglayer", "Unknown EmphasisMark style " << pTCPP->getTextEmphasisMark() );
190 SAL_FALLTHROUGH;
191 case primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE: eFontEmphasisMark = FontEmphasisMark::NONE; break;
192 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT: eFontEmphasisMark = FontEmphasisMark::Dot; break;
193 case primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE: eFontEmphasisMark = FontEmphasisMark::Circle; break;
194 case primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC: eFontEmphasisMark = FontEmphasisMark::Disc; break;
195 case primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT: eFontEmphasisMark = FontEmphasisMark::Accent; break;
198 if( eFontEmphasisMark != FontEmphasisMark::NONE )
200 DBG_ASSERT( (pTCPP->getEmphasisMarkAbove() != pTCPP->getEmphasisMarkBelow()),
201 "DrawingLayer: Bad EmphasisMark position!" );
202 if( pTCPP->getEmphasisMarkAbove() )
203 eFontEmphasisMark |= FontEmphasisMark::PosAbove;
204 else
205 eFontEmphasisMark |= FontEmphasisMark::PosBelow;
206 aFont.SetEmphasisMark( eFontEmphasisMark );
209 // set Relief attribute
210 FontRelief eFontRelief = RELIEF_NONE;
211 switch( pTCPP->getTextRelief() )
213 default:
214 SAL_WARN( "drawinglayer", "Unknown Relief style " << pTCPP->getTextRelief() );
215 SAL_FALLTHROUGH;
216 case primitive2d::TEXT_RELIEF_NONE: eFontRelief = RELIEF_NONE; break;
217 case primitive2d::TEXT_RELIEF_EMBOSSED: eFontRelief = RELIEF_EMBOSSED; break;
218 case primitive2d::TEXT_RELIEF_ENGRAVED: eFontRelief = RELIEF_ENGRAVED; break;
221 if( eFontRelief != RELIEF_NONE )
222 aFont.SetRelief( eFontRelief );
224 // set Shadow attribute
225 if( pTCPP->getShadow() )
226 aFont.SetShadow( true );
229 // create transformed integer DXArray in view coordinate system
230 ::std::vector< long > aTransformedDXArray;
232 if(rTextCandidate.getDXArray().size())
234 aTransformedDXArray.reserve(rTextCandidate.getDXArray().size());
235 const basegfx::B2DVector aPixelVector(maCurrentTransformation * basegfx::B2DVector(1.0, 0.0));
236 const double fPixelVectorFactor(aPixelVector.getLength());
238 for(::std::vector< double >::const_iterator aStart(rTextCandidate.getDXArray().begin());
239 aStart != rTextCandidate.getDXArray().end(); ++aStart)
241 aTransformedDXArray.push_back(basegfx::fround((*aStart) * fPixelVectorFactor));
245 // set parameters and paint text snippet
246 const basegfx::BColor aRGBFontColor(maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
247 const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0));
248 const Point aStartPoint(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY()));
249 const ComplexTextLayoutMode nOldLayoutMode(mpOutputDevice->GetLayoutMode());
251 if(rTextCandidate.getFontAttribute().getRTL())
253 ComplexTextLayoutMode nRTLLayoutMode(nOldLayoutMode & ~(TEXT_LAYOUT_COMPLEX_DISABLED|TEXT_LAYOUT_BIDI_STRONG));
254 nRTLLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT;
255 mpOutputDevice->SetLayoutMode(nRTLLayoutMode);
258 mpOutputDevice->SetFont(aFont);
259 mpOutputDevice->SetTextColor(Color(aRGBFontColor));
261 OUString aText( rTextCandidate.getText() );
262 sal_Int32 nPos = rTextCandidate.getTextPosition();
263 sal_Int32 nLen = rTextCandidate.getTextLength();
265 long* pDXArray = aTransformedDXArray.size() ? &(aTransformedDXArray[0]) : nullptr ;
267 if ( rTextCandidate.isFilled() )
269 basegfx::B2DVector aOldFontScaling, aOldTranslate;
270 double fOldRotate, fOldShearX;
271 rTextCandidate.getTextTransform().decompose(aOldFontScaling, aOldTranslate, fOldRotate, fOldShearX);
273 long nWidthToFill = static_cast<long>(rTextCandidate.getWidthToFill( ) * aFontScaling.getX() / aOldFontScaling.getX());
275 long nWidth = mpOutputDevice->GetTextArray( rTextCandidate.getText(), pDXArray, 0, 1 );
276 long nChars = 2;
277 if ( nWidth )
278 nChars = nWidthToFill / nWidth;
280 OUStringBuffer aFilled;
281 comphelper::string::padToLength(aFilled, nChars, aText[0]);
282 aText = aFilled.makeStringAndClear();
283 nPos = 0;
284 nLen = nChars;
287 if(!aTransformedDXArray.empty())
289 mpOutputDevice->DrawTextArray(
290 aStartPoint,
291 aText,
292 pDXArray,
293 nPos,
294 nLen);
296 else
298 mpOutputDevice->DrawText(
299 aStartPoint,
300 aText,
301 nPos,
302 nLen);
305 if(rTextCandidate.getFontAttribute().getRTL())
307 mpOutputDevice->SetLayoutMode(nOldLayoutMode);
310 bPrimitiveAccepted = true;
314 if(!bPrimitiveAccepted)
316 // let break down
317 process(rTextCandidate.get2DDecomposition(getViewInformation2D()));
321 // direct draw of hairline
322 void VclProcessor2D::RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate, bool bPixelBased)
324 const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
325 mpOutputDevice->SetLineColor(Color(aHairlineColor));
326 mpOutputDevice->SetFillColor();
328 basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon());
329 aLocalPolygon.transform(maCurrentTransformation);
331 static bool bCheckTrapezoidDecomposition(false);
332 static bool bShowOutlinesThere(false);
333 if(bCheckTrapezoidDecomposition)
335 // clip against discrete ViewPort
336 const basegfx::B2DRange& rDiscreteViewport = getViewInformation2D().getDiscreteViewport();
337 basegfx::B2DPolyPolygon aLocalPolyPolygon(basegfx::tools::clipPolygonOnRange(
338 aLocalPolygon, rDiscreteViewport, true, false));
340 if(aLocalPolyPolygon.count())
342 // subdivide
343 aLocalPolyPolygon = basegfx::tools::adaptiveSubdivideByDistance(
344 aLocalPolyPolygon, 0.5);
346 // trapezoidize
347 static double fLineWidth(2.0);
348 basegfx::B2DTrapezoidVector aB2DTrapezoidVector;
349 basegfx::tools::createLineTrapezoidFromB2DPolyPolygon(aB2DTrapezoidVector, aLocalPolyPolygon, fLineWidth);
351 const sal_uInt32 nCount(aB2DTrapezoidVector.size());
353 if(nCount)
355 basegfx::BColor aInvPolygonColor(aHairlineColor);
356 aInvPolygonColor.invert();
358 for(sal_uInt32 a(0); a < nCount; a++)
360 const basegfx::B2DPolygon aTempPolygon(aB2DTrapezoidVector[a].getB2DPolygon());
362 if(bShowOutlinesThere)
364 mpOutputDevice->SetFillColor(Color(aHairlineColor));
365 mpOutputDevice->SetLineColor();
368 mpOutputDevice->DrawPolygon(aTempPolygon);
370 if(bShowOutlinesThere)
372 mpOutputDevice->SetFillColor();
373 mpOutputDevice->SetLineColor(Color(aInvPolygonColor));
374 mpOutputDevice->DrawPolyLine(aTempPolygon, 0.0);
380 else
382 if(bPixelBased && getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete())
384 // #i98289#
385 // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
386 // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since
387 // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This
388 // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
389 aLocalPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon);
392 mpOutputDevice->DrawPolyLine(aLocalPolygon, 0.0);
396 // direct draw of transformed BitmapEx primitive
397 void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
399 BitmapEx aBitmapEx(rBitmapCandidate.getBitmapEx());
400 const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rBitmapCandidate.getTransform());
402 if(maBColorModifierStack.count())
404 aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
406 if(aBitmapEx.IsEmpty())
408 // color gets completely replaced, get it
409 const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor()));
410 basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon());
411 aPolygon.transform(aLocalTransform);
413 mpOutputDevice->SetFillColor(Color(aModifiedColor));
414 mpOutputDevice->SetLineColor();
415 mpOutputDevice->DrawPolygon(aPolygon);
417 return;
421 // #122923# do no longer add Alpha channel here; the right place to do this is when really
422 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
424 // draw using OutputDevice'sDrawTransformedBitmapEx
425 mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmapEx);
428 void VclProcessor2D::RenderFillGraphicPrimitive2D(const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate)
430 const attribute::FillGraphicAttribute& rFillGraphicAttribute(rFillBitmapCandidate.getFillGraphic());
431 bool bPrimitiveAccepted(false);
432 static bool bTryTilingDirect = true;
434 // #121194# when tiling is used and content is bitmap-based, do direct tiling in the
435 // renderer on pixel base to ensure tight fitting. Do not do this when
436 // the fill is rotated or sheared.
438 // override static bool (for debug) and tiling is active
439 if(bTryTilingDirect && rFillGraphicAttribute.getTiling())
441 // content is bitmap(ex)
443 // for SVG support, force decomposition when SVG is present. This will lead to use
444 // the primitive representation of the svg directly.
446 // when graphic is animated, force decomposition to use the correct graphic, else
447 // fill style will not be animated
448 if(GRAPHIC_BITMAP == rFillGraphicAttribute.getGraphic().GetType()
449 && !rFillGraphicAttribute.getGraphic().getSvgData().get()
450 && !rFillGraphicAttribute.getGraphic().IsAnimated())
452 // decompose matrix to check for shear, rotate and mirroring
453 basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rFillBitmapCandidate.getTransformation());
454 basegfx::B2DVector aScale, aTranslate;
455 double fRotate, fShearX;
456 aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
458 // when nopt rotated/sheared
459 if(basegfx::fTools::equalZero(fRotate) && basegfx::fTools::equalZero(fShearX))
461 // no shear or rotate, draw direct in pixel coordinates
462 bPrimitiveAccepted = true;
464 // transform object range to device coordinates (pixels). Use
465 // the device transformation for better accuracy
466 basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale);
467 aObjectRange.transform(mpOutputDevice->GetViewTransformation());
469 // extract discrete size of object
470 const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth()));
471 const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight()));
473 // only do something when object has a size in discrete units
474 if(nOWidth > 0 && nOHeight > 0)
476 // transform graphic range to device coordinates (pixels). Use
477 // the device transformation for better accuracy
478 basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange());
479 aGraphicRange.transform(mpOutputDevice->GetViewTransformation() * aLocalTransform);
481 // extract discrete size of graphic
482 // caution: when getting to zero, nothing would be painted; thus, do not allow this
483 const sal_Int32 nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth())));
484 const sal_Int32 nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight())));
486 // only do something when bitmap fill has a size in discrete units
487 if(nBWidth > 0 && nBHeight > 0)
489 // nBWidth, nBHeight is the pixel size of the neede bitmap. To not need to scale it
490 // in vcl many times, create a size-optimized version
491 const Size aNeededBitmapSizePixel(nBWidth, nBHeight);
492 BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
493 static bool bEnablePreScaling(true);
494 const bool bPreScaled(bEnablePreScaling && nBWidth * nBHeight < (250 * 250));
496 // ... but only up to a maximum size, else it gets too expensive
497 if(bPreScaled)
499 // if color depth is below 24bit, expand before scaling for better quality.
500 // This is even needed for low colors, else the scale will produce
501 // a bitmap in gray or Black/White (!)
502 if(aBitmapEx.GetBitCount() < 24)
504 aBitmapEx.Convert(BMP_CONVERSION_24BIT);
507 aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate);
510 bool bPainted(false);
512 if(maBColorModifierStack.count())
514 // when color modifier, apply to bitmap
515 aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
517 // impModifyBitmapEx uses empty bitmap as sign to return that
518 // the content will be completely replaced to mono color, use shortcut
519 if(aBitmapEx.IsEmpty())
521 // color gets completely replaced, get it
522 const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor()));
523 basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon());
524 aPolygon.transform(aLocalTransform);
526 mpOutputDevice->SetFillColor(Color(aModifiedColor));
527 mpOutputDevice->SetLineColor();
528 mpOutputDevice->DrawPolygon(aPolygon);
530 bPainted = true;
534 if(!bPainted)
536 sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX()));
537 sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY()));
538 const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX()));
539 const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY()));
540 sal_Int32 nPosX(0);
541 sal_Int32 nPosY(0);
543 if(nBLeft > nOLeft)
545 const sal_Int32 nDiff((nBLeft / nBWidth) + 1);
547 nPosX -= nDiff;
548 nBLeft -= nDiff * nBWidth;
551 if(nBLeft + nBWidth <= nOLeft)
553 const sal_Int32 nDiff(-nBLeft / nBWidth);
555 nPosX += nDiff;
556 nBLeft += nDiff * nBWidth;
559 if(nBTop > nOTop)
561 const sal_Int32 nDiff((nBTop / nBHeight) + 1);
563 nPosY -= nDiff;
564 nBTop -= nDiff * nBHeight;
567 if(nBTop + nBHeight <= nOTop)
569 const sal_Int32 nDiff(-nBTop / nBHeight);
571 nPosY += nDiff;
572 nBTop += nDiff * nBHeight;
575 // prepare OutDev
576 const Point aEmptyPoint(0, 0);
577 const Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel());
578 const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
579 mpOutputDevice->EnableMapMode(false);
581 // check if offset is used
582 const sal_Int32 nOffsetX(basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth));
584 if(nOffsetX)
586 // offset in X, so iterate over Y first and draw lines
587 for(sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; nYPos += nBHeight, nPosY++)
589 for(sal_Int32 nXPos((nPosY % 2) ? nBLeft - nBWidth + nOffsetX : nBLeft);
590 nXPos < nOLeft + nOWidth; nXPos += nBWidth)
592 const Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
594 if(aOutRectPixel.IsOver(aVisiblePixel))
596 if(bPreScaled)
598 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
600 else
602 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, aBitmapEx);
608 else
610 // check if offset is used
611 const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight));
613 // possible offset in Y, so iterate over X first and draw columns
614 for(sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; nXPos += nBWidth, nPosX++)
616 for(sal_Int32 nYPos((nPosX % 2) ? nBTop - nBHeight + nOffsetY : nBTop);
617 nYPos < nOTop + nOHeight; nYPos += nBHeight)
619 const Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
621 if(aOutRectPixel.IsOver(aVisiblePixel))
623 if(bPreScaled)
625 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
627 else
629 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, aBitmapEx);
636 // restore OutDev
637 mpOutputDevice->EnableMapMode(bWasEnabled);
645 if(!bPrimitiveAccepted)
647 // do not accept, use decomposition
648 process(rFillBitmapCandidate.get2DDecomposition(getViewInformation2D()));
652 // direct draw of Graphic
653 void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D& rPolygonCandidate)
655 bool bDone(false);
656 const basegfx::B2DPolyPolygon& rPolyPolygon = rPolygonCandidate.getB2DPolyPolygon();
658 // #121194# Todo: check if this works
659 if(!rPolyPolygon.count())
661 // empty polyPolygon, done
662 bDone = true;
664 else
666 const attribute::FillGraphicAttribute& rFillGraphicAttribute = rPolygonCandidate.getFillGraphic();
668 // try to catch cases where the graphic will be color-modified to a single
669 // color (e.g. shadow)
670 switch(rFillGraphicAttribute.getGraphic().GetType())
672 case GRAPHIC_GDIMETAFILE:
674 // metafiles are potentially transparent, cannot optimize, not done
675 break;
677 case GRAPHIC_BITMAP:
679 if(!rFillGraphicAttribute.getGraphic().IsTransparent() && !rFillGraphicAttribute.getGraphic().IsAlpha())
681 // bitmap is not transparent and has no alpha
682 const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count());
684 if(nBColorModifierStackCount)
686 const basegfx::BColorModifierSharedPtr& rTopmostModifier = maBColorModifierStack.getBColorModifier(nBColorModifierStackCount - 1);
687 const basegfx::BColorModifier_replace* pReplacer = dynamic_cast< const basegfx::BColorModifier_replace* >(rTopmostModifier.get());
689 if(pReplacer)
691 // the bitmap fill is in unified color, so we can replace it with
692 // a single polygon fill. The form of the fill depends on tiling
693 if(rFillGraphicAttribute.getTiling())
695 // with tiling, fill the whole tools::PolyPolygon with the modifier color
696 basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
698 aLocalPolyPolygon.transform(maCurrentTransformation);
699 mpOutputDevice->SetLineColor();
700 mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
701 mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
703 else
705 // without tiling, only the area common to the bitmap tile and the
706 // tools::PolyPolygon is filled. Create the bitmap tile area in object
707 // coordinates. For this, the object transformation needs to be created
708 // from the already scaled PolyPolygon. The tile area in object
709 // coordinates wil always be non-rotated, so it's not necessary to
710 // work with a polygon here
711 basegfx::B2DRange aTileRange(rFillGraphicAttribute.getGraphicRange());
712 const basegfx::B2DRange aPolyPolygonRange(rPolyPolygon.getB2DRange());
713 const basegfx::B2DHomMatrix aNewObjectTransform(
714 basegfx::tools::createScaleTranslateB2DHomMatrix(
715 aPolyPolygonRange.getRange(),
716 aPolyPolygonRange.getMinimum()));
718 aTileRange.transform(aNewObjectTransform);
720 // now clip the object polyPolygon against the tile range
721 // to get the common area
722 basegfx::B2DPolyPolygon aTarget = basegfx::tools::clipPolyPolygonOnRange(
723 rPolyPolygon,
724 aTileRange,
725 true,
726 false);
728 if(aTarget.count())
730 aTarget.transform(maCurrentTransformation);
731 mpOutputDevice->SetLineColor();
732 mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
733 mpOutputDevice->DrawPolyPolygon(aTarget);
737 // simplified output executed, we are done
738 bDone = true;
742 break;
744 default: //GRAPHIC_NONE, GRAPHIC_DEFAULT
746 // empty graphic, we are done
747 bDone = true;
748 break;
753 if(!bDone)
755 // use default decomposition
756 process(rPolygonCandidate.get2DDecomposition(getViewInformation2D()));
760 // direct draw of MetaFile
761 void VclProcessor2D::RenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D& rMetaCandidate)
763 // decompose matrix to check for shear, rotate and mirroring
764 basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rMetaCandidate.getTransform());
765 basegfx::B2DVector aScale, aTranslate;
766 double fRotate, fShearX;
767 aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
769 if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
771 // #i102175# handle special case: If scale is negative in (x,y) (3rd quadrant), it can
772 // be expressed as rotation by PI. This needs to be done for Metafiles since
773 // these can be rotated, but not really mirrored
774 aScale = basegfx::absolute(aScale);
775 fRotate += F_PI;
778 // get BoundRect
779 basegfx::B2DRange aOutlineRange(rMetaCandidate.getB2DRange(getViewInformation2D()));
780 aOutlineRange.transform(maCurrentTransformation);
782 // Due to the integer MapModes used from VCL aind inside MetaFiles errors of up to three
783 // pixels in size may happen. As long as there is no better way (e.g. convert the MetaFile
784 // to primitives) it is necessary to reduce maximum pixel size by 1 in X and Y and to use
785 // the inner pixel bounds accordingly (ceil resp. floor). This will also be done for logic
786 // units e.g. when creating a new MetaFile, but since much huger value ranges are used
787 // there typically will be okay for this compromise.
788 Rectangle aDestRectView(
789 // !!CAUTION!! Here, ceil and floor are exchanged BY PURPOSE, do NOT copy when
790 // looking for a standard conversion to rectangle (!)
791 (sal_Int32)ceil(aOutlineRange.getMinX()), (sal_Int32)ceil(aOutlineRange.getMinY()),
792 (sal_Int32)floor(aOutlineRange.getMaxX()), (sal_Int32)floor(aOutlineRange.getMaxY()));
794 // get metafile (copy it)
795 GDIMetaFile aMetaFile;
797 if(maBColorModifierStack.count())
799 const basegfx::BColor aRGBBaseColor(0, 0, 0);
800 const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(aRGBBaseColor));
801 aMetaFile = rMetaCandidate.getMetaFile().GetMonochromeMtf(Color(aRGBColor));
803 else
805 aMetaFile = rMetaCandidate.getMetaFile();
808 // rotation
809 if(!basegfx::fTools::equalZero(fRotate))
811 // #i103530#
812 // MetaFile::Rotate has no input parameter check, so the parameter needs to be
813 // well-aligned to the old range [0..3600] 10th degrees with inverse orientation
814 sal_Int16 nRotation((sal_Int16)((fRotate / F_PI180) * -10.0));
816 while(nRotation < 0)
817 nRotation += 3600;
819 while(nRotation >= 3600)
820 nRotation -= 3600;
822 aMetaFile.Rotate(nRotation);
825 // Prepare target output size
826 Size aDestSize(aDestRectView.GetSize());
828 if(aDestSize.getWidth() > 0 && aDestSize.getHeight() > 0)
830 // Get preferred Metafile output size. When it's very equal to the output size, it's probably
831 // a rounding error somewhere, so correct it to get a 1:1 output without single pixel scalings
832 // of the Metafile (esp. for contaned Bitmaps, e.g 3D charts)
833 const Size aPrefSize(mpOutputDevice->LogicToPixel(aMetaFile.GetPrefSize(), aMetaFile.GetPrefMapMode()));
835 if(aPrefSize.getWidth() && (aPrefSize.getWidth() - 1 == aDestSize.getWidth() || aPrefSize.getWidth() + 1 == aDestSize.getWidth()))
837 aDestSize.setWidth(aPrefSize.getWidth());
840 if(aPrefSize.getHeight() && (aPrefSize.getHeight() - 1 == aDestSize.getHeight() || aPrefSize.getHeight() + 1 == aDestSize.getHeight()))
842 aDestSize.setHeight(aPrefSize.getHeight());
845 // paint it
846 aMetaFile.WindStart();
847 aMetaFile.Play(mpOutputDevice, aDestRectView.TopLeft(), aDestSize);
851 // mask group. Force output to VDev and create mask from given mask
852 void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate)
854 if(!rMaskCandidate.getChildren().empty())
856 basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
858 if(aMask.count())
860 aMask.transform(maCurrentTransformation);
861 const basegfx::B2DRange aRange(basegfx::tools::getRange(aMask));
862 impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
864 if(aBufferDevice.isVisible())
866 // remember last OutDev and set to content
867 OutputDevice* pLastOutputDevice = mpOutputDevice;
868 mpOutputDevice = &aBufferDevice.getContent();
870 // paint to it
871 process(rMaskCandidate.getChildren());
873 // back to old OutDev
874 mpOutputDevice = pLastOutputDevice;
876 // draw mask
877 if(getOptionsDrawinglayer().IsAntiAliasing())
879 // with AA, use 8bit AlphaMask to get nice borders
880 VirtualDevice& rTransparence = aBufferDevice.getTransparence();
881 rTransparence.SetLineColor();
882 rTransparence.SetFillColor(COL_BLACK);
883 rTransparence.DrawPolyPolygon(aMask);
885 // dump buffer to outdev
886 aBufferDevice.paint();
888 else
890 // No AA, use 1bit mask
891 VirtualDevice& rMask = aBufferDevice.getMask();
892 rMask.SetLineColor();
893 rMask.SetFillColor(COL_BLACK);
894 rMask.DrawPolyPolygon(aMask);
896 // dump buffer to outdev
897 aBufferDevice.paint();
904 // modified color group. Force output to unified color.
905 void VclProcessor2D::RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
907 if(!rModifiedCandidate.getChildren().empty())
909 maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
910 process(rModifiedCandidate.getChildren());
911 maBColorModifierStack.pop();
915 // unified sub-transparence. Draw to VDev first.
916 void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate)
918 static bool bForceToDecomposition(false);
920 if(!rTransCandidate.getChildren().empty())
922 if(bForceToDecomposition)
924 // use decomposition
925 process(rTransCandidate.get2DDecomposition(getViewInformation2D()));
927 else
929 if(0.0 == rTransCandidate.getTransparence())
931 // no transparence used, so just use the content
932 process(rTransCandidate.getChildren());
934 else if(rTransCandidate.getTransparence() > 0.0 && rTransCandidate.getTransparence() < 1.0)
936 // transparence is in visible range
937 basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
938 aRange.transform(maCurrentTransformation);
939 impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
941 if(aBufferDevice.isVisible())
943 // remember last OutDev and set to content
944 OutputDevice* pLastOutputDevice = mpOutputDevice;
945 mpOutputDevice = &aBufferDevice.getContent();
947 // paint content to it
948 process(rTransCandidate.getChildren());
950 // back to old OutDev
951 mpOutputDevice = pLastOutputDevice;
953 // dump buffer to outdev using given transparence
954 aBufferDevice.paint(rTransCandidate.getTransparence());
961 // sub-transparence group. Draw to VDev first.
962 void VclProcessor2D::RenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransCandidate)
964 if(!rTransCandidate.getChildren().empty())
966 basegfx::B2DRange aRange(rTransCandidate.getChildren().getB2DRange(getViewInformation2D()));
967 aRange.transform(maCurrentTransformation);
968 impBufferDevice aBufferDevice(*mpOutputDevice, aRange);
970 if(aBufferDevice.isVisible())
972 // remember last OutDev and set to content
973 OutputDevice* pLastOutputDevice = mpOutputDevice;
974 mpOutputDevice = &aBufferDevice.getContent();
976 // paint content to it
977 process(rTransCandidate.getChildren());
979 // set to mask
980 mpOutputDevice = &aBufferDevice.getTransparence();
982 // when painting transparence masks, reset the color stack
983 basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack);
984 maBColorModifierStack = basegfx::BColorModifierStack();
986 // paint mask to it (always with transparence intensities, evtl. with AA)
987 process(rTransCandidate.getTransparence());
989 // back to old color stack
990 maBColorModifierStack = aLastBColorModifierStack;
992 // back to old OutDev
993 mpOutputDevice = pLastOutputDevice;
995 // dump buffer to outdev
996 aBufferDevice.paint();
1001 // transform group.
1002 void VclProcessor2D::RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D& rTransformCandidate)
1004 // remember current transformation and ViewInformation
1005 const basegfx::B2DHomMatrix aLastCurrentTransformation(maCurrentTransformation);
1006 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
1008 // create new transformations for CurrentTransformation
1009 // and for local ViewInformation2D
1010 maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation();
1011 const geometry::ViewInformation2D aViewInformation2D(
1012 getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
1013 getViewInformation2D().getViewTransformation(),
1014 getViewInformation2D().getViewport(),
1015 getViewInformation2D().getVisualizedPage(),
1016 getViewInformation2D().getViewTime(),
1017 getViewInformation2D().getExtendedInformationSequence());
1018 updateViewInformation(aViewInformation2D);
1020 // process content
1021 process(rTransformCandidate.getChildren());
1023 // restore transformations
1024 maCurrentTransformation = aLastCurrentTransformation;
1025 updateViewInformation(aLastViewInformation2D);
1028 // new XDrawPage for ViewInformation2D
1029 void VclProcessor2D::RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate)
1031 // remember current transformation and ViewInformation
1032 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
1034 // create new local ViewInformation2D
1035 const geometry::ViewInformation2D aViewInformation2D(
1036 getViewInformation2D().getObjectTransformation(),
1037 getViewInformation2D().getViewTransformation(),
1038 getViewInformation2D().getViewport(),
1039 rPagePreviewCandidate.getXDrawPage(),
1040 getViewInformation2D().getViewTime(),
1041 getViewInformation2D().getExtendedInformationSequence());
1042 updateViewInformation(aViewInformation2D);
1044 // process decomposed content
1045 process(rPagePreviewCandidate.get2DDecomposition(getViewInformation2D()));
1047 // restore transformations
1048 updateViewInformation(aLastViewInformation2D);
1051 // marker
1052 void VclProcessor2D::RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D& rMarkArrayCandidate)
1054 static bool bCheckCompleteMarkerDecompose(false);
1055 if(bCheckCompleteMarkerDecompose)
1057 process(rMarkArrayCandidate.get2DDecomposition(getViewInformation2D()));
1058 return;
1061 // get data
1062 const std::vector< basegfx::B2DPoint >& rPositions = rMarkArrayCandidate.getPositions();
1063 const sal_uInt32 nCount(rPositions.size());
1065 if(nCount && !rMarkArrayCandidate.getMarker().IsEmpty())
1067 // get pixel size
1068 const BitmapEx& rMarker(rMarkArrayCandidate.getMarker());
1069 const Size aBitmapSize(rMarker.GetSizePixel());
1071 if(aBitmapSize.Width() && aBitmapSize.Height())
1073 // get discrete half size
1074 const basegfx::B2DVector aDiscreteHalfSize(
1075 (aBitmapSize.getWidth() - 1.0) * 0.5,
1076 (aBitmapSize.getHeight() - 1.0) * 0.5);
1077 const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
1079 // do not forget evtl. moved origin in target device MapMode when
1080 // switching it off; it would be missing and lead to wrong positions.
1081 // All his could be done using logic sizes and coordinates, too, but
1082 // we want a 1:1 bitmap rendering here, so it's more safe and faster
1083 // to work with switching off MapMode usage completely.
1084 const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin());
1086 mpOutputDevice->EnableMapMode(false);
1088 for(std::vector< basegfx::B2DPoint >::const_iterator aIter(rPositions.begin()); aIter != rPositions.end(); ++aIter)
1090 const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * (*aIter)) - aDiscreteHalfSize);
1091 const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()), basegfx::fround(aDiscreteTopLeft.getY()));
1093 mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker);
1096 mpOutputDevice->EnableMapMode(bWasEnabled);
1101 // point
1102 void VclProcessor2D::RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
1104 const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions();
1105 const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
1106 const Color aVCLColor(aRGBColor);
1108 for(std::vector< basegfx::B2DPoint >::const_iterator aIter(rPositions.begin()); aIter != rPositions.end(); ++aIter)
1110 const basegfx::B2DPoint aViewPosition(maCurrentTransformation * (*aIter));
1111 const Point aPos(basegfx::fround(aViewPosition.getX()), basegfx::fround(aViewPosition.getY()));
1113 mpOutputDevice->DrawPixel(aPos, aVCLColor);
1117 void VclProcessor2D::RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
1119 // #i101491# method restructured to clearly use the DrawPolyLine
1120 // calls starting from a defined line width
1121 const attribute::LineAttribute& rLineAttribute = rPolygonStrokeCandidate.getLineAttribute();
1122 const double fLineWidth(rLineAttribute.getWidth());
1123 bool bDone(false);
1125 if(basegfx::fTools::more(fLineWidth, 0.0))
1127 const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(fLineWidth, 0.0));
1128 const double fDiscreteLineWidth(aDiscreteUnit.getLength());
1129 const attribute::StrokeAttribute& rStrokeAttribute = rPolygonStrokeCandidate.getStrokeAttribute();
1130 const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor()));
1131 basegfx::B2DPolyPolygon aHairlinePolyPolygon;
1133 mpOutputDevice->SetLineColor(Color(aHairlineColor));
1134 mpOutputDevice->SetFillColor();
1136 if(0.0 == rStrokeAttribute.getFullDotDashLen())
1138 // no line dashing, just copy
1139 aHairlinePolyPolygon.append(rPolygonStrokeCandidate.getB2DPolygon());
1141 else
1143 // else apply LineStyle
1144 basegfx::tools::applyLineDashing(rPolygonStrokeCandidate.getB2DPolygon(),
1145 rStrokeAttribute.getDotDashArray(),
1146 &aHairlinePolyPolygon, nullptr, rStrokeAttribute.getFullDotDashLen());
1149 const sal_uInt32 nCount(aHairlinePolyPolygon.count());
1151 if(nCount)
1153 const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing());
1154 aHairlinePolyPolygon.transform(maCurrentTransformation);
1156 if(bAntiAliased)
1158 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0))
1160 // line in range ]0.0 .. 1.0[
1161 // paint as simple hairline
1162 for(sal_uInt32 a(0); a < nCount; a++)
1164 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
1167 bDone = true;
1169 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0))
1171 // line in range [1.0 .. 2.0[
1172 // paint as 2x2 with dynamic line distance
1173 basegfx::B2DHomMatrix aMat;
1174 const double fDistance(fDiscreteLineWidth - 1.0);
1175 const double fHalfDistance(fDistance * 0.5);
1177 for(sal_uInt32 a(0); a < nCount; a++)
1179 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1181 aMat.set(0, 2, -fHalfDistance);
1182 aMat.set(1, 2, -fHalfDistance);
1183 aCandidate.transform(aMat);
1184 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1186 aMat.set(0, 2, fDistance);
1187 aMat.set(1, 2, 0.0);
1188 aCandidate.transform(aMat);
1189 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1191 aMat.set(0, 2, 0.0);
1192 aMat.set(1, 2, fDistance);
1193 aCandidate.transform(aMat);
1194 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1196 aMat.set(0, 2, -fDistance);
1197 aMat.set(1, 2, 0.0);
1198 aCandidate.transform(aMat);
1199 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1202 bDone = true;
1204 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 3.0))
1206 // line in range [2.0 .. 3.0]
1207 // paint as cross in a 3x3 with dynamic line distance
1208 basegfx::B2DHomMatrix aMat;
1209 const double fDistance((fDiscreteLineWidth - 1.0) * 0.5);
1211 for(sal_uInt32 a(0); a < nCount; a++)
1213 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1215 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1217 aMat.set(0, 2, -fDistance);
1218 aMat.set(1, 2, 0.0);
1219 aCandidate.transform(aMat);
1220 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1222 aMat.set(0, 2, fDistance);
1223 aMat.set(1, 2, -fDistance);
1224 aCandidate.transform(aMat);
1225 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1227 aMat.set(0, 2, fDistance);
1228 aMat.set(1, 2, fDistance);
1229 aCandidate.transform(aMat);
1230 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1232 aMat.set(0, 2, -fDistance);
1233 aMat.set(1, 2, fDistance);
1234 aCandidate.transform(aMat);
1235 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1238 bDone = true;
1240 else
1242 // #i101491# line width above 3.0
1245 else
1247 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.5))
1249 // line width below 1.5, draw the basic hairline polygon
1250 for(sal_uInt32 a(0); a < nCount; a++)
1252 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
1255 bDone = true;
1257 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.5))
1259 // line width is in range ]1.5 .. 2.5], use four hairlines
1260 // drawn in a square
1261 for(sal_uInt32 a(0); a < nCount; a++)
1263 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1264 basegfx::B2DHomMatrix aMat;
1266 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1268 aMat.set(0, 2, 1.0);
1269 aMat.set(1, 2, 0.0);
1270 aCandidate.transform(aMat);
1272 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1274 aMat.set(0, 2, 0.0);
1275 aMat.set(1, 2, 1.0);
1276 aCandidate.transform(aMat);
1278 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1280 aMat.set(0, 2, -1.0);
1281 aMat.set(1, 2, 0.0);
1282 aCandidate.transform(aMat);
1284 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1287 bDone = true;
1289 else
1291 // #i101491# line width is above 2.5
1295 if(!bDone && rPolygonStrokeCandidate.getB2DPolygon().count() > 1000)
1297 // #i101491# If the polygon complexity uses more than a given amount, do
1298 // use OuputDevice::DrawPolyLine directly; this will avoid buffering all
1299 // decompositions in primitives (memory) and fallback to old line painting
1300 // for very complex polygons, too
1301 for(sal_uInt32 a(0); a < nCount; a++)
1303 mpOutputDevice->DrawPolyLine(
1304 aHairlinePolyPolygon.getB2DPolygon(a),
1305 fDiscreteLineWidth,
1306 rLineAttribute.getLineJoin(),
1307 rLineAttribute.getLineCap(),
1308 rLineAttribute.getMiterMinimumAngle());
1311 bDone = true;
1316 if(!bDone)
1318 // remember that we enter a PolygonStrokePrimitive2D decomposition,
1319 // used for AA thick line drawing
1320 mnPolygonStrokePrimitive2D++;
1322 // line width is big enough for standard filled polygon visualisation or zero
1323 process(rPolygonStrokeCandidate.get2DDecomposition(getViewInformation2D()));
1325 // leave PolygonStrokePrimitive2D
1326 mnPolygonStrokePrimitive2D--;
1330 void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D)
1332 // The new decomposition of Metafiles made it necessary to add an Eps
1333 // primitive to handle embedded Eps data. On some devices, this can be
1334 // painted directly (mac, printer).
1335 // To be able to handle the replacement correctly, i need to handle it myself
1336 // since DrawEPS will not be able e.g. to rotate the replacement. To be able
1337 // to do that, i added a boolean return to OutputDevice::DrawEPS(..)
1338 // to know when EPS was handled directly already.
1339 basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
1340 aRange.transform(maCurrentTransformation * rEpsPrimitive2D.getEpsTransform());
1342 if(!aRange.isEmpty())
1344 const Rectangle aRectangle(
1345 (sal_Int32)floor(aRange.getMinX()), (sal_Int32)floor(aRange.getMinY()),
1346 (sal_Int32)ceil(aRange.getMaxX()), (sal_Int32)ceil(aRange.getMaxY()));
1348 if(!aRectangle.IsEmpty())
1350 bool bWillReallyRender = mpOutputDevice->IsDeviceOutputNecessary();
1351 // try to paint EPS directly without fallback visualisation
1352 const bool bEPSPaintedDirectly = bWillReallyRender &&
1353 mpOutputDevice->DrawEPS(
1354 aRectangle.TopLeft(),
1355 aRectangle.GetSize(),
1356 rEpsPrimitive2D.getGfxLink());
1358 if(!bEPSPaintedDirectly)
1360 // use the decomposition which will correctly handle the
1361 // fallback visualisation using full transformation (e.g. rotation)
1362 process(rEpsPrimitive2D.get2DDecomposition(getViewInformation2D()));
1368 void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D& rCandidate)
1370 const double fDelta(rCandidate.getOffsetB() - rCandidate.getOffsetA());
1372 if(basegfx::fTools::more(fDelta, 0.0))
1374 const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA()));
1375 const basegfx::BColor aColorB(maBColorModifierStack.getModifiedColor(rCandidate.getColorB()));
1377 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1378 const basegfx::B2DVector aDiscreteVector(getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1379 const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373));
1381 // use color distance and discrete lengths to calculate step count
1382 const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit));
1384 // switch off line painting
1385 mpOutputDevice->SetLineColor();
1387 // prepare polygon in needed width at start position (with discrete overlap)
1388 const basegfx::B2DPolygon aPolygon(
1389 basegfx::tools::createPolygonFromRect(
1390 basegfx::B2DRange(
1391 rCandidate.getOffsetA() - fDiscreteUnit,
1392 0.0,
1393 rCandidate.getOffsetA() + (fDelta / nSteps) + fDiscreteUnit,
1394 1.0)));
1397 // prepare loop ([0.0 .. 1.0[)
1398 double fUnitScale(0.0);
1399 const double fUnitStep(1.0 / nSteps);
1401 // loop and paint
1402 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1404 basegfx::B2DPolygon aNew(aPolygon);
1406 aNew.transform(maCurrentTransformation * basegfx::tools::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
1407 mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorA, aColorB, fUnitScale)));
1408 mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew));
1413 void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D& rCandidate)
1415 const double fDeltaScale(rCandidate.getScaleB() - rCandidate.getScaleA());
1417 if(basegfx::fTools::more(fDeltaScale, 0.0))
1419 const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA()));
1420 const basegfx::BColor aColorB(maBColorModifierStack.getModifiedColor(rCandidate.getColorB()));
1422 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1423 const basegfx::B2DVector aDiscreteVector(getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1424 const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373));
1426 // use color distance and discrete lengths to calculate step count
1427 const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDeltaScale, fDiscreteUnit));
1429 // switch off line painting
1430 mpOutputDevice->SetLineColor();
1432 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1433 double fUnitScale(0.0);
1434 const double fUnitStep(1.0 / nSteps);
1436 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1438 basegfx::B2DHomMatrix aTransform;
1439 const double fEndScale(rCandidate.getScaleB() - (fDeltaScale * fUnitScale));
1441 if(rCandidate.isTranslateSet())
1443 const basegfx::B2DVector aTranslate(
1444 basegfx::interpolate(
1445 rCandidate.getTranslateB(),
1446 rCandidate.getTranslateA(),
1447 fUnitScale));
1449 aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix(
1450 fEndScale,
1451 fEndScale,
1452 aTranslate.getX(),
1453 aTranslate.getY());
1455 else
1457 aTransform = basegfx::tools::createScaleB2DHomMatrix(
1458 fEndScale,
1459 fEndScale);
1462 basegfx::B2DPolygon aNew(basegfx::tools::createPolygonFromUnitCircle());
1464 aNew.transform(maCurrentTransformation * aTransform);
1465 mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorB, aColorA, fUnitScale)));
1466 mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew));
1471 void VclProcessor2D::adaptLineToFillDrawMode() const
1473 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1475 if(nOriginalDrawMode & (DrawModeFlags::BlackLine|DrawModeFlags::GrayLine|DrawModeFlags::GhostedLine|DrawModeFlags::WhiteLine|DrawModeFlags::SettingsLine))
1477 DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
1479 if(nOriginalDrawMode & DrawModeFlags::BlackLine)
1481 nAdaptedDrawMode |= DrawModeFlags::BlackFill;
1483 else
1485 nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
1488 if(nOriginalDrawMode & DrawModeFlags::GrayLine)
1490 nAdaptedDrawMode |= DrawModeFlags::GrayFill;
1492 else
1494 nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
1497 if(nOriginalDrawMode & DrawModeFlags::GhostedLine)
1499 nAdaptedDrawMode |= DrawModeFlags::GhostedFill;
1501 else
1503 nAdaptedDrawMode &= ~DrawModeFlags::GhostedFill;
1506 if(nOriginalDrawMode & DrawModeFlags::WhiteLine)
1508 nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
1510 else
1512 nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
1515 if(nOriginalDrawMode & DrawModeFlags::SettingsLine)
1517 nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
1519 else
1521 nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
1524 mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
1528 void VclProcessor2D::adaptTextToFillDrawMode() const
1530 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1531 if(nOriginalDrawMode & (DrawModeFlags::BlackText|DrawModeFlags::GrayText|DrawModeFlags::GhostedText|DrawModeFlags::WhiteText|DrawModeFlags::SettingsText))
1533 DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
1535 if(nOriginalDrawMode & DrawModeFlags::BlackText)
1537 nAdaptedDrawMode |= DrawModeFlags::BlackFill;
1539 else
1541 nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
1544 if(nOriginalDrawMode & DrawModeFlags::GrayText)
1546 nAdaptedDrawMode |= DrawModeFlags::GrayFill;
1548 else
1550 nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
1553 if(nOriginalDrawMode & DrawModeFlags::GhostedText)
1555 nAdaptedDrawMode |= DrawModeFlags::GhostedFill;
1557 else
1559 nAdaptedDrawMode &= ~DrawModeFlags::GhostedFill;
1562 if(nOriginalDrawMode & DrawModeFlags::WhiteText)
1564 nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
1566 else
1568 nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
1571 if(nOriginalDrawMode & DrawModeFlags::SettingsText)
1573 nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
1575 else
1577 nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
1580 mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
1584 // process support
1586 VclProcessor2D::VclProcessor2D(
1587 const geometry::ViewInformation2D& rViewInformation,
1588 OutputDevice& rOutDev)
1589 : BaseProcessor2D(rViewInformation),
1590 mpOutputDevice(&rOutDev),
1591 maBColorModifierStack(),
1592 maCurrentTransformation(),
1593 maDrawinglayerOpt(),
1594 mnPolygonStrokePrimitive2D(0)
1596 // set digit language, derived from SvtCTLOptions to have the correct
1597 // number display for arabic/hindi numerals
1598 rOutDev.SetDigitLanguage(drawinglayer::detail::getDigitLanguage());
1601 VclProcessor2D::~VclProcessor2D()
1607 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */