C# 创建用于绘制像素和渲染文本的网格

C# Create Grid For Painting Pixels and Rendering Text

我正在尝试找到创建像素的最佳途径 "grid" 允许基本的绘画功能,例如通过单击和移动鼠标为像素着色,选择要复制、粘贴的区域,或移动,或使用其他图形功能以将文本或形状渲染到像素。我看过一些示例,例如 this example 覆盖了面板控件并且与我想要实现的外观相似,但是绘画速度很慢而且看起来效果不佳用于用鼠标绘图。是否有控件或我可以覆盖的控件允许我正在寻找的功能?

下面是上述示例的示例: Sample pixel grid

以及我从上面的例子改编而来的代码:

public class Pixel
{
    public Rectangle Bounds { get; set; }
    public bool IsOn { get; set; }
    public bool IsSelected { get; set; }
}


public class PixelGridControl : Panel
{
    public int Columns = 99;
    public int Rows = 63;
    private readonly Pixel[,] pixels;

    public PixelGridControl()
    {
        this.DoubleBuffered = true;
        this.ResizeRedraw = true;

        // initialize pixel grid:
        pixels = new Pixel[Columns, Rows];
        for (int y = 0; y < Rows; ++y)
        {
            for (int x = 0; x < Columns; ++x)
            {
                pixels[x, y] = new Pixel();
            }
        }
    }

    // adjust each column and row to fit entire client area:
    protected override void OnResize(EventArgs e)
    {
        int top = 0;
        for (int y = 0; y < Rows; ++y)
        {
            int left = 0;
            int height = (this.ClientSize.Height - top) / (Rows - y);
            for (int x = 0; x < Columns; ++x)
            {
                int width = (this.ClientSize.Width - left) / (Columns - x);
                pixels[x, y].Bounds = new Rectangle(left, top, width, height);
                left += width;
            }
            top += height;
        }
        base.OnResize(e);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        for (int y = 0; y < Rows; ++y)
        {
            for (int x = 0; x < Columns; ++x)
            {
                if (pixels[x, y].IsOn)
                {
                    e.Graphics.FillRectangle(Brushes.Gold, pixels[x, y].Bounds);
                }
                else
                {
                    ControlPaint.DrawButton(e.Graphics, pixels[x, y].Bounds,
                                            ButtonState.Normal);
                }
            }
        }
        base.OnPaint(e);
    }

    // determine which button the user pressed:
    protected override void OnMouseDown(MouseEventArgs e)
    {
        for (int y = 0; y < Rows; ++y)
        {
            for (int x = 0; x < Columns; ++x)
            {
                if (pixels[x, y].Bounds.Contains(e.Location))
                {
                    pixels[x, y].IsOn = true;
                    this.Invalidate();
                    MessageBox.Show(
                      string.Format("You pressed on button ({0}, {1})",
                      x.ToString(), y.ToString())
                    );
                }
            }
        }
        base.OnMouseDown(e);
    }
}

这是一个最小的例子,它使用位图作为像素存储,并在面板中显示放大的像素以供编辑。

您可以通过将其设置为编辑 Picturebox pBox_Target.Image 中的像素来使用它,可能像这样:

public Form1()
{
    InitializeComponent();
    ..
    ..
    pixelEditor1.APBox = pBox_Target;
    pixelEditor1.TgtBitmap = (Bitmap)pixelEditor1.APBox.Image;
    ..

}

编辑器在最少的属性中公开了像素大小和绘制颜色..

总而言之,它略多于 100 行 - 当然是为了扩展!

这是在工作:

class PixelEditor : Panel
{
    public Color DrawColor { get; set; }
    public Color GridColor { get; set; }
    int pixelSize = 8;
    public int PixelSize
    {
        get { return pixelSize; }
        set
        {
            pixelSize = value;
            Invalidate();
        }
    }


    public Bitmap TgtBitmap { get; set; }
    public Point TgtMousePos { get; set; }

    Point lastPoint = Point.Empty;

    PictureBox aPBox = null;
    public PictureBox APBox {
        get { return aPBox; }
        set
        {
            if (value == null) return;
            aPBox = value;
            aPBox.MouseClick -=APBox_MouseClick;
            aPBox.MouseClick +=APBox_MouseClick;
        }
    }

    private void APBox_MouseClick(object sender, MouseEventArgs e)
    {
        TgtMousePos = e.Location;
        Invalidate();
    }


    public PixelEditor()
    {
        DoubleBuffered = true;
        BackColor = Color.White;
        GridColor = Color.DimGray;
        DrawColor = Color.Red;
        PixelSize = 10;
        TgtMousePos = Point.Empty;

        if (APBox != null && APBox.Image != null)
            TgtBitmap = (Bitmap)APBox.Image;

        MouseClick +=PixelEditor_MouseClick;
        MouseMove +=PixelEditor_MouseMove;
        Paint +=PixelEditor_Paint;
    }

    private void PixelEditor_Paint(object sender, PaintEventArgs e)
    {
        if (DesignMode) return;

        Graphics g = e.Graphics;

        int cols = ClientSize.Width / PixelSize;
        int rows = ClientSize.Height / PixelSize;

        if (TgtMousePos.X < 0 || TgtMousePos.Y < 0) return;

        for (int x = 0; x < cols; x++)
            for (int y = 0; y < rows; y++)
            {
                int sx = TgtMousePos.X + x;
                int sy = TgtMousePos.Y + y;

                if (sx > TgtBitmap.Width  || sy > TgtBitmap.Height) continue;

                Color col = TgtBitmap.GetPixel(sx, sy);

                using (SolidBrush b = new SolidBrush(col))
                using (Pen p = new Pen(GridColor))
                {
                    Rectangle rect = new Rectangle(x * PixelSize, y * PixelSize, 
                                                       PixelSize, PixelSize);
                    g.FillRectangle(b, rect);
                    g.DrawRectangle(p, rect);
                }
            }
    }

    private void PixelEditor_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left) return;

        int x = TgtMousePos.X + e.X / PixelSize;
        int y = TgtMousePos.Y + e.Y / PixelSize;

        if (new Point(x, y) == lastPoint) return;

        Bitmap bmp = (Bitmap)APBox.Image;
        bmp.SetPixel(x,y, DrawColor);
        APBox.Image = bmp;
        Invalidate();
        lastPoint = new Point(x, y);
    }

    private void PixelEditor_MouseClick(object sender, MouseEventArgs e)
    {
        int x = TgtMousePos.X + e.X / PixelSize;
        int y = TgtMousePos.Y + e.Y / PixelSize;
        Bitmap bmp = (Bitmap)APBox.Image;
        bmp.SetPixel(x,y, DrawColor);
        APBox.Image = bmp;
        Invalidate();
    }


}

请注意,此解决方案既不使用特殊像素 class,也不对选择像素集做出任何规定。要实现大多数选择工具,您可以使用 GraphicsPath;你可以填充路径或使用 IsVisible 遍历其边界以枚举路径中的像素..


更新: 而不是 Panel,这是一个 Container 控件,并不是真正要在上面绘制,您可以使用 PictureboxLabelAutosize=false);两者都开箱即用 DoubleBuffered 属性 并且比 Panels 更好地支持绘图。