I'm writing in c# and using this to fill my PictureBox with a byte array
var bmp = new Bitmap(48, 32, PixelFormat.Format1bppIndexed);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(fileArray, 0, bmpData.Scan0, fileArray.Length);
bmp.UnlockBits(bmpData);
return bmp;
The result is very strange.
It only fills up to the 125th byte. So I tried playing with the data and the 0-125 bytes show as they should but anything after byte 125 is not shown and it overwrites byte 125.
So if I do 0-125 all 0xff I get as solid bar up top. but adding 126 as 0x00 it replaces 125 with 0x00.
Instead of inserting the padding into your data you can move the data row by row:
int w = 48;
int h = 32;
int unpadded = w / 8; // unpadded byte length of one line in your data
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
int stride = 8 // padded length of scanline
for (int i = 0; i < h; i++)
{
Marshal.Copy(blob, i * unpadded , bmpData.Scan0 + i * stride, unpadded );
}
bmp.UnlockBits(bmpData);
return bmp;
Note that to make bitmap operations faster all physical rows must have a length that's a multiple of 4 bytes. The resulting, padded length is called stride.
The stride is the width of a single row of pixels (a scan line),
rounded up to a four-byte boundary. If the stride is positive, the
bitmap is top-down. If the stride is negative, the bitmap is
bottom-up.
With a logical length of 48 pixels and 1bpp your logical length is 6 bytes, so it must be padded to 8 bytes internally.
Best not to move your data in managed code to prepare this but to move only the right portions to the right slots..
Here are more examples of calculating stride for other formats
This solution worked for me. Its by far not the best and shows a lack of understanding or simply bitmaps can not support monochrome 1 bit per pixel images. Never the less it works. All this does is adds 2 bytes on each row of 6 bytes. So that the stride can deal with it.
int j = 1;
int k = 0;
for (int i = 0; i < 192; i++)
{
fixArray[k] = blob[i];
j++;
if (j == 7)
{
j = 1;
fixArray[++k] = 0;
fixArray[++k] = 0;
}
k++;
}
Related
I have a bitmap that I want to convert into a color array, faster than if I had used the GetPixel function.
So far I have seen people convert the bitmap into a byte array first then into the color array:
Bitmap bmBit = (Bitmap)bit;
var bitmapData = bmBit.LockBits(new Rectangle(0, 0, bmBit.Width, bmBit.Height),
ImageLockMode.ReadWrite, bmBit.PixelFormat);
var length = bitmapData.Stride * bitmapData.Height;
byte[] bytes = new byte[length];
Marshal.Copy(bitmapData.Scan0, bytes, 0, length);
bmBit.UnlockBits(bitmapData);
But this returns a bmBit with incorrect numbers.
What am I doing wrong, and is there a better way to do this without converting to a byte array first?
There are different ways for pixels to be stored in a bitmap (16 color, 256 color, 24 bits per pixel, etc...), and rows are padded to multiples of 4 bytes.
for (int i = 0; i < bitmapData.Height; i++)
{
//the row starts at (i * bitmapData.Stride).
//we must do this because bitmapData.Stride includes the pad bytes.
int rowStart = i * bitmapData.Stride;
//you need to use bitmapData.Width and bitmapData.PixelFormat
// to determine how to parse a row.
//assuming 24 bit. (bitmapData.PixelFormat == Format24bppRgb)
if (bitmapData.PixelFormat == PixelFormat.Format24bppRgb)
{
for (int j = 0; j < bitmapData.Width; j++)
{
//the pixel is contained in:
// bytes[pixelStart] .. bytes[pixelStart + 2];
int pixelStart = rowStart + j * 3;
}
}
}
I am copying from one 1bpp bitmap to a smaller 1bpp bitmap. I just want to clip out a region so I can count the number of black pixels.
I use the following to make the copies:
private Bitmap Copy(Bitmap srcBitmap, Rectangle section)
{
BitmapData SourceLockedData;
BitmapData DestLockedData;
Rectangle DestRect;
byte[] SrcImageData;
byte[] DestImageData;
int ByteCount;
int WidthCount = 0;
int CurrentLine = 0;
int DestStride;
int SrcStride = 0;
// Create the new bitmap and associated graphics object
Bitmap Newbmp = new Bitmap(section.Width, section.Height, PixelFormat.Format1bppIndexed);
Newbmp.SetResolution(srcBitmap.HorizontalResolution, srcBitmap.VerticalResolution);
//Lock the bits
SourceLockedData = srcBitmap.LockBits(section, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
SrcStride = SourceLockedData.Stride;
//Get a count of the number of bytes to copy. Remember, bytes are not pixels.
ByteCount = SourceLockedData.Stride * SourceLockedData.Height;
//Initialize the source byte array
SrcImageData = new byte[ByteCount];
//Copy the data to the source byte array
Marshal.Copy(SourceLockedData.Scan0, SrcImageData, 0, ByteCount);
//Unlock the bits
srcBitmap.UnlockBits(SourceLockedData);
//Set a rectangle to the size of the New bitmap
DestRect = new Rectangle(new Point(0, 0), Newbmp.Size);
//Lock the bits
DestLockedData = Newbmp.LockBits(DestRect, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
DestStride = DestLockedData.Stride;
//Get a count of the number of bytes to copy. Remember, bytes are not pixels.
ByteCount = DestLockedData.Stride * DestLockedData.Height;
//Initialize the source byte array
DestImageData = new byte[ByteCount];
//Copy the data to the destination byte array
Marshal.Copy(DestLockedData.Scan0, DestImageData, 0, ByteCount);
//Unlock for now
Newbmp.UnlockBits(DestLockedData);
for (int ArrayIndex = 0; ArrayIndex < ByteCount; ArrayIndex++)
{
if (WidthCount == Newbmp.Width)
{
//increment the line and push the index by the stride
ArrayIndex = (++CurrentLine) * DestStride;
continue;
}
DestImageData[ArrayIndex] = SrcImageData[ArrayIndex];
WidthCount++;
}
//Lock the bits again
DestLockedData = Newbmp.LockBits(DestRect, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
//Copy data from byte array to IntPtr
Marshal.Copy(DestImageData, 0, DestLockedData.Scan0, ByteCount);
//Unlock bits
Newbmp.UnlockBits(DestLockedData);
// Return the bitmap
return Newbmp;
}
The biggest problem I am having is that both the SourceLockedData.Stride and DestLockedData.Stride are smaller than the width of the respective images. How can that be? From everything I know about stride it's the number of bits from one scan line of data to the next scan line of data. How is it mathematically possible for this to be less than the width?
Am I using LockBits or BitmapData wrong? Can BitmapData not be trusted? Should I calculate the stride by hand?
Tom P.
I figured out that the stride can be less than the width if you are dealing with RLE bitmaps. Since the bitmaps that I am loading are TIFFs they are RLE8 encoded.
I am creating a program that scans all the pixels of an image, and whenever it finds a pixel that contains the color pink. It makes the pixel black. But it doesn't seem to find a pink pixel when there is two of them on the image. I do not know if I am using LockBits correctly, maybe I am using it wrong. Can someone please help me solve this I would greatly appreciate it.
Here is the code below:
Bitmap bitmap = pictureBox1.Image as Bitmap;
System.Drawing.Imaging.BitmapData d = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr ptr = d.Scan0;
byte[] rgbs = new byte[Math.Abs(d.Stride) * bitmap.Height];
Marshal.Copy(ptr, rgbs, 0, rgbs.Length);
Graphics g = pictureBox1.CreateGraphics();
for (int index = 2; index < rgbs.Length; index += 3)
{
if (rgbs[index] == 255 && rgbs[index - 1] == 0 && rgbs[index - 2] == 255) // If color = RGB(255, 0, 255) Then ...
{
// This never gets executed!
rgbs[index] = 0;
rgbs[index - 1] = 0;
rgbs[index - 2] = 0;
}
}
Marshal.Copy(rgbs, 0, ptr, rgbs.Length); // Copy rgb values back to the memory location of the bitmap.
pictureBox1.Image = bitmap;
bitmap.UnlockBits(d);
You don't need to copy the pixel data into an array. The point of LockBits is it gives you direct (unsafe) access the memory. You can just iterate the pixels and change them as you find them. You will need to know the format of the image to do this successfully.
BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10),
ImageLockMode.ReadOnly, bm.PixelFormat);
// Blue, Green, Red, Alpha (Format32BppArgb)
int pixelSize=4;
for(int y=0; y<bmd.Height; y++)
{
byte* row=(byte *)bmd.Scan0+(y*bmd.Stride);
for(int x=0; x<bmd.Width; x++)
{
int offSet = x*pixelSize;
// read pixels
byte blue = row[offSet];
byte green = row[offSet+1];
byte red = row[offSet+2];
byte alpha = row[offSet+3];
// set blue pixel
row[x*pixelSize]=255;
}
}
It's a little more tricky in VB than C# as VB has no knowledge of pointers and requires the use of the marshal class to access unmanaged data. Here's some sample code. (For some reason I originally though this was a VB question).
Dim x As Integer
Dim y As Integer
' Blue, Green, Red, Alpha (Format32BppArgb)
Dim PixelSize As Integer = 4
Dim bmd As BitmapData = bm.LockBits(new Rectangle(0, 0, 10, 10),
ImageLockMode.ReadOnly, bm.PixelFormat)
For y = 0 To bmd.Height - 1
For x = 0 To bmd.Width - 1
Dim offSet As Int32 = (bmd.Stride * y) + (4 * x)
' read pixel data
Dim blue As Byte = Marshal.ReadByte(bmd.Scan0, offSet)
Dim green As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 1)
Dim red As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 2)
Dim alpha As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 3)
' set blue pixel
Marshal.WriteByte(bmd.Scan0, offSet , 255)
Next
Next
I have been trying to set the alpha values manually for a Bitmap with the format of Format32bppArgb . In this code example, I am setting them all to 0.5f, however, they will be different values in the future and not all 0.5f/128 (as this is my test case to just get this working). How can I properly set the alpha values for a bitmap quickly? I could use SetPixel(), however, SetPixel() is horrifically slow for large images compared to just locking/unlocking the bitmap.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
for (int counter = 0; counter < rgbValues.Length; counter += 4)
{
rgbValues[counter] = 255;
rgbValues[counter + 1] = 255;
rgbValues[counter + 2] = 255;
rgbValues[counter + 3] = 128;
}
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
The best way to do this, providing you want to have the same alpha value on your entire bitmap, is to use a ColorMatrix. Check out this example by Microsoft:
http://msdn.microsoft.com/en-us/library/w177ax15(v=vs.71).aspx
This is the 3rd part to this topic. Part 1, Part 2..
I was successfully able to print my monochrome bitmap to my printer, however there is a large black stripe along the right of the image when the item prints.
Here is the original
(Scanned in)What the printer printed
Code to generate binary blob
Rectangle rect = new Rectangle(0, 0, Bitmap.Width, Bitmap.Height);
System.Drawing.Imaging.BitmapData bmpData = null;
byte[] bitVaues = null;
int stride = 0;
try
{
bmpData = Bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, Bitmap.PixelFormat);
IntPtr ptr = bmpData.Scan0;
stride = bmpData.Stride;
int bytes = bmpData.Stride * Bitmap.Height;
bitVaues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, bitVaues, 0, bytes);
}
finally
{
if (bmpData != null)
Bitmap.UnlockBits(bmpData);
}
string str = String.Format("GW{0},{1},{2},{3},", X, Y, stride, Bitmap.Height);
byte[] ascii = Encoding.ASCII.GetBytes(str);
byte[] buffer = new byte[ascii.Length + bitVaues.Length + 1];
Buffer.BlockCopy(ascii, 0, buffer, 0, ascii.Length);
Buffer.BlockCopy(bitVaues, 0, buffer, ascii.Length, bitVaues.Length);
buffer[buffer.Length - 1] = (byte)'\n';
return buffer;
My initial theory is the BMP format is adding that line as a end of line marker and is not viable when rendered. I am thinking I may have to reparse the file after I have the binary array and take out the 00 00 00 at the end of every line. But I am posting here in case anyone thinks of a better way.
Microsoft bitmaps are always padded to an even 32 bits. When you generate the bitmap, round the width up to a multiple of 32 and you should be fine.