Не могу использовать GDI+. Можно ли нарисовать линию на растровом изображении с помощью вызова Windows API?

У нас есть приложение, написанное на C++ 6, которое заполняет пустые полигоны на карте трафика, чтобы показать текущие условия движения. Зеленый — хорошо, желтый — больше загруженности и т. д.

«Карты-оболочки» — это растровые изображения дорог с использованием прямоугольников и многоугольников, которые ничем не заполнены. Данные собираются с дорожных датчиков (проволочные индукторные петли), а полигоны заполняются на основе данных петлевого детектора.

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

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

В .Net периметр прокрашивается идеально.

В приложении C++ 6 некоторые многоугольники указывают на мое приложение. сборы отображаются неправильно.

Я посмотрел на msPaint, и .Net не рисует точки так же, как MS Paint.

Вот быстрый пример. Код из одной формы с одной кнопкой и одной меткой. Линия рисуется на растровом изображении, растровое изображение «увеличивается», чтобы вы могли его видеть, и отображается на этикетке.

Нарисованный сегмент линии не совпадает с сегментом линии, который вы рисуете в MS Paint, если вы рисуете линию в Paint, используя те же две точки.

  private void button1_Click(object sender, EventArgs e)
    {
        Bitmap bm = new Bitmap(16, 16);
        Graphics g = Graphics.FromImage(bm);
        //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
        //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
        //g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Default;
        Point pt = new Point(1, 3);
        Point pt2 = new Point(5, 1);
        // If you reverse the points, the same line is drawn.
        g.DrawLine(Pens.Orange, pt, pt2);
        // label1.Image = bm;
        Bitmap bm2 = ZoomMap(8, bm);
        g.Dispose();
        bm.Dispose();
        label1.Image = bm2;
    }

    private Bitmap ZoomMap(int zoomValue, Bitmap bm)
    {
        Bitmap zoomedBitMap;
        Size sz = bm.Size;// new Size(75, 75);
        Size newSize = new Size(sz.Width * zoomValue, sz.Height * zoomValue);
        zoomedBitMap = new Bitmap(newSize.Width, newSize.Height);
        Graphics gr = Graphics.FromImage(zoomedBitMap);
        gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
        gr.PageUnit = GraphicsUnit.Pixel;
        gr.DrawImage(bm, 1, 1, newSize.Width, newSize.Height);
        gr.Dispose();
        return zoomedBitMap;
    }

альтернативный текст

Независимо от того, какие настройки я применяю, невозможно имитировать MS Paint в C#, но в C++ 6 он отлично имитирует MS Paint.

Есть ли какой-либо тип API Windows, который я могу вызвать для рисования на существующем растровом изображении?

Заранее спасибо за любые ответы.


person netcat    schedule 01.01.2011    source источник
comment
Мне кажется, если вы повернете изображение, нарисованное .Net, на 180 градусов, вы получите тот же результат. Итак, если вы рисуете на холсте, поверните его и наложите на него, чтобы получить то, что вы хотите.   -  person Mikael Svenson    schedule 01.01.2011


Ответы (1)


Использование либо цифрового дифференциального анализатора (DDA), либо алгоритма Брезенхэма даст те же результаты, что и для GDI+, но если вы посмотрите на стандартную реализацию рисования линий GDI, вы заметите, что стандартная GDI LineTo фактически рисует линию на один пиксель короче, чем вы указываете, цитируя MSDN

'Функция LineTo рисует линию от текущей позиции до указанной точки, но не включая ее.'

Из-за этого, используя GDI, ваша линия рисуется из (1,3)-(4,1), если вы используете те же самые координаты в GDI+, вы увидите, что пиксельная структура линии соответствует тому, что вы видите в более старых версиях Paint. , за исключением, конечно, того, что последний пиксель в (5,1) не рисуется. Такая программа, как Paint, просто добавляла бы дополнительный пиксель, чтобы закончить линию.

Использование прямого GDI потребует, чтобы вы обрабатывали взаимодействие для создания перьев, выбора объекта в DC, удаления объектов и т. д. Все это очень возможно, но в конечном итоге неудобно. Вы могли бы смоделировать это в GDI+, нарисовав линию короче на один пиксель, а затем нарисовав линию от этой конечной точки до конечной конечной точки, это заполнит последний пиксель.

Примечание. Я говорю о более старых версиях Paint, потому что, если вы используете Paint, например, в Windows 7, вы увидите, что линия рисуется правильно.

Ради интереса я решил быстро показать, как вы можете использовать классический GDI из .NET, вот некоторый код взаимодействия.

[DllImport("gdi32")]
static extern bool MoveToEx(IntPtr hdc, int x, int y, out Point point);

[DllImport("gdi32")]
static extern bool LineTo(IntPtr hdc, int x, int y);

[DllImport("gdi32")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

[DllImport("gdi32")]
static extern IntPtr CreatePen(int penStyle, int width, int color);

[DllImport("gdi32")]
static extern bool DeleteObject(IntPtr hgiobj);

private void Form1_Paint(object sender, PaintEventArgs e)
{
  Point ignore;
  IntPtr hdc = e.Graphics.GetHdc();
  IntPtr pen = CreatePen(1, 1, ColorTranslator.ToWin32(Color.Red));
  IntPtr oldPen = SelectObject(hdc, pen);
  MoveToEx(hdc, 1, 3, out ignore);
  LineTo(hdc, 5, 1);                
  SelectObject(hdc, oldPen);
  DeleteObject(pen);      
}

Обратите внимание, что линия на самом деле короче на один пиксель. Лично я бы выбрал собственное решение .NET с использованием графического устройства и просто определил бы правильную координату линии, чтобы воспроизвести классическую функциональность GDI.

Я подумал, что вам может быть интересно узнать, почему GDI пропускает последний пиксель, было обычным делом использовать XOR для рисования линии на экране, что сделало бы удаление той же линии очень простым, просто перерисуйте линию с помощью XOR, и она будет удалена с помощью исходный фон без изменений. Конечно, это означает, что вам нужно быть осторожным, чтобы не рисовать пиксель дважды, иначе вы оставите артефакты на экране.

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

person Chris Taylor    schedule 01.01.2011
comment
Извините, он нажал Enter в Google Chrome и ответил. Я смог использовать опубликованные вами функции, и GDI работает именно так, как вы сказали. Я сделал быстрый пример формы, которая рисует многоугольник на растровом изображении и устанавливает метку для этого растрового изображения с помощью GDI. Спасибо за отличную информацию и пример кода, который вы опубликовали. - person netcat; 03.01.2011
comment
Похоже, я не смогу опубликовать свой код — эта страница продолжает выдавать ошибку. Я попробую еще раз позже. - person netcat; 03.01.2011
comment
@ Мэтт Фомич, я рад, что помог. - person Chris Taylor; 03.01.2011