Безупречная мозаика прямоугольников в WPF при сохранении субпиксельной точности?

У меня возникла проблема, описанная в вопросе бесшовных мозаичных прямоугольников в WPF, но я не действительно доволен ответами, данными там.

Я рисую гистограмму, раскрашивая множество прямоугольников рядом друг с другом. В зависимости от масштаба холста, на котором они находятся, между некоторыми из них видны небольшие промежутки в результате субпиксельного рендеринга.

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

К сожалению, на моей диаграмме может отображаться больше полос, чем пикселей. Не считая крошечных промежутков (которые проявляются как периодическое изменение насыщенности цвета), это хорошо работает. Однако, если я прикрепляю каждую полосу к пикселям экрана, большинство полос исчезают, поэтому я ищу другое решение.

Заранее спасибо!


person Jens    schedule 27.05.2010    source источник


Ответы (1)


Причина проблемы

Формы субпикселей используют альфа-смешение внутри пикселя. К сожалению, не существует алгоритма альфа-смешивания, который приводит к плавному смешиванию прямоугольников при стыке.

Например, если:

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

Каждый прямоугольник будет окрашен в черный цвет с непрозрачностью 50%. Первый преобразует белый пиксель в серый. Второй преобразует его в более тёмно-серый, но не в чёрный. Если эти прямоугольники продолжают оставаться черными в соседних пикселях, вы увидите темно-серый пиксель среди черного.

Два типа решений

Есть два основных способа решить эту проблему:

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

Как использовать одну геометрию

Если у вас есть только набор Rectangles, вы можете создать простой элемент управления, который закрашивает весь набор прямоугольников с помощью одного PathGeometry, содержащего комбинированную форму. Чтобы проиллюстрировать идею, если у вас есть два прямоугольника разной высоты, расположенные рядом друг с другом, например:

<Rectangle Canvas.Left="0" Canvas.Top="0" Width="1.5" Height="2" Fill="Red" />
<Rectangle Canvas.Left="1.5" Canvas.Top="0" Width="1.5" Height="4" Fill="Red" />

Вы можете визуализировать его с помощью одной PathGeometry следующим образом:

<Path Data="M0,0 L0,2 L1.5,2 L1.5,4 L3,4 L3,0 Z" Fill="Red" />

Практический способ реализовать это:

  • Нарисуйте прямоугольники прозрачной кистью, чтобы они были интерактивными, но не были видны.
  • Добавьте элемент управления Path под прямоугольниками в порядке Z
  • Связывание данных свойства Data элемента управления Path с источником данных с помощью преобразователя, который создает геометрию.

Если вы используете систему макета для размещения своих прямоугольников, вы можете вместо этого использовать AdornerLayer, создав Adorner для каждого прямоугольника, а затем при рендеринге украшения вычислить комбинированный путь для первого и сделать остальные невидимыми.

Вышеупомянутое предполагает, что легко создать PathGeometry из исходных данных. Для более сложных сценариев элемент управления Path может быть разделен на подклассы для поиска указанных фигур в визуальном дереве его родительского элемента и использования общих геометрических алгоритмов для вычисления PathGeometry, представляющего их объединение без дополнительных ребер.

Если ваши прямоугольники будут иметь несколько цветов, вы можете использовать несколько элементов управления Path, по одному для каждого цвета, или вы можете создать объект Drawing и показать его.

Вот структура кода для создания PathGeometry:

var geo = new PathGeometry();
var figure = new PathFigure();
var segment = new PolyLineSegment();
segment.Points.Add(...);
segment.Points.Add(...);
segment.Points.Add(...);
segment.Points.Add(...);
segment.Points.Add(...);
figure.Segments.Add(segment);
geo.Figures.Add(figure);

Как заставить начальную визуализацию иметь высокое разрешение

Чтобы принудительно выполнить рендеринг с более высоким разрешением:

  1. Создайте внутреннюю диаграмму в несколько раз больше, чем вы хотите ее отображать, например, заключив ее в ViewBox.
  2. Используйте VisualBrush или RenderTargetBitmap, чтобы диаграмма отображалась отдельно
  3. Добавьте прямоугольник, нарисованный этой VisualBrush, в свой пользовательский интерфейс.

Обратите внимание, что обычно WPF хорошо разбирается в рендеринге с фактическим разрешением, требуемым при использовании ViewBrush, но его можно обмануть, если фактическая диаграмма будет отображаться на экране в большем размере, но затем будет обрезана родительским элементом управления, чтобы вы не На самом деле не вижу слишком большую версию.

Этой проблемы, конечно, не существует с RenderTargetBitmap, поскольку вы указываете желаемое разрешение, но может быть сложно узнать, когда повторно визуализировать растровое изображение. Если вы повторно визуализируете только изменения данных, вы можете использовать событие, но если вы хотите, чтобы какое-либо визуальное изменение запускало повторную визуализацию, это сложнее.

person Ray Burns    schedule 02.06.2010