如何使用.NET在内存中动态创建jpg图像?

时间:2011-03-28 15:32:19

标签: c# .net graphics jpeg

我有一个用C#编写的.NET(3.5 SP1)库(DLL)。我必须通过类方法扩展此库,该方法具有以下签名:

public byte[] CreateGridImage(int maxXCells, int maxYCells,
    int cellXPosition, int cellYPosition)
{
    ...
}

此方法应执行以下操作:

  • 输入参数maxXCellsmaxYCells定义X和Y方向上的单元格网格的大小。 maxXCellsmaxYCells是每个方向上的单元格数。单个细胞是方形的。 (所以它是一种不对称的棋盘。)
  • 输入参数cellXPositioncellYPosition标识此网格中的一个特殊单元格,此单元格必须用十字形填充。
  • 不需要花哨的图形,白色背景上只有黑色网格线,其中一个单元格中只有X.
  • 生成的图形必须为jpg格式。
  • 此图形的创建必须在内存中进行,任何内容都不应存储在磁盘上的文件中,也不应绘制在屏幕上。
  • 该方法将生成的图像作为byte[]
  • 返回

我对.NET中的图形功能非常不熟悉所以我的问题是:

  • 对于没有其他第三方库(我想避免使用)的.NET 3.5 SP1,这是否可行?
  • 我必须遵循的基本步骤是什么?为实现这一目标,我需要了解哪些重要的.NET命名空间,类和方法(特别是在内存中绘制线条和其他简单的图形元素)并转换结果以jpg格式转换为字节数组?)

提前感谢您的建议!

4 个答案:

答案 0 :(得分:31)

以下是一个完整的代码示例,它将使用GDI绘制网格并放置一个十字(红色背景),就像下面的示例图像一样。它像其他答案一样使用GDI,但实际工作是通过循环遍历单元格和绘制网格线来实现的。

以下代码

byte[] bytes = CreateGridImage(10,10, 9, 9, 30);

将创建一个10x10网格,其交叉位于9x9位置:

enter image description here

CreateGridImage()的新增加是添加了一个boxSize参数,用于设置网格中每个“square”的大小

public static byte[] CreateGridImage(
            int maxXCells,
            int maxYCells,
            int cellXPosition,
            int cellYPosition,
            int boxSize)
{
    using (var bmp = new System.Drawing.Bitmap(maxXCells * boxSize+1, maxYCells * boxSize+1))
    {
        using (Graphics g = Graphics.FromImage(bmp))
        {
            g.Clear(Color.Yellow);
            Pen pen = new Pen(Color.Black);
            pen.Width = 1;

            //Draw red rectangle to go behind cross
            Rectangle rect = new Rectangle(boxSize * (cellXPosition - 1), boxSize * (cellYPosition - 1), boxSize, boxSize);
            g.FillRectangle(new SolidBrush(Color.Red), rect);

            //Draw cross
            g.DrawLine(pen, boxSize * (cellXPosition - 1), boxSize * (cellYPosition - 1), boxSize * cellXPosition, boxSize * cellYPosition);
            g.DrawLine(pen, boxSize * (cellXPosition - 1), boxSize * cellYPosition, boxSize * cellXPosition, boxSize * (cellYPosition - 1));

            //Draw horizontal lines
            for (int i = 0; i <= maxXCells;i++ )
            {
                g.DrawLine(pen, (i * boxSize), 0, i * boxSize, boxSize * maxYCells);
            }

            //Draw vertical lines            
            for (int i = 0; i <= maxYCells; i++)
            {
                g.DrawLine(pen, 0, (i * boxSize), boxSize * maxXCells, i * boxSize);
            }                    
        }

        var memStream = new MemoryStream();
        bmp.Save(memStream, ImageFormat.Jpeg);
        return memStream.ToArray();
    }
}

答案 1 :(得分:7)

  1. 创建System.Drawing.Bitmap对象。

  2. 创建一个Graphics对象来进行绘图。

  3. 将Bitmap作为JPEG对象保存到MemoryStream。

  4. 不要忘记在临时位图上调用Dispose!

    示例代码如下,您可以更改下面的像素格式和各种选项,看看MSDN文档。

        public static byte[] CreateGridImage(
            int maxXCells, 
            int maxYCells,
            int cellXPosition, 
            int cellYPosition)
        {
            // Specify pixel format if you like..
            using(var bmp = new System.Drawing.Bitmap(maxXCells, maxYCells)) 
            {
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    // Do your drawing here
                }
    
                var memStream = new MemoryStream();
                bmp.Save(memStream, ImageFormat.Jpeg);
                return memStream.ToArray();
            }
        }
    

答案 2 :(得分:4)

首先,关于绘图,你可以:

  • 使用Graphics类来使用GDI为您提供的内容
  • 锁定位图并手动绘制

至于保存,您可以使用MemoryStream类来保留您的字节,然后从中获取字节数组。

示例代码可能如下所示(假设您要使用Graphics对象来绘制位图:

public byte[] CreateGridImage(int maxXCells, int maxYCells,
                    int cellXPosition, int cellYPosition)
{
    int imageWidth = 1;
    int imageHeight = 2;
    Bitmap bmp = new Bitmap(imageWidth, imageHeight);

    using (Graphics g = Graphics.FromImage(bmp))
    {
        //draw code in here
    }

    MemoryStream imageStream = new MemoryStream();

    bmp.Save(imageStream, System.Drawing.Imaging.ImageFormat.Jpeg);
    bmp.Dispose();

    return imageStream.ToArray();
}

答案 3 :(得分:2)

Slauma,

这是另一种方法,它使用WindowsForm的 DataGridView 控件来绘制网格。

    public byte[] GetData()
    {
        Form form = new Form();
        //Create a new instance of DataGridView(WindowsForm) control.
        DataGridView dataGridView1 = new DataGridView();
        form.Controls.Add(dataGridView1);

        //Customize output.
        dataGridView1.RowHeadersVisible = false;
        dataGridView1.ColumnHeadersVisible = false;
        dataGridView1.ScrollBars = ScrollBars.None;
        dataGridView1.AutoSize = true;

        //Set datasource.
        dataGridView1.DataSource = GetDataTable();

        //Export as image.
        Bitmap bitmap = new Bitmap(dataGridView1.Width, dataGridView1.Height);
        dataGridView1.DrawToBitmap(bitmap, new Rectangle(Point.Empty, dataGridView1.Size));
        //bitmap.Save("sample.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

        MemoryStream ms = new MemoryStream();
        bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);

        bitmap.Dispose();
        form.Dispose();

        return ms.ToArray();
    }

    /// <summary>
    /// Helper method.
    /// </summary>
    DataTable GetDataTable()
    {
        DataTable dt = new DataTable();

        for (int i = 0; i < 2; i++)
            dt.Columns.Add(string.Format("Column{0}", i));

        for (int i = 0; i < dt.Columns.Count; i++)
        {
            for (int j = 0; j < 10; j++)
            {
                dt.Rows.Add(new string[] { "X1", "Y1" });
            }
        }

        return dt;
    }

=== 在客户端app.config中(替换此行):

<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />

===

快乐的编码!