LCOV - code coverage report
Current view: top level - libreoffice/drawinglayer/source/primitive2d - borderlineprimitive2d.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 148 171 86.5 %
Date: 2012-12-27 Functions: 7 8 87.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       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/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      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 .
      18             :  */
      19             : 
      20             : #include <drawinglayer/geometry/viewinformation2d.hxx>
      21             : #include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
      22             : #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
      23             : #include <basegfx/polygon/b2dpolygon.hxx>
      24             : #include <basegfx/polygon/b2dpolygonclipper.hxx>
      25             : #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
      26             : #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
      27             : #include <svtools/borderhelper.hxx>
      28             : #include <numeric>
      29             : #include <algorithm>
      30             : 
      31             : //////////////////////////////////////////////////////////////////////////////
      32             : 
      33             : namespace drawinglayer
      34             : {
      35             :     // fdo#49438: heuristic pseudo hack
      36        6142 :     static bool lcl_UseHairline(double const fW,
      37             :             basegfx::B2DPoint const& rStart, basegfx::B2DPoint const& rEnd,
      38             :             geometry::ViewInformation2D const& rViewInformation)
      39             :     {
      40        6142 :         basegfx::B2DTuple scale;
      41        6142 :         basegfx::B2DTuple translation;
      42             :         double fRotation;
      43             :         double fShear;
      44        6142 :         rViewInformation.getObjectToViewTransformation().decompose(
      45        6142 :                 scale, translation, fRotation, fShear);
      46             :         double const fScale(
      47       12284 :             (rEnd.getX() - rStart.getX() > rEnd.getY() - rStart.getY())
      48       12284 :                 ? scale.getY() : scale.getX());
      49        6142 :         return (fW * fScale < 0.51);
      50             :     }
      51             : 
      52        5534 :     static double lcl_GetCorrectedWidth(double const fW,
      53             :             basegfx::B2DPoint const& rStart, basegfx::B2DPoint const& rEnd,
      54             :             geometry::ViewInformation2D const& rViewInformation)
      55             :     {
      56        5534 :         return (lcl_UseHairline(fW, rStart, rEnd, rViewInformation)) ? 0.0 : fW;
      57             :     }
      58             : 
      59             :     namespace primitive2d
      60             :     {
      61        1620 :         double BorderLinePrimitive2D::getWidth(
      62             :             geometry::ViewInformation2D const& rViewInformation) const
      63             :         {
      64        1620 :             return lcl_GetCorrectedWidth(mfLeftWidth, getStart(), getEnd(),
      65        1620 :                         rViewInformation)
      66        1620 :                  + lcl_GetCorrectedWidth(mfDistance, getStart(), getEnd(),
      67        1620 :                          rViewInformation)
      68        1620 :                  + lcl_GetCorrectedWidth(mfRightWidth, getStart(), getEnd(),
      69        3240 :                          rViewInformation);
      70             :         }
      71             : 
      72         540 :         basegfx::B2DPolyPolygon BorderLinePrimitive2D::getClipPolygon(
      73             :             geometry::ViewInformation2D const& rViewInformation) const
      74             :         {
      75         540 :             basegfx::B2DPolygon clipPolygon;
      76             : 
      77             :             // Get the vectors
      78         540 :             basegfx::B2DVector aVector( getEnd() - getStart() );
      79         540 :             aVector.normalize();
      80         540 :             const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
      81             : 
      82             :             // Get the points
      83         540 :             const double fWidth(getWidth(rViewInformation));
      84             :             const basegfx::B2DVector aLeftOff(
      85         540 :                     aPerpendicular * (-0.5 * std::max(fWidth, 1.0)));
      86             :             const basegfx::B2DVector aRightOff(
      87         540 :                     aPerpendicular * (0.5 * std::max(fWidth, 1.0)));
      88             : 
      89         540 :             const basegfx::B2DVector aSLVector( aLeftOff - ( getExtendLeftStart() * aVector ) );
      90         540 :             clipPolygon.append( basegfx::B2DPoint( getStart() + aSLVector * 2.0 ) );
      91             : 
      92         540 :             clipPolygon.append( getStart( ) );
      93             : 
      94         540 :             const basegfx::B2DVector aSRVector( aRightOff - ( getExtendRightStart() * aVector ) );
      95         540 :             clipPolygon.append( basegfx::B2DPoint( getStart() + aSRVector * 2.0 ) );
      96             : 
      97         540 :             const basegfx::B2DVector aERVector( aRightOff + ( getExtendRightEnd() * aVector ) );
      98         540 :             clipPolygon.append( basegfx::B2DPoint( getEnd() + aERVector * 2.0 ) );
      99             : 
     100         540 :             clipPolygon.append( getEnd( ) );
     101             : 
     102         540 :             const basegfx::B2DVector aELVector( aLeftOff + ( getExtendLeftEnd() * aVector ) );
     103         540 :             clipPolygon.append( basegfx::B2DPoint( getEnd() + aELVector * 2.0 ) );
     104             : 
     105         540 :             clipPolygon.setClosed( true );
     106             : 
     107         540 :             return basegfx::B2DPolyPolygon( clipPolygon );
     108             :         }
     109             : 
     110         542 :         Primitive2DSequence BorderLinePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
     111             :         {
     112         542 :             Primitive2DSequence xRetval;
     113             : 
     114         542 :             if(!getStart().equal(getEnd()) && ( isInsideUsed() || isOutsideUsed() ) )
     115             :             {
     116             :                 // get data and vectors
     117         540 :                 const double fWidth(getWidth(rViewInformation));
     118         540 :                 basegfx::B2DVector aVector(getEnd() - getStart());
     119         540 :                 aVector.normalize();
     120         540 :                 const basegfx::B2DVector aPerpendicular(basegfx::getPerpendicular(aVector));
     121             : 
     122             :                 const basegfx::B2DPolyPolygon& aClipRegion =
     123         540 :                     getClipPolygon(rViewInformation);
     124             : 
     125         540 :                 if(isOutsideUsed() && isInsideUsed())
     126             :                 {
     127          68 :                     const double fExt = getWidth(rViewInformation);  // Extend a lot: it'll be clipped after
     128             : 
     129             :                     // both used, double line definition. Create left and right offset
     130          68 :                     xRetval.realloc(2);
     131          68 :                     sal_uInt32 nInsert(0);
     132             : 
     133          68 :                     basegfx::B2DPolygon aGap;
     134             : 
     135             :                     {
     136             :                         // create geometry for left
     137          68 :                         const basegfx::B2DVector aLeftOff(aPerpendicular * (0.5 * (lcl_GetCorrectedWidth(mfLeftWidth, getStart(), getEnd(), rViewInformation) - fWidth + 1)));
     138          68 :                         const basegfx::B2DPoint aTmpStart(getStart() + aLeftOff - ( fExt * aVector));
     139          68 :                         const basegfx::B2DPoint aTmpEnd(getEnd() + aLeftOff + ( fExt * aVector));
     140          68 :                         basegfx::B2DPolygon aLeft;
     141             : 
     142         136 :                         if (lcl_UseHairline(mfLeftWidth, getStart(), getEnd(),
     143          68 :                                     rViewInformation))
     144             :                         {
     145             :                             // create hairline primitive
     146          34 :                             aLeft.append(aTmpStart);
     147          34 :                             aLeft.append(aTmpEnd);
     148             : 
     149             :                             basegfx::B2DPolyPolygon const aClipped =
     150             :                                 basegfx::tools::clipPolygonOnPolyPolygon(
     151          34 :                                     aLeft, aClipRegion, true, true);
     152             : 
     153          34 :                             xRetval[nInsert++] =
     154             :                                 new PolyPolygonHairlinePrimitive2D(
     155             :                                     aClipped,
     156          68 :                                     getRGBColorLeft());
     157             : 
     158          34 :                             aGap.append( getStart() - getExtendLeftStart() * aVector );
     159          34 :                             aGap.append( getEnd() + getExtendLeftEnd() * aVector );
     160             :                         }
     161             :                         else
     162             :                         {
     163             :                             // create filled polygon primitive. Already tried to create thick lines
     164             :                             // with the correct LineWidth, but this leads to problems when no AA
     165             :                             // is available and fat line special case reductions between 0.5 < x < 2.5 line widths
     166             :                             // are executed due to the FilledPolygon-do-not-paint-their-bottom-and-right-lines.
     167          34 :                             const basegfx::B2DVector aLineWidthOffset((lcl_GetCorrectedWidth(mfLeftWidth, getStart(), getEnd(), rViewInformation) * 0.5) * aPerpendicular);
     168             : 
     169          34 :                             aLeft.append(aTmpStart + aLineWidthOffset);
     170          34 :                             aLeft.append(aTmpEnd + aLineWidthOffset);
     171          34 :                             aLeft.append(aTmpEnd - aLineWidthOffset);
     172          34 :                             aLeft.append(aTmpStart - aLineWidthOffset);
     173          34 :                             aLeft.setClosed(true);
     174             : 
     175             :                             basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon(
     176          34 :                                     aLeft, aClipRegion, true, false );
     177             : 
     178          34 :                             aGap.append( aTmpStart + aLineWidthOffset );
     179          34 :                             aGap.append( aTmpEnd + aLineWidthOffset );
     180             : 
     181          34 :                             xRetval[nInsert++] = Primitive2DReference(new PolyPolygonColorPrimitive2D(
     182          68 :                                 aClipped, getRGBColorLeft()));
     183          68 :                         }
     184             :                     }
     185             : 
     186             :                     {
     187             :                         // create geometry for right
     188          68 :                         const basegfx::B2DVector aRightOff(aPerpendicular * (0.5 * (fWidth - lcl_GetCorrectedWidth(mfRightWidth, getStart(), getEnd(), rViewInformation) + 1)));
     189          68 :                         const basegfx::B2DPoint aTmpStart(getStart() + aRightOff - ( fExt * aVector));
     190          68 :                         const basegfx::B2DPoint aTmpEnd(getEnd() + aRightOff + ( fExt * aVector));
     191          68 :                         basegfx::B2DPolygon aRight;
     192             : 
     193         136 :                         if (lcl_UseHairline(mfRightWidth, getStart(), getEnd(),
     194          68 :                                     rViewInformation))
     195             :                         {
     196             :                             // create hairline primitive
     197          36 :                             aRight.append(aTmpStart);
     198          36 :                             aRight.append(aTmpEnd);
     199             : 
     200             :                             basegfx::B2DPolyPolygon const aClipped =
     201             :                                 basegfx::tools::clipPolygonOnPolyPolygon(
     202          36 :                                     aRight, aClipRegion, true, true);
     203             : 
     204          36 :                             xRetval[nInsert++] =
     205             :                                 new PolyPolygonHairlinePrimitive2D(
     206             :                                     aClipped,
     207          72 :                                     getRGBColorRight());
     208             : 
     209          36 :                             aGap.append( getStart() - getExtendRightStart() * aVector );
     210          36 :                             aGap.append( getEnd() + getExtendRightEnd() * aVector );
     211             :                         }
     212             :                         else
     213             :                         {
     214             :                             // create filled polygon primitive
     215          32 :                             const basegfx::B2DVector aLineWidthOffset((lcl_GetCorrectedWidth(mfRightWidth, getStart(), getEnd(), rViewInformation) * 0.5) * aPerpendicular);
     216             : 
     217          32 :                             aRight.append(aTmpStart + aLineWidthOffset);
     218          32 :                             aRight.append(aTmpEnd + aLineWidthOffset);
     219          32 :                             aRight.append(aTmpEnd - aLineWidthOffset);
     220          32 :                             aRight.append(aTmpStart - aLineWidthOffset);
     221          32 :                             aRight.setClosed(true);
     222             : 
     223             :                             basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon(
     224          32 :                                     aRight, aClipRegion, true, false );
     225             : 
     226          32 :                             xRetval[nInsert++] = Primitive2DReference(new PolyPolygonColorPrimitive2D(
     227          64 :                                 aClipped, getRGBColorRight()));
     228             : 
     229          32 :                             aGap.append( aTmpEnd - aLineWidthOffset );
     230          32 :                             aGap.append( aTmpStart - aLineWidthOffset );
     231          68 :                         }
     232             :                     }
     233             : 
     234          68 :                     if (hasGapColor() && aGap.count() == 4)
     235             :                     {
     236           0 :                         xRetval.realloc( xRetval.getLength() + 1 );
     237             :                         // create geometry for filled gap
     238           0 :                         aGap.setClosed( true );
     239             : 
     240             :                         basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon(
     241           0 :                                 aGap, aClipRegion, true, false );
     242             : 
     243           0 :                         xRetval[nInsert++] = Primitive2DReference( new PolyPolygonColorPrimitive2D(
     244           0 :                               aClipped, getRGBColorGap() ) );
     245          68 :                     }
     246             :                 }
     247             :                 else
     248             :                 {
     249             :                     // single line, create geometry
     250         472 :                     basegfx::B2DPolygon aPolygon;
     251         472 :                     const double fExt = getWidth(rViewInformation);  // Extend a lot: it'll be clipped after
     252         472 :                     const basegfx::B2DPoint aTmpStart(getStart() - (fExt * aVector));
     253         472 :                     const basegfx::B2DPoint aTmpEnd(getEnd() + (fExt * aVector));
     254         472 :                     xRetval.realloc(1);
     255             : 
     256             :                     // Get which is the line to show
     257         472 :                     bool bIsSolidline = isSolidLine();
     258         472 :                     double nWidth = getLeftWidth();
     259         472 :                     basegfx::BColor aColor = getRGBColorLeft();
     260         472 :                     if ( basegfx::fTools::equal( 0.0, mfLeftWidth ) )
     261             :                     {
     262           6 :                         nWidth = getRightWidth();
     263           6 :                         aColor = getRGBColorRight();
     264             :                     }
     265             :                     bool const bIsHairline = lcl_UseHairline(
     266         472 :                             nWidth, getStart(), getEnd(), rViewInformation);
     267             :                     nWidth = lcl_GetCorrectedWidth(nWidth,
     268         472 :                                 getStart(), getEnd(), rViewInformation);
     269             : 
     270         472 :                     if(bIsHairline && bIsSolidline)
     271             :                     {
     272             :                         // create hairline primitive
     273         376 :                         aPolygon.append( getStart() );
     274         376 :                         aPolygon.append( getEnd() );
     275             : 
     276         376 :                         xRetval[0] = Primitive2DReference(new PolygonHairlinePrimitive2D(
     277             :                             aPolygon,
     278         752 :                             aColor));
     279             :                     }
     280             :                     else
     281             :                     {
     282             :                         // create filled polygon primitive
     283          96 :                         const basegfx::B2DVector aLineWidthOffset(((nWidth + 1) * 0.5) * aPerpendicular);
     284          96 :                         basegfx::B2DVector aScale( rViewInformation.getInverseObjectToViewTransformation() * aVector );
     285             : 
     286          96 :                         aPolygon.append( aTmpStart );
     287          96 :                         aPolygon.append( aTmpEnd );
     288             : 
     289             :                         basegfx::B2DPolyPolygon aDashed = svtools::ApplyLineDashing(
     290          96 :                                aPolygon, getStyle(), MAP_PIXEL, aScale.getLength() );
     291         192 :                         for (sal_uInt32 i = 0; i < aDashed.count(); i++ )
     292             :                         {
     293          96 :                             basegfx::B2DPolygon aDash = aDashed.getB2DPolygon( i );
     294          96 :                             basegfx::B2DPoint aDashStart = aDash.getB2DPoint( 0 );
     295          96 :                             basegfx::B2DPoint aDashEnd = aDash.getB2DPoint( aDash.count() - 1 );
     296             : 
     297          96 :                             basegfx::B2DPolygon aDashPolygon;
     298          96 :                             aDashPolygon.append( aDashStart + aLineWidthOffset );
     299          96 :                             aDashPolygon.append( aDashEnd + aLineWidthOffset );
     300          96 :                             aDashPolygon.append( aDashEnd - aLineWidthOffset );
     301          96 :                             aDashPolygon.append( aDashStart - aLineWidthOffset );
     302          96 :                             aDashPolygon.setClosed( true );
     303             : 
     304             :                             basegfx::B2DPolyPolygon aClipped = basegfx::tools::clipPolygonOnPolyPolygon(
     305          96 :                                 aDashPolygon, aClipRegion, true, false );
     306             : 
     307          96 :                             if ( aClipped.count() )
     308          96 :                                 aDashed.setB2DPolygon( i, aClipped.getB2DPolygon( 0 ) );
     309          96 :                         }
     310             : 
     311          96 :                         xRetval[0] = Primitive2DReference(new PolyPolygonColorPrimitive2D(
     312         192 :                                 basegfx::B2DPolyPolygon( aDashed ), aColor));
     313         472 :                     }
     314         540 :                 }
     315             :             }
     316             : 
     317         542 :             return xRetval;
     318             :         }
     319             : 
     320         542 :         BorderLinePrimitive2D::BorderLinePrimitive2D(
     321             :             const basegfx::B2DPoint& rStart,
     322             :             const basegfx::B2DPoint& rEnd,
     323             :             double fLeftWidth,
     324             :             double fDistance,
     325             :             double fRightWidth,
     326             :             double fExtendLeftStart,
     327             :             double fExtendLeftEnd,
     328             :             double fExtendRightStart,
     329             :             double fExtendRightEnd,
     330             :             const basegfx::BColor& rRGBColorRight,
     331             :             const basegfx::BColor& rRGBColorLeft,
     332             :             const basegfx::BColor& rRGBColorGap,
     333             :             bool bHasGapColor,
     334             :             const short nStyle)
     335             :         :   BufferedDecompositionPrimitive2D(),
     336             :             maStart(rStart),
     337             :             maEnd(rEnd),
     338             :             mfLeftWidth(fLeftWidth),
     339             :             mfDistance(fDistance),
     340             :             mfRightWidth(fRightWidth),
     341             :             mfExtendLeftStart(fExtendLeftStart),
     342             :             mfExtendLeftEnd(fExtendLeftEnd),
     343             :             mfExtendRightStart(fExtendRightStart),
     344             :             mfExtendRightEnd(fExtendRightEnd),
     345             :             maRGBColorRight(rRGBColorRight),
     346             :             maRGBColorLeft(rRGBColorLeft),
     347             :             maRGBColorGap(rRGBColorGap),
     348             :             mbHasGapColor(bHasGapColor),
     349         542 :             mnStyle(nStyle)
     350             :         {
     351         542 :         }
     352             : 
     353           0 :         bool BorderLinePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
     354             :         {
     355           0 :             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
     356             :             {
     357           0 :                 const BorderLinePrimitive2D& rCompare = (BorderLinePrimitive2D&)rPrimitive;
     358             : 
     359           0 :                 return (getStart() == rCompare.getStart()
     360           0 :                     && getEnd() == rCompare.getEnd()
     361           0 :                     && getLeftWidth() == rCompare.getLeftWidth()
     362           0 :                     && getDistance() == rCompare.getDistance()
     363           0 :                     && getRightWidth() == rCompare.getRightWidth()
     364           0 :                     && getExtendLeftStart() == rCompare.getExtendLeftStart()
     365           0 :                     && getExtendLeftEnd() == rCompare.getExtendLeftEnd()
     366           0 :                     && getExtendRightStart() == rCompare.getExtendRightStart()
     367           0 :                     && getExtendRightEnd() == rCompare.getExtendRightEnd()
     368           0 :                     && getRGBColorRight() == rCompare.getRGBColorRight()
     369           0 :                     && getRGBColorLeft() == rCompare.getRGBColorLeft()
     370           0 :                     && getRGBColorGap() == rCompare.getRGBColorGap()
     371           0 :                     && hasGapColor() == rCompare.hasGapColor()
     372           0 :                     && getStyle() == rCompare.getStyle());
     373             :             }
     374             : 
     375           0 :             return false;
     376             :         }
     377             : 
     378             :         // provide unique ID
     379         542 :         ImplPrimitrive2DIDBlock(BorderLinePrimitive2D, PRIMITIVE2D_ID_BORDERLINEPRIMITIVE2D)
     380             : 
     381             :     } // end of namespace primitive2d
     382             : } // end of namespace drawinglayer
     383             : 
     384             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10