且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何使用XNA创建类似Paint的应用程序?

更新时间:2023-02-17 18:58:38

您提供的文章建议了一种使用基本体绘制线条的方法;换句话说,矢量图形.诸如Paint之类的应用程序大多是基于 pixels 的(即使像Photoshop这样的更高级的软件也具有矢量和光栅化功能).

The article you provided suggests a method of drawing lines with primitives; vector graphics, in other words. Applications like Paint are mostly pixel based (even though more advanced software like Photoshop has vector and rasterization features).

由于您希望它是"Paint-like",所以我绝对会使用基于像素的方法:

Since you want it to be "Paint-like" I would definitely go with the pixel based approach:

  1. 创建颜色值的网格. (扩展 System.Drawing.Bitmap 类或实现自己的.)
  2. 开始(游戏)循环:
    • 处理输入并相应地更新网格中的颜色值.
    • 将位图转换为Texture2D .
    • 使用sprite批处理或自定义渲染器将纹理绘制到屏幕上.
  1. Create a grid of color values. (Extend the System.Drawing.Bitmap class or implement your own.)
  2. Start the (game) loop:
    • Process input and update the color values in the grid accordingly.
    • Convert the Bitmap to a Texture2D.
    • Use a sprite batch or custom renderer to draw the texture to the screen.

在位图上绘制

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

Drawing on the bitmap

I added a rough draft of the image class I am using here at the bottom of the answer. But the code should be quite self-explanatory anyways.

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

As mentioned before you also need to implement a method for converting the image to a Texture2D and draw it to the screen.

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

First we create a new 10x10 image and set all pixels to white.

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

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

Next we set up a brush. A brush is in essence just a function that is applied on the whole image. In this case the function should set all pixels inside the specified circle to a dark red color.

// 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

现在,我们应用画笔.有关如何识别圆圈内的像素,请参见我的此类解答. 您可以使用鼠标输入作为笔刷偏移量,并允许用户实际在位图上绘制.

Now we apply the brush. See this SO answer of mine on how to identify the pixels inside a circle. You can use mouse input for the brush offsets and enable the user to actually draw on the bitmap.

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;
});

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

You could also interpolate between the brush color and the old pixel. For example, you can implement a "soft" brush by letting the blend amount depend on the distance between the brush center and the current pixel.

要绘制徒手线条,只需重复应用画笔,每次都有不同的偏移量(取决于鼠标的移动):

In order to draw a freehand line simply apply the brush repeatedly, each time with a different offset (depending on the mouse movement):

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

I obviously skipped some necessary properties, methods and data validation, but you get the idea:

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);
             }
         }
    }
}