fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / drawinglayer / source / processor2d / canvasprocessor.cxx
blobb8d260aa941ed39e32cc39dd4c8a1c94a48b27f6
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 <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
70 namespace processor2d
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());
84 if(!aMask.count())
86 // no mask, no clipping. recursively paint content
87 process(rChildren);
89 else
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
95 // change
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);
156 // paint content
157 process(rChildren);
159 // draw mask
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);
171 else
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();
199 else
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);
215 else
217 // use mask directly
218 maClipPolyPolygon = aMask;
221 // set at ViewState
222 if(maClipPolyPolygon.count())
224 // set new as clip polygon
225 maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon);
227 else
229 // empty, reset
230 maViewState.Clip.clear();
233 // paint content
234 process(rChildren);
236 // restore local current to rescued clip polygon
237 maClipPolyPolygon = aLastClipPolyPolygon;
239 // set at ViewState
240 if(maClipPolyPolygon.count())
242 // set new as clip polygon
243 maViewState.Clip = basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), maClipPolyPolygon);
245 else
247 // empty, reset
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));
265 else
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()));
275 if(pMtfRenderer)
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())
287 double fShearX(0.0);
289 const basegfx::B2DHomMatrix aLocalTransform(getViewInformation2D().getObjectToViewTransformation() * rTextCandidate.getTextTransform());
290 basegfx::B2DVector aScale, aTranslate;
291 double fRotate;
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()));
301 else
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));
329 if(xFont.is())
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));
337 if(xLayout.is())
339 // got a text layout, apply DXArray if given
340 const ::std::vector< double >& rDXArray = rTextCandidate.getDXArray();
341 const sal_uInt32 nDXCount(rDXArray.size());
343 if(nDXCount)
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);
351 // set text color
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());
359 // paint
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);
385 else
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);
474 // paint content
475 process(rChildren);
477 // set to mask
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;
541 break;
542 case basegfx::B2DLINEJOIN_BEVEL:
543 aStrokeAttribute.JoinType = rendering::PathJoinType::BEVEL;
544 break;
545 case basegfx::B2DLINEJOIN_MITER:
546 aStrokeAttribute.JoinType = rendering::PathJoinType::MITER;
547 break;
548 case basegfx::B2DLINEJOIN_ROUND:
549 aStrokeAttribute.JoinType = rendering::PathJoinType::ROUND;
550 break;
553 switch(rLineAttribute.getLineCap())
555 case com::sun::star::drawing::LineCap_ROUND:
556 aStrokeAttribute.StartCapType = rendering::PathCapType::ROUND;
557 aStrokeAttribute.EndCapType = rendering::PathCapType::ROUND;
558 break;
559 case com::sun::star::drawing::LineCap_SQUARE:
560 aStrokeAttribute.StartCapType = rendering::PathCapType::SQUARE;
561 aStrokeAttribute.EndCapType = rendering::PathCapType::SQUARE;
562 break;
563 default: // com::sun::star::drawing::LineCap_BUTT
564 aStrokeAttribute.StartCapType = rendering::PathCapType::BUTT;
565 aStrokeAttribute.EndCapType = rendering::PathCapType::BUTT;
566 break;
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);
577 bOutputDone = true;
581 if(!bOutputDone)
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);
616 else
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);
646 aSeq[0] = aTexture;
648 // draw textured rectangle
649 mxCanvas->fillTexturedPolyPolygon(
650 basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(mxCanvas->getDevice(), basegfx::B2DPolyPolygon(aOutlineRectangle)),
651 maViewState, maRenderState, aSeq);
655 bOutputDone = true;
659 if(!bOutputDone)
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);
707 bOutputDone = true;
711 if(!bOutputDone)
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);
743 break;
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);
757 break;
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();
771 break;
773 case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
775 // mask group
776 impRenderMaskPrimitive2D(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate));
778 break;
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);
796 // set at canvas
797 canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
799 // proccess content
800 process(rTransformCandidate.getChildren());
802 // restore transformations
803 updateViewInformation(aLastViewInformation2D);
805 // restore at canvas
806 canvas::tools::setRenderStateTransform(maRenderState, getViewInformation2D().getObjectTransformation());
808 break;
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);
833 break;
835 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
837 // MetaFile primitive
838 impRenderMetafilePrimitive2D(static_cast< const primitive2d::MetafilePrimitive2D& >(rCandidate));
840 break;
842 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
844 // PointArray primitive
845 const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate = static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate);
847 // set point color
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);
861 break;
863 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
865 // TextSimplePortion primitive
866 impRenderTextSimplePortionPrimitive2D(static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate));
868 break;
870 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
872 // Bitmap primitive
873 impRenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
875 break;
877 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
879 // Transparence primitive
880 impRenderTransparencePrimitive2D(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate));
882 break;
884 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D:
886 // PolygonStrokePrimitive
887 impRenderPolygonStrokePrimitive2D(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate));
889 break;
891 case PRIMITIVE2D_ID_FILLBITMAPPRIMITIVE2D :
893 // FillBitmapPrimitive2D
894 impRenderFillBitmapPrimitive2D(static_cast< const primitive2d::FillBitmapPrimitive2D& >(rCandidate));
896 break;
898 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D :
900 // UnifiedTransparencePrimitive2D
901 impRenderUnifiedTransparencePrimitive2D(static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate));
903 break;
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,
914 *mpOutputDevice,
915 getViewInformation2D().getObjectToViewTransformation(),
916 maBColorModifierStack))
918 // fallback to decomposition (MetaFile)
919 process(rWrongSpellPrimitive.get2DDecomposition(getViewInformation2D()));
922 break;
925 // nice to have:
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
933 default :
935 // process recursively
936 process(rCandidate.get2DDecomposition(getViewInformation2D()));
938 break;
943 //////////////////////////////////////////////////////////////////////////////
944 // process support
946 canvasProcessor2D::canvasProcessor2D(
947 const geometry::ViewInformation2D& rViewInformation,
948 OutputDevice& rOutDev)
949 : BaseProcessor2D(rViewInformation),
950 mpOutputDevice(&rOutDev),
951 mxCanvas(rOutDev.GetCanvas()),
952 maViewState(),
953 maRenderState(),
954 maBColorModifierStack(),
955 maDrawinglayerOpt(),
956 maClipPolyPolygon(),
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);
976 else
978 mpOutputDevice->SetAntialiasing(mpOutputDevice->GetAntialiasing() & ~ANTIALIASING_ENABLE_B2DDRAW);
982 canvasProcessor2D::~canvasProcessor2D()
984 // restore MapMode
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: */