2014-04-14 21:54:43 +10:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice :
*
* Licensed to the Apache Software Foundation ( ASF ) under one or more
* contributor license agreements . See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership . The ASF licenses this file to you under the Apache
* License , Version 2.0 ( the " License " ) ; you may not use this file
* except in compliance with the License . You may obtain a copy of
* the License at http : //www.apache.org/licenses/LICENSE-2.0 .
*/
2015-06-18 10:37:01 +03:00
# include <cassert>
2014-04-27 05:07:47 -05:00
# include <sal/types.h>
2014-04-14 21:54:43 +10:00
# include <basegfx/matrix/b2dhommatrix.hxx>
# include <basegfx/polygon/b2dpolygontools.hxx>
# include <basegfx/polygon/b2dlinegeometry.hxx>
2014-04-27 05:07:47 -05:00
# include <vcl/outdev.hxx>
# include <vcl/settings.hxx>
2015-05-23 18:52:00 +02:00
# include <vcl/virdev.hxx>
# include <vcl/window.hxx>
2014-04-27 05:07:47 -05:00
2017-10-23 22:28:18 +02:00
# include <salgdi.hxx>
2014-04-27 05:07:47 -05:00
2015-08-16 16:45:12 -05:00
void OutputDevice : : DrawPolyLine ( const tools : : Polygon & rPoly )
2014-04-14 21:54:43 +10:00
{
2015-06-18 10:37:01 +03:00
assert ( ! is_double_buffered_window ( ) ) ;
2014-04-14 21:54:43 +10:00
if ( mpMetaFile )
mpMetaFile - > AddAction ( new MetaPolyLineAction ( rPoly ) ) ;
sal_uInt16 nPoints = rPoly . GetSize ( ) ;
if ( ! IsDeviceOutputNecessary ( ) | | ! mbLineColor | | ( nPoints < 2 ) | | ImplIsRecordLayout ( ) )
return ;
// we need a graphics
2014-04-27 05:07:47 -05:00
if ( ! mpGraphics & & ! AcquireGraphics ( ) )
return ;
2014-04-14 21:54:43 +10:00
if ( mbInitClipRegion )
2014-04-25 02:43:06 +10:00
InitClipRegion ( ) ;
2014-04-27 05:07:47 -05:00
2014-04-14 21:54:43 +10:00
if ( mbOutputClipped )
return ;
if ( mbInitLineColor )
2014-04-22 00:40:20 +10:00
InitLineColor ( ) ;
2014-04-14 21:54:43 +10:00
// use b2dpolygon drawing if possible
2014-11-02 13:10:13 +11:00
if ( DrawPolyLineDirect ( rPoly . getB2DPolygon ( ) ) )
2014-04-14 21:54:43 +10:00
{
2014-11-02 13:10:13 +11:00
basegfx : : B2DPolygon aB2DPolyLine ( rPoly . getB2DPolygon ( ) ) ;
2015-12-15 15:31:50 +02:00
const basegfx : : B2DHomMatrix aTransform = ImplGetDeviceTransformation ( ) ;
const basegfx : : B2DVector aB2DLineWidth ( 1.0 , 1.0 ) ;
2014-04-14 21:54:43 +10:00
2014-11-02 13:10:13 +11:00
// transform the polygon
aB2DPolyLine . transform ( aTransform ) ;
2014-04-14 21:54:43 +10:00
2015-05-19 12:16:31 +02:00
if ( mnAntialiasing & AntialiasingFlags : : PixelSnapHairline )
2014-11-02 13:10:13 +11:00
{
2017-09-22 14:12:07 +03:00
aB2DPolyLine = basegfx : : utils : : snapPointsOfHorizontalOrVerticalEdges ( aB2DPolyLine ) ;
2014-11-02 13:10:13 +11:00
}
2014-04-14 21:54:43 +10:00
2016-04-09 23:15:09 +02:00
if ( mpGraphics - > DrawPolyLine (
aB2DPolyLine ,
0.0 ,
aB2DLineWidth ,
basegfx : : B2DLineJoin : : NONE ,
css : : drawing : : LineCap_BUTT ,
15.0 * F_PI180 /*default fMiterMinimumAngle, not used*/ ,
this ) )
2014-11-02 13:10:13 +11:00
{
return ;
2014-04-14 21:54:43 +10:00
}
}
2015-08-16 16:45:12 -05:00
tools : : Polygon aPoly = ImplLogicToDevicePixel ( rPoly ) ;
2017-09-18 10:37:17 +02:00
SalPoint * pPtAry = reinterpret_cast < SalPoint * > ( aPoly . GetPointAry ( ) ) ;
2014-04-14 21:54:43 +10:00
// #100127# Forward beziers to sal, if any
if ( aPoly . HasFlags ( ) )
{
2016-12-05 12:47:37 +02:00
const PolyFlags * pFlgAry = aPoly . GetConstFlagAry ( ) ;
2014-04-14 21:54:43 +10:00
if ( ! mpGraphics - > DrawPolyLineBezier ( nPoints , pPtAry , pFlgAry , this ) )
{
2015-08-16 16:45:12 -05:00
aPoly = tools : : Polygon : : SubdivideBezier ( aPoly ) ;
2017-09-18 10:37:17 +02:00
pPtAry = reinterpret_cast < SalPoint * > ( aPoly . GetPointAry ( ) ) ;
2014-04-14 21:54:43 +10:00
mpGraphics - > DrawPolyLine ( aPoly . GetSize ( ) , pPtAry , this ) ;
}
}
else
{
mpGraphics - > DrawPolyLine ( nPoints , pPtAry , this ) ;
}
if ( mpAlphaVDev )
mpAlphaVDev - > DrawPolyLine ( rPoly ) ;
}
2015-08-16 16:45:12 -05:00
void OutputDevice : : DrawPolyLine ( const tools : : Polygon & rPoly , const LineInfo & rLineInfo )
2014-04-14 21:54:43 +10:00
{
2015-06-18 10:37:01 +03:00
assert ( ! is_double_buffered_window ( ) ) ;
2014-04-14 21:54:43 +10:00
if ( rLineInfo . IsDefault ( ) )
{
DrawPolyLine ( rPoly ) ;
return ;
}
// #i101491#
// Try direct Fallback to B2D-Version of DrawPolyLine
2015-05-19 12:16:31 +02:00
if ( ( mnAntialiasing & AntialiasingFlags : : EnableB2dDraw ) & &
2016-09-02 08:36:23 +02:00
LineStyle : : Solid = = rLineInfo . GetStyle ( ) )
2014-04-14 21:54:43 +10:00
{
2016-04-09 23:15:09 +02:00
DrawPolyLine (
rPoly . getB2DPolygon ( ) ,
static_cast < double > ( rLineInfo . GetWidth ( ) ) ,
rLineInfo . GetLineJoin ( ) ,
rLineInfo . GetLineCap ( ) ,
15.0 * F_PI180 /* default fMiterMinimumAngle, value not available in LineInfo */ ) ;
2014-04-14 21:54:43 +10:00
return ;
}
if ( mpMetaFile )
mpMetaFile - > AddAction ( new MetaPolyLineAction ( rPoly , rLineInfo ) ) ;
2014-11-02 12:38:16 +11:00
drawPolyLine ( rPoly , rLineInfo ) ;
2014-04-14 21:54:43 +10:00
}
2014-04-27 05:07:47 -05:00
void OutputDevice : : DrawPolyLine ( const basegfx : : B2DPolygon & rB2DPolygon ,
double fLineWidth ,
basegfx : : B2DLineJoin eLineJoin ,
2016-04-09 23:15:09 +02:00
css : : drawing : : LineCap eLineCap ,
double fMiterMinimumAngle )
2014-04-14 21:54:43 +10:00
{
2015-06-18 10:37:01 +03:00
assert ( ! is_double_buffered_window ( ) ) ;
2014-04-14 21:54:43 +10:00
if ( mpMetaFile )
{
LineInfo aLineInfo ;
if ( fLineWidth ! = 0.0 )
aLineInfo . SetWidth ( static_cast < long > ( fLineWidth + 0.5 ) ) ;
2014-04-27 05:07:47 -05:00
2015-08-16 16:45:12 -05:00
const tools : : Polygon aToolsPolygon ( rB2DPolygon ) ;
2014-04-14 21:54:43 +10:00
mpMetaFile - > AddAction ( new MetaPolyLineAction ( aToolsPolygon , aLineInfo ) ) ;
}
// Do not paint empty PolyPolygons
if ( ! rB2DPolygon . count ( ) | | ! IsDeviceOutputNecessary ( ) )
return ;
// we need a graphics
2014-04-27 05:07:47 -05:00
if ( ! mpGraphics & & ! AcquireGraphics ( ) )
return ;
2014-04-14 21:54:43 +10:00
if ( mbInitClipRegion )
2014-04-25 02:43:06 +10:00
InitClipRegion ( ) ;
2014-04-27 05:07:47 -05:00
2014-04-14 21:54:43 +10:00
if ( mbOutputClipped )
return ;
if ( mbInitLineColor )
2014-04-22 00:40:20 +10:00
InitLineColor ( ) ;
2014-04-14 21:54:43 +10:00
// use b2dpolygon drawing if possible
2016-04-09 23:15:09 +02:00
if ( DrawPolyLineDirect ( rB2DPolygon , fLineWidth , 0.0 , eLineJoin , eLineCap , fMiterMinimumAngle ) )
2014-11-02 13:10:13 +11:00
return ;
2014-04-14 21:54:43 +10:00
// #i101491#
// no output yet; fallback to geometry decomposition and use filled polygon paint
// when line is fat and not too complex. ImplDrawPolyPolygonWithB2DPolyPolygon
// will do internal needed AA checks etc.
2014-04-27 05:07:47 -05:00
if ( fLineWidth > = 2.5 & &
rB2DPolygon . count ( ) & &
rB2DPolygon . count ( ) < = 1000 )
2014-04-14 21:54:43 +10:00
{
const double fHalfLineWidth ( ( fLineWidth * 0.5 ) + 0.5 ) ;
const basegfx : : B2DPolyPolygon aAreaPolyPolygon (
2017-09-22 14:12:07 +03:00
basegfx : : utils : : createAreaGeometry ( rB2DPolygon ,
2014-04-27 05:07:47 -05:00
fHalfLineWidth ,
eLineJoin ,
2016-04-09 23:15:09 +02:00
eLineCap ,
fMiterMinimumAngle ) ) ;
2014-04-14 21:54:43 +10:00
const Color aOldLineColor ( maLineColor ) ;
const Color aOldFillColor ( maFillColor ) ;
SetLineColor ( ) ;
2014-04-22 00:40:20 +10:00
InitLineColor ( ) ;
2014-04-14 21:54:43 +10:00
SetFillColor ( aOldLineColor ) ;
2014-04-27 19:38:55 +10:00
InitFillColor ( ) ;
2014-04-14 21:54:43 +10:00
2015-07-02 18:25:58 +02:00
// draw using a loop; else the topology will paint a PolyPolygon
2014-04-14 21:54:43 +10:00
for ( sal_uInt32 a ( 0 ) ; a < aAreaPolyPolygon . count ( ) ; a + + )
{
ImplDrawPolyPolygonWithB2DPolyPolygon (
basegfx : : B2DPolyPolygon ( aAreaPolyPolygon . getB2DPolygon ( a ) ) ) ;
}
SetLineColor ( aOldLineColor ) ;
2014-04-22 00:40:20 +10:00
InitLineColor ( ) ;
2014-04-14 21:54:43 +10:00
SetFillColor ( aOldFillColor ) ;
2014-04-27 19:38:55 +10:00
InitFillColor ( ) ;
2014-04-14 21:54:43 +10:00
2015-05-19 12:16:31 +02:00
const bool bTryAA ( ( mnAntialiasing & AntialiasingFlags : : EnableB2dDraw ) & &
2016-09-01 14:18:39 +02:00
mpGraphics - > supportsOperation ( OutDevSupportType : : B2DDraw ) & &
2016-09-01 15:48:15 +02:00
RasterOp : : OverPaint = = GetRasterOp ( ) & &
2014-11-02 13:10:13 +11:00
IsLineColor ( ) ) ;
2014-11-02 13:42:34 +11:00
// when AA it is necessary to also paint the filled polygon's outline
// to avoid optical gaps
for ( sal_uInt32 a ( 0 ) ; a < aAreaPolyPolygon . count ( ) ; a + + )
2014-04-14 21:54:43 +10:00
{
2017-01-21 15:03:16 +00:00
( void ) DrawPolyLineDirect ( aAreaPolyPolygon . getB2DPolygon ( a ) , 0.0 , 0.0 , basegfx : : B2DLineJoin : : NONE , css : : drawing : : LineCap_BUTT , 15.0 * F_PI180 /*default, not used*/ , bTryAA ) ;
2014-04-14 21:54:43 +10:00
}
}
else
{
// fallback to old polygon drawing if needed
2015-08-16 16:45:12 -05:00
const tools : : Polygon aToolsPolygon ( rB2DPolygon ) ;
2014-04-14 21:54:43 +10:00
LineInfo aLineInfo ;
if ( fLineWidth ! = 0.0 )
aLineInfo . SetWidth ( static_cast < long > ( fLineWidth + 0.5 ) ) ;
2014-04-27 05:07:47 -05:00
2014-11-02 12:38:16 +11:00
drawPolyLine ( aToolsPolygon , aLineInfo ) ;
2014-04-14 21:54:43 +10:00
}
}
2015-08-16 16:45:12 -05:00
void OutputDevice : : drawPolyLine ( const tools : : Polygon & rPoly , const LineInfo & rLineInfo )
2014-04-14 21:54:43 +10:00
{
sal_uInt16 nPoints ( rPoly . GetSize ( ) ) ;
2016-09-02 08:36:23 +02:00
if ( ! IsDeviceOutputNecessary ( ) | | ! mbLineColor | | ( nPoints < 2 ) | | ( LineStyle : : NONE = = rLineInfo . GetStyle ( ) ) | | ImplIsRecordLayout ( ) )
2014-04-14 21:54:43 +10:00
return ;
2015-08-16 16:45:12 -05:00
tools : : Polygon aPoly = ImplLogicToDevicePixel ( rPoly ) ;
2014-04-14 21:54:43 +10:00
// we need a graphics
2014-04-23 07:22:08 +10:00
if ( ! mpGraphics & & ! AcquireGraphics ( ) )
2014-04-14 21:54:43 +10:00
return ;
if ( mbInitClipRegion )
2014-04-25 02:43:06 +10:00
InitClipRegion ( ) ;
2014-04-14 21:54:43 +10:00
if ( mbOutputClipped )
return ;
if ( mbInitLineColor )
2014-04-22 00:40:20 +10:00
InitLineColor ( ) ;
2014-04-14 21:54:43 +10:00
const LineInfo aInfo ( ImplLogicToDevicePixel ( rLineInfo ) ) ;
2016-09-02 08:36:23 +02:00
const bool bDashUsed ( LineStyle : : Dash = = aInfo . GetStyle ( ) ) ;
2014-04-14 21:54:43 +10:00
const bool bLineWidthUsed ( aInfo . GetWidth ( ) > 1 ) ;
if ( bDashUsed | | bLineWidthUsed )
{
2014-11-05 22:30:49 +11:00
drawLine ( basegfx : : B2DPolyPolygon ( aPoly . getB2DPolygon ( ) ) , aInfo ) ;
2014-04-14 21:54:43 +10:00
}
else
{
// #100127# the subdivision HAS to be done here since only a pointer
// to an array of points is given to the DrawPolyLine method, there is
// NO way to find out there that it's a curve.
if ( aPoly . HasFlags ( ) )
{
2015-08-16 16:45:12 -05:00
aPoly = tools : : Polygon : : SubdivideBezier ( aPoly ) ;
2014-04-14 21:54:43 +10:00
nPoints = aPoly . GetSize ( ) ;
}
2017-09-18 10:37:17 +02:00
mpGraphics - > DrawPolyLine ( nPoints , reinterpret_cast < SalPoint * > ( aPoly . GetPointAry ( ) ) , this ) ;
2014-04-14 21:54:43 +10:00
}
if ( mpAlphaVDev )
mpAlphaVDev - > DrawPolyLine ( rPoly , rLineInfo ) ;
}
2014-11-02 13:10:13 +11:00
bool OutputDevice : : DrawPolyLineDirect ( const basegfx : : B2DPolygon & rB2DPolygon ,
2014-11-02 13:42:34 +11:00
double fLineWidth ,
double fTransparency ,
basegfx : : B2DLineJoin eLineJoin ,
css : : drawing : : LineCap eLineCap ,
2016-04-09 23:15:09 +02:00
double fMiterMinimumAngle ,
bool bBypassAACheck )
2014-04-14 21:54:43 +10:00
{
2015-06-18 10:37:01 +03:00
assert ( ! is_double_buffered_window ( ) ) ;
2015-05-23 18:52:00 +02:00
2014-04-14 21:54:43 +10:00
// AW: Do NOT paint empty PolyPolygons
if ( ! rB2DPolygon . count ( ) )
return true ;
// we need a graphics
2014-04-27 05:07:47 -05:00
if ( ! mpGraphics & & ! AcquireGraphics ( ) )
return false ;
2014-04-14 21:54:43 +10:00
if ( mbInitClipRegion )
2014-04-25 02:43:06 +10:00
InitClipRegion ( ) ;
2014-04-14 21:54:43 +10:00
if ( mbOutputClipped )
return true ;
if ( mbInitLineColor )
2014-04-22 00:40:20 +10:00
InitLineColor ( ) ;
2014-04-14 21:54:43 +10:00
2014-11-02 13:42:34 +11:00
const bool bTryAA ( bBypassAACheck | |
2015-05-19 12:16:31 +02:00
( ( mnAntialiasing & AntialiasingFlags : : EnableB2dDraw ) & &
2016-09-01 14:18:39 +02:00
mpGraphics - > supportsOperation ( OutDevSupportType : : B2DDraw ) & &
2016-09-01 15:48:15 +02:00
RasterOp : : OverPaint = = GetRasterOp ( ) & &
2014-11-02 13:42:34 +11:00
IsLineColor ( ) ) ) ;
2014-04-14 21:54:43 +10:00
if ( bTryAA )
{
2014-11-02 13:42:34 +11:00
const basegfx : : B2DHomMatrix aTransform = ImplGetDeviceTransformation ( ) ;
basegfx : : B2DVector aB2DLineWidth ( 1.0 , 1.0 ) ;
// transform the line width if used
if ( fLineWidth ! = 0.0 )
{
2015-12-15 15:31:50 +02:00
aB2DLineWidth = aTransform * basegfx : : B2DVector ( fLineWidth , fLineWidth ) ;
2014-11-02 13:42:34 +11:00
}
// transform the polygon
basegfx : : B2DPolygon aB2DPolygon ( rB2DPolygon ) ;
aB2DPolygon . transform ( aTransform ) ;
2015-05-19 12:16:31 +02:00
if ( ( mnAntialiasing & AntialiasingFlags : : PixelSnapHairline ) & &
2014-11-02 13:42:34 +11:00
aB2DPolygon . count ( ) < 1000 )
{
// #i98289#, #i101491#
// better to remove doubles on device coordinates. Also assume from a given amount
// of points that the single edges are not long enough to smooth
aB2DPolygon . removeDoublePoints ( ) ;
2017-09-22 14:12:07 +03:00
aB2DPolygon = basegfx : : utils : : snapPointsOfHorizontalOrVerticalEdges ( aB2DPolygon ) ;
2014-11-02 13:42:34 +11:00
}
// draw the polyline
bool bDrawSuccess = mpGraphics - > DrawPolyLine ( aB2DPolygon ,
fTransparency ,
aB2DLineWidth ,
eLineJoin ,
eLineCap ,
2016-04-09 23:15:09 +02:00
fMiterMinimumAngle ,
2014-11-02 13:42:34 +11:00
this ) ;
if ( bDrawSuccess )
2014-04-14 21:54:43 +10:00
{
// worked, add metafile action (if recorded) and return true
if ( mpMetaFile )
{
LineInfo aLineInfo ;
if ( fLineWidth ! = 0.0 )
aLineInfo . SetWidth ( static_cast < long > ( fLineWidth + 0.5 ) ) ;
2017-03-02 09:38:03 +01:00
// Transport known information, might be needed
2016-04-26 20:04:24 +03:00
aLineInfo . SetLineJoin ( eLineJoin ) ;
aLineInfo . SetLineCap ( eLineCap ) ;
// MiterMinimumAngle does not exist yet in LineInfo
2015-08-16 16:45:12 -05:00
const tools : : Polygon aToolsPolygon ( rB2DPolygon ) ;
2014-04-14 21:54:43 +10:00
mpMetaFile - > AddAction ( new MetaPolyLineAction ( aToolsPolygon , aLineInfo ) ) ;
}
return true ;
}
}
return false ;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */