I have a very strange issue.
Here is a simplified code of mine to explain :
class Bitmap1
{
public Bitmap nImage;
public IntPtr data;
public Bitmap1()
{
int w = 2450;
int h = 2450;
this.data = Marshal.AllocHGlobal(w*h);
nImage = new Bitmap(w, h, w, PixelFormat.Format8bppIndexed, data);
}
}
When w and h are equal to 2448, everything works well if I invocate the constructor.
But when h and w are equal to 2450, I have an ArgumentException which seems launch by the "new Bitmap(...);"
I can't understand, and the documentation doesn't say there is a limited size for Marshal.AllocHGlobal.
What's wrong? Are there other ways to do what I want ?
Thank you very much.
stride
Type: System.Int32
Integer that specifies the byte offset between the beginning of one scan line and the next. This is usually (but not necessarily) the number of bytes in the pixel format (for example, 2 for 16 bits per pixel) multiplied by the width of the bitmap. The value passed to this parameter must be a multiple of four..
http://msdn.microsoft.com/en-us/library/zy1a2d14.aspx
So you need something like the following:
int w = 2450;
int h = 2450;
int s = 2452;//Next multiple of 4 after w
this.data = Marshal.AllocHGlobal(s*h);
nImage = new Bitmap(w, h, s, PixelFormat.Format8bppIndexed, data);
This means there are 2 bytes between every line that are just padding and not part of the bitmap itself. When doing pointer arithmetic you obviously need to do s*y+x and not w*y+x to account for the padding.
Bitmap bmp = new Bitmap("SomeImage");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, bytes);
Related
i have a problem with Marshal.UnsafeAddrOfPinnedArrayElement() method.
What i want to do, is return Bitmap object from byte[] array. But first, some code below with explenation what i do.
First, i load my Bitmap to method that i return byte[] array from it:
//return tuple with pointer to array and byte[]array
public static (byte[], IntPtr) GetByteArray(Bitmap bitmap)
{
//lockbits
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite,
bitmap.PixelFormat
);
int pixels = bitmapData.Stride * bitmap.Height;
byte[] resultArray = new byte[pixels];
//copying bytes to array
Marshal.Copy(bitmapData.Scan0, resultArray, 0, pixels);
bitmap.UnlockBits(bitmapData);
//returns array and pointer to it
return (resultArray, bitmapData.Scan0);
}
Second, i want to edit that byte array:
public static Bitmap Execute(Bitmap bitmap, int[] filter)
{
//get byte array from method that i mentioned before with pointer to it
(byte[] pixelsFromBitmap, IntPtr pointer) = PictureUtilities.GetByteArray(bitmap);
byte[] newPixels = pixelsFromBitmap;
int stride = bitmap.Width;
int height = bitmap.Height;
int width = bitmap.Width;
Parallel.For(0, height - 1, y =>
{
int offset = y * stride;
for(int x = 0; x < width - 1; x++)
{
//some stuff i doing with array, not neceserry what im doing here
int positionOfPixel = x + offset;
newPixels[positionOfPixel] = (byte)122;
}
});
//copying values from newPixels to pixelsFromBitmap that i get from method GetByteArray() that i mentioned it before
newPixels.CopyTo(pixelsFromBitmap, 0);
//copying bytes again
Marshal.Copy(pixelsFromBitmap, 0, pointer, pixelsFromBitmap.Length);
//generete new bitmap from byte array
Bitmap result = new Bitmap(bitmap.Width, bitmap.Height, stride,
bitmap.PixelFormat,
pointer);
return result;
}
After all that process i get an Exception in Execute() method: System.ArgumentException in line, when i getting new Bitmap result.
Could you tell me, what i doing wrong? i want to get a byte array from my bitmap from method (for readability), edit it and return new bitmap depends on my new byte array.
I bet, that i not clearly get, how Marshall.Copy works and i get wrong pointer to returning byte array from GetByteArray() method.
Thanks for help
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++;
}
I am taking a screenshot of an android device using ADB and receiving the screenshot as raw byte data.
I know the raw byte data coming through is in the format rgba
Red is offset 0, Green offset 8, Blue offset 16, Alpha offset 24, each value is 1 byte. This makes up the entire byte array.
I am trying to convert this byte array to a Bitmap in C# and it is working for the most part, the image looks correct in every way apart from the fact that it is coming through with a 'blue hue' -- the coloring is off.
The following is the code I'm using to convert my raw byte data:
int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
using (var stream = new MemoryStream(imageData))
using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
IntPtr pNative = bmpData.Scan0;
Marshal.Copy(imageData, 0, pNative, imageData.Length);
bmp.UnlockBits(bmpData);
bmp.Save(filename);
}
return 1;
}
I've read in this post that it could be to do with the byte order of the actual rgba values. As you can see in the code above, I tried casting my bmpData.Scan0 to int* and it is still coming through with a blue hue.
I'm wracking my brain as to what I can do now to get this image to come through with the correct colors. I'm assuming it's reading red as blue and blue as red or vice versa.
I thought I could manipulate the raw byte data so that it is in the correct byte order when converting it to a bitmap, however I'm not sure how I can go about doing that.
Any suggestions?
So, in the end my solution was simple.
int WriteBitmapFile(string filename, int width, int height, byte[] imageData)
{
byte[] newData = new byte[imageData.Length];
for(int x = 0; x < imageData.Length; x+= 4)
{
byte[] pixel = new byte[4];
Array.Copy(imageData, x, pixel, 0, 4);
byte r = pixel[0];
byte g = pixel[1];
byte b = pixel[2];
byte a = pixel[3];
byte[] newPixel = new byte[] { b, g, r, a };
Array.Copy(newPixel, 0, newData, x, 4);
}
imageData = newData;
using (var stream = new MemoryStream(imageData))
using (var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0,
bmp.Width,
bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
IntPtr pNative = bmpData.Scan0;
Marshal.Copy(imageData, 0, pNative, imageData.Length);
bmp.UnlockBits(bmpData);
bmp.Save(filename);
}
return 1;
}
All I had to do was loop through the imageData and adjust the bytes that made up the pixels and re-order them so that they would suit the expected format for windows bitmap which is BGRA.
Obviously, I can still make some small optimisations in the for loop that shifts the bytes around, but it's working.
Did you have a look at this post ?
You're stating that every 'value' is 8 bytes, so 1 pixel is 4x8 = 32 bytes? But you are using 32bpp image format, so 32 bits per pixel -> 1 pixel = 4 bytes.
Also pay attention to little/big endian, if you're acquiring the image from another processor/network/...
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.
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.