【问题标题】:How to create Paint-like app with XNA?如何使用 XNA 创建类似 Paint 的应用程序?
【发布时间】:2013-01-13 10:16:24
【问题描述】:

here 已涵盖使用 XNA 以编程方式绘制线条的问题。但是,我希望允许用户像使用 MS Paint 等绘图应用程序一样在画布上绘图。

这当然需要鼠标指针位置的每个 x 和/或 y 坐标变化,以导致在画布上以蜡笔颜色实时绘制线条的另一个“点”。

在鼠标移动事件中,为了逐点绘制线,XNA API 需要考虑哪些因素?当然,从字面上看,我不是画一条线,而是一系列“点”。每个“点”可以并且可能应该大于单个像素。想想用毡尖笔画画。

【问题讨论】:

    标签: xna mouse line draw paint


    【解决方案1】:

    您提供的文章建议了一种使用图元绘制线条的方法; 矢量图形,换句话说。 Paint 等应用程序大多基于像素(尽管 Photoshop 等更高级的软件具有矢量和光栅化功能)。

    位图编辑器

    既然您希望它“类似于绘画”,我肯定会采用基于像素的方法:

    1. 创建颜色值网格。 (扩展 System.Drawing.Bitmap 类或实现您自己的。)
    2. 开始(游戏)循环:
      • 处理输入并相应地更新网格中的颜色值。
      • Convert the Bitmap to a Texture2D。
      • 使用精灵批处理或自定义渲染器将纹理绘制到屏幕上。
    3. 如果需要,请保存位图。

    在位图上绘图

    我在答案的底部添加了我在这里使用的图像类的草稿。但无论如何,代码应该是不言自明的。

    如前所述,您还需要实现将图像转换为 Texture2D 并将其绘制到屏幕上的方法。


    首先我们创建一个新的 10x10 图像并将所有像素设置为白色。

    var image = new Grid<Color>(10, 10);
    image.Initilaize(() => Color.White);
    

    接下来我们设置一个画笔。画笔本质上只是一个应用于整个图像的功能。在这种情况下,该函数应将指定圆圈内的所有像素设置为深红色。

    // Create a circular brush
    float brushRadius = 2.5f;
    int brushX = 4;
    int brushY = 4;
    Color brushColor = new Color(0.5f, 0, 0, 1); // dark red
    

    现在我们应用画笔。关于如何identify the pixels inside a circle,请参阅我的这个 SO 答案。 您可以将鼠标输入用于画笔偏移,并使用户能够在位图上实际绘制。

    double radiusSquared = brushRadius * brushRadius;
    
    image.Modify((x, y, oldColor) =>
    {
        // Use the circle equation
        int deltaX = x - brushX;
        int deltaY = y - brushY;
        double distanceSquared = Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2); 
    
        // Current pixel lies inside the circle
        if (distanceSquared <= radiusSquared)
        {
            return brushColor;
        }
    
        return oldColor;
    });
    

    您还可以在画笔颜色和旧像素之间进行插值。例如,您可以通过让混合量取决于画笔中心和当前像素之间的距离来实现“软”画笔。

    画一条线

    为了绘制手绘线,只需重复应用画笔,每次使用不同的偏移量(取决于鼠标移动):


    自定义图片类

    我显然跳过了一些必要的属性、方法和数据验证,但你明白了:

    public class Image
    {
        public Color[,] Pixels { get; private set; }
    
        public Image(int width, int height)
        {
            Pixels= new Color[width, height];
        }
    
        public void Initialize(Func<Color> createColor)
        {
             for (int x = 0; x < Width; x++)
             {
                 for (int y = 0; y < Height; y++)
                 {
                      Pixels[x, y] = createColor();
                 }
             }
        }
    
        public void Modify(Func<int, int, Color, Color> modifyColor)
        {
             for (int x = 0; x < Width; x++)
             {
                 for (int y = 0; y < Height; y++)
                 {
                      Color current = Pixels[x, y];
                      Pixels[x, y] = modifyColor(x, y, current);
                 }
             }
        }
    }
    

    【讨论】:

    • 很好的答案 - 虽然我认为在 Color[] 缓冲区上工作可能会更好,您可以直接传递给 Texture2D.GetData/SetData,而不是在那里进行额外的转换步骤来自二维数组或Bitmap。
    • 谢谢!我选择了二维数组,因为我认为它更直观。幸运的是,如果将其替换为简单的数组,界面不会有太大变化。 Image 方法中的循环只会变平为单个循环,并且必须计算偏移量:int x = i / Height; 和 int y = i % Height;。
    • 卢修斯,这太棒了。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多