问题
I'm trying to draw maps using Java2D. When my map is zoomed out my roads are full of drawing artefacts. This is a small part of the screen when drawing a complete US state:
This is a similar section of the road when zoomed closer in:
The line style used is a solid blue line with width scaled to be equivalent to 2 pixels. I've tried various rendering hints and line joining rules and nothing seems to help.
I'm using Open JDK 1.7 on a Mac running the OS/X 10.8 and this is also reproducible on a Linux machine with a Sun JDK 1.6.
All shapes and transforms are double precision as far as possible with Java2D. The geometry of the line has many closely spaced points and I suspect that the cause of the drawing artefacts is that the renderer is getting confused by consecutive points that are closer than a single pixel.
Is there a way to improve the appearance of the zoomed out shapes without thinning the points?
Edit The drawing artefacts are at the points where separate line segments meet, so the missing pixels are something to do with the line caps (ends) not meeting, even when the end points are identical. This image shows the meeting point between two line segments. I have highlighted each line segment in a 7 pixel scaled line style (XOR-ed with white) but if you look very closely you can still see part of the original blue line (this is due to the rounded caps overlapping and the XOR draw mode.) At ordinary scales the ends seem to overlap, but when zoomed out and back in ordinary paint mode there is a broken line effect.
One workaround would be to join all the contiguous line segments together before drawing them, but I would still like to know the real cause of the drawing artefacts.
回答1:
I am unable to recreate the situation you have using the OS X 1.6 JDK, but I still have some suggestions for you.
If you are just using this to outline states, consider using the GeneralPath class. You can use the lineTo(x,y)
method to establish each of your points on the line. Again, because I can't recreate your problem using Line2D.Double
, I don't know if this will actually be any different.
Second, and possibly more importantly, is how you are zooming in and out. I am using an AffineTransform (with setScaleTo(x,y)
) on my Graphics2D
object, and everything is working swimmingly. Compared to the alternative of scaling the points in your data by a zoom factor (or whatever else you could do), this is fairly easy. You'll also have to adjust the stroke of the lines by the factor, because it will scale everything down. I can post screenshots if you'd like.
回答2:
Please check Xiaolin Wu's line algorithm it should answer you question!
Basic Concept
function plot(x, y, c) is
plot the pixel at (x, y) with brightness c (where 0 ≤ c ≤ 1)
function ipart(x) is
return integer part of x
function round(x) is
return ipart(x + 0.5)
function fpart(x) is
return fractional part of x
function rfpart(x) is
return 1 - fpart(x)
function drawLine(x1,y1,x2,y2) is
dx = x2 - x1
dy = y2 - y1
if abs(dx) < abs(dy) then
swap x1, y1
swap x2, y2
swap dx, dy
end if
if x2 < x1
swap x1, x2
swap y1, y2
end if
gradient = dy / dx
// handle first endpoint
xend = round(x1)
yend = y1 + gradient * (xend - x1)
xgap = rfpart(x1 + 0.5)
xpxl1 = xend // this will be used in the main loop
ypxl1 = ipart(yend)
plot(xpxl1, ypxl1, rfpart(yend) * xgap)
plot(xpxl1, ypxl1 + 1, fpart(yend) * xgap)
intery = yend + gradient // first y-intersection for the main loop
// handle second endpoint
xend = round (x2)
yend = y2 + gradient * (xend - x2)
xgap = fpart(x2 + 0.5)
xpxl2 = xend // this will be used in the main loop
ypxl2 = ipart (yend)
plot (xpxl2, ypxl2, rfpart (yend) * xgap)
plot (xpxl2, ypxl2 + 1, fpart (yend) * xgap)
// main loop
for x from xpxl1 + 1 to xpxl2 - 1 do
plot (x, ipart (intery), rfpart (intery))
plot (x, ipart (intery) + 1, fpart (intery))
intery = intery + gradient
end function
来源:https://stackoverflow.com/questions/11952377/how-do-i-draw-thick-lines-with-closely-spaced-points-properly-with-java2d-graphi