1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <drawinglayer/processor2d/canvasprocessor.hxx>
21 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
22 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
23 #include <com/sun/star/rendering/XCanvas.hpp>
24 #include <vcl/canvastools.hxx>
25 #include <basegfx/tools/canvastools.hxx>
26 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
29 #include <canvas/canvastools.hxx>
30 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
31 #include <basegfx/polygon/b2dpolygonclipper.hxx>
32 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
33 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
34 #include <cppcanvas/basegfxfactory.hxx>
35 #include <com/sun/star/rendering/XBitmapCanvas.hpp>
36 #include <cppcanvas/vclfactory.hxx>
37 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
39 #include <com/sun/star/rendering/TextDirection.hpp>
40 #include <vclhelperbitmaptransform.hxx>
41 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
42 #include <basegfx/polygon/b2dpolygontools.hxx>
43 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
44 #include <basegfx/tuple/b2i64tuple.hxx>
45 #include <basegfx/range/b2irange.hxx>
46 #include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp>
47 #include <com/sun/star/rendering/PanoseProportion.hpp>
48 #include <com/sun/star/rendering/CompositeOperation.hpp>
49 #include <com/sun/star/rendering/StrokeAttributes.hpp>
50 #include <com/sun/star/rendering/PathJoinType.hpp>
51 #include <com/sun/star/rendering/PathCapType.hpp>
52 #include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx>
53 #include <com/sun/star/rendering/TexturingMode.hpp>
54 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
55 #include <vclhelperbufferdevice.hxx>
56 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
57 #include <helperwrongspellrenderer.hxx>
58 #include <basegfx/matrix/b2dhommatrixtools.hxx>
60 #include "getdigitlanguage.hxx"
62 //////////////////////////////////////////////////////////////////////////////
64 using namespace com::sun::star
;
66 //////////////////////////////////////////////////////////////////////////////
68 namespace drawinglayer
72 //////////////////////////////////////////////////////////////////////////////
73 // single primitive renderers
75 void canvasProcessor2D::impRenderMaskPrimitive2D(const primitive2d::MaskPrimitive2D
& rMaskCandidate
)
77 const primitive2d::Primitive2DSequence
& rChildren
= rMaskCandidate
.getChildren();
78 static bool bUseMaskBitmapMethod(true);
80 if(rChildren
.hasElements())
82 basegfx::B2DPolyPolygon
aMask(rMaskCandidate
.getMask());
86 // no mask, no clipping. recursively paint content
91 // there are principally two methods for implementing the mask primitive. One
92 // is to set a clip polygon at the canvas, the other is to create and use a
93 // transparence-using XBitmap for content and draw the mask as transparence. Both have their
94 // advantages and disadvantages, so here are both with a bool allowing simple
96 if(bUseMaskBitmapMethod
)
98 // get logic range of transparent part, clip with ViewRange
99 basegfx::B2DRange
aLogicRange(aMask
.getB2DRange());
101 if(!getViewInformation2D().getViewport().isEmpty())
103 aLogicRange
.intersect(getViewInformation2D().getViewport());
106 if(!aLogicRange
.isEmpty())
108 // get discrete range of transparent part
109 basegfx::B2DRange
aDiscreteRange(aLogicRange
);
110 aDiscreteRange
.transform(getViewInformation2D().getObjectToViewTransformation());
112 // expand to next covering discrete values (pixel bounds)
113 aDiscreteRange
.expand(basegfx::B2DTuple(floor(aDiscreteRange
.getMinX()), floor(aDiscreteRange
.getMinY())));
114 aDiscreteRange
.expand(basegfx::B2DTuple(ceil(aDiscreteRange
.getMaxX()), ceil(aDiscreteRange
.getMaxY())));
116 // use VCL-based buffer device
117 impBufferDevice
aBufferDevice(*mpOutputDevice
, aDiscreteRange
, false);
119 if(aBufferDevice
.isVisible())
121 // remember current OutDev, Canvas and ViewInformation
122 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
123 uno::Reference
< rendering::XCanvas
> xLastCanvas(mxCanvas
);
124 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
126 // prepare discrete offset for XBitmap, do not forget that the buffer bitmap
127 // may be truncated to discrete visible pixels
128 const basegfx::B2DHomMatrix
aDiscreteOffset(basegfx::tools::createTranslateB2DHomMatrix(
129 aDiscreteRange
.getMinX() > 0.0 ? -aDiscreteRange
.getMinX() : 0.0,
130 aDiscreteRange
.getMinY() > 0.0 ? -aDiscreteRange
.getMinY() : 0.0));
132 // create new local ViewInformation2D with new transformation
133 const geometry::ViewInformation2D
aViewInformation2D(
134 getViewInformation2D().getObjectTransformation(),
135 aDiscreteOffset
* getViewInformation2D().getViewTransformation(),
136 getViewInformation2D().getViewport(),
137 getViewInformation2D().getVisualizedPage(),
138 getViewInformation2D().getViewTime(),
139 getViewInformation2D().getExtendedInformationSequence());
140 updateViewInformation(aViewInformation2D
);
142 // set OutDev and Canvas to content target
143 mpOutputDevice
= &aBufferDevice
.getContent();
144 mxCanvas
= mpOutputDevice
->GetCanvas();
145 canvas::tools::setViewStateTransform(maViewState
, getViewInformation2D().getViewTransformation());
147 // if ViewState transform is changed, the clipping polygon needs to be adapted, too
148 const basegfx::B2DPolyPolygon
aOldClipPolyPolygon(maClipPolyPolygon
);
150 if(maClipPolyPolygon
.count())
152 maClipPolyPolygon
.transform(aDiscreteOffset
);
153 maViewState
.Clip
= basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), maClipPolyPolygon
);
160 const basegfx::BColor
aBlack(0.0, 0.0, 0.0);
161 maRenderState
.DeviceColor
= aBlack
.colorToDoubleSequence(mxCanvas
->getDevice());
163 if(getOptionsDrawinglayer().IsAntiAliasing())
165 // with AA, use 8bit AlphaMask to get nice borders
166 VirtualDevice
& rTransparence
= aBufferDevice
.getTransparence();
167 rTransparence
.GetCanvas()->fillPolyPolygon(
168 basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), aMask
),
169 maViewState
, maRenderState
);
173 // No AA, use 1bit mask
174 VirtualDevice
& rMask
= aBufferDevice
.getMask();
175 rMask
.GetCanvas()->fillPolyPolygon(
176 basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), aMask
),
177 maViewState
, maRenderState
);
180 // back to old color stack, OutDev, Canvas and ViewTransform
181 mpOutputDevice
= pLastOutputDevice
;
182 mxCanvas
= xLastCanvas
;
183 updateViewInformation(aLastViewInformation2D
);
184 canvas::tools::setViewStateTransform(maViewState
, getViewInformation2D().getViewTransformation());
186 // restore clipping polygon
187 maClipPolyPolygon
= aOldClipPolyPolygon
;
189 if(maClipPolyPolygon
.count())
191 maViewState
.Clip
= basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), maClipPolyPolygon
);
194 // dump buffer to outdev
195 aBufferDevice
.paint();
201 // transform new mask polygon to view coordinates for processing. All masks
202 // are processed in view coordinates and clipped against each other evtl. to
203 // create multi-clips
204 aMask
.transform(getViewInformation2D().getObjectTransformation());
206 // remember last current clip polygon
207 const basegfx::B2DPolyPolygon
aLastClipPolyPolygon(maClipPolyPolygon
);
209 if(maClipPolyPolygon
.count())
211 // there is already a clip polygon set; build clipped union of
212 // current mask polygon and new one
213 maClipPolyPolygon
= basegfx::tools::clipPolyPolygonOnPolyPolygon(aMask
, maClipPolyPolygon
, false, false);
218 maClipPolyPolygon
= aMask
;
222 if(maClipPolyPolygon
.count())
224 // set new as clip polygon
225 maViewState
.Clip
= basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), maClipPolyPolygon
);
230 maViewState
.Clip
.clear();
236 // restore local current to rescued clip polygon
237 maClipPolyPolygon
= aLastClipPolyPolygon
;
240 if(maClipPolyPolygon
.count())
242 // set new as clip polygon
243 maViewState
.Clip
= basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), maClipPolyPolygon
);
248 maViewState
.Clip
.clear();
255 void canvasProcessor2D::impRenderMetafilePrimitive2D(const primitive2d::MetafilePrimitive2D
& rMetaCandidate
)
257 GDIMetaFile aMetaFile
;
259 if(maBColorModifierStack
.count())
261 const basegfx::BColor
aRGBBaseColor(0, 0, 0);
262 const basegfx::BColor
aRGBColor(maBColorModifierStack
.getModifiedColor(aRGBBaseColor
));
263 aMetaFile
= rMetaCandidate
.getMetaFile().GetMonochromeMtf(Color(aRGBColor
));
267 aMetaFile
= rMetaCandidate
.getMetaFile();
270 cppcanvas::BitmapCanvasSharedPtr
pCanvas(cppcanvas::VCLFactory::getInstance().createCanvas(
271 uno::Reference
<rendering::XBitmapCanvas
>(mxCanvas
, uno::UNO_QUERY_THROW
)));
272 cppcanvas::RendererSharedPtr
pMtfRenderer(cppcanvas::VCLFactory::getInstance().createRenderer(
273 pCanvas
, aMetaFile
, cppcanvas::Renderer::Parameters()));
277 pCanvas
->setTransformation(getViewInformation2D().getObjectToViewTransformation());
278 pMtfRenderer
->setTransformation(rMetaCandidate
.getTransform());
279 pMtfRenderer
->draw();
283 void canvasProcessor2D::impRenderTextSimplePortionPrimitive2D(const primitive2d::TextSimplePortionPrimitive2D
& rTextCandidate
)
285 if(rTextCandidate
.getTextLength())
289 const basegfx::B2DHomMatrix
aLocalTransform(getViewInformation2D().getObjectToViewTransformation() * rTextCandidate
.getTextTransform());
290 basegfx::B2DVector aScale
, aTranslate
;
292 aLocalTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
295 if(!basegfx::fTools::equalZero(fShearX
))
297 // text is sheared. As long as the canvas renderers do not support this,
298 // use the decomposed primitive
299 process(rTextCandidate
.get2DDecomposition(getViewInformation2D()));
303 const attribute::FontAttribute
& rFontAttr(rTextCandidate
.getFontAttribute());
304 rendering::FontRequest aFontRequest
;
306 aFontRequest
.FontDescription
.FamilyName
= rFontAttr
.getFamilyName();
307 aFontRequest
.FontDescription
.StyleName
= rFontAttr
.getStyleName();
308 aFontRequest
.FontDescription
.IsSymbolFont
= rFontAttr
.getSymbol() ? util::TriState_YES
: util::TriState_NO
;
309 aFontRequest
.FontDescription
.IsVertical
= rFontAttr
.getVertical() ? util::TriState_YES
: util::TriState_NO
;
310 // TODO(F2): improve vclenum->panose conversion
311 aFontRequest
.FontDescription
.FontDescription
.Weight
= static_cast< sal_uInt8
>(rFontAttr
.getWeight());
312 aFontRequest
.FontDescription
.FontDescription
.Proportion
=
313 rFontAttr
.getMonospaced()
314 ? rendering::PanoseProportion::MONO_SPACED
315 : rendering::PanoseProportion::ANYTHING
;
316 aFontRequest
.FontDescription
.FontDescription
.Letterform
= rFontAttr
.getItalic() ? 9 : 0;
318 // init CellSize to 1.0, else a default font height will be used
319 aFontRequest
.CellSize
= 1.0;
320 aFontRequest
.Locale
= rTextCandidate
.getLocale();
322 // font matrix should only be used for glyph rotations etc.
323 com::sun::star::geometry::Matrix2D aFontMatrix
;
324 canvas::tools::setIdentityMatrix2D(aFontMatrix
);
326 uno::Reference
<rendering::XCanvasFont
> xFont(mxCanvas
->createFont(
327 aFontRequest
, uno::Sequence
< beans::PropertyValue
>(), aFontMatrix
));
331 // got a font, now try to get a TextLayout
332 const rendering::StringContext
aStringContext(
333 rTextCandidate
.getText(), rTextCandidate
.getTextPosition(), rTextCandidate
.getTextLength());
334 uno::Reference
<rendering::XTextLayout
> xLayout(xFont
->createTextLayout(
335 aStringContext
, com::sun::star::rendering::TextDirection::WEAK_LEFT_TO_RIGHT
, 0));
339 // got a text layout, apply DXArray if given
340 const ::std::vector
< double >& rDXArray
= rTextCandidate
.getDXArray();
341 const sal_uInt32
nDXCount(rDXArray
.size());
345 // DXArray does not need to be adapted to getTextPosition/getTextLength,
346 // it is already provided correctly
347 const uno::Sequence
< double > aDXSequence(&rDXArray
[0], nDXCount
);
348 xLayout
->applyLogicalAdvancements(aDXSequence
);
352 const basegfx::BColor
aRGBColor(maBColorModifierStack
.getModifiedColor(rTextCandidate
.getFontColor()));
353 maRenderState
.DeviceColor
= aRGBColor
.colorToDoubleSequence(mxCanvas
->getDevice());
355 // set text transformation
356 canvas::tools::setRenderStateTransform(maRenderState
,
357 getViewInformation2D().getObjectTransformation() * rTextCandidate
.getTextTransform());
360 mxCanvas
->drawTextLayout(xLayout
, maViewState
, maRenderState
);
367 void canvasProcessor2D::impRenderBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D
& rBitmapCandidate
)
369 // apply possible color modification to BitmapEx
370 BitmapEx
aModifiedBitmapEx(impModifyBitmapEx(maBColorModifierStack
, rBitmapCandidate
.getBitmapEx()));
372 if(aModifiedBitmapEx
.IsEmpty())
374 // replace with color filled polygon
375 const basegfx::BColor
aModifiedColor(maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
376 const basegfx::B2DPolygon
aPolygon(basegfx::tools::createUnitPolygon());
378 maRenderState
.DeviceColor
= aModifiedColor
.colorToDoubleSequence(mxCanvas
->getDevice());
379 canvas::tools::setRenderStateTransform(maRenderState
,
380 getViewInformation2D().getObjectTransformation() * rBitmapCandidate
.getTransform());
382 mxCanvas
->fillPolyPolygon(basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
383 mxCanvas
->getDevice(), basegfx::B2DPolyPolygon(aPolygon
)), maViewState
, maRenderState
);
387 // adapt object's transformation to the correct scale
388 basegfx::B2DVector aScale
, aTranslate
;
389 const Size
aSizePixel(aModifiedBitmapEx
.GetSizePixel());
391 if(0 != aSizePixel
.Width() && 0 != aSizePixel
.Height())
393 double fRotate
, fShearX
;
394 rBitmapCandidate
.getTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
);
395 const basegfx::B2DHomMatrix
aNewMatrix(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
396 aScale
.getX() / aSizePixel
.Width(), aScale
.getY() / aSizePixel
.Height(),
397 fShearX
, fRotate
, aTranslate
.getX(), aTranslate
.getY()));
399 canvas::tools::setRenderStateTransform(maRenderState
,
400 getViewInformation2D().getObjectTransformation() * aNewMatrix
);
402 mxCanvas
->drawBitmap(
403 vcl::unotools::xBitmapFromBitmapEx(mxCanvas
->getDevice(), aModifiedBitmapEx
),
404 maViewState
, maRenderState
);
409 void canvasProcessor2D::impRenderTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D
& rTransparenceCandidate
)
411 const primitive2d::Primitive2DSequence
& rChildren
= rTransparenceCandidate
.getChildren();
412 const primitive2d::Primitive2DSequence
& rTransparence
= rTransparenceCandidate
.getTransparence();
414 if(rChildren
.hasElements() && rTransparence
.hasElements())
416 // get logic range of transparent part and clip with ViewRange
417 basegfx::B2DRange
aLogicRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rChildren
, getViewInformation2D()));
419 if(!getViewInformation2D().getViewport().isEmpty())
421 aLogicRange
.intersect(getViewInformation2D().getViewport());
424 if(!aLogicRange
.isEmpty())
426 // get discrete range of transparent part
427 basegfx::B2DRange
aDiscreteRange(aLogicRange
);
428 aDiscreteRange
.transform(getViewInformation2D().getObjectToViewTransformation());
430 // expand to next covering discrete values (pixel bounds)
431 aDiscreteRange
.expand(basegfx::B2DTuple(floor(aDiscreteRange
.getMinX()), floor(aDiscreteRange
.getMinY())));
432 aDiscreteRange
.expand(basegfx::B2DTuple(ceil(aDiscreteRange
.getMaxX()), ceil(aDiscreteRange
.getMaxY())));
434 // use VCL-based buffer device
435 impBufferDevice
aBufferDevice(*mpOutputDevice
, aDiscreteRange
, false);
437 if(aBufferDevice
.isVisible())
439 // remember current OutDev, Canvas and ViewInformation
440 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
441 uno::Reference
< rendering::XCanvas
> xLastCanvas(mxCanvas
);
442 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
444 // prepare discrete offset for XBitmap, do not forget that the buffer bitmap
445 // may be truncated to discrete visible pixels
446 const basegfx::B2DHomMatrix
aDiscreteOffset(basegfx::tools::createTranslateB2DHomMatrix(
447 aDiscreteRange
.getMinX() > 0.0 ? -aDiscreteRange
.getMinX() : 0.0,
448 aDiscreteRange
.getMinY() > 0.0 ? -aDiscreteRange
.getMinY() : 0.0));
450 // create new local ViewInformation2D with new transformation
451 const geometry::ViewInformation2D
aViewInformation2D(
452 getViewInformation2D().getObjectTransformation(),
453 aDiscreteOffset
* getViewInformation2D().getViewTransformation(),
454 getViewInformation2D().getViewport(),
455 getViewInformation2D().getVisualizedPage(),
456 getViewInformation2D().getViewTime(),
457 getViewInformation2D().getExtendedInformationSequence());
458 updateViewInformation(aViewInformation2D
);
460 // set OutDev and Canvas to content target
461 mpOutputDevice
= &aBufferDevice
.getContent();
462 mxCanvas
= mpOutputDevice
->GetCanvas();
463 canvas::tools::setViewStateTransform(maViewState
, getViewInformation2D().getViewTransformation());
465 // if ViewState transform is changed, the clipping polygon needs to be adapted, too
466 const basegfx::B2DPolyPolygon
aOldClipPolyPolygon(maClipPolyPolygon
);
468 if(maClipPolyPolygon
.count())
470 maClipPolyPolygon
.transform(aDiscreteOffset
);
471 maViewState
.Clip
= basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), maClipPolyPolygon
);
478 mpOutputDevice
= &aBufferDevice
.getTransparence();
479 mxCanvas
= mpOutputDevice
->GetCanvas();
480 canvas::tools::setViewStateTransform(maViewState
, getViewInformation2D().getViewTransformation());
482 // when painting transparence masks, reset the color stack
483 basegfx::BColorModifierStack
aLastBColorModifierStack(maBColorModifierStack
);
484 maBColorModifierStack
= basegfx::BColorModifierStack();
486 // paint mask to it (always with transparence intensities, evtl. with AA)
487 process(rTransparence
);
489 // back to old color stack, OutDev, Canvas and ViewTransform
490 maBColorModifierStack
= aLastBColorModifierStack
;
491 mpOutputDevice
= pLastOutputDevice
;
492 mxCanvas
= xLastCanvas
;
493 updateViewInformation(aLastViewInformation2D
);
494 canvas::tools::setViewStateTransform(maViewState
, getViewInformation2D().getViewTransformation());
496 // restore clipping polygon
497 maClipPolyPolygon
= aOldClipPolyPolygon
;
499 if(maClipPolyPolygon
.count())
501 maViewState
.Clip
= basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), maClipPolyPolygon
);
504 // dump buffer to outdev
505 aBufferDevice
.paint();
511 void canvasProcessor2D::impRenderPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D
& rPolygonStrokePrimitive
)
513 // support direct fat line geometry. This moves the decomposition to the canvas.
514 // As long as our canvases are used (which also use basegfx tooling) this makes
515 // no difference, but potentially canvases may better support this
516 static bool bSupportFatLineDirectly(true);
517 bool bOutputDone(false);
519 if(bSupportFatLineDirectly
)
521 const attribute::LineAttribute
& rLineAttribute
= rPolygonStrokePrimitive
.getLineAttribute();
522 const attribute::StrokeAttribute
& rStrokeAttribute
= rPolygonStrokePrimitive
.getStrokeAttribute();
524 if(0.0 < rLineAttribute
.getWidth() || 0 != rStrokeAttribute
.getDotDashArray().size())
526 rendering::StrokeAttributes aStrokeAttribute
;
528 aStrokeAttribute
.StrokeWidth
= rLineAttribute
.getWidth();
529 aStrokeAttribute
.MiterLimit
= 15.0; // degrees; maybe here (15.0 * F_PI180) is needed, not clear in the documentation
530 const ::std::vector
< double >& rDotDashArray
= rStrokeAttribute
.getDotDashArray();
532 if(!rDotDashArray
.empty())
534 aStrokeAttribute
.DashArray
= uno::Sequence
< double >(&rDotDashArray
[0], rDotDashArray
.size());
537 switch(rLineAttribute
.getLineJoin())
539 default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
540 aStrokeAttribute
.JoinType
= rendering::PathJoinType::NONE
;
542 case basegfx::B2DLINEJOIN_BEVEL
:
543 aStrokeAttribute
.JoinType
= rendering::PathJoinType::BEVEL
;
545 case basegfx::B2DLINEJOIN_MITER
:
546 aStrokeAttribute
.JoinType
= rendering::PathJoinType::MITER
;
548 case basegfx::B2DLINEJOIN_ROUND
:
549 aStrokeAttribute
.JoinType
= rendering::PathJoinType::ROUND
;
553 switch(rLineAttribute
.getLineCap())
555 case com::sun::star::drawing::LineCap_ROUND
:
556 aStrokeAttribute
.StartCapType
= rendering::PathCapType::ROUND
;
557 aStrokeAttribute
.EndCapType
= rendering::PathCapType::ROUND
;
559 case com::sun::star::drawing::LineCap_SQUARE
:
560 aStrokeAttribute
.StartCapType
= rendering::PathCapType::SQUARE
;
561 aStrokeAttribute
.EndCapType
= rendering::PathCapType::SQUARE
;
563 default: // com::sun::star::drawing::LineCap_BUTT
564 aStrokeAttribute
.StartCapType
= rendering::PathCapType::BUTT
;
565 aStrokeAttribute
.EndCapType
= rendering::PathCapType::BUTT
;
569 const basegfx::BColor
aHairlineColor(maBColorModifierStack
.getModifiedColor(rLineAttribute
.getColor()));
570 maRenderState
.DeviceColor
= aHairlineColor
.colorToDoubleSequence(mxCanvas
->getDevice());
571 canvas::tools::setRenderStateTransform(maRenderState
, getViewInformation2D().getObjectTransformation());
573 mxCanvas
->strokePolyPolygon(
574 basegfx::unotools::xPolyPolygonFromB2DPolygon(mxCanvas
->getDevice(), rPolygonStrokePrimitive
.getB2DPolygon()),
575 maViewState
, maRenderState
, aStrokeAttribute
);
583 // process decomposition
584 process(rPolygonStrokePrimitive
.get2DDecomposition(getViewInformation2D()));
588 void canvasProcessor2D::impRenderFillBitmapPrimitive2D(const primitive2d::FillBitmapPrimitive2D
& rFillBitmapPrimitive2D
)
590 // support tiled fills directly when tiling is on
591 static bool bSupportFillBitmapDirectly(true);
592 bool bOutputDone(false);
594 if(bSupportFillBitmapDirectly
)
596 const attribute::FillBitmapAttribute
& rFillBitmapAttribute
= rFillBitmapPrimitive2D
.getFillBitmap();
598 if(rFillBitmapAttribute
.getTiling())
600 // apply possible color modification to Bitmap
601 const BitmapEx
aChangedBitmapEx(impModifyBitmapEx(maBColorModifierStack
, rFillBitmapAttribute
.getBitmapEx()));
603 if(aChangedBitmapEx
.IsEmpty())
605 // replace with color filled polygon
606 const basegfx::BColor
aModifiedColor(maBColorModifierStack
.getModifiedColor(basegfx::BColor()));
607 const basegfx::B2DPolygon
aPolygon(basegfx::tools::createUnitPolygon());
609 maRenderState
.DeviceColor
= aModifiedColor
.colorToDoubleSequence(mxCanvas
->getDevice());
610 canvas::tools::setRenderStateTransform(maRenderState
,
611 getViewInformation2D().getObjectTransformation() * rFillBitmapPrimitive2D
.getTransformation());
613 mxCanvas
->fillPolyPolygon(basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
614 mxCanvas
->getDevice(), basegfx::B2DPolyPolygon(aPolygon
)), maViewState
, maRenderState
);
618 const Size
aSizePixel(aChangedBitmapEx
.GetSizePixel());
620 if(0 != aSizePixel
.Width() && 0 != aSizePixel
.Height())
622 // create texture matrix from texture to object (where object is unit square here),
623 // so use values directly
624 const basegfx::B2DHomMatrix
aTextureMatrix(basegfx::tools::createScaleTranslateB2DHomMatrix(
625 rFillBitmapAttribute
.getSize().getX(), rFillBitmapAttribute
.getSize().getY(),
626 rFillBitmapAttribute
.getTopLeft().getX(), rFillBitmapAttribute
.getTopLeft().getY()));
628 // create and fill texture
629 rendering::Texture aTexture
;
631 basegfx::unotools::affineMatrixFromHomMatrix(aTexture
.AffineTransform
, aTextureMatrix
);
632 aTexture
.Alpha
= 1.0;
633 aTexture
.Bitmap
= vcl::unotools::xBitmapFromBitmapEx(mxCanvas
->getDevice(), aChangedBitmapEx
);
634 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
635 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
637 // canvas needs a polygon to fill, create unit rectangle polygon
638 const basegfx::B2DPolygon
aOutlineRectangle(basegfx::tools::createUnitPolygon());
640 // set primitive's transformation as render state transform
641 canvas::tools::setRenderStateTransform(maRenderState
,
642 getViewInformation2D().getObjectTransformation() * rFillBitmapPrimitive2D
.getTransformation());
644 // put texture into a uno sequence for handover
645 uno::Sequence
< rendering::Texture
> aSeq(1);
648 // draw textured rectangle
649 mxCanvas
->fillTexturedPolyPolygon(
650 basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), basegfx::B2DPolyPolygon(aOutlineRectangle
)),
651 maViewState
, maRenderState
, aSeq
);
661 // process decomposition
662 process(rFillBitmapPrimitive2D
.get2DDecomposition(getViewInformation2D()));
666 void canvasProcessor2D::impRenderUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D
& rUniTransparenceCandidate
)
668 if(0.0 == rUniTransparenceCandidate
.getTransparence())
670 // not transparent at all, directly use content
671 process(rUniTransparenceCandidate
.getChildren());
673 else if(rUniTransparenceCandidate
.getTransparence() > 0.0 && rUniTransparenceCandidate
.getTransparence() < 1.0)
675 const primitive2d::Primitive2DSequence rChildren
= rUniTransparenceCandidate
.getChildren();
677 if(rChildren
.hasElements())
679 bool bOutputDone(false);
681 // Detect if a single PolyPolygonColorPrimitive2D is contained; in that case,
682 // use the fillPolyPolygon method with correctly set transparence. This is a often used
683 // case, so detectiong it is valuable
684 if(1 == rChildren
.getLength())
686 const primitive2d::Primitive2DReference
xReference(rChildren
[0]);
687 const primitive2d::PolyPolygonColorPrimitive2D
* pPoPoColor
= dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D
* >(xReference
.get());
689 if(pPoPoColor
&& PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
== pPoPoColor
->getPrimitive2DID())
691 // direct draw of PolyPolygon with color and transparence
692 const basegfx::BColor
aPolygonColor(maBColorModifierStack
.getModifiedColor(pPoPoColor
->getBColor()));
694 // add transparence modulation value to DeviceColor
695 uno::Sequence
< double > aColor(4);
697 aColor
[0] = aPolygonColor
.getRed();
698 aColor
[1] = aPolygonColor
.getGreen();
699 aColor
[2] = aPolygonColor
.getBlue();
700 aColor
[3] = 1.0 - rUniTransparenceCandidate
.getTransparence();
701 maRenderState
.DeviceColor
= aColor
;
703 canvas::tools::setRenderStateTransform(maRenderState
, getViewInformation2D().getObjectTransformation());
704 mxCanvas
->fillPolyPolygon(
705 basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), pPoPoColor
->getB2DPolyPolygon()),
706 maViewState
, maRenderState
);
713 // process decomposition. This will be decomposed to an TransparencePrimitive2D
714 // with the same child context and a single polygon for transparent context. This could be
715 // directly handled here with known VCL-buffer technology, but would only
716 // make a small difference compared to directly rendering the TransparencePrimitive2D
717 // using impRenderTransparencePrimitive2D above.
718 process(rUniTransparenceCandidate
.get2DDecomposition(getViewInformation2D()));
724 //////////////////////////////////////////////////////////////////////////////
725 // internal processing support
727 void canvasProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
729 switch(rCandidate
.getPrimitive2DID())
731 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
:
733 // direct draw of hairline
734 const primitive2d::PolygonHairlinePrimitive2D
& rPolygonCandidate
= static_cast< const primitive2d::PolygonHairlinePrimitive2D
& >(rCandidate
);
735 const basegfx::BColor
aHairlineColor(maBColorModifierStack
.getModifiedColor(rPolygonCandidate
.getBColor()));
737 maRenderState
.DeviceColor
= aHairlineColor
.colorToDoubleSequence(mxCanvas
->getDevice());
738 canvas::tools::setRenderStateTransform(maRenderState
, getViewInformation2D().getObjectTransformation());
739 mxCanvas
->drawPolyPolygon(
740 basegfx::unotools::xPolyPolygonFromB2DPolygon(mxCanvas
->getDevice(), rPolygonCandidate
.getB2DPolygon()),
741 maViewState
, maRenderState
);
745 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
:
747 // direct draw of PolyPolygon with color
748 const primitive2d::PolyPolygonColorPrimitive2D
& rPolygonCandidate
= static_cast< const primitive2d::PolyPolygonColorPrimitive2D
& >(rCandidate
);
749 const basegfx::BColor
aPolygonColor(maBColorModifierStack
.getModifiedColor(rPolygonCandidate
.getBColor()));
751 maRenderState
.DeviceColor
= aPolygonColor
.colorToDoubleSequence(mxCanvas
->getDevice());
752 canvas::tools::setRenderStateTransform(maRenderState
, getViewInformation2D().getObjectTransformation());
753 mxCanvas
->fillPolyPolygon(
754 basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas
->getDevice(), rPolygonCandidate
.getB2DPolyPolygon()),
755 maViewState
, maRenderState
);
759 case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D
:
761 // modified color group. Force output to unified color.
762 const primitive2d::ModifiedColorPrimitive2D
& rModifiedCandidate
= static_cast< const primitive2d::ModifiedColorPrimitive2D
& >(rCandidate
);
764 if(rModifiedCandidate
.getChildren().hasElements())
766 maBColorModifierStack
.push(rModifiedCandidate
.getColorModifier());
767 process(rModifiedCandidate
.getChildren());
768 maBColorModifierStack
.pop();
773 case PRIMITIVE2D_ID_MASKPRIMITIVE2D
:
776 impRenderMaskPrimitive2D(static_cast< const primitive2d::MaskPrimitive2D
& >(rCandidate
));
780 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
:
782 // transform group. Remember current ViewInformation2D
783 const primitive2d::TransformPrimitive2D
& rTransformCandidate
= static_cast< const primitive2d::TransformPrimitive2D
& >(rCandidate
);
784 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
786 // create new local ViewInformation2D with new transformation
787 const geometry::ViewInformation2D
aViewInformation2D(
788 getViewInformation2D().getObjectTransformation() * rTransformCandidate
.getTransformation(),
789 getViewInformation2D().getViewTransformation(),
790 getViewInformation2D().getViewport(),
791 getViewInformation2D().getVisualizedPage(),
792 getViewInformation2D().getViewTime(),
793 getViewInformation2D().getExtendedInformationSequence());
794 updateViewInformation(aViewInformation2D
);
797 canvas::tools::setRenderStateTransform(maRenderState
, getViewInformation2D().getObjectTransformation());
800 process(rTransformCandidate
.getChildren());
802 // restore transformations
803 updateViewInformation(aLastViewInformation2D
);
806 canvas::tools::setRenderStateTransform(maRenderState
, getViewInformation2D().getObjectTransformation());
810 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D
:
812 // new XDrawPage for ViewInformation2D
813 const primitive2d::PagePreviewPrimitive2D
& rPagePreviewCandidate
= static_cast< const primitive2d::PagePreviewPrimitive2D
& >(rCandidate
);
815 // remember current transformation and ViewInformation
816 const geometry::ViewInformation2D
aLastViewInformation2D(getViewInformation2D());
818 // create new local ViewInformation2D
819 const geometry::ViewInformation2D
aViewInformation2D(
820 getViewInformation2D().getObjectTransformation(),
821 getViewInformation2D().getViewTransformation(),
822 getViewInformation2D().getViewport(),
823 rPagePreviewCandidate
.getXDrawPage(),
824 getViewInformation2D().getViewTime(),
825 getViewInformation2D().getExtendedInformationSequence());
826 updateViewInformation(aViewInformation2D
);
828 // proccess decomposed content
829 process(rPagePreviewCandidate
.get2DDecomposition(getViewInformation2D()));
831 // restore transformations
832 updateViewInformation(aLastViewInformation2D
);
835 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D
:
837 // MetaFile primitive
838 impRenderMetafilePrimitive2D(static_cast< const primitive2d::MetafilePrimitive2D
& >(rCandidate
));
842 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D
:
844 // PointArray primitive
845 const primitive2d::PointArrayPrimitive2D
& rPointArrayCandidate
= static_cast< const primitive2d::PointArrayPrimitive2D
& >(rCandidate
);
848 const basegfx::BColor
aRGBColor(maBColorModifierStack
.getModifiedColor(rPointArrayCandidate
.getRGBColor()));
849 maRenderState
.DeviceColor
= aRGBColor
.colorToDoubleSequence(mxCanvas
->getDevice());
850 canvas::tools::setRenderStateTransform(maRenderState
, getViewInformation2D().getObjectTransformation());
852 const std::vector
< basegfx::B2DPoint
>& rPointVector
= rPointArrayCandidate
.getPositions();
853 const sal_uInt32
nPointCount(rPointVector
.size());
855 for(sal_uInt32
a(0); a
< nPointCount
; a
++)
857 const basegfx::B2DPoint
& rPoint
= rPointVector
[a
];
858 mxCanvas
->drawPoint(basegfx::unotools::point2DFromB2DPoint(rPoint
), maViewState
, maRenderState
);
863 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
:
865 // TextSimplePortion primitive
866 impRenderTextSimplePortionPrimitive2D(static_cast< const primitive2d::TextSimplePortionPrimitive2D
& >(rCandidate
));
870 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D
:
873 impRenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D
& >(rCandidate
));
877 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D
:
879 // Transparence primitive
880 impRenderTransparencePrimitive2D(static_cast< const primitive2d::TransparencePrimitive2D
& >(rCandidate
));
884 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
:
886 // PolygonStrokePrimitive
887 impRenderPolygonStrokePrimitive2D(static_cast< const primitive2d::PolygonStrokePrimitive2D
& >(rCandidate
));
891 case PRIMITIVE2D_ID_FILLBITMAPPRIMITIVE2D
:
893 // FillBitmapPrimitive2D
894 impRenderFillBitmapPrimitive2D(static_cast< const primitive2d::FillBitmapPrimitive2D
& >(rCandidate
));
898 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D
:
900 // UnifiedTransparencePrimitive2D
901 impRenderUnifiedTransparencePrimitive2D(static_cast< const primitive2d::UnifiedTransparencePrimitive2D
& >(rCandidate
));
905 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D
:
907 // wrong spell primitive. Handled directly here using VCL since VCL has a nice and
908 // very direct waveline painting which is needed for this. If VCL is to be avoided,
909 // this can be removed anytime and the decomposition may be used
910 const primitive2d::WrongSpellPrimitive2D
& rWrongSpellPrimitive
= static_cast< const primitive2d::WrongSpellPrimitive2D
& >(rCandidate
);
912 if(!renderWrongSpellPrimitive2D(
913 rWrongSpellPrimitive
,
915 getViewInformation2D().getObjectToViewTransformation(),
916 maBColorModifierStack
))
918 // fallback to decomposition (MetaFile)
919 process(rWrongSpellPrimitive
.get2DDecomposition(getViewInformation2D()));
927 // case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
928 // - support FormControls more direct eventually, not sure if this is needed
929 // with the canvas renderer. The decomposition provides a bitmap representation
930 // of the control which will work
935 // process recursively
936 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
943 //////////////////////////////////////////////////////////////////////////////
946 canvasProcessor2D::canvasProcessor2D(
947 const geometry::ViewInformation2D
& rViewInformation
,
948 OutputDevice
& rOutDev
)
949 : BaseProcessor2D(rViewInformation
),
950 mpOutputDevice(&rOutDev
),
951 mxCanvas(rOutDev
.GetCanvas()),
954 maBColorModifierStack(),
957 meLang(drawinglayer::detail::getDigitLanguage())
959 canvas::tools::initViewState(maViewState
);
960 canvas::tools::initRenderState(maRenderState
);
961 canvas::tools::setViewStateTransform(maViewState
, getViewInformation2D().getViewTransformation());
963 // set digit language, derived from SvtCTLOptions to have the correct
964 // number display for arabic/hindi numerals
965 rOutDev
.SetDigitLanguage(meLang
);
967 // prepare output directly to pixels
968 mpOutputDevice
->Push(PUSH_MAPMODE
);
969 mpOutputDevice
->SetMapMode();
971 // react on AntiAliasing settings
972 if(getOptionsDrawinglayer().IsAntiAliasing())
974 mpOutputDevice
->SetAntialiasing(mpOutputDevice
->GetAntialiasing() | ANTIALIASING_ENABLE_B2DDRAW
);
978 mpOutputDevice
->SetAntialiasing(mpOutputDevice
->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW
);
982 canvasProcessor2D::~canvasProcessor2D()
985 mpOutputDevice
->Pop();
987 // restore AntiAliasing
988 mpOutputDevice
->SetAntialiasing(mpOutputDevice
->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW
);
990 } // end of namespace processor2d
991 } // end of namespace drawinglayer
993 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */