Bump version to 5.0-14
[LibreOffice.git] / cppcanvas / source / mtfrenderer / mtftools.cxx
blob52d5d6aa9e6b0592732daf64cd4d18f1f1f76826
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 <canvas/debug.hxx>
22 #include <tools/diagnose_ex.h>
23 #include <canvas/verbosetrace.hxx>
24 #include <com/sun/star/rendering/RenderState.hpp>
25 #include <com/sun/star/rendering/XCanvas.hpp>
26 #include <basegfx/numeric/ftools.hxx>
27 #include <basegfx/tools/canvastools.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <basegfx/polygon/b2dpolygon.hxx>
30 #include <basegfx/range/b2drectangle.hxx>
31 #include <basegfx/vector/b2dvector.hxx>
32 #include <canvas/canvastools.hxx>
33 #include <vcl/gdimtf.hxx>
34 #include <vcl/metaact.hxx>
35 #include <vcl/virdev.hxx>
36 #include <vcl/metric.hxx>
37 #include <tools/poly.hxx>
38 #include "mtftools.hxx"
39 #include "outdevstate.hxx"
40 #include "polypolyaction.hxx"
41 #include <basegfx/matrix/b2dhommatrixtools.hxx>
45 using namespace ::com::sun::star;
47 namespace cppcanvas
49 namespace tools
51 void initRenderState( rendering::RenderState& renderState,
52 const ::cppcanvas::internal::OutDevState& outdevState )
54 ::canvas::tools::initRenderState( renderState );
55 ::canvas::tools::setRenderStateTransform( renderState,
56 outdevState.transform );
57 renderState.Clip = outdevState.xClipPoly;
60 ::Size getBaselineOffset( const ::cppcanvas::internal::OutDevState& outdevState,
61 const VirtualDevice& rVDev )
63 const ::FontMetric& aMetric = rVDev.GetFontMetric();
65 // calc offset for text output, the XCanvas always renders
66 // baseline offset.
67 switch( outdevState.textReferencePoint )
69 case ALIGN_TOP:
70 return ::Size( 0,
71 aMetric.GetIntLeading() + aMetric.GetAscent() );
73 default:
74 ENSURE_OR_THROW( false,
75 "tools::getBaselineOffset(): Unexpected TextAlign value" );
76 // FALLTHROUGH intended (to calm compiler warning - case won't happen)
77 case ALIGN_BASELINE:
78 return ::Size( 0, 0 );
80 case ALIGN_BOTTOM:
81 return ::Size( 0,
82 -aMetric.GetDescent() );
87 ::basegfx::B2DHomMatrix& calcLogic2PixelLinearTransform( ::basegfx::B2DHomMatrix& o_rMatrix,
88 const VirtualDevice& rVDev )
90 // select size value in the middle of the available range,
91 // to have headroom both when map mode scales up, and when
92 // it scales down.
93 const ::Size aSizeLogic( 0x00010000L,
94 0x00010000L );
96 const ::Size aSizePixel( rVDev.LogicToPixel( aSizeLogic ) );
98 o_rMatrix = basegfx::tools::createScaleB2DHomMatrix(
99 aSizePixel.Width() / (double)aSizeLogic.Width(),
100 aSizePixel.Height() / (double)aSizeLogic.Height() );
102 return o_rMatrix;
105 ::basegfx::B2DHomMatrix& calcLogic2PixelAffineTransform( ::basegfx::B2DHomMatrix& o_rMatrix,
106 const VirtualDevice& rVDev )
108 // retrieves scale
109 calcLogic2PixelLinearTransform(o_rMatrix, rVDev);
111 // translate according to curr map mode/pref map mode offset
112 const ::Point aEmptyPoint;
113 const ::Point& rTranslatedPoint(
114 rVDev.LogicToPixel( aEmptyPoint ));
116 o_rMatrix.translate(rTranslatedPoint.X(),
117 rTranslatedPoint.Y());
119 return o_rMatrix;
122 bool modifyClip( rendering::RenderState& o_rRenderState,
123 const struct ::cppcanvas::internal::OutDevState& rOutdevState,
124 const CanvasSharedPtr& rCanvas,
125 const ::basegfx::B2DPoint& rOffset,
126 const ::basegfx::B2DVector* pScaling,
127 const double* pRotation )
129 const bool bOffsetting( !rOffset.equalZero() );
130 const bool bScaling( pScaling &&
131 pScaling->getX() != 1.0 &&
132 pScaling->getY() != 1.0 );
133 const bool bRotation( pRotation &&
134 *pRotation != 0.0 );
136 if( !bOffsetting && !bScaling && !bRotation )
137 return false; // nothing to do
139 if( rOutdevState.clip.count() )
141 // general polygon case
143 ::basegfx::B2DPolyPolygon aLocalClip( rOutdevState.clip );
144 ::basegfx::B2DHomMatrix aTransform;
146 if( bOffsetting )
147 aTransform.translate( -rOffset.getX(),
148 -rOffset.getY() );
149 if( bScaling )
150 aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() );
152 if( bRotation )
153 aTransform.rotate( - *pRotation );
155 aLocalClip.transform( aTransform );
157 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
158 rCanvas->getUNOCanvas()->getDevice(),
159 aLocalClip );
161 return true;
163 else if( !rOutdevState.clipRect.IsEmpty() )
165 // simple rect case
167 const ::Rectangle aLocalClipRect( rOutdevState.clipRect );
169 if( bRotation )
171 // rotation involved - convert to polygon first,
172 // then transform that
173 ::basegfx::B2DPolygon aLocalClip(
174 ::basegfx::tools::createPolygonFromRect(
175 ::basegfx::B2DRectangle(
176 (double)(aLocalClipRect.Left()),
177 (double)(aLocalClipRect.Top()),
178 (double)(aLocalClipRect.Right()),
179 (double)(aLocalClipRect.Bottom()) ) ) );
180 ::basegfx::B2DHomMatrix aTransform;
182 if( bOffsetting )
183 aTransform.translate( -rOffset.getX(),
184 -rOffset.getY() );
185 if( bScaling )
186 aTransform.scale( 1.0/pScaling->getX(), 1.0/pScaling->getY() );
188 aTransform.rotate( - *pRotation );
190 aLocalClip.transform( aTransform );
192 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
193 rCanvas->getUNOCanvas()->getDevice(),
194 ::basegfx::B2DPolyPolygon( aLocalClip ) );
196 else if( bScaling )
198 // scale and offset - do it on the fly, have to
199 // convert to float anyway.
200 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
201 rCanvas->getUNOCanvas()->getDevice(),
202 ::basegfx::B2DPolyPolygon(
203 ::basegfx::tools::createPolygonFromRect(
204 ::basegfx::B2DRectangle(
205 (double)(aLocalClipRect.Left() - rOffset.getX())/pScaling->getX(),
206 (double)(aLocalClipRect.Top() - rOffset.getY())/pScaling->getY(),
207 (double)(aLocalClipRect.Right() - rOffset.getX())/pScaling->getX(),
208 (double)(aLocalClipRect.Bottom() - rOffset.getY())/pScaling->getY() ) ) ) );
210 else
212 // offset only - do it on the fly, have to convert
213 // to float anyway.
214 o_rRenderState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
215 rCanvas->getUNOCanvas()->getDevice(),
216 ::basegfx::B2DPolyPolygon(
217 ::basegfx::tools::createPolygonFromRect(
218 ::basegfx::B2DRectangle( aLocalClipRect.Left() - rOffset.getX(),
219 aLocalClipRect.Top() - rOffset.getY(),
220 aLocalClipRect.Right() - rOffset.getX(),
221 aLocalClipRect.Bottom() - rOffset.getY() ) ) ) );
224 return true;
227 // empty clip, nothing to do
228 return false;
231 // create overline/underline/strikeout line info struct
232 TextLineInfo createTextLineInfo( const ::VirtualDevice& rVDev,
233 const ::cppcanvas::internal::OutDevState& rState )
235 const bool bOldMode( rVDev.IsMapModeEnabled() );
237 // #i68512# Force metric regeneration with mapmode enabled
238 // (prolly OutDev bug)
239 rVDev.GetFontMetric();
241 // will restore map mode below
242 const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( false );
244 const ::FontMetric aMetric = rVDev.GetFontMetric();
246 TextLineInfo aTextInfo(
247 (aMetric.GetDescent() + 2) / 4.0,
248 ((aMetric.GetIntLeading() + 1.5) / 3.0),
249 (aMetric.GetIntLeading() / 2.0) - aMetric.GetAscent(),
250 aMetric.GetDescent() / 2.0,
251 (aMetric.GetIntLeading() - aMetric.GetAscent()) / 3.0,
252 rState.textOverlineStyle,
253 rState.textUnderlineStyle,
254 rState.textStrikeoutStyle );
256 const_cast< ::VirtualDevice& >(rVDev).EnableMapMode( bOldMode );
258 return aTextInfo;
261 namespace
263 void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly,
264 const ::basegfx::B2DPoint& rStartPos,
265 const double nX1,
266 const double nY1,
267 const double nX2,
268 const double nY2 )
270 const double x( rStartPos.getX() );
271 const double y( rStartPos.getY() );
273 o_rPoly.append(
274 ::basegfx::tools::createPolygonFromRect(
275 ::basegfx::B2DRectangle( x + nX1, y + nY1, x + nX2, y + nY2 ) ) );
278 void appendRect( ::basegfx::B2DPolyPolygon& o_rPoly,
279 const double nX1,
280 const double nY1,
281 const double nX2,
282 const double nY2 )
284 o_rPoly.append(
285 ::basegfx::tools::createPolygonFromRect(
286 ::basegfx::B2DRectangle( nX1, nY1, nX2, nY2 ) ) );
289 void appendDashes( ::basegfx::B2DPolyPolygon& o_rPoly,
290 const double nX,
291 const double nY,
292 const double nLineWidth,
293 const double nLineHeight,
294 const double nDashWidth,
295 const double nDashSkip )
297 const sal_Int32 nNumLoops(
298 static_cast< sal_Int32 >(
299 ::std::max( 1.0,
300 nLineWidth / nDashSkip ) + .5) );
302 double x = nX;
303 for( sal_Int32 i=0; i<nNumLoops; ++i )
305 appendRect( o_rPoly,
306 x, nY,
307 x + nDashWidth, nY + nLineHeight );
309 x += nDashSkip;
314 // create line actions for text such as underline and
315 // strikeout
316 ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const ::basegfx::B2DPoint& rStartPos,
317 const double& rLineWidth,
318 const TextLineInfo& rTextLineInfo )
320 // fill the polypolygon with all text lines
321 ::basegfx::B2DPolyPolygon aTextLinesPolyPoly;
323 switch( rTextLineInfo.mnOverlineStyle )
325 case UNDERLINE_NONE: // nothing to do
326 // FALLTHROUGH intended
327 case UNDERLINE_DONTKNOW:
328 break;
330 case UNDERLINE_SMALLWAVE: // TODO(F3): NYI
331 // FALLTHROUGH intended
332 case UNDERLINE_WAVE: // TODO(F3): NYI
333 // FALLTHROUGH intended
334 case UNDERLINE_SINGLE:
335 appendRect(
336 aTextLinesPolyPoly,
337 rStartPos,
339 rTextLineInfo.mnOverlineOffset,
340 rLineWidth,
341 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight );
342 break;
344 case UNDERLINE_BOLDDOTTED: // TODO(F3): NYI
345 // FALLTHROUGH intended
346 case UNDERLINE_BOLDDASH: // TODO(F3): NYI
347 // FALLTHROUGH intended
348 case UNDERLINE_BOLDLONGDASH: // TODO(F3): NYI
349 // FALLTHROUGH intended
350 case UNDERLINE_BOLDDASHDOT: // TODO(F3): NYI
351 // FALLTHROUGH intended
352 case UNDERLINE_BOLDDASHDOTDOT:// TODO(F3): NYI
353 // FALLTHROUGH intended
354 case UNDERLINE_BOLDWAVE: // TODO(F3): NYI
355 // FALLTHROUGH intended
356 case UNDERLINE_BOLD:
357 appendRect(
358 aTextLinesPolyPoly,
359 rStartPos,
361 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight,
362 rLineWidth,
363 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight );
364 break;
366 case UNDERLINE_DOUBLEWAVE: // TODO(F3): NYI
367 // FALLTHROUGH intended
368 case UNDERLINE_DOUBLE:
369 appendRect(
370 aTextLinesPolyPoly,
371 rStartPos,
373 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight * 2.0 ,
374 rLineWidth,
375 rTextLineInfo.mnOverlineOffset - rTextLineInfo.mnOverlineHeight );
377 appendRect(
378 aTextLinesPolyPoly,
379 rStartPos,
381 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight,
382 rLineWidth,
383 rTextLineInfo.mnOverlineOffset + rTextLineInfo.mnOverlineHeight * 2.0 );
384 break;
386 case UNDERLINE_DASHDOTDOT: // TODO(F3): NYI
387 // FALLTHROUGH intended
388 case UNDERLINE_DOTTED:
389 appendDashes(
390 aTextLinesPolyPoly,
391 rStartPos.getX(),
392 rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
393 rLineWidth,
394 rTextLineInfo.mnOverlineHeight,
395 rTextLineInfo.mnOverlineHeight,
396 2*rTextLineInfo.mnOverlineHeight );
397 break;
399 case UNDERLINE_DASHDOT: // TODO(F3): NYI
400 // FALLTHROUGH intended
401 case UNDERLINE_DASH:
402 appendDashes(
403 aTextLinesPolyPoly,
404 rStartPos.getX(),
405 rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
406 rLineWidth,
407 rTextLineInfo.mnOverlineHeight,
408 3*rTextLineInfo.mnOverlineHeight,
409 6*rTextLineInfo.mnOverlineHeight );
410 break;
412 case UNDERLINE_LONGDASH:
413 appendDashes(
414 aTextLinesPolyPoly,
415 rStartPos.getX(),
416 rStartPos.getY() + rTextLineInfo.mnOverlineOffset,
417 rLineWidth,
418 rTextLineInfo.mnOverlineHeight,
419 6*rTextLineInfo.mnOverlineHeight,
420 12*rTextLineInfo.mnOverlineHeight );
421 break;
423 default:
424 ENSURE_OR_THROW( false,
425 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected overline case" );
428 switch( rTextLineInfo.mnUnderlineStyle )
430 case UNDERLINE_NONE: // nothing to do
431 // FALLTHROUGH intended
432 case UNDERLINE_DONTKNOW:
433 break;
435 case UNDERLINE_SMALLWAVE: // TODO(F3): NYI
436 // FALLTHROUGH intended
437 case UNDERLINE_WAVE: // TODO(F3): NYI
438 // FALLTHROUGH intended
439 case UNDERLINE_SINGLE:
440 appendRect(
441 aTextLinesPolyPoly,
442 rStartPos,
444 rTextLineInfo.mnUnderlineOffset,
445 rLineWidth,
446 rTextLineInfo.mnUnderlineOffset + rTextLineInfo.mnLineHeight );
447 break;
449 case UNDERLINE_BOLDDOTTED: // TODO(F3): NYI
450 // FALLTHROUGH intended
451 case UNDERLINE_BOLDDASH: // TODO(F3): NYI
452 // FALLTHROUGH intended
453 case UNDERLINE_BOLDLONGDASH: // TODO(F3): NYI
454 // FALLTHROUGH intended
455 case UNDERLINE_BOLDDASHDOT: // TODO(F3): NYI
456 // FALLTHROUGH intended
457 case UNDERLINE_BOLDDASHDOTDOT:// TODO(F3): NYI
458 // FALLTHROUGH intended
459 case UNDERLINE_BOLDWAVE: // TODO(F3): NYI
460 // FALLTHROUGH intended
461 case UNDERLINE_BOLD:
462 appendRect(
463 aTextLinesPolyPoly,
464 rStartPos,
466 rTextLineInfo.mnUnderlineOffset,
467 rLineWidth,
468 rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight );
469 break;
471 case UNDERLINE_DOUBLEWAVE: // TODO(F3): NYI
472 // FALLTHROUGH intended
473 case UNDERLINE_DOUBLE:
474 appendRect(
475 aTextLinesPolyPoly,
476 rStartPos,
478 rTextLineInfo.mnUnderlineOffset - rTextLineInfo.mnLineHeight,
479 rLineWidth,
480 rTextLineInfo.mnUnderlineOffset );
482 appendRect(
483 aTextLinesPolyPoly,
484 rStartPos,
486 rTextLineInfo.mnUnderlineOffset + 2*rTextLineInfo.mnLineHeight,
487 rLineWidth,
488 rTextLineInfo.mnUnderlineOffset + 3*rTextLineInfo.mnLineHeight );
489 break;
491 case UNDERLINE_DASHDOTDOT: // TODO(F3): NYI
492 // FALLTHROUGH intended
493 case UNDERLINE_DOTTED:
494 appendDashes(
495 aTextLinesPolyPoly,
496 rStartPos.getX(),
497 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
498 rLineWidth,
499 rTextLineInfo.mnLineHeight,
500 rTextLineInfo.mnLineHeight,
501 2*rTextLineInfo.mnLineHeight );
502 break;
504 case UNDERLINE_DASHDOT: // TODO(F3): NYI
505 // FALLTHROUGH intended
506 case UNDERLINE_DASH:
507 appendDashes(
508 aTextLinesPolyPoly,
509 rStartPos.getX(),
510 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
511 rLineWidth,
512 rTextLineInfo.mnLineHeight,
513 3*rTextLineInfo.mnLineHeight,
514 6*rTextLineInfo.mnLineHeight );
515 break;
517 case UNDERLINE_LONGDASH:
518 appendDashes(
519 aTextLinesPolyPoly,
520 rStartPos.getX(),
521 rStartPos.getY() + rTextLineInfo.mnUnderlineOffset,
522 rLineWidth,
523 rTextLineInfo.mnLineHeight,
524 6*rTextLineInfo.mnLineHeight,
525 12*rTextLineInfo.mnLineHeight );
526 break;
528 default:
529 ENSURE_OR_THROW( false,
530 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected underline case" );
533 switch( rTextLineInfo.mnStrikeoutStyle )
535 case STRIKEOUT_NONE: // nothing to do
536 // FALLTHROUGH intended
537 case STRIKEOUT_DONTKNOW:
538 break;
540 case STRIKEOUT_SLASH: // TODO(Q1): we should handle this in the text layer
541 // FALLTHROUGH intended
542 case STRIKEOUT_X:
543 break;
545 case STRIKEOUT_SINGLE:
546 appendRect(
547 aTextLinesPolyPoly,
548 rStartPos,
550 rTextLineInfo.mnStrikeoutOffset,
551 rLineWidth,
552 rTextLineInfo.mnStrikeoutOffset + rTextLineInfo.mnLineHeight );
553 break;
555 case STRIKEOUT_BOLD:
556 appendRect(
557 aTextLinesPolyPoly,
558 rStartPos,
560 rTextLineInfo.mnStrikeoutOffset,
561 rLineWidth,
562 rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight );
563 break;
565 case STRIKEOUT_DOUBLE:
566 appendRect(
567 aTextLinesPolyPoly,
568 rStartPos,
570 rTextLineInfo.mnStrikeoutOffset - rTextLineInfo.mnLineHeight,
571 rLineWidth,
572 rTextLineInfo.mnStrikeoutOffset );
574 appendRect(
575 aTextLinesPolyPoly,
576 rStartPos,
578 rTextLineInfo.mnStrikeoutOffset + 2*rTextLineInfo.mnLineHeight,
579 rLineWidth,
580 rTextLineInfo.mnStrikeoutOffset + 3*rTextLineInfo.mnLineHeight );
581 break;
583 default:
584 ENSURE_OR_THROW( false,
585 "::cppcanvas::internal::createTextLinesPolyPolygon(): Unexpected strikeout case" );
588 return aTextLinesPolyPoly;
591 ::basegfx::B2DRange calcDevicePixelBounds( const ::basegfx::B2DRange& rBounds,
592 const rendering::ViewState& viewState,
593 const rendering::RenderState& renderState )
595 ::basegfx::B2DHomMatrix aTransform;
596 ::canvas::tools::mergeViewAndRenderTransform( aTransform,
597 viewState,
598 renderState );
600 ::basegfx::B2DRange aTransformedBounds;
601 return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds,
602 rBounds,
603 aTransform );
606 // create line actions for text such as underline and
607 // strikeout
608 ::basegfx::B2DPolyPolygon createTextLinesPolyPolygon( const double& rStartOffset,
609 const double& rLineWidth,
610 const TextLineInfo& rTextLineInfo )
612 return createTextLinesPolyPolygon(
613 ::basegfx::B2DPoint( rStartOffset,
614 0.0 ),
615 rLineWidth,
616 rTextLineInfo );
621 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */