Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid broken dashed lines in GDI+?

Thes screenshots, taken from my MFC application, show lines 2 that should be parallel. 2 dashed, parallel lines 2 parallel lines, one solid the other dashed

The Lines are drawn using Gdiplus::Graphics::Drawline().

A minor issue is that the dashed lines are not straigt but broken. You notice it but it's not a big deal...

The real problem is that it's not consistent and lines drawn with Gdiplus::DashStyleSolid are really straight.

Is this a bug in GDI+? Is there a workaround?

Update 1

I tried making a minimal example but I'm struggling to reproduce it... It may have something to do with very large coordinate values and little difference between their coordinates. These are some example line coordinates:

Line 1: PointF(2.21866e+006, 1.40198e+006), PointF(2.21732e+006, 1.40111e+006)
Line 2: PointF(2.21866e+006, 1.40198e+006), PointF(2.21732e+006, 1.40112e+006)

I noticed that the start points are identical but this may be caused by rounding in the debug output...

Update 2

I could reproduce it now: The sample application was generated as SDI project and the view class inherits CScrollView (code and screenshot below). The "broken line" effect also appears to be related to the pen's thickness. The line runs a bit different with varying thickness but I did not find a value that results in parallel lines all the time.

Vincent Porvik suggests in a comment that this might be the result of rounding errors, since Gdiplus::REAL is defined as float. I believe this is the case as well...

Do I have any options other than somehow using smaller coordinate values here?

Code and Screenshot

These functions were added/modified (in addition to the GDI+ initialization code):

void CgdiplusLinesView::OnDraw(CDC* pDC)
{
    CgdiplusLinesDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    pDC->SetBkMode(TRANSPARENT);

    using namespace Gdiplus;
    Graphics g(pDC->m_hDC);
    g.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);

    Pen solidPen(Color(0,0,0), 2.f);
    Pen dashedPen(Color(0,0,0), 2.f);
    dashedPen.SetDashStyle(DashStyleDash);

    g.DrawLine(&dashedPen, PointF(4438749.500000, 2805806.500000), PointF(4434280.500000, 2802106.500000));
    g.DrawLine(&solidPen,  PointF(4438746.500000, 2805809.500000), PointF(4434277.500000, 2802109.500000));
}

void CgdiplusLinesView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
    pDC->SetMapMode(MM_ISOTROPIC);

    const int mapSizePixels = 8388608;
    pDC->SetWindowExt(mapSizePixels, mapSizePixels);
    pDC->SetViewportExt(mapSizePixels, mapSizePixels);
    pDC->SetWindowOrg(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT));

    //__super::OnPrepareDC(pDC, pInfo);
}

void CgdiplusLinesView::OnInitialUpdate()
{
    CScrollView::OnInitialUpdate();

    SetScrollSizes(MM_TEXT, CSize(8388608, 8388608), CSize(1361, 1054));
    SetScrollPos(SB_HORZ, 4434897.5);
    SetScrollPos(SB_VERT, 2802645.);
}

crossing lines

like image 740
foraidt Avatar asked Oct 26 '12 09:10

foraidt


1 Answers

I don't think it has to do with dashed vs solid, but with float precision. Your coordinates seem to be geographic coordinates. The usual good practice has been to store all coordinates as doubles, and when you need to work with a lower precision library, offset all coordinates yourself before passing them to the graphics functions.

The correct way to do that is to choose a reference point that is close to the points in your dataset (first coordinate you find is ok) and then substract its coordinates to all other points. This keeps the numerical values near 0.0 that is where floats have higher resolution density.

like image 113
tato Avatar answered Nov 04 '22 11:11

tato