bump product version to 4.1.6.2
[LibreOffice.git] / svx / source / sdr / primitive2d / sdrdecompositiontools.cxx
blobfe832016324129cd330019e4caca80622b760230
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 <svx/sdr/primitive2d/sdrdecompositiontools.hxx>
21 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
22 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
23 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
25 #include <basegfx/polygon/b2dpolypolygontools.hxx>
26 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
27 #include <drawinglayer/attribute/strokeattribute.hxx>
28 #include <drawinglayer/attribute/linestartendattribute.hxx>
29 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
30 #include <drawinglayer/attribute/sdrfillbitmapattribute.hxx>
31 #include <basegfx/matrix/b2dhommatrix.hxx>
32 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
33 #include <svx/sdr/attribute/sdrtextattribute.hxx>
34 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
35 #include <svx/svdotext.hxx>
36 #include <basegfx/polygon/b2dpolygontools.hxx>
37 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
38 #include <drawinglayer/animation/animationtiming.hxx>
39 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
40 #include <basegfx/tools/canvastools.hxx>
41 #include <drawinglayer/geometry/viewinformation2d.hxx>
42 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
43 #include <drawinglayer/attribute/sdrfillattribute.hxx>
44 #include <drawinglayer/attribute/sdrlineattribute.hxx>
45 #include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
46 #include <drawinglayer/attribute/sdrshadowattribute.hxx>
48 //////////////////////////////////////////////////////////////////////////////
50 using namespace com::sun::star;
52 //////////////////////////////////////////////////////////////////////////////
54 namespace drawinglayer
56 namespace primitive2d
58 Primitive2DReference createPolyPolygonFillPrimitive(
59 const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
60 const basegfx::B2DHomMatrix& rObjectTransform,
61 const attribute::SdrFillAttribute& rFill,
62 const attribute::FillGradientAttribute& rFillGradient)
64 // prepare fully scaled polygon
65 basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
66 aScaledPolyPolygon.transform(rObjectTransform);
67 BasePrimitive2D* pNewFillPrimitive = 0;
69 if(!rFill.getGradient().isDefault())
71 pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(aScaledPolyPolygon, rFill.getGradient());
73 else if(!rFill.getHatch().isDefault())
75 pNewFillPrimitive = new PolyPolygonHatchPrimitive2D(aScaledPolyPolygon, rFill.getColor(), rFill.getHatch());
77 else if(!rFill.getBitmap().isDefault())
79 const basegfx::B2DRange aRange(basegfx::tools::getRange(aScaledPolyPolygon));
80 pNewFillPrimitive = new PolyPolygonBitmapPrimitive2D(aScaledPolyPolygon, rFill.getBitmap().getFillBitmapAttribute(aRange));
82 else
84 pNewFillPrimitive = new PolyPolygonColorPrimitive2D(aScaledPolyPolygon, rFill.getColor());
87 if(0.0 != rFill.getTransparence())
89 // create simpleTransparencePrimitive, add created fill primitive
90 const Primitive2DReference xRefA(pNewFillPrimitive);
91 const Primitive2DSequence aContent(&xRefA, 1L);
92 return Primitive2DReference(new UnifiedTransparencePrimitive2D(aContent, rFill.getTransparence()));
94 else if(!rFillGradient.isDefault())
96 // create sequence with created fill primitive
97 const Primitive2DReference xRefA(pNewFillPrimitive);
98 const Primitive2DSequence aContent(&xRefA, 1L);
100 // create FillGradientPrimitive2D for transparence and add to new sequence
101 // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways
102 const basegfx::B2DRange aRange(basegfx::tools::getRange(aScaledPolyPolygon));
103 const Primitive2DReference xRefB(new FillGradientPrimitive2D(aRange, rFillGradient));
104 const Primitive2DSequence aAlpha(&xRefB, 1L);
106 // create TransparencePrimitive2D using alpha and content
107 return Primitive2DReference(new TransparencePrimitive2D(aContent, aAlpha));
109 else
111 // add to decomposition
112 return Primitive2DReference(pNewFillPrimitive);
116 Primitive2DReference createPolygonLinePrimitive(
117 const basegfx::B2DPolygon& rUnitPolygon,
118 const basegfx::B2DHomMatrix& rObjectTransform,
119 const attribute::SdrLineAttribute& rLine,
120 const attribute::SdrLineStartEndAttribute& rStroke)
122 // prepare fully scaled polygon
123 basegfx::B2DPolygon aScaledPolygon(rUnitPolygon);
124 aScaledPolygon.transform(rObjectTransform);
126 // create line and stroke attribute
127 const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap());
128 const attribute::StrokeAttribute aStrokeAttribute(rLine.getDotDashArray(), rLine.getFullDotDashLen());
129 BasePrimitive2D* pNewLinePrimitive = 0L;
131 if(!rUnitPolygon.isClosed() && !rStroke.isDefault())
133 attribute::LineStartEndAttribute aStart(rStroke.getStartWidth(), rStroke.getStartPolyPolygon(), rStroke.isStartCentered());
134 attribute::LineStartEndAttribute aEnd(rStroke.getEndWidth(), rStroke.getEndPolyPolygon(), rStroke.isEndCentered());
136 // create data
137 pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(aScaledPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd);
139 else
141 // create data
142 pNewLinePrimitive = new PolygonStrokePrimitive2D(aScaledPolygon, aLineAttribute, aStrokeAttribute);
145 if(0.0 != rLine.getTransparence())
147 // create simpleTransparencePrimitive, add created fill primitive
148 const Primitive2DReference xRefA(pNewLinePrimitive);
149 const Primitive2DSequence aContent(&xRefA, 1L);
150 return Primitive2DReference(new UnifiedTransparencePrimitive2D(aContent, rLine.getTransparence()));
152 else
154 // add to decomposition
155 return Primitive2DReference(pNewLinePrimitive);
159 Primitive2DReference createTextPrimitive(
160 const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
161 const basegfx::B2DHomMatrix& rObjectTransform,
162 const attribute::SdrTextAttribute& rText,
163 const attribute::SdrLineAttribute& rStroke,
164 bool bCellText,
165 bool bWordWrap,
166 bool bClipOnBounds)
168 basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform);
169 SdrTextPrimitive2D* pNew = 0;
171 if(rText.isContour())
173 // contour text
174 if(!rStroke.isDefault() && 0.0 != rStroke.getWidth())
176 // take line width into account and shrink contour polygon accordingly
177 // decompose to get scale
178 basegfx::B2DVector aScale, aTranslate;
179 double fRotate, fShearX;
180 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
182 // scale outline to object's size to allow growing with value relative to that size
183 // and also to keep aspect ratio
184 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
185 aScaledUnitPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(
186 fabs(aScale.getX()), fabs(aScale.getY())));
188 // grow the polygon. To shrink, use negative value (half width)
189 aScaledUnitPolyPolygon = basegfx::tools::growInNormalDirection(aScaledUnitPolyPolygon, -(rStroke.getWidth() * 0.5));
191 // scale back to unit polygon
192 aScaledUnitPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(
193 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0,
194 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0));
196 // create with unit polygon
197 pNew = new SdrContourTextPrimitive2D(
198 &rText.getSdrText(),
199 rText.getOutlinerParaObject(),
200 aScaledUnitPolyPolygon,
201 rObjectTransform);
203 else
205 // create with unit polygon
206 pNew = new SdrContourTextPrimitive2D(
207 &rText.getSdrText(),
208 rText.getOutlinerParaObject(),
209 rUnitPolyPolygon,
210 rObjectTransform);
213 else if(!rText.getSdrFormTextAttribute().isDefault())
215 // text on path, use scaled polygon
216 basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
217 aScaledPolyPolygon.transform(rObjectTransform);
218 pNew = new SdrPathTextPrimitive2D(
219 &rText.getSdrText(),
220 rText.getOutlinerParaObject(),
221 aScaledPolyPolygon,
222 rText.getSdrFormTextAttribute());
224 else
226 // rObjectTransform is the whole SdrObject transformation from unit rectangle
227 // to it's size and position. Decompose to allow working with single values.
228 basegfx::B2DVector aScale, aTranslate;
229 double fRotate, fShearX;
230 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
232 // extract mirroring
233 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
234 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
235 aScale = basegfx::absolute(aScale);
237 // Get the real size, since polygon ountline and scale
238 // from the object transformation may vary (e.g. ellipse segments)
239 basegfx::B2DHomMatrix aJustScaleTransform;
240 aJustScaleTransform.set(0, 0, aScale.getX());
241 aJustScaleTransform.set(1, 1, aScale.getY());
242 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
243 aScaledUnitPolyPolygon.transform(aJustScaleTransform);
244 const basegfx::B2DRange aSnapRange(basegfx::tools::getRange(aScaledUnitPolyPolygon));
246 // create a range describing the wanted text position and size (aTextAnchorRange). This
247 // means to use the text distance values here
248 const basegfx::B2DPoint aTopLeft(aSnapRange.getMinX() + rText.getTextLeftDistance(), aSnapRange.getMinY() + rText.getTextUpperDistance());
249 const basegfx::B2DPoint aBottomRight(aSnapRange.getMaxX() - rText.getTextRightDistance(), aSnapRange.getMaxY() - rText.getTextLowerDistance());
250 basegfx::B2DRange aTextAnchorRange;
251 aTextAnchorRange.expand(aTopLeft);
252 aTextAnchorRange.expand(aBottomRight);
254 // now create a transformation from this basic range (aTextAnchorRange)
255 aAnchorTransform = basegfx::tools::createScaleTranslateB2DHomMatrix(
256 aTextAnchorRange.getWidth(), aTextAnchorRange.getHeight(),
257 aTextAnchorRange.getMinX(), aTextAnchorRange.getMinY());
259 // apply mirroring
260 aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
262 // apply object's other transforms
263 aAnchorTransform = basegfx::tools::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate)
264 * aAnchorTransform;
266 if(rText.isFitToSize())
268 // streched text in range
269 pNew = new SdrStretchTextPrimitive2D(
270 &rText.getSdrText(),
271 rText.getOutlinerParaObject(),
272 aAnchorTransform,
273 rText.isFixedCellHeight());
275 else if(rText.isAutoFit())
277 // isotrophically scaled text in range
278 pNew = new SdrAutoFitTextPrimitive2D(&rText.getSdrText(), rText.getOutlinerParaObject(), aAnchorTransform, bWordWrap);
280 else // text in range
282 // build new primitive
283 pNew = new SdrBlockTextPrimitive2D(
284 &rText.getSdrText(),
285 rText.getOutlinerParaObject(),
286 aAnchorTransform,
287 rText.getSdrTextHorzAdjust(),
288 rText.getSdrTextVertAdjust(),
289 rText.isFixedCellHeight(),
290 rText.isScroll(),
291 bCellText,
292 bWordWrap,
293 bClipOnBounds);
297 OSL_ENSURE(pNew != 0, "createTextPrimitive: no text primitive created (!)");
299 if(rText.isBlink())
301 // prepare animation and primitive list
302 drawinglayer::animation::AnimationEntryList aAnimationList;
303 rText.getBlinkTextTiming(aAnimationList);
305 if(0.0 != aAnimationList.getDuration())
307 // create content sequence
308 const Primitive2DReference xRefA(pNew);
309 const Primitive2DSequence aContent(&xRefA, 1L);
311 // create and add animated switch primitive
312 return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, aContent, true));
314 else
316 // add to decomposition
317 return Primitive2DReference(pNew);
321 if(rText.isScroll())
323 // suppress scroll when FontWork
324 if(rText.getSdrFormTextAttribute().isDefault())
326 // get scroll direction
327 const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection());
328 const bool bHorizontal(SDRTEXTANI_LEFT == eDirection || SDRTEXTANI_RIGHT == eDirection);
330 // decompose to get separated values for the scroll box
331 basegfx::B2DVector aScale, aTranslate;
332 double fRotate, fShearX;
333 aAnchorTransform.decompose(aScale, aTranslate, fRotate, fShearX);
335 // build transform from scaled only to full AnchorTransform and inverse
336 const basegfx::B2DHomMatrix aSRT(basegfx::tools::createShearXRotateTranslateB2DHomMatrix(
337 fShearX, fRotate, aTranslate));
338 basegfx::B2DHomMatrix aISRT(aSRT);
339 aISRT.invert();
341 // bring the primitive back to scaled only and get scaled range, create new clone for this
342 SdrTextPrimitive2D* pNew2 = pNew->createTransformedClone(aISRT);
343 OSL_ENSURE(pNew2, "createTextPrimitive: Could not create transformed clone of text primitive (!)");
344 delete pNew;
345 pNew = pNew2;
347 // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay
348 // since the decompose is view-independent
349 const uno::Sequence< beans::PropertyValue > xViewParameters;
350 geometry::ViewInformation2D aViewInformation2D(xViewParameters);
352 // get range
353 const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D));
355 // create left outside and right outside transformations. Also take care
356 // of the clip rectangle
357 basegfx::B2DHomMatrix aLeft, aRight;
358 basegfx::B2DPoint aClipTopLeft(0.0, 0.0);
359 basegfx::B2DPoint aClipBottomRight(aScale.getX(), aScale.getY());
361 if(bHorizontal)
363 aClipTopLeft.setY(aScaledRange.getMinY());
364 aClipBottomRight.setY(aScaledRange.getMaxY());
365 aLeft.translate(-aScaledRange.getMaxX(), 0.0);
366 aRight.translate(aScale.getX() - aScaledRange.getMinX(), 0.0);
368 else
370 aClipTopLeft.setX(aScaledRange.getMinX());
371 aClipBottomRight.setX(aScaledRange.getMaxX());
372 aLeft.translate(0.0, -aScaledRange.getMaxY());
373 aRight.translate(0.0, aScale.getY() - aScaledRange.getMinY());
376 aLeft *= aSRT;
377 aRight *= aSRT;
379 // prepare animation list
380 drawinglayer::animation::AnimationEntryList aAnimationList;
382 if(bHorizontal)
384 rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth());
386 else
388 rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight());
391 if(0.0 != aAnimationList.getDuration())
393 // create a new Primitive2DSequence containing the animated text in it's scaled only state.
394 // use the decomposition to force to simple text primitives, those will no longer
395 // need the outliner for formatting (alternatively it is also possible to just add
396 // pNew to aNewPrimitiveSequence)
397 Primitive2DSequence aAnimSequence(pNew->get2DDecomposition(aViewInformation2D));
398 delete pNew;
400 // create a new animatedInterpolatePrimitive and add it
401 std::vector< basegfx::B2DHomMatrix > aMatrixStack;
402 aMatrixStack.push_back(aLeft);
403 aMatrixStack.push_back(aRight);
404 const Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D(aMatrixStack, aAnimationList, aAnimSequence, true));
405 const Primitive2DSequence aContent(&xRefA, 1L);
407 // scrolling needs an encapsulating clipping primitive
408 const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight);
409 basegfx::B2DPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(aClipRange));
410 aClipPolygon.transform(aSRT);
411 return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), aContent));
413 else
415 // add to decomposition
416 return Primitive2DReference(pNew);
421 if(rText.isInEditMode())
423 // #i97628#
424 // encapsulate with TextHierarchyEditPrimitive2D to allow renderers
425 // to suppress actively edited content if needed
426 const Primitive2DReference xRefA(pNew);
427 const Primitive2DSequence aContent(&xRefA, 1L);
429 // create and add TextHierarchyEditPrimitive2D primitive
430 return Primitive2DReference(new TextHierarchyEditPrimitive2D(aContent));
432 else
434 // add to decomposition
435 return Primitive2DReference(pNew);
439 Primitive2DSequence createEmbeddedShadowPrimitive(
440 const Primitive2DSequence& rContent,
441 const attribute::SdrShadowAttribute& rShadow)
443 if(rContent.hasElements())
445 Primitive2DSequence aRetval(2);
446 basegfx::B2DHomMatrix aShadowOffset;
448 // prepare shadow offset
449 aShadowOffset.set(0, 2, rShadow.getOffset().getX());
450 aShadowOffset.set(1, 2, rShadow.getOffset().getY());
452 // create shadow primitive and add content
453 aRetval[0] = Primitive2DReference(
454 new ShadowPrimitive2D(
455 aShadowOffset,
456 rShadow.getColor(),
457 rContent));
459 if(0.0 != rShadow.getTransparence())
461 // create SimpleTransparencePrimitive2D
462 const Primitive2DSequence aTempContent(&aRetval[0], 1);
464 aRetval[0] = Primitive2DReference(
465 new UnifiedTransparencePrimitive2D(
466 aTempContent,
467 rShadow.getTransparence()));
470 aRetval[1] = Primitive2DReference(new GroupPrimitive2D(rContent));
471 return aRetval;
473 else
475 return rContent;
478 } // end of namespace primitive2d
479 } // end of namespace drawinglayer
481 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */