Как правильно рисовать толстые линии с близко расположенными точками с помощью графики Java2D?

Я пытаюсь рисовать карты с помощью Java2D. Когда моя карта уменьшена, мои дороги полны артефактов рисования. Это небольшая часть экрана при рисовании полного штата США:

Участок дороги в масштабе штата, показывающий артефакты рисования

Это похожий участок дороги при увеличении:

Участок дороги, нарисованный в масштабе округа, без артефактов рисования

Используемый стиль линии - это сплошная синяя линия с шириной, масштабированной до 2 пикселей. Я пробовал различные подсказки рендеринга и правила соединения строк, и ничего не помогло.

Я использую Open JDK 1.7 на Mac под управлением OS / X 10.8, и это также можно воспроизвести на машине Linux с Sun JDK 1.6.

Все формы и преобразования имеют двойную точность, насколько это возможно с Java2D. Геометрия линии имеет много близко расположенных точек, и я подозреваю, что причиной артефактов отрисовки является то, что средство визуализации сбивается с толку из-за последовательных точек, расположенных ближе, чем на один пиксель.

Есть ли способ улучшить внешний вид уменьшенных фигур без утончения точек?

Редактировать Артефакты рисования находятся в точках пересечения отдельных сегментов линии, поэтому недостающие пиксели связаны с несовпадением концов (концов) линий, даже если конечные точки идентичны. На этом изображении показана точка встречи двух отрезков линии. Я выделил каждый сегмент линии в стиле масштабированной линии 7 пикселей (XOR-ed с белым), но если вы посмотрите очень внимательно, вы все равно можете увидеть часть исходной синей линии (это связано с перекрытием закругленных заглавных букв и режимом рисования XOR .) В обычных масштабах кажется, что концы перекрываются, но при уменьшении и возврате в обычном режиме рисования появляется эффект ломаной линии.

Одним из обходных путей было бы объединить все смежные линейные сегменты перед их рисованием, но я все же хотел бы знать настоящую причину артефактов рисования.

Соединение между двумя сегментами дороги


person richj    schedule 14.08.2012    source источник


Ответы (2)


Я не могу воссоздать ситуацию, сложившуюся у вас при использовании OS X 1.6 JDK, но у меня все еще есть несколько предложений для вас.

Если вы используете это только для обозначения состояний, рассмотрите возможность использования GeneralPath класс. Вы можете использовать метод lineTo(x,y), чтобы установить каждую из ваших точек на линии. Опять же, поскольку я не могу воссоздать вашу проблему с помощью Line2D.Double, я не знаю, действительно ли это будет по-другому.

Во-вторых, что, возможно, более важно, это то, как вы увеличиваете и уменьшаете масштаб. Я использую AffineTransformsetScaleTo(x,y)) на моем Graphics2D объекте, и все работает нормально. По сравнению с альтернативой масштабирования точек в ваших данных с помощью коэффициента масштабирования (или чего-то еще, что вы могли бы сделать) это довольно просто. Вам также придется отрегулировать обводку линий по коэффициенту, потому что это уменьшит все. Могу выложить скриншоты, если хотите.

person Andy Hill    schedule 15.08.2012
comment
Спасибо за ваш ответ. Я использую AffineTransform, который представляет собой комбинацию операций масштабирования и перевода. Я также масштабирую ширину штриха. Некоторые из моих координат находятся на расстоянии 5-10 метров друг от друга. Полная ширина холста при уменьшении составляет около 1 000 000 м. Я посмотрю, смогу ли я вернуться к старой версии кода, который работает с Apple 1.6 JDK. В основном я вижу проблему при рисовании дорог. Границы графств кажутся хорошими, но, вероятно, не имеют такого же уровня детализации. - person richj; 15.08.2012
comment
Я также вижу проблему с Apple JDK 1.6. Я начинаю задаваться вопросом, связана ли проблема не с рисованием артефактов внутри линии, а с конечными точками сегментов линии. Если внимательно присмотреться к данным, дороги представляют собой не длинные непрерывные линии с множеством вершин, а множество более коротких отрезков линии с смежными конечными точками. - person richj; 16.08.2012
comment
Принятие этого ответа, поскольку он в конечном итоге привел меня к решению. Мне нужно либо отключить одну из моих оптимизаций рисования для этого слоя, либо объединить сегменты для каждой дороги. - person richj; 18.08.2012

Пожалуйста, проверьте линейный алгоритм Xiaolin Wu, он должен ответить на ваш вопрос!

Основная концепция

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
person StackFlowed    schedule 14.08.2012
comment
+1 для алгоритма, в частности ссылка в Википедии, в которой говорится, что строки длиной менее одного пикселя должны обрабатываться как особый случай. Статья была бы еще более актуальной, если бы в ней объяснялось, как именно обрабатывать этот особый случай, поскольку вопрос конкретно ссылается на субпиксельные линейные сегменты как на возможную причину артефактов. Я ожидал бы, что низкоуровневые алгоритмы рендеринга будут реализованы в Java2D API. Решение, которое я ищу, объяснит, как использовать Java2D API для получения непрерывной утолщенной линии. Спасибо за информативный ответ. - person richj; 15.08.2012
comment
Другое приемлемое решение объяснило бы, почему в настоящее время это невозможно, и предоставило бы обходной путь лучше, чем тот, который был предложен в вопросе (прореживание точки перед визуализацией формы). - person richj; 15.08.2012