c#使用gdi32.dll和System.Drawing.Graphics绘图

时间:2017-09-13 21:14:57

标签: c# gdi system.drawing graphic

所以我有一些代码在gdi32.dll的图片框顶部创建了一个高亮效果,我想知道是否有一种更简单的方法可以用System.Drawing.Graphics做到这一点?基本上使用gdi32.dll,我必须在绘制后捕获屏幕截图,将其发布到我的图片框然后我能够绘制更多的东西并更改我使用的笔的颜色。如果我只是尝试更改笔的粗细和颜色并再次在屏幕上绘图,如果我改变了已经绘制的内容。

现在我有一个使用System.Drawing.GraphicsFillPolygon的大量数学的版本,但是如果我画了一个我已经画过的区域,它只会让我画的区域更暗。只要你还没有使用鼠标遮住区域,它就不能用gdi32.dll进行justr阴影处理。有什么建议吗?

public partial class Form9 : Form
{
    private bool is_mouse_down { get; set; } // Will check if the mouse is down or not.

    private Color Pen_Color = new Color();

    private int Pen_Type { get; set; }

    private int Thickness { get; set; }

    private bool Start { get; set; }

    List<Point> Points = new List<Point>();

    public Form9()
    {
        InitializeComponent();

        pictureBox1.Dock = DockStyle.Fill;

        Pen_Color = Color.Blue;
        Pen_Type = 13; // Type = 9 for highlighter, Type = 13 for solid.
        Thickness = 2;
        Start = false;

        pictureBox1.MouseDown += pictureBox1_MouseDown;
        pictureBox1.MouseUp += pictureBox1_MouseUp;
        pictureBox1.MouseMove += pictureBox1_MouseMove;
        pictureBox1.Paint += pictureBox1_OnPaint;
    }

    private void DrawHighlight(Graphics g, Point[] usePoints, int brushSize, int penType, Color brushColor)
    {
        int useColor = System.Drawing.ColorTranslator.ToWin32(brushColor);
        IntPtr pen = GetImage.GDI32.CreatePen(GetImage.GDI32.PS_SOLID, brushSize, (uint)useColor);
        IntPtr hDC = g.GetHdc();
        IntPtr xDC = GetImage.GDI32.SelectObject(hDC, pen);
        GetImage.GDI32.SetROP2(hDC, penType);//GetImage.GDI32.R2_MASKPEN);
        for (int i = 1; i <= usePoints.Length - 1; i++)
        {
            Point p1 = usePoints[i - 1];
            Point p2 = usePoints[i];
            GetImage.GDI32.MoveToEx(hDC, p1.X, p1.Y, IntPtr.Zero);
            GetImage.GDI32.LineTo(hDC, p2.X, p2.Y);
        }
        GetImage.GDI32.SetROP2(hDC, GetImage.GDI32.R2_COPYPEN);
        GetImage.GDI32.SelectObject(hDC, xDC);
        GetImage.GDI32.DeleteObject(pen);
        g.ReleaseHdc(hDC);
    }

    private void pictureBox1_OnPaint(object sender, PaintEventArgs e)
    {
        if (Start)
        {
            base.OnPaint(e);
            if (is_mouse_down)
            {
                DrawHighlight(e.Graphics, Points.ToArray(), Thickness, Pen_Type, Pen_Color);
            }
        }
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        Points.Clear();

        Start = true;
        is_mouse_down = true;
    }

    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        is_mouse_down = false;

        using (Image img = CaptureScreen())
        {
            try
            {
                if (System.IO.File.Exists(Program.ProgramPath + @"\Temp\marked.bmp"))
                {
                    System.IO.File.Delete(Program.ProgramPath + @"\Temp\marked.bmp");
                }
            }
            catch (Exception Ex)
            {
                MessageBox.Show("File Delete Error" + Environment.NewLine + Convert.ToString(Ex));
            }

            try
            {
                img.Save(Program.ProgramPath + @"\Temp\marked.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
            }
            catch (Exception Ex)
            {
                MessageBox.Show("Unable to save Screenshot" + Environment.NewLine + Convert.ToString(Ex));
            }
        }

        if (System.IO.File.Exists(Program.ProgramPath + @"\Temp\marked.bmp"))
        {
            using (FileStream fs = new System.IO.FileStream(Program.ProgramPath + @"\Temp\marked.bmp", System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.Read))
            {
                pictureBox1.Image = Image.FromStream(fs);
            }
        }

        pictureBox1.Invalidate(); // Refreshes picturebox image.
    }

    public Image CaptureScreen()
    {
        GetImage gi = new GetImage();

        return gi.CaptureWindow(GetImage.User32.GetDesktopWindow());
    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (is_mouse_down == true) // Check to see if the mouse button is down while moving over the form.
        {
            Points.Add(new Point(e.X, e.Y));

            pictureBox1.Invalidate(); // Refreshes picturebox image.
        }
    }

以下是我正在谈论的几张照片:

使用System.Drawing.GraphicsUsing <code>System.Drawing.Graphics</code>

使用gdi32.dllUsing <code>gdi32.dll</code>

更新

在测试了一些代码后......我得到了一些奇怪的东西。

enter image description here

1 个答案:

答案 0 :(得分:6)

这是一种绘制半透明颜色的多个独立笔划而不会堆积alpha的方法:

enter image description here

它使用两个列表,一个用于笔划,一个用于当前笔划:

List<List<Point>> strokes = new List<List<Point>>();
List<Point> currentStroke = new List<Point>();

他们以通常的方式填补

private void canvas_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button.HasFlag(MouseButtons.Left))
    {
        currentStroke.Add(e.Location);
        if (currentStroke.Count == 1)
            currentStroke.Add(new Point(currentStroke[0].X + 1, 
                                        currentStroke[0].Y));
        canvasInvalidate();

    }
}

private void canvas_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button.HasFlag(MouseButtons.Left))
    {
        currentStroke.Add(e.Location);
        canvas.Invalidate();
    }
}

private void canvas_MouseUp(object sender, MouseEventArgs e)
{
    if (currentStroke.Count > 1)
    {
        strokes.Add(currentStroke.ToList());
        currentStroke.Clear();
    }
    canvas.Invalidate();
}

在此版本中,我们通过仅在一次调用中绘制所有像素来避免重叠笔划的叠加效果。通过从笔划创建GraphicsPath并填充它来绘制所有像素:

private void canvas_Paint(object sender, PaintEventArgs e)
{
    if (strokes.Count > 0 || currentStroke.Count > 0)
    {
        GraphicsPath gp = new GraphicsPath();
        gp.FillMode = FillMode.Winding;
        if (currentStroke.Count > 0)
        {
            gp.AddCurve(currentStroke.ToArray());
            gp.CloseFigure();
        }

        foreach (var stroke in strokes)
        {
            gp.AddCurve(stroke.ToArray());
            gp.CloseFigure();
        }
        using (SolidBrush b = new SolidBrush(Color.FromArgb(77, 177, 99, 22)))
        {
            e.Graphics.FillPath(b, gp);
        }
    }
}

请注意,在绘制时应注意不要移回当前笔划,否则会产生破洞的路径部分!

ClearSave Button很简单,前Clears两个列表和无效,后者会使用DrawToBitmap来保存控件。

注意:为避免闪烁,make sure canval面板为DoubleBuffered

<强>更新

以下是使用Pen绘制叠加层的另一种方法。为了避免堆积alpha和更改的颜色值(取决于PixelFormat),它使用快速函数来修改叠加层中的所有设置像素以具有相同的叠加颜色:

笔划集合代码是相同的。 Paint简化为调用函数来创建叠加位图并绘制它:

private void canvas_Paint(object sender, PaintEventArgs e)
{
    using (Bitmap bmp = new Bitmap(canvas.ClientSize.Width, 
                                   canvas.ClientSize.Height, PixelFormat.Format32bppPArgb))
    {
        PaintToBitmap(bmp);
        e.Graphics.DrawImage(bmp, 0, 0);
    }

第一个函数完成绘图,与之前非常相似,但使用简单的笔划:

private void PaintToBitmap(Bitmap bmp)
{
    Color overlayColor = Color.FromArgb(77, 22, 99, 99);
    using (Graphics g = Graphics.FromImage(bmp))
    using (Pen p = new Pen(overlayColor, 15f))
    {
        p.MiterLimit = p.Width / 2;
        p.EndCap = LineCap.Round;
        p.StartCap = LineCap.Round;
        p.LineJoin = LineJoin.Round;
        g.SmoothingMode = SmoothingMode.AntiAlias;
        if (currentStroke.Count > 0)
        {
            g.DrawCurve(p, currentStroke.ToArray());
        }

        foreach (var stroke in strokes)
            g.DrawCurve(p, stroke.ToArray());
    }
    SetAlphaOverlay(bmp, overlayColor);
}

它还调用将所有设置像素“展平”为叠加颜色的函数:

void SetAlphaOverlay(Bitmap bmp, Color col)
{
    Size s = bmp.Size;
    PixelFormat fmt = bmp.PixelFormat;
    Rectangle rect = new Rectangle(Point.Empty, s);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt);
    int size1 = bmpData.Stride * bmpData.Height;
    byte[] data = new byte[size1];
    System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, data, 0, size1);
    for (int y = 0; y < s.Height; y++)
    {
        for (int x = 0; x < s.Width; x++)
        {
            int index = y * bmpData.Stride + x * 4;
            if (data[index + 0] + data[index + 1] + data[index + 2] > 0)
            {

                data[index + 0] = col.B;
                data[index + 1] = col.G;
                data[index + 2] = col.R;
                data[index + 3] = col.A;
            }
        }
    }
    System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
    bmp.UnlockBits(bmpData);
}

它使用LockBits,因此它非常快......

这是在行动:

enter image description here

更新2:

只是为了它的乐趣,这里只有几行的扩展,增加了绘制填充曲线的选项:

填充模式通过将第一个元素两次存储在cheapo hack中。这些是变化:

MouseDown

        currentStroke.Add(e.Location);
        if (cbx_Fill.Checked) 
            currentStroke.Add(e.Location);

PaintToBitmap

        g.SmoothingMode = SmoothingMode.AntiAlias;
        if (currentStroke.Count > 0)
        {
            if (cbx_Fill.Checked)
                g.FillClosedCurve(b,  currentStroke.ToArray());
            else
                g.DrawCurve(p, currentStroke.ToArray());
        }

        foreach (var stroke in strokes)
            if (stroke[0]==stroke[1])
                g.FillClosedCurve(b,  stroke.ToArray());
            else
                g.DrawCurve(p, stroke.ToArray());

还有一个演示:

enter image description here