1
votes

I need to create an image in memory (can be huge image!) and to extract from it byte array in the size of width x height. Each byte must have value of 0-255 (256 gray scale values: 0 for white and 255 for black). The part of creating the image is easy, here is a simple example of my code:

img = new Bitmap(width, height);
drawing = Graphics.FromImage(img);
drawing.Clear(Color.Black);// paint the background
drawing.DrawString(text, font, Brushes.White, 0, 0);

Problem is to convert it to "my" special gray scale byte array. When I'm using any pixel format other then Format8bppIndexed, the byte array I'm getting from the bitmap is not in the size I need (width*length) so I need a conversion that takes too much time. When I'm using Format8bppIndexed I'm getting the byte array very fast and in the right size, but each byte/pixel is 0-15.

Changing the bitmap palette has no affect:

var pal = img.Palette;
for (int i = 1; i < 256; i++){
   pal.Entries[i] = Color.FromArgb(255, 255, 255);
}
img.Palette = pal;

Any idea how to do it?

Edit: Full code:

// assume font can be Times New Roman, size 7500!
static private Bitmap DrawText(String text, Font font)
{
    //first, create a dummy bitmap just to get a graphics object
    var img = new Bitmap(1, 1);
    var drawing = Graphics.FromImage(img);

    //measure the string to see how big the image needs to be
    var textSize = drawing.MeasureString(text, font);

    //free up the dummy image and old graphics object
    img.Dispose();
    drawing.Dispose();

    //create a new image of the right size (must be multiple of 4)
    int width = (int) (textSize.Width/4) * 4;
    int height = (int)(textSize.Height / 4) * 4;
    img = new Bitmap(width, height);

    drawing = Graphics.FromImage(img);

    // paint the background
    drawing.Clear(Color.Black);

    drawing.DrawString(text, font, Brushes.White, 0, 0);

    var bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height),    ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);

    var newBitmap = new Bitmap(width, height, bmpData.Stride, PixelFormat.Format8bppIndexed, bmpData.Scan0);

    drawing.Dispose();

    return newBitmap;
}

private static byte[] GetGrayscleBytesFastest(Bitmap bitmap)
{
    BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
    int numbytes = bmpdata.Stride * bitmap.Height;
    byte[] bytedata = new byte[numbytes];
    IntPtr ptr = bmpdata.Scan0;

    Marshal.Copy(ptr, bytedata, 0, numbytes);

    bitmap.UnlockBits(bmpdata);

    return bytedata;
}
1
Is this c# / .NET? Please tag it upgeedubb
A few notes: Do you aim at an image or a raw byte array? Your create a palette code should be pal.Entries[i] = Color.FromArgb(i, i, i); going from 0-255. In the rest of the world 0=black and 255=white. Can you show us the real code for creating a Format8bppIndexed image? Have you looked at lockbits? Necessary to make things fast.TaW
I added the full code. This is the fastest way I have found, with only problem of getting 0-15 values per pixel.Yariv
BTW, I tried the pal.Entries[i] = Color.FromArgb(i, i, i) before calling to my GetGrayscleBytesFastest. It had no affect (still 0-15 values)Yariv

1 Answers

1
votes

You probably want to do this in two steps. First, create a 16bpp grayscale copy of your original image as described in Convert an image to grayscale.

Then, create your 8bpp image with the appropriate color table and draw the 16bpp grayscale image onto that image. That will do the conversion for you, converting the 16-bit grayscale values to your 256 different colors.

You should then have an 8bpp image with your 256 different shades of gray. You can then call LockBits to get access to the bitmap bits, which will be index values in the range 0 to 255.