Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / cppcanvas / source / mtfrenderer / mtftools.cxx
blob56b948e13a165496025b66a8b500d9f45924acd5
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 .
21 #include <tools/diagnose_ex.h>
22 #include <com/sun/star/rendering/RenderState.hpp>
23 #include <com/sun/star/rendering/XCanvas.hpp>
24 #include <basegfx/numeric/ftools.hxx>
25 #include <basegfx/tools/canvastools.hxx>
26 #include <basegfx/polygon/b2dpolygontools.hxx>
27 #include <basegfx/polygon/b2dpolygon.hxx>
28 #include <basegfx/range/b2drectangle.hxx>
29 #include <basegfx/vector/b2dvector.hxx>
30 #include <canvas/canvastools.hxx>
31 #include <vcl/gdimtf.hxx>
32 #include <vcl/metaact.hxx>
33 #include <vcl/virdev.hxx>
34 #include <vcl/metric.hxx>
35 #include <tools/poly.hxx>
36 #include "mtftools.hxx"
37 #include "outdevstate.hxx"
38 #include "polypolyaction.hxx"
39 #include <basegfx/matrix/b2dhommatrixtools.hxx>
42 using namespace ::com::sun::star;
44 namespace cppcanvas
46 namespace tools
48 void initRenderState( rendering::RenderState& renderState,
49 const ::cppcanvas::internal::OutDevState& outdevState )
51 ::canvas::tools::initRenderState( renderState );
52 ::canvas::tools::setRenderStateTransform( renderState,
53 outdevState.transform );
54 renderState.Clip = outdevState.xClipPoly;
57 ::Size getBaselineOffset( const ::cppcanvas::internal::OutDevState& outdevState,
58 const VirtualDevice& rVDev )
60 const ::FontMetric& aMetric = rVDev.GetFontMetric();
62 // calc offset for text output, the XCanvas always renders
63 // baseline offset.
64 switch( outdevState.textReferencePoint )
66 case ALIGN_TOP:
67 return ::Size( 0,
68 aMetric.GetInternalLeading() + aMetric.GetAscent() );
70 default:
71 ENSURE_OR_THROW( false,
72 "tools::getBaselineOffset(): Unexpected TextAlign value" );
73 // FALLTHROUGH intended (to calm compiler warning - case won't happen)
74 case ALIGN_BASELINE:
75 return ::Size( 0, 0 );
77 case ALIGN_BOTTOM:
78 return ::Size( 0,
79 -aMetric.GetDescent() );
84 ::basegfx::B2DHomMatrix& calcLogic2PixelLinearTransform( ::basegfx::B2DHomMatrix& o_rMatrix,
85 const VirtualDevice& rVDev )
87 // select size value in the middle of the available range,
88 // to have headroom both when map mode scales up, and when
89 // it scales down.
90 const ::Size aSizeLogic( 0x00010000L,
91 0x00010000L );
93 const ::Size aSizePixel( rVDev.LogicToPixel( aSizeLogic ) );
95 o_rMatrix = basegfx::tools::createScaleB2DHomMatrix(
96 aSizePixel.Width() / (double)aSizeLogic.Width(),
97 aSizePixel.Height() / (double)aSizeLogic.Height() );
99 return o_rMatrix;
102 ::basegfx::B2DHomMatrix& calcLogic2PixelAffineTransform( ::basegfx::B2DHomMatrix& o_rMatrix,
103 const VirtualDevice& rVDev )
105 // retrieves scale
106 calcLogic2PixelLinearTransform(o_rMatrix, rVDev);
108 // translate according to curr map mode/pref map mode offset
109 const ::Point aEmptyPoint;
110 const ::Point& rTranslatedPoint(
111 rVDev.LogicToPixel( aEmptyPoint ));
113 o_rMatrix.translate(rTranslatedPoint.X(),
114 rTranslatedPoint.Y());
116 return o_rMatrix;
119 bool modifyClip( rendering::RenderState& o_rRenderState,
120 const struct ::cppcanvas::internal::OutDevState& rOutdevState,
121 const CanvasSharedPtr& rCanvas,
122 const ::basegfx::B2DPoint& rOffset,
123 const ::basegfx::B2DVector* pScaling,
124 const double* pRotation )
126 const bool bOffsetting( !rOffset.equalZero() );
127 const bool bScaling( pScaling &&
128 !rtl::math::approxEqual(pScaling->getX(), 1.0) &&
129 !rtl::math::approxEqual(pScaling->getY(), 1.0) );
130 const bool bRotation( pRotation &&
131 *pRotation != 0.0 );
133 if( !bOffsetting && !bScaling && !bRotation )
134 return false; // nothing to do
136 if( rOutdevState.clip.count() )
138 // general polygon case
140 ::basegfx::B2DPolyPolygon aLocalClip( rOutdevState.clip );
141 ::basegfx::B2DHomMatrix aTransform;
143 if( bOffsetting )
144 aTransform.translate( -rOffset.getX(),
145 -rOffset.getY() );
146 if( bScaling )
147 aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() );
149 if( bRotation )
150 aTransform.rotate( - *pRotation );
152 aLocalClip.transform( aTransform );
154 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
155 rCanvas->getUNOCanvas()->getDevice(),
156 aLocalClip );
158 return true;
160 else if( !rOutdevState.clipRect.IsEmpty() )
162 // simple rect case
164 const ::Rectangle aLocalClipRect( rOutdevState.clipRect );
166 if( bRotation )
168 // rotation involved - convert to polygon first,
169 // then transform that
170 ::basegfx::B2DPolygon aLocalClip(
171 ::basegfx::tools::createPolygonFromRect(
172 ::basegfx::B2DRectangle(
173 (double)(aLocalClipRect.Left()),
174 (double)(aLocalClipRect.Top()),
175 (double)(aLocalClipRect.Right()),
176 (double)(aLocalClipRect.Bottom()) ) ) );
177 ::basegfx::B2DHomMatrix aTransform;
179 if( bOffsetting )
180 aTransform.translate( -rOffset.getX(),
181 -rOffset.getY() );
182 if( bScaling )
183 aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() );
185 aTransform.rotate( - *pRotation );
187 aLocalClip.transform( aTransform );
189 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
190 rCanvas->getUNOCanvas()->getDevice(),
191 ::basegfx::B2DPolyPolygon( aLocalClip ) );
193 else if( bScaling )
195 // scale and offset - do it on the fly, have to
196 // convert to float anyway.
197 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
198 rCanvas->getUNOCanvas()->getDevice(),
199 ::basegfx::B2DPolyPolygon(
200 ::basegfx::tools::createPolygonFromRect(
201 ::basegfx::B2DRectangle(
202 (double)(aLocalClipRect.Left() - rOffset.getX())/pScaling->getX(),
203 (double)(aLocalClipRect.Top() - rOffset.getY())/pScaling->getY(),
204 (double)(aLocalClipRect.Right() - rOffset.getX())/pScaling->getX(),
205 (double)(aLocalClipRect.Bottom() - rOffset.getY())/pScaling->getY() ) ) ) );
207 else
209 // offset only - do it on the fly, have to convert
210 // to float anyway.
211 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
212 rCanvas->getUNOCanvas()->getDevice(),
213 ::basegfx::B2DPolyPolygon(
214 ::basegfx::tools::createPolygonFromRect(
215 ::basegfx::B2DRectangle( aLocalClipRect.Left() - rOffset.getX(),
216 aLocalClipRect.Top() - rOffset.getY(),
217 aLocalClipRect.Right() - rOffset.getX(),
218 aLocalClipRect.Bottom() - rOffset.getY() ) ) ) );
221 return true;
224 // empty clip, nothing to do
225 return false;
228 // create overline/underline/strikeout line info struct
229 TextLineInfo createTextLineInfo( const ::VirtualDevice& rVDev,
230 const ::cppcanvas::internal::OutDevState& rState )
232 const bool bOldMode( rVDev.IsMapModeEnabled() );
234 // #i68512# Force metric regeneration with mapmode enabled
235 // (prolly OutDev bug)
236 rVDev.GetFontMetric();
238 // will restore map mode below
239 const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( false );
241 const ::FontMetric aMetric = rVDev.GetFontMetric();
243 TextLineInfo aTextInfo(
244 (aMetric.GetDescent() + 2) / 4.0,
245 ((aMetric.GetInternalLeading() + 1.5) / 3.0),
246 (aMetric.GetInternalLeading() / 2.0) - aMetric.GetAscent(),
247 aMetric.GetDescent() / 2.0,
248 (aMetric.GetInternalLeading() - aMetric.GetAscent()) / 3.0,
249 rState.textOverlineStyle,
250 rState.textUnderlineStyle,
251 rState.textStrikeoutStyle );
253 const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( bOldMode );
255 return aTextInfo;
258 namespace
260 void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly,
261 const ::basegfx::B2DPoint& rStartPos,
262 const double nX1,
263 const double nY1,
264 const double nX2,
265 const double nY2 )
267 const double x( rStartPos.getX() );
268 const double y( rStartPos.getY() );
270 o_rPoly.append(
271 ::basegfx::tools::createPolygonFromRect(
272 ::basegfx::B2DRectangle( x + nX1, y + nY1, x + nX2, y + nY2 ) ) );
275 void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly,
276 const double nX1,
277 const double nY1,
278 const double nX2,
279 const double nY2 )
281 o_rPoly.append(
282 ::basegfx::tools::createPolygonFromRect(
283 ::basegfx::B2DRectangle( nX1, nY1, nX2, nY2 ) ) );
286 void appendDashes( ::basegfx::B2DPolyPolygon& o_rPoly,
287 const double nX,
288 const double nY,
289 const double nLineWidth,
290 const double nLineHeight,
291 const double nDashWidth,
292 const double nDashSkip )
294 const sal_Int32 nNumLoops(
295 static_cast< sal_Int32 >(
296 ::std::max( 1.0,
297 nLineWidth / nDashSkip ) + .5) );
299 double x = nX;
300 for( sal_Int32 i=0; i<nNumLoops; ++i )
302 appendRect( o_rPoly,
303 x, nY,
304 x + nDashWidth, nY + nLineHeight );
306 x += nDashSkip;
311 // create line actions for text such as underline and
312 // strikeout
313 ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const ::basegfx::B2DPoint& rStartPos,
314 const double& rLineWidth,
315 const TextLineInfo& rTextLineInfo )
317 // fill the polypolygon with all text lines
318 ::basegfx::B2DPolyPolygon aTextLinesPolyPoly;
320 switch( rTextLineInfo.mnOverlineStyle )
322 case LINESTYLE_NONE: // nothing to do
323 // FALLTHROUGH intended
324 case LINESTYLE_DONTKNOW:
325 break;
327 case LINESTYLE_SMALLWAVE: // TODO(F3): NYI
328 // FALLTHROUGH intended
329 case LINESTYLE_WAVE: // TODO(F3): NYI
330 // FALLTHROUGH intended
331 case LINESTYLE_SINGLE:
332 appendRect(
333 aTextLinesPolyPoly,
334 rStartPos,
336 rTextLineInfo.mnOverlineOffset,
337 rLineWidth,
338 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight );
339 break;
341 case LINESTYLE_BOLDDOTTED: // TODO(F3): NYI
342 // FALLTHROUGH intended
343 case LINESTYLE_BOLDDASH: // TODO(F3): NYI
344 // FALLTHROUGH intended
345 case LINESTYLE_BOLDLONGDASH: // TODO(F3): NYI
346 // FALLTHROUGH intended
347 case LINESTYLE_BOLDDASHDOT: // TODO(F3): NYI
348 // FALLTHROUGH intended
349 case LINESTYLE_BOLDDASHDOTDOT:// TODO(F3): NYI
350 // FALLTHROUGH intended
351 case LINESTYLE_BOLDWAVE: // TODO(F3): NYI
352 // FALLTHROUGH intended
353 case LINESTYLE_BOLD:
354 appendRect(
355 aTextLinesPolyPoly,
356 rStartPos,
358 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight,
359 rLineWidth,
360 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight );
361 break;
363 case LINESTYLE_DOUBLEWAVE: // TODO(F3): NYI
364 // FALLTHROUGH intended
365 case LINESTYLE_DOUBLE:
366 appendRect(
367 aTextLinesPolyPoly,
368 rStartPos,
370 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight * 2.0 ,
371 rLineWidth,
372 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight );
374 appendRect(
375 aTextLinesPolyPoly,
376 rStartPos,
378 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight,
379 rLineWidth,
380 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight * 2.0 );
381 break;
383 case LINESTYLE_DASHDOTDOT: // TODO(F3): NYI
384 // FALLTHROUGH intended
385 case LINESTYLE_DOTTED:
386 appendDashes(
387 aTextLinesPolyPoly,
388 rStartPos.getX(),
389 rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
390 rLineWidth,
391 rTextLineInfo.mnOverlineHeight,
392 rTextLineInfo.mnOverlineHeight,
393 2*rTextLineInfo.mnOverlineHeight );
394 break;
396 case LINESTYLE_DASHDOT: // TODO(F3): NYI
397 // FALLTHROUGH intended
398 case LINESTYLE_DASH:
399 appendDashes(
400 aTextLinesPolyPoly,
401 rStartPos.getX(),
402 rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
403 rLineWidth,
404 rTextLineInfo.mnOverlineHeight,
405 3*rTextLineInfo.mnOverlineHeight,
406 6*rTextLineInfo.mnOverlineHeight );
407 break;
409 case LINESTYLE_LONGDASH:
410 appendDashes(
411 aTextLinesPolyPoly,
412 rStartPos.getX(),
413 rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
414 rLineWidth,
415 rTextLineInfo.mnOverlineHeight,
416 6*rTextLineInfo.mnOverlineHeight,
417 12*rTextLineInfo.mnOverlineHeight );
418 break;
420 default:
421 ENSURE_OR_THROW( false,
422 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected overline case" );
425 switch( rTextLineInfo.mnUnderlineStyle )
427 case LINESTYLE_NONE: // nothing to do
428 // FALLTHROUGH intended
429 case LINESTYLE_DONTKNOW:
430 break;
432 case LINESTYLE_SMALLWAVE: // TODO(F3): NYI
433 // FALLTHROUGH intended
434 case LINESTYLE_WAVE: // TODO(F3): NYI
435 // FALLTHROUGH intended
436 case LINESTYLE_SINGLE:
437 appendRect(
438 aTextLinesPolyPoly,
439 rStartPos,
441 rTextLineInfo.mnUnderlineOffset,
442 rLineWidth,
443 rTextLineInfo.mnUnderlineOffset + rTextLineInfo.mnLineHeight );
444 break;
446 case LINESTYLE_BOLDDOTTED: // TODO(F3): NYI
447 // FALLTHROUGH intended
448 case LINESTYLE_BOLDDASH: // TODO(F3): NYI
449 // FALLTHROUGH intended
450 case LINESTYLE_BOLDLONGDASH: // TODO(F3): NYI
451 // FALLTHROUGH intended
452 case LINESTYLE_BOLDDASHDOT: // TODO(F3): NYI
453 // FALLTHROUGH intended
454 case LINESTYLE_BOLDDASHDOTDOT:// TODO(F3): NYI
455 // FALLTHROUGH intended
456 case LINESTYLE_BOLDWAVE: // TODO(F3): NYI
457 // FALLTHROUGH intended
458 case LINESTYLE_BOLD:
459 appendRect(
460 aTextLinesPolyPoly,
461 rStartPos,
463 rTextLineInfo.mnUnderlineOffset,
464 rLineWidth,
465 rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight );
466 break;
468 case LINESTYLE_DOUBLEWAVE: // TODO(F3): NYI
469 // FALLTHROUGH intended
470 case LINESTYLE_DOUBLE:
471 appendRect(
472 aTextLinesPolyPoly,
473 rStartPos,
475 rTextLineInfo.mnUnderlineOffset - rTextLineInfo.mnLineHeight,
476 rLineWidth,
477 rTextLineInfo.mnUnderlineOffset );
479 appendRect(
480 aTextLinesPolyPoly,
481 rStartPos,
483 rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight,
484 rLineWidth,
485 rTextLineInfo.mnUnderlineOffset + 3*rTextLineInfo.mnLineHeight );
486 break;
488 case LINESTYLE_DASHDOTDOT: // TODO(F3): NYI
489 // FALLTHROUGH intended
490 case LINESTYLE_DOTTED:
491 appendDashes(
492 aTextLinesPolyPoly,
493 rStartPos.getX(),
494 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
495 rLineWidth,
496 rTextLineInfo.mnLineHeight,
497 rTextLineInfo.mnLineHeight,
498 2*rTextLineInfo.mnLineHeight );
499 break;
501 case LINESTYLE_DASHDOT: // TODO(F3): NYI
502 // FALLTHROUGH intended
503 case LINESTYLE_DASH:
504 appendDashes(
505 aTextLinesPolyPoly,
506 rStartPos.getX(),
507 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
508 rLineWidth,
509 rTextLineInfo.mnLineHeight,
510 3*rTextLineInfo.mnLineHeight,
511 6*rTextLineInfo.mnLineHeight );
512 break;
514 case LINESTYLE_LONGDASH:
515 appendDashes(
516 aTextLinesPolyPoly,
517 rStartPos.getX(),
518 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
519 rLineWidth,
520 rTextLineInfo.mnLineHeight,
521 6*rTextLineInfo.mnLineHeight,
522 12*rTextLineInfo.mnLineHeight );
523 break;
525 default:
526 ENSURE_OR_THROW( false,
527 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected underline case" );
530 switch( rTextLineInfo.mnStrikeoutStyle )
532 case STRIKEOUT_NONE: // nothing to do
533 // FALLTHROUGH intended
534 case STRIKEOUT_DONTKNOW:
535 break;
537 case STRIKEOUT_SLASH: // TODO(Q1): we should handle this in the text layer
538 // FALLTHROUGH intended
539 case STRIKEOUT_X:
540 break;
542 case STRIKEOUT_SINGLE:
543 appendRect(
544 aTextLinesPolyPoly,
545 rStartPos,
547 rTextLineInfo.mnStrikeoutOffset,
548 rLineWidth,
549 rTextLineInfo.mnStrikeoutOffset + rTextLineInfo.mnLineHeight );
550 break;
552 case STRIKEOUT_BOLD:
553 appendRect(
554 aTextLinesPolyPoly,
555 rStartPos,
557 rTextLineInfo.mnStrikeoutOffset,
558 rLineWidth,
559 rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight );
560 break;
562 case STRIKEOUT_DOUBLE:
563 appendRect(
564 aTextLinesPolyPoly,
565 rStartPos,
567 rTextLineInfo.mnStrikeoutOffset - rTextLineInfo.mnLineHeight,
568 rLineWidth,
569 rTextLineInfo.mnStrikeoutOffset );
571 appendRect(
572 aTextLinesPolyPoly,
573 rStartPos,
575 rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight,
576 rLineWidth,
577 rTextLineInfo.mnStrikeoutOffset + 3*rTextLineInfo.mnLineHeight );
578 break;
580 default:
581 ENSURE_OR_THROW( false,
582 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected strikeout case" );
585 return aTextLinesPolyPoly;
588 ::basegfx::B2DRange calcDevicePixelBounds( const ::basegfx::B2DRange& rBounds,
589 const rendering::ViewState& viewState,
590 const rendering::RenderState& renderState )
592 ::basegfx::B2DHomMatrix aTransform;
593 ::canvas::tools::mergeViewAndRenderTransform( aTransform,
594 viewState,
595 renderState );
597 ::basegfx::B2DRange aTransformedBounds;
598 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds,
599 rBounds,
600 aTransform );
603 // create line actions for text such as underline and
604 // strikeout
605 ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const double& rStartOffset,
606 const double& rLineWidth,
607 const TextLineInfo& rTextLineInfo )
609 return createTextLinesPolyPolygon(
610 ::basegfx::B2DPoint( rStartOffset,
611 0.0 ),
612 rLineWidth,
613 rTextLineInfo );
618 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */