How to create a jpg image dynamically in memory wi

2019-01-19 10:36发布

问题:

I have a .NET (3.5 SP1) library (DLL) written in C#. I have to extend this library by a class method which will have the following signature:

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

This method is supposed to do the following:

  • Input parameters maxXCells and maxYCells define the size of a grid of cells in X and Y direction. maxXCells and maxYCells is the number of cells in each direction. The individual cells are square-shaped. (So it's kind of an asymmetric chess board.)
  • Input parameters cellXPosition and cellYPosition identify one special cell within this grid and this cell has to be filled with a cross.
  • No fancy graphics are necessary, really only black grid lines on a white background and a X in one of the cells.
  • The resulting graphic must have jpg format.
  • Creation of this graphic must happen in memory and nothing should be stored in a file on disk nor painted on the screen.
  • The method returns the generated image as a byte[]

I'm very unfamiliar with graphics functions in .NET so my questions are:

  • Is this possible at all with .NET 3.5 SP1 without additional third-party libraries (which I would like to avoid)?
  • What are the basic steps I have to follow and what are the important .NET namespaces, classes and methods I need to know to achieve this goal (epecially to draw lines and other simple graphical elements "in memory" and convert the result into an byte array in jpg format)?

Thank you for suggestions in advance!

回答1:

The following is a full code sample that will use GDI to draw a grid and place a cross (with a red background) just like in the example image below. It uses GDI just like the other answers but the real work takes places by looping through the cells and drawing gridlines.

The following code

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

will create a 10x10 grid with a cross in the 9x9 position:

A new addition to CreateGridImage() is the addition of a boxSize argument which sets the size of each "square" in the grid

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


回答2:

  1. Create a System.Drawing.Bitmap Object.

  2. Create a Graphics object to do your drawing.

  3. Save the Bitmap to a MemoryStream as a JPEG object.

Don't forget to call Dispose on your temporary bitmap!

Sample code is below, you can change the pixel formats and various options below, have a look at the MSDN documentation.

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


回答3:

First of all, about drawing, you can either:

  • Use Graphics class to use what GDI gives you
  • Lock bitmap and draw on it manually

As for saving, you could use MemoryStream class do keep your bytes and then get array of bytes out of it.

Sample code could look like this (assuming you want to use Graphics object to draw on bitmap:

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


回答4:

Slauma,

Here is yet another way, which uses WindowsForm's DataGridView control to draw grid.

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

=== In the client app.config (replace this line):

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

===

Happy Coding !