How to create a jpg image dynamically in memory with .NET?

34,860

Solution 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:

enter image description here

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

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

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

Solution 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 !

Share:
34,860
Slauma
Author by

Slauma

Updated on September 07, 2020

Comments

  • Slauma
    Slauma over 3 years

    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!

  • Slauma
    Slauma about 13 years
    Thanks! In your example are already all important pieces together. To draw I need functions like g.DrawLine and Pen and Brush objects and so on, right? I do not really understand the difference between the two options you mentioned ("Use Graphics..." vs. "Lock bitmap..."). What does this mean exactly and which option is to prefer?
  • Marcin Deptuła
    Marcin Deptuła about 13 years
    Yes, DrawLines and Pen/Brush objects are perfectly fine, the other option is not to use Graphics, but operate on bitmap's memory by yourself. If you are happy with what Graphics object gives you and also - with how fast it works, you definately don't need to play with locking bitmaps.
  • Slauma
    Slauma about 13 years
    Number of cells in X and Y direction will probably be always <= 60 (so that I have to draw 120 lines in the worst case) and this function will be called once in an hour of so. So, the graphical operations won't be huge, I guess. It can't be a performance problem, can it?
  • Slauma
    Slauma about 13 years
    Hm, interesting solution! But I am not in a Windows Forms application, currently I don't reference any Windows Forms assemblies. The library which will contain this new function is part of a WCF service. Is your solution applicable in this case?
  • Karthik Mahalingam
    Karthik Mahalingam about 13 years
    @Slauma, This solution works under WCF too, I just tested it. But you have to add reference to "System.Windows.Forms.dll" and "System.Drawing.dll" in your project. Cheers.
  • Marcin Deptuła
    Marcin Deptuła about 13 years
    In that case - no. If you want aim into huge bitmaps or real time / sub real time rendering (like 10 frames per seconds) then you could start considering other options :).
  • Slauma
    Slauma about 13 years
    That's awesome! I really didn't expect the detailed work. Thank you very much!
  • NakedBrunch
    NakedBrunch about 13 years
    Yup, there are many ways to skin this cat but the above code should definitely get you going.
  • Jonathan
    Jonathan over 10 years
    Thanks much for the detailed example - useful in a similar instance for me.
  • davvs
    davvs over 10 years
    Thank you. Excellent example!