update dev300-m58
[ooovba.git] / svx / source / sdr / primitive2d / sdrdecompositiontools.cxx
bloba8274923f156dd6f14a441672966df1c2fbad651
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: sdrdecompositiontools.cxx,v $
11 * $Revision: 1.2.18.1 $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 #include "precompiled_svx.hxx"
33 #include <svx/sdr/primitive2d/sdrdecompositiontools.hxx>
34 #include <svx/sdr/attribute/sdrallattribute.hxx>
35 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
36 #include <drawinglayer/attribute/sdrattribute.hxx>
37 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/unifiedalphaprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/alphaprimitive2d.hxx>
40 #include <basegfx/polygon/b2dpolypolygontools.hxx>
41 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
42 #include <drawinglayer/attribute/strokeattribute.hxx>
43 #include <drawinglayer/attribute/linestartendattribute.hxx>
44 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
45 #include <drawinglayer/attribute/sdrfillbitmapattribute.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
48 #include <svx/sdr/attribute/sdrtextattribute.hxx>
49 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
50 #include <svx/svdotext.hxx>
51 #include <basegfx/polygon/b2dpolygontools.hxx>
52 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
53 #include <drawinglayer/animation/animationtiming.hxx>
54 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
55 #include <basegfx/tools/canvastools.hxx>
56 #include <drawinglayer/geometry/viewinformation2d.hxx>
57 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
59 //////////////////////////////////////////////////////////////////////////////
61 using namespace com::sun::star;
63 //////////////////////////////////////////////////////////////////////////////
65 namespace drawinglayer
67 namespace primitive2d
69 Primitive2DReference createPolyPolygonFillPrimitive(
70 const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
71 const basegfx::B2DHomMatrix& rObjectTransform,
72 const attribute::SdrFillAttribute& rFill,
73 const attribute::FillGradientAttribute* pFillGradient)
75 // prepare fully scaled polygon
76 basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
77 aScaledPolyPolygon.transform(rObjectTransform);
78 BasePrimitive2D* pNewFillPrimitive = 0L;
80 if(rFill.isGradient())
82 pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(aScaledPolyPolygon, rFill.getColor(), *rFill.getGradient());
84 else if(rFill.isHatch())
86 pNewFillPrimitive = new PolyPolygonHatchPrimitive2D(aScaledPolyPolygon, rFill.getColor(), *rFill.getHatch());
88 else if(rFill.isBitmap())
90 const basegfx::B2DRange aRange(basegfx::tools::getRange(aScaledPolyPolygon));
91 pNewFillPrimitive = new PolyPolygonBitmapPrimitive2D(aScaledPolyPolygon, rFill.getColor(), rFill.getBitmap()->getFillBitmapAttribute(aRange));
93 else
95 pNewFillPrimitive = new PolyPolygonColorPrimitive2D(aScaledPolyPolygon, rFill.getColor());
98 if(0.0 != rFill.getTransparence())
100 // create simpleTransparencePrimitive, add created fill primitive
101 const Primitive2DReference xRefA(pNewFillPrimitive);
102 const Primitive2DSequence aContent(&xRefA, 1L);
103 return Primitive2DReference(new UnifiedAlphaPrimitive2D(aContent, rFill.getTransparence()));
105 else if(pFillGradient)
107 // create sequence with created fill primitive
108 const Primitive2DReference xRefA(pNewFillPrimitive);
109 const Primitive2DSequence aContent(&xRefA, 1L);
111 // create FillGradientPrimitive2D for transparence and add to new sequence
112 // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways
113 const basegfx::B2DRange aRange(basegfx::tools::getRange(aScaledPolyPolygon));
114 const Primitive2DReference xRefB(new FillGradientPrimitive2D(aRange, *pFillGradient));
115 const Primitive2DSequence aAlpha(&xRefB, 1L);
117 // create AlphaPrimitive2D using alpha and content
118 return Primitive2DReference(new AlphaPrimitive2D(aContent, aAlpha));
120 else
122 // add to decomposition
123 return Primitive2DReference(pNewFillPrimitive);
127 Primitive2DReference createPolygonLinePrimitive(
128 const basegfx::B2DPolygon& rUnitPolygon,
129 const basegfx::B2DHomMatrix& rObjectTransform,
130 const attribute::SdrLineAttribute& rLine,
131 const attribute::SdrLineStartEndAttribute* pStroke)
133 // prepare fully scaled polygon
134 basegfx::B2DPolygon aScaledPolygon(rUnitPolygon);
135 aScaledPolygon.transform(rObjectTransform);
137 // create line and stroke attribute
138 const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin());
139 const attribute::StrokeAttribute aStrokeAttribute(rLine.getDotDashArray(), rLine.getFullDotDashLen());
140 BasePrimitive2D* pNewLinePrimitive = 0L;
142 if(!rUnitPolygon.isClosed() && pStroke)
144 attribute::LineStartEndAttribute aStart(pStroke->getStartWidth(), pStroke->getStartPolyPolygon(), pStroke->isStartCentered());
145 attribute::LineStartEndAttribute aEnd(pStroke->getEndWidth(), pStroke->getEndPolyPolygon(), pStroke->isEndCentered());
147 // create data
148 pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(aScaledPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd);
150 else
152 // create data
153 pNewLinePrimitive = new PolygonStrokePrimitive2D(aScaledPolygon, aLineAttribute, aStrokeAttribute);
156 if(0.0 != rLine.getTransparence())
158 // create simpleTransparencePrimitive, add created fill primitive
159 const Primitive2DReference xRefA(pNewLinePrimitive);
160 const Primitive2DSequence aContent(&xRefA, 1L);
161 return Primitive2DReference(new UnifiedAlphaPrimitive2D(aContent, rLine.getTransparence()));
163 else
165 // add to decomposition
166 return Primitive2DReference(pNewLinePrimitive);
170 Primitive2DReference createTextPrimitive(
171 const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
172 const basegfx::B2DHomMatrix& rObjectTransform,
173 const attribute::SdrTextAttribute& rText,
174 const attribute::SdrLineAttribute* pStroke,
175 bool bCellText,
176 bool bWordWrap)
178 basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform);
179 SdrTextPrimitive2D* pNew = 0;
181 if(rText.isContour())
183 // contour text
184 if(pStroke && 0.0 != pStroke->getWidth())
186 // take line width into account and shrink contour polygon accordingly
187 // decompose to get scale
188 basegfx::B2DVector aScale, aTranslate;
189 double fRotate, fShearX;
190 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
192 // scale outline to object's size to allow growing with value relative to that size
193 // and also to keep aspect ratio
194 basegfx::B2DHomMatrix aScaleTransform;
195 aScaleTransform.set(0, 0, fabs(aScale.getX()));
196 aScaleTransform.set(1, 1, fabs(aScale.getY()));
197 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
198 aScaledUnitPolyPolygon.transform(aScaleTransform);
200 // grow the polygon. To shrink, use negative value (half width)
201 aScaledUnitPolyPolygon = basegfx::tools::growInNormalDirection(aScaledUnitPolyPolygon, -(pStroke->getWidth() * 0.5));
203 // scale back to unit polygon
204 aScaleTransform.set(0, 0, 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0);
205 aScaleTransform.set(1, 1, 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0);
206 aScaledUnitPolyPolygon.transform(aScaleTransform);
208 // create with unit polygon
209 pNew = new SdrContourTextPrimitive2D(
210 &rText.getSdrText(),
211 rText.getOutlinerParaObject(),
212 aScaledUnitPolyPolygon,
213 rObjectTransform);
215 else
217 // create with unit polygon
218 pNew = new SdrContourTextPrimitive2D(
219 &rText.getSdrText(),
220 rText.getOutlinerParaObject(),
221 rUnitPolyPolygon,
222 rObjectTransform);
225 else if(rText.getSdrFormTextAttribute())
227 // text on path, use scaled polygon
228 basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
229 aScaledPolyPolygon.transform(rObjectTransform);
230 pNew = new SdrPathTextPrimitive2D(
231 &rText.getSdrText(),
232 rText.getOutlinerParaObject(),
233 aScaledPolyPolygon,
234 *rText.getSdrFormTextAttribute());
236 else
238 // rObjectTransform is the whole SdrObject transformation from unit rectangle
239 // to it's size and position. Decompose to allow working with single values.
240 basegfx::B2DVector aScale, aTranslate;
241 double fRotate, fShearX;
242 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
244 // extract mirroring
245 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
246 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
247 aScale = basegfx::absolute(aScale);
249 // Get the real size, since polygon ountline and scale
250 // from the object transformation may vary (e.g. ellipse segments)
251 basegfx::B2DHomMatrix aJustScaleTransform;
252 aJustScaleTransform.set(0, 0, aScale.getX());
253 aJustScaleTransform.set(1, 1, aScale.getY());
254 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
255 aScaledUnitPolyPolygon.transform(aJustScaleTransform);
256 const basegfx::B2DRange aSnapRange(basegfx::tools::getRange(aScaledUnitPolyPolygon));
258 // create a range describing the wanted text position and size (aTextAnchorRange). This
259 // means to use the text distance values here
260 const basegfx::B2DPoint aTopLeft(aSnapRange.getMinX() + rText.getTextLeftDistance(), aSnapRange.getMinY() + rText.getTextUpperDistance());
261 const basegfx::B2DPoint aBottomRight(aSnapRange.getMaxX() - rText.getTextRightDistance(), aSnapRange.getMaxY() - rText.getTextLowerDistance());
262 basegfx::B2DRange aTextAnchorRange;
263 aTextAnchorRange.expand(aTopLeft);
264 aTextAnchorRange.expand(aBottomRight);
266 // now create a transformation from this basic range (aTextAnchorRange)
267 aAnchorTransform.identity();
268 aAnchorTransform.scale(aTextAnchorRange.getWidth(), aTextAnchorRange.getHeight());
269 aAnchorTransform.translate(aTextAnchorRange.getMinX(), aTextAnchorRange.getMinY());
271 // apply mirroring
272 aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
274 // apply object's other transforms
275 aAnchorTransform.shearX(fShearX);
276 aAnchorTransform.rotate(fRotate);
277 aAnchorTransform.translate(aTranslate.getX(), aTranslate.getY());
279 if(rText.isFitToSize())
281 // streched text in range
282 pNew = new SdrStretchTextPrimitive2D(
283 &rText.getSdrText(),
284 rText.getOutlinerParaObject(),
285 aAnchorTransform);
287 else if(rText.isAutoFit())
289 // isotrophically scaled text in range
290 pNew = new SdrAutoFitTextPrimitive2D(&rText.getSdrText(), rText.getOutlinerParaObject(), aAnchorTransform, bWordWrap);
292 else // text in range
294 // build new primitive
295 pNew = new SdrBlockTextPrimitive2D(
296 &rText.getSdrText(),
297 rText.getOutlinerParaObject(),
298 aAnchorTransform,
299 rText.isScroll(),
300 bCellText,
301 bWordWrap);
305 OSL_ENSURE(pNew != 0, "createTextPrimitive: no text primitive created (!)");
307 if(rText.isBlink())
309 // prepare animation and primitive list
310 drawinglayer::animation::AnimationEntryList aAnimationList;
311 rText.getBlinkTextTiming(aAnimationList);
313 if(0.0 != aAnimationList.getDuration())
315 // create content sequence
316 const Primitive2DReference xRefA(pNew);
317 const Primitive2DSequence aContent(&xRefA, 1L);
319 // create and add animated switch primitive
320 return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, aContent, true));
322 else
324 // add to decomposition
325 return Primitive2DReference(pNew);
329 if(rText.isScroll())
331 // suppress scroll when FontWork
332 if(!rText.getSdrFormTextAttribute())
334 // get scroll direction
335 const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection());
336 const bool bHorizontal(SDRTEXTANI_LEFT == eDirection || SDRTEXTANI_RIGHT == eDirection);
338 // decompose to get separated values for the scroll box
339 basegfx::B2DVector aScale, aTranslate;
340 double fRotate, fShearX;
341 aAnchorTransform.decompose(aScale, aTranslate, fRotate, fShearX);
343 // build transform from scaled only to full AnchorTransform and inverse
344 basegfx::B2DHomMatrix aSRT;
345 aSRT.shearX(fShearX);
346 aSRT.rotate(fRotate);
347 aSRT.translate(aTranslate.getX(), aTranslate.getY());
348 basegfx::B2DHomMatrix aISRT(aSRT);
349 aISRT.invert();
351 // bring the primitive back to scaled only and get scaled range, create new clone for this
352 SdrTextPrimitive2D* pNew2 = pNew->createTransformedClone(aISRT);
353 OSL_ENSURE(pNew2, "createTextPrimitive: Could not create transformed clone of text primitive (!)");
354 delete pNew;
355 pNew = pNew2;
357 // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay
358 // since the decompose is view-independent
359 const uno::Sequence< beans::PropertyValue > xViewParameters;
360 geometry::ViewInformation2D aViewInformation2D(xViewParameters);
362 // get range
363 const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D));
365 // create left outside and right outside transformations. Also take care
366 // of the clip rectangle
367 basegfx::B2DHomMatrix aLeft, aRight;
368 basegfx::B2DPoint aClipTopLeft(0.0, 0.0);
369 basegfx::B2DPoint aClipBottomRight(aScale.getX(), aScale.getY());
371 if(bHorizontal)
373 aClipTopLeft.setY(aScaledRange.getMinY());
374 aClipBottomRight.setY(aScaledRange.getMaxY());
375 aLeft.translate(-aScaledRange.getMaxX(), 0.0);
376 aRight.translate(aScale.getX() - aScaledRange.getMinX(), 0.0);
378 else
380 aClipTopLeft.setX(aScaledRange.getMinX());
381 aClipBottomRight.setX(aScaledRange.getMaxX());
382 aLeft.translate(0.0, -aScaledRange.getMaxY());
383 aRight.translate(0.0, aScale.getY() - aScaledRange.getMinY());
386 aLeft *= aSRT;
387 aRight *= aSRT;
389 // prepare animation list
390 drawinglayer::animation::AnimationEntryList aAnimationList;
392 if(bHorizontal)
394 rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth());
396 else
398 rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight());
401 if(0.0 != aAnimationList.getDuration())
403 // create a new Primitive2DSequence containing the animated text in it's scaled only state.
404 // use the decomposition to force to simple text primitives, those will no longer
405 // need the outliner for formatting (alternatively it is also possible to just add
406 // pNew to aNewPrimitiveSequence)
407 Primitive2DSequence aAnimSequence(pNew->get2DDecomposition(aViewInformation2D));
408 delete pNew;
410 // create a new animatedInterpolatePrimitive and add it
411 std::vector< basegfx::B2DHomMatrix > aMatrixStack;
412 aMatrixStack.push_back(aLeft);
413 aMatrixStack.push_back(aRight);
414 const Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D(aMatrixStack, aAnimationList, aAnimSequence, true));
415 const Primitive2DSequence aContent(&xRefA, 1L);
417 // scrolling needs an encapsulating clipping primitive
418 const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight);
419 basegfx::B2DPolygon aClipPolygon(basegfx::tools::createPolygonFromRect(aClipRange));
420 aClipPolygon.transform(aSRT);
421 return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), aContent));
423 else
425 // add to decomposition
426 return Primitive2DReference(pNew);
431 if(rText.isInEditMode())
433 // #i97628#
434 // encapsulate with TextHierarchyEditPrimitive2D to allow renderers
435 // to suppress actively edited content if needed
436 const Primitive2DReference xRefA(pNew);
437 const Primitive2DSequence aContent(&xRefA, 1L);
439 // create and add TextHierarchyEditPrimitive2D primitive
440 return Primitive2DReference(new TextHierarchyEditPrimitive2D(aContent));
442 else
444 // add to decomposition
445 return Primitive2DReference(pNew);
449 Primitive2DSequence createEmbeddedShadowPrimitive(
450 const Primitive2DSequence& rContent,
451 const attribute::SdrShadowAttribute& rShadow)
453 if(rContent.hasElements())
455 Primitive2DSequence aRetval(2);
456 basegfx::B2DHomMatrix aShadowOffset;
458 // prepare shadow offset
459 aShadowOffset.set(0, 2, rShadow.getOffset().getX());
460 aShadowOffset.set(1, 2, rShadow.getOffset().getY());
462 // create shadow primitive and add content
463 aRetval[0] = Primitive2DReference(
464 new ShadowPrimitive2D(
465 aShadowOffset,
466 rShadow.getColor(),
467 rContent));
469 if(0.0 != rShadow.getTransparence())
471 // create SimpleTransparencePrimitive2D
472 const Primitive2DSequence aTempContent(&aRetval[0], 1);
474 aRetval[0] = Primitive2DReference(
475 new UnifiedAlphaPrimitive2D(
476 aTempContent,
477 rShadow.getTransparence()));
480 aRetval[1] = Primitive2DReference(new GroupPrimitive2D(rContent));
481 return aRetval;
483 else
485 return rContent;
488 } // end of namespace primitive2d
489 } // end of namespace drawinglayer
491 //////////////////////////////////////////////////////////////////////////////
492 // eof