update emoji autocorrect entries from po-files
[LibreOffice.git] / drawinglayer / source / processor2d / vclprocessor2d.cxx
blobd8fee138000dc6e48655245b539a0106cf545bc8
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 <comphelper/string.hxx>
21 #include "vclprocessor2d.hxx"
22 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
23 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
24 #include <tools/debug.hxx>
25 #include <vcl/outdev.hxx>
26 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
30 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
31 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
33 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
34 #include <basegfx/polygon/b2dpolypolygontools.hxx>
35 #include <vclhelperbufferdevice.hxx>
36 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/wrongspellprimitive2d.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 <com/sun/star/awt/XWindow2.hpp>
59 #include <com/sun/star/awt/PosSize.hpp>
60 #include <com/sun/star/awt/XView.hpp>
61 #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
62 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
64 // for test, can be removed again
65 #include <basegfx/polygon/b2dpolygonclipper.hxx>
66 #include <basegfx/polygon/b2dtrapezoid.hxx>
67 // <- for test
69 #include <drawinglayer/primitive2d/openglprimitive2d.hxx>
71 using namespace com::sun::star;
73 namespace
75 sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit)
77 // use color distance, assume to do every color step
78 sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0));
80 if(nSteps)
82 // calc discrete length to change color each disctete unit (pixel)
83 const sal_uInt32 nDistSteps(basegfx::fround(fDelta / fDiscreteUnit));
85 nSteps = std::min(nSteps, nDistSteps);
88 // reduce quality to 3 discrete units or every 3rd color step for rendering
89 nSteps /= 2;
91 // roughly cut when too big or too small (not full quality, reduce complexity)
92 nSteps = std::min(nSteps, sal_uInt32(255));
93 nSteps = std::max(nSteps, sal_uInt32(1));
95 return nSteps;
99 namespace drawinglayer
101 namespace processor2d
103 // UNO class usages
104 using ::com::sun::star::uno::Reference;
105 using ::com::sun::star::uno::UNO_QUERY;
106 using ::com::sun::star::uno::UNO_QUERY_THROW;
107 using ::com::sun::star::uno::Exception;
108 using ::com::sun::star::awt::XView;
109 using ::com::sun::star::awt::XGraphics;
110 using ::com::sun::star::awt::XWindow;
111 using ::com::sun::star::awt::PosSize::POSSIZE;
113 // rendering support
115 // directdraw of text simple portion or decorated portion primitive. When decorated, all the extra
116 // information is translated to VCL parameters and set at the font.
117 // Acceptance is restricted to no shearing and positive scaling in X and Y (no font mirroring
118 // for VCL)
119 void VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate)
121 // decompose matrix to have position and size of text
122 basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rTextCandidate.getTextTransform());
123 basegfx::B2DVector aFontScaling, aTranslate;
124 double fRotate, fShearX;
125 aLocalTransform.decompose(aFontScaling, aTranslate, fRotate, fShearX);
126 bool bPrimitiveAccepted(false);
128 if(basegfx::fTools::equalZero(fShearX))
130 if(basegfx::fTools::less(aFontScaling.getX(), 0.0) && basegfx::fTools::less(aFontScaling.getY(), 0.0))
132 // handle special case: If scale is negative in (x,y) (3rd quadrant), it can
133 // be expressed as rotation by PI. Use this since the Font rendering will not
134 // apply the negative scales in any form
135 aFontScaling = basegfx::absolute(aFontScaling);
136 fRotate += F_PI;
139 if(basegfx::fTools::more(aFontScaling.getX(), 0.0) && basegfx::fTools::more(aFontScaling.getY(), 0.0))
141 // Get the VCL font (use FontHeight as FontWidth)
142 vcl::Font aFont(primitive2d::getVclFontFromFontAttribute(
143 rTextCandidate.getFontAttribute(),
144 aFontScaling.getX(),
145 aFontScaling.getY(),
146 fRotate,
147 rTextCandidate.getLocale()));
149 // set FillColor Attribute
150 const Color aFillColor( rTextCandidate.getTextFillColor() );
151 if( aFillColor != COL_TRANSPARENT )
153 aFont.SetFillColor(aFillColor);
154 aFont.SetTransparent(false);
157 // Don't draw fonts without height
158 if( aFont.GetHeight() <= 0 )
159 return;
161 // handle additional font attributes
162 const primitive2d::TextDecoratedPortionPrimitive2D* pTCPP =
163 dynamic_cast<const primitive2d::TextDecoratedPortionPrimitive2D*>( &rTextCandidate );
165 if( pTCPP != NULL )
168 // set the color of text decorations
169 const basegfx::BColor aTextlineColor = maBColorModifierStack.getModifiedColor(pTCPP->getTextlineColor());
170 mpOutputDevice->SetTextLineColor( Color(aTextlineColor) );
172 // set Overline attribute
173 const FontUnderline eFontOverline(primitive2d::mapTextLineToFontUnderline( pTCPP->getFontOverline() ));
174 if( eFontOverline != UNDERLINE_NONE )
176 aFont.SetOverline( eFontOverline );
177 const basegfx::BColor aOverlineColor = maBColorModifierStack.getModifiedColor(pTCPP->getOverlineColor());
178 mpOutputDevice->SetOverlineColor( Color(aOverlineColor) );
179 if( pTCPP->getWordLineMode() )
180 aFont.SetWordLineMode( true );
183 // set Underline attribute
184 const FontUnderline eFontUnderline(primitive2d::mapTextLineToFontUnderline( pTCPP->getFontUnderline() ));
185 if( eFontUnderline != UNDERLINE_NONE )
187 aFont.SetUnderline( eFontUnderline );
188 if( pTCPP->getWordLineMode() )
189 aFont.SetWordLineMode( true );
192 // set Strikeout attribute
193 const FontStrikeout eFontStrikeout(primitive2d::mapTextStrikeoutToFontStrikeout(pTCPP->getTextStrikeout()));
195 if( eFontStrikeout != STRIKEOUT_NONE )
196 aFont.SetStrikeout( eFontStrikeout );
199 // set EmphasisMark attribute
200 FontEmphasisMark eFontEmphasisMark = EMPHASISMARK_NONE;
201 switch( pTCPP->getTextEmphasisMark() )
203 default:
204 SAL_WARN("drawinglayer", "Unknown EmphasisMark style " << pTCPP->getTextEmphasisMark() );
205 // fall through
206 case primitive2d::TEXT_EMPHASISMARK_NONE: eFontEmphasisMark = EMPHASISMARK_NONE; break;
207 case primitive2d::TEXT_EMPHASISMARK_DOT: eFontEmphasisMark = EMPHASISMARK_DOT; break;
208 case primitive2d::TEXT_EMPHASISMARK_CIRCLE: eFontEmphasisMark = EMPHASISMARK_CIRCLE; break;
209 case primitive2d::TEXT_EMPHASISMARK_DISC: eFontEmphasisMark = EMPHASISMARK_DISC; break;
210 case primitive2d::TEXT_EMPHASISMARK_ACCENT: eFontEmphasisMark = EMPHASISMARK_ACCENT; break;
213 if( eFontEmphasisMark != EMPHASISMARK_NONE )
215 DBG_ASSERT( (pTCPP->getEmphasisMarkAbove() != pTCPP->getEmphasisMarkBelow()),
216 "DrawingLayer: Bad EmphasisMark position!" );
217 if( pTCPP->getEmphasisMarkAbove() )
218 eFontEmphasisMark |= EMPHASISMARK_POS_ABOVE;
219 else
220 eFontEmphasisMark |= EMPHASISMARK_POS_BELOW;
221 aFont.SetEmphasisMark( eFontEmphasisMark );
224 // set Relief attribute
225 FontRelief eFontRelief = RELIEF_NONE;
226 switch( pTCPP->getTextRelief() )
228 default:
229 SAL_WARN( "drawinglayer", "Unknown Relief style " << pTCPP->getTextRelief() );
230 // fall through
231 case primitive2d::TEXT_RELIEF_NONE: eFontRelief = RELIEF_NONE; break;
232 case primitive2d::TEXT_RELIEF_EMBOSSED: eFontRelief = RELIEF_EMBOSSED; break;
233 case primitive2d::TEXT_RELIEF_ENGRAVED: eFontRelief = RELIEF_ENGRAVED; break;
236 if( eFontRelief != RELIEF_NONE )
237 aFont.SetRelief( eFontRelief );
239 // set Shadow attribute
240 if( pTCPP->getShadow() )
241 aFont.SetShadow( true );
244 // create transformed integer DXArray in view coordinate system
245 ::std::vector< long > aTransformedDXArray;
247 if(rTextCandidate.getDXArray().size())
249 aTransformedDXArray.reserve(rTextCandidate.getDXArray().size());
250 const basegfx::B2DVector aPixelVector(maCurrentTransformation * basegfx::B2DVector(1.0, 0.0));
251 const double fPixelVectorFactor(aPixelVector.getLength());
253 for(::std::vector< double >::const_iterator aStart(rTextCandidate.getDXArray().begin());
254 aStart != rTextCandidate.getDXArray().end(); ++aStart)
256 aTransformedDXArray.push_back(basegfx::fround((*aStart) * fPixelVectorFactor));
260 // set parameters and paint text snippet
261 const basegfx::BColor aRGBFontColor(maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
262 const basegfx::B2DPoint aPoint(aLocalTransform * basegfx::B2DPoint(0.0, 0.0));
263 const Point aStartPoint(basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY()));
264 const ComplexTextLayoutMode nOldLayoutMode(mpOutputDevice->GetLayoutMode());
266 if(rTextCandidate.getFontAttribute().getRTL())
268 ComplexTextLayoutMode nRTLLayoutMode(nOldLayoutMode & ~(TEXT_LAYOUT_COMPLEX_DISABLED|TEXT_LAYOUT_BIDI_STRONG));
269 nRTLLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT;
270 mpOutputDevice->SetLayoutMode(nRTLLayoutMode);
273 mpOutputDevice->SetFont(aFont);
274 mpOutputDevice->SetTextColor(Color(aRGBFontColor));
276 OUString aText( rTextCandidate.getText() );
277 sal_Int32 nPos = rTextCandidate.getTextPosition();
278 sal_Int32 nLen = rTextCandidate.getTextLength();
280 long* pDXArray = aTransformedDXArray.size() ? &(aTransformedDXArray[0]) : NULL ;
282 if ( rTextCandidate.isFilled() )
284 basegfx::B2DVector aOldFontScaling, aOldTranslate;
285 double fOldRotate, fOldShearX;
286 rTextCandidate.getTextTransform().decompose(aOldFontScaling, aOldTranslate, fOldRotate, fOldShearX);
288 long nWidthToFill = static_cast<long>(rTextCandidate.getWidthToFill( ) * aFontScaling.getX() / aOldFontScaling.getX());
290 long nWidth = mpOutputDevice->GetTextArray( rTextCandidate.getText(), pDXArray, 0, 1 );
291 long nChars = 2;
292 if ( nWidth )
293 nChars = nWidthToFill / nWidth;
295 OUStringBuffer aFilled;
296 comphelper::string::padToLength(aFilled, nChars, aText[0]);
297 aText = aFilled.makeStringAndClear();
298 nPos = 0;
299 nLen = nChars;
302 if(!aTransformedDXArray.empty())
304 mpOutputDevice->DrawTextArray(
305 aStartPoint,
306 aText,
307 pDXArray,
308 nPos,
309 nLen);
311 else
313 mpOutputDevice->DrawText(
314 aStartPoint,
315 aText,
316 nPos,
317 nLen);
320 if(rTextCandidate.getFontAttribute().getRTL())
322 mpOutputDevice->SetLayoutMode(nOldLayoutMode);
325 bPrimitiveAccepted = true;
329 if(!bPrimitiveAccepted)
331 // let break down
332 process(rTextCandidate.get2DDecomposition(getViewInformation2D()));
336 // direct draw of hairline
337 void VclProcessor2D::RenderPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate, bool bPixelBased)
339 const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
340 mpOutputDevice->SetLineColor(Color(aHairlineColor));
341 mpOutputDevice->SetFillColor();
343 basegfx::B2DPolygon aLocalPolygon(rPolygonCandidate.getB2DPolygon());
344 aLocalPolygon.transform(maCurrentTransformation);
346 static bool bCheckTrapezoidDecomposition(false);
347 static bool bShowOutlinesThere(false);
348 if(bCheckTrapezoidDecomposition)
350 // clip against discrete ViewPort
351 const basegfx::B2DRange& rDiscreteViewport = getViewInformation2D().getDiscreteViewport();
352 basegfx::B2DPolyPolygon aLocalPolyPolygon(basegfx::tools::clipPolygonOnRange(
353 aLocalPolygon, rDiscreteViewport, true, false));
355 if(aLocalPolyPolygon.count())
357 // subdivide
358 aLocalPolyPolygon = basegfx::tools::adaptiveSubdivideByDistance(
359 aLocalPolyPolygon, 0.5);
361 // trapezoidize
362 static double fLineWidth(2.0);
363 basegfx::B2DTrapezoidVector aB2DTrapezoidVector;
364 basegfx::tools::createLineTrapezoidFromB2DPolyPolygon(aB2DTrapezoidVector, aLocalPolyPolygon, fLineWidth);
366 const sal_uInt32 nCount(aB2DTrapezoidVector.size());
368 if(nCount)
370 basegfx::BColor aInvPolygonColor(aHairlineColor);
371 aInvPolygonColor.invert();
373 for(sal_uInt32 a(0); a < nCount; a++)
375 const basegfx::B2DPolygon aTempPolygon(aB2DTrapezoidVector[a].getB2DPolygon());
377 if(bShowOutlinesThere)
379 mpOutputDevice->SetFillColor(Color(aHairlineColor));
380 mpOutputDevice->SetLineColor();
383 mpOutputDevice->DrawPolygon(aTempPolygon);
385 if(bShowOutlinesThere)
387 mpOutputDevice->SetFillColor();
388 mpOutputDevice->SetLineColor(Color(aInvPolygonColor));
389 mpOutputDevice->DrawPolyLine(aTempPolygon, 0.0);
395 else
397 if(bPixelBased && getOptionsDrawinglayer().IsAntiAliasing() && getOptionsDrawinglayer().IsSnapHorVerLinesToDiscrete())
399 // #i98289#
400 // when a Hairline is painted and AntiAliasing is on the option SnapHorVerLinesToDiscrete
401 // allows to suppress AntiAliasing for pure horizontal or vertical lines. This is done since
402 // not-AntiAliased such lines look more pleasing to the eye (e.g. 2D chart content). This
403 // NEEDS to be done in discrete coordinates, so only useful for pixel based rendering.
404 aLocalPolygon = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aLocalPolygon);
407 mpOutputDevice->DrawPolyLine(aLocalPolygon, 0.0);
411 // direct draw of transformed BitmapEx primitive
412 void VclProcessor2D::RenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
414 BitmapEx aBitmapEx(rBitmapCandidate.getBitmapEx());
415 const basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rBitmapCandidate.getTransform());
417 if(maBColorModifierStack.count())
419 aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
421 if(aBitmapEx.IsEmpty())
423 // color gets completely replaced, get it
424 const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor()));
425 basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon());
426 aPolygon.transform(aLocalTransform);
428 mpOutputDevice->SetFillColor(Color(aModifiedColor));
429 mpOutputDevice->SetLineColor();
430 mpOutputDevice->DrawPolygon(aPolygon);
432 return;
436 // #122923# do no longer add Alpha channel here; the right place to do this is when really
437 // the own transformer is used (see OutputDevice::DrawTransformedBitmapEx).
439 // draw using OutputDevice'sDrawTransformedBitmapEx
440 mpOutputDevice->DrawTransformedBitmapEx(aLocalTransform, aBitmapEx);
443 void VclProcessor2D::RenderFillGraphicPrimitive2D(const primitive2d::FillGraphicPrimitive2D& rFillBitmapCandidate)
445 const attribute::FillGraphicAttribute& rFillGraphicAttribute(rFillBitmapCandidate.getFillGraphic());
446 bool bPrimitiveAccepted(false);
447 static bool bTryTilingDirect = true;
449 // #121194# when tiling is used and content is bitmap-based, do direct tiling in the
450 // renderer on pixel base to ensure tight fitting. Do not do this when
451 // the fill is rotated or sheared.
453 // ovveride static bool (for debug) and tiling is active
454 if(bTryTilingDirect && rFillGraphicAttribute.getTiling())
456 // content is bitmap(ex)
458 // for SVG support, force decomposition when SVG is present. This will lead to use
459 // the primitive representation of the svg directly.
461 // when graphic is animated, force decomposition to use the correct graphic, else
462 // fill style will not be animated
463 if(GRAPHIC_BITMAP == rFillGraphicAttribute.getGraphic().GetType()
464 && !rFillGraphicAttribute.getGraphic().getSvgData().get()
465 && !rFillGraphicAttribute.getGraphic().IsAnimated())
467 // decompose matrix to check for shear, rotate and mirroring
468 basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rFillBitmapCandidate.getTransformation());
469 basegfx::B2DVector aScale, aTranslate;
470 double fRotate, fShearX;
471 aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
473 // when nopt rotated/sheared
474 if(basegfx::fTools::equalZero(fRotate) && basegfx::fTools::equalZero(fShearX))
476 // no shear or rotate, draw direct in pixel coordinates
477 bPrimitiveAccepted = true;
479 // transform object range to device coordinates (pixels). Use
480 // the device transformation for better accuracy
481 basegfx::B2DRange aObjectRange(aTranslate, aTranslate + aScale);
482 aObjectRange.transform(mpOutputDevice->GetViewTransformation());
484 // extract discrete size of object
485 const sal_Int32 nOWidth(basegfx::fround(aObjectRange.getWidth()));
486 const sal_Int32 nOHeight(basegfx::fround(aObjectRange.getHeight()));
488 // only do something when object has a size in discrete units
489 if(nOWidth > 0 && nOHeight > 0)
491 // transform graphic range to device coordinates (pixels). Use
492 // the device transformation for better accuracy
493 basegfx::B2DRange aGraphicRange(rFillGraphicAttribute.getGraphicRange());
494 aGraphicRange.transform(mpOutputDevice->GetViewTransformation() * aLocalTransform);
496 // extract discrete size of graphic
497 // caution: when getting to zero, nothing would be painted; thus, do not allow this
498 const sal_Int32 nBWidth(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getWidth())));
499 const sal_Int32 nBHeight(std::max(sal_Int32(1), basegfx::fround(aGraphicRange.getHeight())));
501 // only do something when bitmap fill has a size in discrete units
502 if(nBWidth > 0 && nBHeight > 0)
504 // nBWidth, nBHeight is the pixel size of the neede bitmap. To not need to scale it
505 // in vcl many times, create a size-optimized version
506 const Size aNeededBitmapSizePixel(nBWidth, nBHeight);
507 BitmapEx aBitmapEx(rFillGraphicAttribute.getGraphic().GetBitmapEx());
508 static bool bEnablePreScaling(true);
509 const bool bPreScaled(bEnablePreScaling && nBWidth * nBHeight < (250 * 250));
511 // ... but only up to a maximum size, else it gets too expensive
512 if(bPreScaled)
514 // if color depth is below 24bit, expand before scaling for better quality.
515 // This is even needed for low colors, else the scale will produce
516 // a bitmap in gray or Black/White (!)
517 if(aBitmapEx.GetBitCount() < 24)
519 aBitmapEx.Convert(BMP_CONVERSION_24BIT);
522 aBitmapEx.Scale(aNeededBitmapSizePixel, BmpScaleFlag::Interpolate);
525 bool bPainted(false);
527 if(maBColorModifierStack.count())
529 // when color modifier, apply to bitmap
530 aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
532 // impModifyBitmapEx uses empty bitmap as sign to return that
533 // the content will be completely replaced to mono color, use shortcut
534 if(aBitmapEx.IsEmpty())
536 // color gets completely replaced, get it
537 const basegfx::BColor aModifiedColor(maBColorModifierStack.getModifiedColor(basegfx::BColor()));
538 basegfx::B2DPolygon aPolygon(basegfx::tools::createUnitPolygon());
539 aPolygon.transform(aLocalTransform);
541 mpOutputDevice->SetFillColor(Color(aModifiedColor));
542 mpOutputDevice->SetLineColor();
543 mpOutputDevice->DrawPolygon(aPolygon);
545 bPainted = true;
549 if(!bPainted)
551 sal_Int32 nBLeft(basegfx::fround(aGraphicRange.getMinX()));
552 sal_Int32 nBTop(basegfx::fround(aGraphicRange.getMinY()));
553 const sal_Int32 nOLeft(basegfx::fround(aObjectRange.getMinX()));
554 const sal_Int32 nOTop(basegfx::fround(aObjectRange.getMinY()));
555 sal_Int32 nPosX(0);
556 sal_Int32 nPosY(0);
558 if(nBLeft > nOLeft)
560 const sal_Int32 nDiff((nBLeft / nBWidth) + 1);
562 nPosX -= nDiff;
563 nBLeft -= nDiff * nBWidth;
566 if(nBLeft + nBWidth <= nOLeft)
568 const sal_Int32 nDiff(-nBLeft / nBWidth);
570 nPosX += nDiff;
571 nBLeft += nDiff * nBWidth;
574 if(nBTop > nOTop)
576 const sal_Int32 nDiff((nBTop / nBHeight) + 1);
578 nPosY -= nDiff;
579 nBTop -= nDiff * nBHeight;
582 if(nBTop + nBHeight <= nOTop)
584 const sal_Int32 nDiff(-nBTop / nBHeight);
586 nPosY += nDiff;
587 nBTop += nDiff * nBHeight;
590 // prepare OutDev
591 const Point aEmptyPoint(0, 0);
592 const Rectangle aVisiblePixel(aEmptyPoint, mpOutputDevice->GetOutputSizePixel());
593 const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
594 mpOutputDevice->EnableMapMode(false);
596 // check if offset is used
597 const sal_Int32 nOffsetX(basegfx::fround(rFillGraphicAttribute.getOffsetX() * nBWidth));
599 if(nOffsetX)
601 // offset in X, so iterate over Y first and draw lines
602 for(sal_Int32 nYPos(nBTop); nYPos < nOTop + nOHeight; nYPos += nBHeight, nPosY++)
604 for(sal_Int32 nXPos(nPosY % 2 ? nBLeft - nBWidth + nOffsetX : nBLeft);
605 nXPos < nOLeft + nOWidth; nXPos += nBWidth)
607 const Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
609 if(aOutRectPixel.IsOver(aVisiblePixel))
611 if(bPreScaled)
613 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
615 else
617 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, aBitmapEx);
623 else
625 // check if offset is used
626 const sal_Int32 nOffsetY(basegfx::fround(rFillGraphicAttribute.getOffsetY() * nBHeight));
628 // possible offset in Y, so iterate over X first and draw columns
629 for(sal_Int32 nXPos(nBLeft); nXPos < nOLeft + nOWidth; nXPos += nBWidth, nPosX++)
631 for(sal_Int32 nYPos(nPosX % 2 ? nBTop - nBHeight + nOffsetY : nBTop);
632 nYPos < nOTop + nOHeight; nYPos += nBHeight)
634 const Rectangle aOutRectPixel(Point(nXPos, nYPos), aNeededBitmapSizePixel);
636 if(aOutRectPixel.IsOver(aVisiblePixel))
638 if(bPreScaled)
640 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aBitmapEx);
642 else
644 mpOutputDevice->DrawBitmapEx(aOutRectPixel.TopLeft(), aNeededBitmapSizePixel, aBitmapEx);
651 // restore OutDev
652 mpOutputDevice->EnableMapMode(bWasEnabled);
660 if(!bPrimitiveAccepted)
662 // do not accept, use decomposition
663 process(rFillBitmapCandidate.get2DDecomposition(getViewInformation2D()));
667 // direct draw of Graphic
668 void VclProcessor2D::RenderPolyPolygonGraphicPrimitive2D(const primitive2d::PolyPolygonGraphicPrimitive2D& rPolygonCandidate)
670 bool bDone(false);
671 const basegfx::B2DPolyPolygon& rPolyPolygon = rPolygonCandidate.getB2DPolyPolygon();
673 // #121194# Todo: check if this works
674 if(!rPolyPolygon.count())
676 // empty polyPolygon, done
677 bDone = true;
679 else
681 const attribute::FillGraphicAttribute& rFillGraphicAttribute = rPolygonCandidate.getFillGraphic();
683 // try to catch cases where the graphic will be color-modified to a single
684 // color (e.g. shadow)
685 switch(rFillGraphicAttribute.getGraphic().GetType())
687 case GRAPHIC_GDIMETAFILE:
689 // metafiles are potentially transparent, cannot optimize, not done
690 break;
692 case GRAPHIC_BITMAP:
694 if(!rFillGraphicAttribute.getGraphic().IsTransparent() && !rFillGraphicAttribute.getGraphic().IsAlpha())
696 // bitmap is not transparent and has no alpha
697 const sal_uInt32 nBColorModifierStackCount(maBColorModifierStack.count());
699 if(nBColorModifierStackCount)
701 const basegfx::BColorModifierSharedPtr& rTopmostModifier = maBColorModifierStack.getBColorModifier(nBColorModifierStackCount - 1);
702 const basegfx::BColorModifier_replace* pReplacer = dynamic_cast< const basegfx::BColorModifier_replace* >(rTopmostModifier.get());
704 if(pReplacer)
706 // the bitmap fill is in unified color, so we can replace it with
707 // a single polygon fill. The form of the fill depends on tiling
708 if(rFillGraphicAttribute.getTiling())
710 // with tiling, fill the whole tools::PolyPolygon with the modifier color
711 basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
713 aLocalPolyPolygon.transform(maCurrentTransformation);
714 mpOutputDevice->SetLineColor();
715 mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
716 mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
718 else
720 // without tiling, only the area common to the bitmap tile and the
721 // tools::PolyPolygon is filled. Create the bitmap tile area in object
722 // coordinates. For this, the object transformation needs to be created
723 // from the already scaled PolyPolygon. The tile area in object
724 // coordinates wil always be non-rotated, so it's not necessary to
725 // work with a polygon here
726 basegfx::B2DRange aTileRange(rFillGraphicAttribute.getGraphicRange());
727 const basegfx::B2DRange aPolyPolygonRange(rPolyPolygon.getB2DRange());
728 const basegfx::B2DHomMatrix aNewObjectTransform(
729 basegfx::tools::createScaleTranslateB2DHomMatrix(
730 aPolyPolygonRange.getRange(),
731 aPolyPolygonRange.getMinimum()));
733 aTileRange.transform(aNewObjectTransform);
735 // now clip the object polyPolygon against the tile range
736 // to get the common area
737 basegfx::B2DPolyPolygon aTarget = basegfx::tools::clipPolyPolygonOnRange(
738 rPolyPolygon,
739 aTileRange,
740 true,
741 false);
743 if(aTarget.count())
745 aTarget.transform(maCurrentTransformation);
746 mpOutputDevice->SetLineColor();
747 mpOutputDevice->SetFillColor(Color(pReplacer->getBColor()));
748 mpOutputDevice->DrawPolyPolygon(aTarget);
752 // simplified output executed, we are done
753 bDone = true;
757 break;
759 default: //GRAPHIC_NONE, GRAPHIC_DEFAULT
761 // empty graphic, we are done
762 bDone = true;
763 break;
768 if(!bDone)
770 // use default decomposition
771 process(rPolygonCandidate.get2DDecomposition(getViewInformation2D()));
775 // direct draw of MetaFile
776 void VclProcessor2D::RenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D& rMetaCandidate)
778 // decompose matrix to check for shear, rotate and mirroring
779 basegfx::B2DHomMatrix aLocalTransform(maCurrentTransformation * rMetaCandidate.getTransform());
780 basegfx::B2DVector aScale, aTranslate;
781 double fRotate, fShearX;
782 aLocalTransform.decompose(aScale, aTranslate, fRotate, fShearX);
784 if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
786 // #i102175# handle special case: If scale is negative in (x,y) (3rd quadrant), it can
787 // be expressed as rotation by PI. This needs to be done for Metafiles since
788 // these can be rotated, but not really mirrored
789 aScale = basegfx::absolute(aScale);
790 fRotate += F_PI;
793 // get BoundRect
794 basegfx::B2DRange aOutlineRange(rMetaCandidate.getB2DRange(getViewInformation2D()));
795 aOutlineRange.transform(maCurrentTransformation);
797 // Due to the integer MapModes used from VCL aind inside MetaFiles errors of up to three
798 // pixels in size may happen. As long as there is no better way (e.g. convert the MetaFile
799 // to primitives) it is necessary to reduce maximum pixel size by 1 in X and Y and to use
800 // the inner pixel bounds accordingly (ceil resp. floor). This will also be done for logic
801 // units e.g. when creating a new MetaFile, but since much huger value ranges are used
802 // there typically will be okay for this compromize.
803 Rectangle aDestRectView(
804 // !!CAUTION!! Here, ceil and floor are exchanged BY PURPOSE, do NOT copy when
805 // looking for a standard conversion to rectangle (!)
806 (sal_Int32)ceil(aOutlineRange.getMinX()), (sal_Int32)ceil(aOutlineRange.getMinY()),
807 (sal_Int32)floor(aOutlineRange.getMaxX()), (sal_Int32)floor(aOutlineRange.getMaxY()));
809 // get metafile (copy it)
810 GDIMetaFile aMetaFile;
812 if(maBColorModifierStack.count())
814 const basegfx::BColor aRGBBaseColor(0, 0, 0);
815 const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(aRGBBaseColor));
816 aMetaFile = rMetaCandidate.getMetaFile().GetMonochromeMtf(Color(aRGBColor));
818 else
820 aMetaFile = rMetaCandidate.getMetaFile();
823 // rotation
824 if(!basegfx::fTools::equalZero(fRotate))
826 // #i103530#
827 // MetaFile::Rotate has no input parameter check, so the parameter needs to be
828 // well-aligned to the old range [0..3600] 10th degrees with inverse orientation
829 sal_Int16 nRotation((sal_Int16)((fRotate / F_PI180) * -10.0));
831 while(nRotation < 0)
832 nRotation += 3600;
834 while(nRotation >= 3600)
835 nRotation -= 3600;
837 aMetaFile.Rotate(nRotation);
840 // Prepare target output size
841 Size aDestSize(aDestRectView.GetSize());
843 if(aDestSize.getWidth() > 0 && aDestSize.getHeight() > 0)
845 // Get preferred Metafile output size. When it's very equal to the output size, it's probably
846 // a rounding error somewhere, so correct it to get a 1:1 output without single pixel scalings
847 // of the Metafile (esp. for contaned Bitmaps, e.g 3D charts)
848 const Size aPrefSize(mpOutputDevice->LogicToPixel(aMetaFile.GetPrefSize(), aMetaFile.GetPrefMapMode()));
850 if(aPrefSize.getWidth() && (aPrefSize.getWidth() - 1 == aDestSize.getWidth() || aPrefSize.getWidth() + 1 == aDestSize.getWidth()))
852 aDestSize.setWidth(aPrefSize.getWidth());
855 if(aPrefSize.getHeight() && (aPrefSize.getHeight() - 1 == aDestSize.getHeight() || aPrefSize.getHeight() + 1 == aDestSize.getHeight()))
857 aDestSize.setHeight(aPrefSize.getHeight());
860 // paint it
861 aMetaFile.WindStart();
862 aMetaFile.Play(mpOutputDevice, aDestRectView.TopLeft(), aDestSize);
866 // mask group. Force output to VDev and create mask from given mask
867 void VclProcessor2D::RenderMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D& rMaskCandidate)
869 if(rMaskCandidate.getChildren().hasElements())
871 basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
873 if(aMask.count())
875 aMask.transform(maCurrentTransformation);
876 const basegfx::B2DRange aRange(basegfx::tools::getRange(aMask));
877 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true);
879 if(aBufferDevice.isVisible())
881 // remember last OutDev and set to content
882 OutputDevice* pLastOutputDevice = mpOutputDevice;
883 mpOutputDevice = &aBufferDevice.getContent();
885 // paint to it
886 process(rMaskCandidate.getChildren());
888 // back to old OutDev
889 mpOutputDevice = pLastOutputDevice;
891 // draw mask
892 if(getOptionsDrawinglayer().IsAntiAliasing())
894 // with AA, use 8bit AlphaMask to get nice borders
895 VirtualDevice& rTransparence = aBufferDevice.getTransparence();
896 rTransparence.SetLineColor();
897 rTransparence.SetFillColor(COL_BLACK);
898 rTransparence.DrawPolyPolygon(aMask);
900 // dump buffer to outdev
901 aBufferDevice.paint();
903 else
905 // No AA, use 1bit mask
906 VirtualDevice& rMask = aBufferDevice.getMask();
907 rMask.SetLineColor();
908 rMask.SetFillColor(COL_BLACK);
909 rMask.DrawPolyPolygon(aMask);
911 // dump buffer to outdev
912 aBufferDevice.paint();
919 // modified color group. Force output to unified color.
920 void VclProcessor2D::RenderModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
922 if(rModifiedCandidate.getChildren().hasElements())
924 maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
925 process(rModifiedCandidate.getChildren());
926 maBColorModifierStack.pop();
930 // unified sub-transparence. Draw to VDev first.
931 void VclProcessor2D::RenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate)
933 static bool bForceToDecomposition(false);
935 if(rTransCandidate.getChildren().hasElements())
937 if(bForceToDecomposition)
939 // use decomposition
940 process(rTransCandidate.get2DDecomposition(getViewInformation2D()));
942 else
944 if(0.0 == rTransCandidate.getTransparence())
946 // no transparence used, so just use the content
947 process(rTransCandidate.getChildren());
949 else if(rTransCandidate.getTransparence() > 0.0 && rTransCandidate.getTransparence() < 1.0)
951 // transparence is in visible range
952 basegfx::B2DRange aRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rTransCandidate.getChildren(), getViewInformation2D()));
953 aRange.transform(maCurrentTransformation);
954 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true);
956 if(aBufferDevice.isVisible())
958 // remember last OutDev and set to content
959 OutputDevice* pLastOutputDevice = mpOutputDevice;
960 mpOutputDevice = &aBufferDevice.getContent();
962 // paint content to it
963 process(rTransCandidate.getChildren());
965 // back to old OutDev
966 mpOutputDevice = pLastOutputDevice;
968 // dump buffer to outdev using given transparence
969 aBufferDevice.paint(rTransCandidate.getTransparence());
976 // sub-transparence group. Draw to VDev first.
977 void VclProcessor2D::RenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D& rTransCandidate)
979 if(rTransCandidate.getChildren().hasElements())
981 basegfx::B2DRange aRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rTransCandidate.getChildren(), getViewInformation2D()));
982 aRange.transform(maCurrentTransformation);
983 impBufferDevice aBufferDevice(*mpOutputDevice, aRange, true);
985 if(aBufferDevice.isVisible())
987 // remember last OutDev and set to content
988 OutputDevice* pLastOutputDevice = mpOutputDevice;
989 mpOutputDevice = &aBufferDevice.getContent();
991 // paint content to it
992 process(rTransCandidate.getChildren());
994 // set to mask
995 mpOutputDevice = &aBufferDevice.getTransparence();
997 // when painting transparence masks, reset the color stack
998 basegfx::BColorModifierStack aLastBColorModifierStack(maBColorModifierStack);
999 maBColorModifierStack = basegfx::BColorModifierStack();
1001 // paint mask to it (always with transparence intensities, evtl. with AA)
1002 process(rTransCandidate.getTransparence());
1004 // back to old color stack
1005 maBColorModifierStack = aLastBColorModifierStack;
1007 // back to old OutDev
1008 mpOutputDevice = pLastOutputDevice;
1010 // dump buffer to outdev
1011 aBufferDevice.paint();
1016 // transform group.
1017 void VclProcessor2D::RenderTransformPrimitive2D(const primitive2d::TransformPrimitive2D& rTransformCandidate)
1019 // remember current transformation and ViewInformation
1020 const basegfx::B2DHomMatrix aLastCurrentTransformation(maCurrentTransformation);
1021 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
1023 // create new transformations for CurrentTransformation
1024 // and for local ViewInformation2D
1025 maCurrentTransformation = maCurrentTransformation * rTransformCandidate.getTransformation();
1026 const geometry::ViewInformation2D aViewInformation2D(
1027 getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
1028 getViewInformation2D().getViewTransformation(),
1029 getViewInformation2D().getViewport(),
1030 getViewInformation2D().getVisualizedPage(),
1031 getViewInformation2D().getViewTime(),
1032 getViewInformation2D().getExtendedInformationSequence());
1033 updateViewInformation(aViewInformation2D);
1035 // process content
1036 process(rTransformCandidate.getChildren());
1038 // restore transformations
1039 maCurrentTransformation = aLastCurrentTransformation;
1040 updateViewInformation(aLastViewInformation2D);
1043 // new XDrawPage for ViewInformation2D
1044 void VclProcessor2D::RenderPagePreviewPrimitive2D(const primitive2d::PagePreviewPrimitive2D& rPagePreviewCandidate)
1046 // remember current transformation and ViewInformation
1047 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
1049 // create new local ViewInformation2D
1050 const geometry::ViewInformation2D aViewInformation2D(
1051 getViewInformation2D().getObjectTransformation(),
1052 getViewInformation2D().getViewTransformation(),
1053 getViewInformation2D().getViewport(),
1054 rPagePreviewCandidate.getXDrawPage(),
1055 getViewInformation2D().getViewTime(),
1056 getViewInformation2D().getExtendedInformationSequence());
1057 updateViewInformation(aViewInformation2D);
1059 // process decomposed content
1060 process(rPagePreviewCandidate.get2DDecomposition(getViewInformation2D()));
1062 // restore transformations
1063 updateViewInformation(aLastViewInformation2D);
1066 // marker
1067 void VclProcessor2D::RenderMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D& rMarkArrayCandidate)
1069 static bool bCheckCompleteMarkerDecompose(false);
1070 if(bCheckCompleteMarkerDecompose)
1072 process(rMarkArrayCandidate.get2DDecomposition(getViewInformation2D()));
1073 return;
1076 // get data
1077 const std::vector< basegfx::B2DPoint >& rPositions = rMarkArrayCandidate.getPositions();
1078 const sal_uInt32 nCount(rPositions.size());
1080 if(nCount && !rMarkArrayCandidate.getMarker().IsEmpty())
1082 // get pixel size
1083 const BitmapEx& rMarker(rMarkArrayCandidate.getMarker());
1084 const Size aBitmapSize(rMarker.GetSizePixel());
1086 if(aBitmapSize.Width() && aBitmapSize.Height())
1088 // get discrete half size
1089 const basegfx::B2DVector aDiscreteHalfSize(
1090 (aBitmapSize.getWidth() - 1.0) * 0.5,
1091 (aBitmapSize.getHeight() - 1.0) * 0.5);
1092 const bool bWasEnabled(mpOutputDevice->IsMapModeEnabled());
1094 // do not forget evtl. moved origin in target device MapMode when
1095 // switching it off; it would be missing and lead to wrong positions.
1096 // All his could be done using logic sizes and coordinates, too, but
1097 // we want a 1:1 bitmap rendering here, so it's more safe and faster
1098 // to work with switching off MapMode usage completely.
1099 const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin());
1101 mpOutputDevice->EnableMapMode(false);
1103 for(std::vector< basegfx::B2DPoint >::const_iterator aIter(rPositions.begin()); aIter != rPositions.end(); ++aIter)
1105 const basegfx::B2DPoint aDiscreteTopLeft((maCurrentTransformation * (*aIter)) - aDiscreteHalfSize);
1106 const Point aDiscretePoint(basegfx::fround(aDiscreteTopLeft.getX()), basegfx::fround(aDiscreteTopLeft.getY()));
1108 mpOutputDevice->DrawBitmapEx(aDiscretePoint + aOrigin, rMarker);
1111 mpOutputDevice->EnableMapMode(bWasEnabled);
1116 // point
1117 void VclProcessor2D::RenderPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
1119 const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions();
1120 const basegfx::BColor aRGBColor(maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
1121 const Color aVCLColor(aRGBColor);
1123 for(std::vector< basegfx::B2DPoint >::const_iterator aIter(rPositions.begin()); aIter != rPositions.end(); ++aIter)
1125 const basegfx::B2DPoint aViewPosition(maCurrentTransformation * (*aIter));
1126 const Point aPos(basegfx::fround(aViewPosition.getX()), basegfx::fround(aViewPosition.getY()));
1128 mpOutputDevice->DrawPixel(aPos, aVCLColor);
1132 void VclProcessor2D::RenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
1134 // #i101491# method restructured to clearly use the DrawPolyLine
1135 // calls starting from a deined line width
1136 const attribute::LineAttribute& rLineAttribute = rPolygonStrokeCandidate.getLineAttribute();
1137 const double fLineWidth(rLineAttribute.getWidth());
1138 bool bDone(false);
1140 if(basegfx::fTools::more(fLineWidth, 0.0))
1142 const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(fLineWidth, 0.0));
1143 const double fDiscreteLineWidth(aDiscreteUnit.getLength());
1144 const attribute::StrokeAttribute& rStrokeAttribute = rPolygonStrokeCandidate.getStrokeAttribute();
1145 const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLineAttribute.getColor()));
1146 basegfx::B2DPolyPolygon aHairlinePolyPolygon;
1148 mpOutputDevice->SetLineColor(Color(aHairlineColor));
1149 mpOutputDevice->SetFillColor();
1151 if(0.0 == rStrokeAttribute.getFullDotDashLen())
1153 // no line dashing, just copy
1154 aHairlinePolyPolygon.append(rPolygonStrokeCandidate.getB2DPolygon());
1156 else
1158 // else apply LineStyle
1159 basegfx::tools::applyLineDashing(rPolygonStrokeCandidate.getB2DPolygon(),
1160 rStrokeAttribute.getDotDashArray(),
1161 &aHairlinePolyPolygon, 0, rStrokeAttribute.getFullDotDashLen());
1164 const sal_uInt32 nCount(aHairlinePolyPolygon.count());
1166 if(nCount)
1168 const bool bAntiAliased(getOptionsDrawinglayer().IsAntiAliasing());
1169 aHairlinePolyPolygon.transform(maCurrentTransformation);
1171 if(bAntiAliased)
1173 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.0))
1175 // line in range ]0.0 .. 1.0[
1176 // paint as simple hairline
1177 for(sal_uInt32 a(0); a < nCount; a++)
1179 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
1182 bDone = true;
1184 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.0))
1186 // line in range [1.0 .. 2.0[
1187 // paint as 2x2 with dynamic line distance
1188 basegfx::B2DHomMatrix aMat;
1189 const double fDistance(fDiscreteLineWidth - 1.0);
1190 const double fHalfDistance(fDistance * 0.5);
1192 for(sal_uInt32 a(0); a < nCount; a++)
1194 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1196 aMat.set(0, 2, -fHalfDistance);
1197 aMat.set(1, 2, -fHalfDistance);
1198 aCandidate.transform(aMat);
1199 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1201 aMat.set(0, 2, fDistance);
1202 aMat.set(1, 2, 0.0);
1203 aCandidate.transform(aMat);
1204 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1206 aMat.set(0, 2, 0.0);
1207 aMat.set(1, 2, fDistance);
1208 aCandidate.transform(aMat);
1209 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1211 aMat.set(0, 2, -fDistance);
1212 aMat.set(1, 2, 0.0);
1213 aCandidate.transform(aMat);
1214 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1217 bDone = true;
1219 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 3.0))
1221 // line in range [2.0 .. 3.0]
1222 // paint as cross in a 3x3 with dynamic line distance
1223 basegfx::B2DHomMatrix aMat;
1224 const double fDistance((fDiscreteLineWidth - 1.0) * 0.5);
1226 for(sal_uInt32 a(0); a < nCount; a++)
1228 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1230 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1232 aMat.set(0, 2, -fDistance);
1233 aMat.set(1, 2, 0.0);
1234 aCandidate.transform(aMat);
1235 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1237 aMat.set(0, 2, fDistance);
1238 aMat.set(1, 2, -fDistance);
1239 aCandidate.transform(aMat);
1240 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1242 aMat.set(0, 2, fDistance);
1243 aMat.set(1, 2, fDistance);
1244 aCandidate.transform(aMat);
1245 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1247 aMat.set(0, 2, -fDistance);
1248 aMat.set(1, 2, fDistance);
1249 aCandidate.transform(aMat);
1250 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1253 bDone = true;
1255 else
1257 // #i101491# line width above 3.0
1260 else
1262 if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 1.5))
1264 // line width below 1.5, draw the basic hairline polygon
1265 for(sal_uInt32 a(0); a < nCount; a++)
1267 mpOutputDevice->DrawPolyLine(aHairlinePolyPolygon.getB2DPolygon(a), 0.0);
1270 bDone = true;
1272 else if(basegfx::fTools::lessOrEqual(fDiscreteLineWidth, 2.5))
1274 // line width is in range ]1.5 .. 2.5], use four hairlines
1275 // drawn in a square
1276 for(sal_uInt32 a(0); a < nCount; a++)
1278 basegfx::B2DPolygon aCandidate(aHairlinePolyPolygon.getB2DPolygon(a));
1279 basegfx::B2DHomMatrix aMat;
1281 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1283 aMat.set(0, 2, 1.0);
1284 aMat.set(1, 2, 0.0);
1285 aCandidate.transform(aMat);
1287 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1289 aMat.set(0, 2, 0.0);
1290 aMat.set(1, 2, 1.0);
1291 aCandidate.transform(aMat);
1293 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1295 aMat.set(0, 2, -1.0);
1296 aMat.set(1, 2, 0.0);
1297 aCandidate.transform(aMat);
1299 mpOutputDevice->DrawPolyLine(aCandidate, 0.0);
1302 bDone = true;
1304 else
1306 // #i101491# line width is above 2.5
1310 if(!bDone && rPolygonStrokeCandidate.getB2DPolygon().count() > 1000)
1312 // #i101491# If the polygon complexity uses more than a given amount, do
1313 // use OuputDevice::DrawPolyLine directly; this will avoid buffering all
1314 // decompositions in primitives (memory) and fallback to old line painting
1315 // for very complex polygons, too
1316 for(sal_uInt32 a(0); a < nCount; a++)
1318 mpOutputDevice->DrawPolyLine(
1319 aHairlinePolyPolygon.getB2DPolygon(a),
1320 fDiscreteLineWidth,
1321 rLineAttribute.getLineJoin(),
1322 rLineAttribute.getLineCap());
1325 bDone = true;
1330 if(!bDone)
1332 // remember that we enter a PolygonStrokePrimitive2D decomposition,
1333 // used for AA thick line drawing
1334 mnPolygonStrokePrimitive2D++;
1336 // line width is big enough for standard filled polygon visualisation or zero
1337 process(rPolygonStrokeCandidate.get2DDecomposition(getViewInformation2D()));
1339 // leave PolygonStrokePrimitive2D
1340 mnPolygonStrokePrimitive2D--;
1344 void VclProcessor2D::RenderEpsPrimitive2D(const primitive2d::EpsPrimitive2D& rEpsPrimitive2D)
1346 // The new decomposition of Metafiles made it necessary to add an Eps
1347 // primitive to handle embedded Eps data. On some devices, this can be
1348 // painted directly (mac, printer).
1349 // To be able to handle the replacement correctly, i need to handle it myself
1350 // since DrawEPS will not be able e.g. to rotate the replacement. To be able
1351 // to do that, i added a boolean return to OutputDevice::DrawEPS(..)
1352 // to know when EPS was handled directly already.
1353 basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
1354 aRange.transform(maCurrentTransformation * rEpsPrimitive2D.getEpsTransform());
1356 if(!aRange.isEmpty())
1358 const Rectangle aRectangle(
1359 (sal_Int32)floor(aRange.getMinX()), (sal_Int32)floor(aRange.getMinY()),
1360 (sal_Int32)ceil(aRange.getMaxX()), (sal_Int32)ceil(aRange.getMaxY()));
1362 if(!aRectangle.IsEmpty())
1364 bool bWillReallyRender = mpOutputDevice->IsDeviceOutputNecessary();
1365 // try to paint EPS directly without fallback visualisation
1366 const bool bEPSPaintedDirectly = bWillReallyRender &&
1367 mpOutputDevice->DrawEPS(
1368 aRectangle.TopLeft(),
1369 aRectangle.GetSize(),
1370 rEpsPrimitive2D.getGfxLink(),
1373 if(!bEPSPaintedDirectly)
1375 // use the decomposition which will correctly handle the
1376 // fallback visualisation using full transformation (e.g. rotation)
1377 process(rEpsPrimitive2D.get2DDecomposition(getViewInformation2D()));
1383 void VclProcessor2D::RenderSvgLinearAtomPrimitive2D(const primitive2d::SvgLinearAtomPrimitive2D& rCandidate)
1385 const double fDelta(rCandidate.getOffsetB() - rCandidate.getOffsetA());
1387 if(basegfx::fTools::more(fDelta, 0.0))
1389 const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA()));
1390 const basegfx::BColor aColorB(maBColorModifierStack.getModifiedColor(rCandidate.getColorB()));
1392 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1393 const basegfx::B2DVector aDiscreteVector(getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1394 const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373));
1396 // use color distance and discrete lengths to calculate step count
1397 const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDelta, fDiscreteUnit));
1399 // switch off line painting
1400 mpOutputDevice->SetLineColor();
1402 // prepare polygon in needed width at start position (with discrete overlap)
1403 const basegfx::B2DPolygon aPolygon(
1404 basegfx::tools::createPolygonFromRect(
1405 basegfx::B2DRange(
1406 rCandidate.getOffsetA() - fDiscreteUnit,
1407 0.0,
1408 rCandidate.getOffsetA() + (fDelta / nSteps) + fDiscreteUnit,
1409 1.0)));
1412 // prepare loop ([0.0 .. 1.0[)
1413 double fUnitScale(0.0);
1414 const double fUnitStep(1.0 / nSteps);
1416 // loop and paint
1417 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1419 basegfx::B2DPolygon aNew(aPolygon);
1421 aNew.transform(maCurrentTransformation * basegfx::tools::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0));
1422 mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorA, aColorB, fUnitScale)));
1423 mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew));
1428 void VclProcessor2D::RenderSvgRadialAtomPrimitive2D(const primitive2d::SvgRadialAtomPrimitive2D& rCandidate)
1430 const double fDeltaScale(rCandidate.getScaleB() - rCandidate.getScaleA());
1432 if(basegfx::fTools::more(fDeltaScale, 0.0))
1434 const basegfx::BColor aColorA(maBColorModifierStack.getModifiedColor(rCandidate.getColorA()));
1435 const basegfx::BColor aColorB(maBColorModifierStack.getModifiedColor(rCandidate.getColorB()));
1437 // calculate discrete unit in WorldCoordinates; use diagonal (1.0, 1.0) and divide by sqrt(2)
1438 const basegfx::B2DVector aDiscreteVector(getViewInformation2D().getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
1439 const double fDiscreteUnit(aDiscreteVector.getLength() * (1.0 / 1.414213562373));
1441 // use color distance and discrete lengths to calculate step count
1442 const sal_uInt32 nSteps(calculateStepsForSvgGradient(aColorA, aColorB, fDeltaScale, fDiscreteUnit));
1444 // switch off line painting
1445 mpOutputDevice->SetLineColor();
1447 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes)
1448 double fUnitScale(0.0);
1449 const double fUnitStep(1.0 / nSteps);
1451 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep)
1453 basegfx::B2DHomMatrix aTransform;
1454 const double fEndScale(rCandidate.getScaleB() - (fDeltaScale * fUnitScale));
1456 if(rCandidate.isTranslateSet())
1458 const basegfx::B2DVector aTranslate(
1459 basegfx::interpolate(
1460 rCandidate.getTranslateB(),
1461 rCandidate.getTranslateA(),
1462 fUnitScale));
1464 aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix(
1465 fEndScale,
1466 fEndScale,
1467 aTranslate.getX(),
1468 aTranslate.getY());
1470 else
1472 aTransform = basegfx::tools::createScaleB2DHomMatrix(
1473 fEndScale,
1474 fEndScale);
1477 basegfx::B2DPolygon aNew(basegfx::tools::createPolygonFromUnitCircle());
1479 aNew.transform(maCurrentTransformation * aTransform);
1480 mpOutputDevice->SetFillColor(Color(basegfx::interpolate(aColorB, aColorA, fUnitScale)));
1481 mpOutputDevice->DrawPolyPolygon(basegfx::B2DPolyPolygon(aNew));
1486 void VclProcessor2D::adaptLineToFillDrawMode() const
1488 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1490 if(nOriginalDrawMode & (DrawModeFlags::BlackLine|DrawModeFlags::GrayLine|DrawModeFlags::GhostedLine|DrawModeFlags::WhiteLine|DrawModeFlags::SettingsLine))
1492 DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
1494 if(nOriginalDrawMode & DrawModeFlags::BlackLine)
1496 nAdaptedDrawMode |= DrawModeFlags::BlackFill;
1498 else
1500 nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
1503 if(nOriginalDrawMode & DrawModeFlags::GrayLine)
1505 nAdaptedDrawMode |= DrawModeFlags::GrayFill;
1507 else
1509 nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
1512 if(nOriginalDrawMode & DrawModeFlags::GhostedLine)
1514 nAdaptedDrawMode |= DrawModeFlags::GhostedFill;
1516 else
1518 nAdaptedDrawMode &= ~DrawModeFlags::GhostedFill;
1521 if(nOriginalDrawMode & DrawModeFlags::WhiteLine)
1523 nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
1525 else
1527 nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
1530 if(nOriginalDrawMode & DrawModeFlags::SettingsLine)
1532 nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
1534 else
1536 nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
1539 mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
1543 void VclProcessor2D::adaptTextToFillDrawMode() const
1545 const DrawModeFlags nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1546 if(nOriginalDrawMode & (DrawModeFlags::BlackText|DrawModeFlags::GrayText|DrawModeFlags::GhostedText|DrawModeFlags::WhiteText|DrawModeFlags::SettingsText))
1548 DrawModeFlags nAdaptedDrawMode(nOriginalDrawMode);
1550 if(nOriginalDrawMode & DrawModeFlags::BlackText)
1552 nAdaptedDrawMode |= DrawModeFlags::BlackFill;
1554 else
1556 nAdaptedDrawMode &= ~DrawModeFlags::BlackFill;
1559 if(nOriginalDrawMode & DrawModeFlags::GrayText)
1561 nAdaptedDrawMode |= DrawModeFlags::GrayFill;
1563 else
1565 nAdaptedDrawMode &= ~DrawModeFlags::GrayFill;
1568 if(nOriginalDrawMode & DrawModeFlags::GhostedText)
1570 nAdaptedDrawMode |= DrawModeFlags::GhostedFill;
1572 else
1574 nAdaptedDrawMode &= ~DrawModeFlags::GhostedFill;
1577 if(nOriginalDrawMode & DrawModeFlags::WhiteText)
1579 nAdaptedDrawMode |= DrawModeFlags::WhiteFill;
1581 else
1583 nAdaptedDrawMode &= ~DrawModeFlags::WhiteFill;
1586 if(nOriginalDrawMode & DrawModeFlags::SettingsText)
1588 nAdaptedDrawMode |= DrawModeFlags::SettingsFill;
1590 else
1592 nAdaptedDrawMode &= ~DrawModeFlags::SettingsFill;
1595 mpOutputDevice->SetDrawMode(nAdaptedDrawMode);
1599 void VclProcessor2D::RenderOpenGLPrimitive2D(const primitive2d::OpenGLPrimitive2D& rCandidate)
1601 // Just draw a dummy rect to see primitive rendering is working.
1602 mpOutputDevice->SetLineColor(COL_BLACK);
1603 mpOutputDevice->SetFillColor(COL_RED);
1604 mpOutputDevice->DrawRect(Rectangle(rCandidate.getPos(),Size(2000,2000)));
1607 // process support
1609 VclProcessor2D::VclProcessor2D(
1610 const geometry::ViewInformation2D& rViewInformation,
1611 OutputDevice& rOutDev)
1612 : BaseProcessor2D(rViewInformation),
1613 mpOutputDevice(&rOutDev),
1614 maBColorModifierStack(),
1615 maCurrentTransformation(),
1616 maDrawinglayerOpt(),
1617 mnPolygonStrokePrimitive2D(0)
1619 // set digit language, derived from SvtCTLOptions to have the correct
1620 // number display for arabic/hindi numerals
1621 rOutDev.SetDigitLanguage(drawinglayer::detail::getDigitLanguage());
1624 VclProcessor2D::~VclProcessor2D()
1630 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */