Пиксельное поведение FillRectangle и DrawRectangle

почти каждый раз, когда я использую Graphics.DrawRectangle или Graphics.FillRectangle (версии int), я, кажется, пропускаю пиксели на правой и нижней границе прямоугольника, который я хочу рисовать.

Каково точное определение того, какие пиксели заполняются

Graphics.FillRectangle(brush,x,y,width,height)

и

Graphics.DrawRectangle(pen,x,y,width,height) // pen is one pixel width, i.e. width=0f

и есть ли логическое объяснение? (так что я могу, наконец, запомнить поведение:)...)

ИЗМЕНИТЬ

Используя Олега Жука чудесное Попробуйте приложение GDI +, мне удалось найти любопытную разницу между FillRectangle и DrawRectangle:

FillRectangle(brush,0,0,1,1) рисует одну точку в (0,0), которая как-то понятна (это прямоугольник с шириной и высотой в конце концов).

DrawRectangle(pen,0,0,1,1), с другой стороны, рисует маленький прямоугольник размером 2 на 2 пикселя.

То же поведение показано для других комбинаций ширины и высоты, DrawRectangle всегда расширяет один пиксель больше влево и снизу (что немного раздражает, например, при использовании свойства ClientRectangle для рисования кадра вокруг элемента управления )

Мой первый вопрос, решенный (как они ведут себя...), все еще остается вторым: почему они ведут себя таким образом?

Ответ 1

Эти методы работают правильно, вам просто нужно учитывать эффекты сглаживания и округления.

Сначала включите сглаживание, чтобы увидеть все (не только округленный результат):

graphics.SmoothingMode = SmoothingMode.AntiAlias;

Теперь FillRectangle (10,10,10,10) произведет размытый результат, а DrawRectangle (10,10,10,10) будет резким.

Размытый результат означает, что у вас пиксельные координаты вне сетки. Если вам нужно позвонить:

graphics.FillRectangle(9.5f, 9.5f, 10, 10);

чтобы выровнять верхний левый угол прямоугольника на сетке.

DrawRectangle острый, потому что он использует Pen шириной 1.0. Таким образом, линия начинается с центра пикселя и проходит 0,5 пиксела в обоих направлениях, таким образом, заполняя ровно 1 пиксель.

Обратите внимание, что ширина и высота вычисляются следующим образом:

width = (right - left) = 19.5 - 9.5 = 10
height = (bottom - top) = ...

Обратите внимание, что 19.5 - это правая/нижняя координата прямоугольника, также выровненного по сетке, и результат - целое число.

Вы можете использовать другую формулу:

width2 = (right - left + 1)

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

Ответ 2

Прямоугольник в обоих методах Graphics в вашем вопросе ограничен (x, y) в верхнем левом углу и (x + width - 1, y + height - 1) в правом нижнем углу. Это следствие указания ширины и высоты, а не нижней правой точки.

Когда вы вычисляете ширину и высоту из двух точек, вы не забудьте включить пиксель начала, добавив 1 к разнице.

Например, скажем, вы хотите заполнить прямоугольник от точки (5, 5) до точки (10, 20). Ширина прямоугольника равна 6, а не 5. Высота прямоугольника равна 16, а не 15.

Ответ 3

Различие в поведении связано с несколько противоположным интуитивным фактом, что эти два алгоритма делают совершенно разные вещи.

DrawRectangle рисует четыре строки в форме прямоугольника. FillRectangle создает скопление W * H заполненных пикселей. Из этого легко увидеть причину разницы в поведении. А именно, неправильность предположения о подобии. Рисование четырех строк сильно отличается от создания прямоугольного скопления пикселей.