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

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

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

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

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

В .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;
    }

alt text

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

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

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

1 Ответ

4 голосов
/ 02 января 2011

Использование цифрового дифференциального анализатора (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 могут безопасно начинаться в том месте, где заканчивалась предыдущая строка, поскольку последний пиксель предыдущей строки не был нарисован.

...