Related
I'm trying to get cursor data (mask and color, if available) as a byte array using GetDIBits(), with the correct size and correct animation frame but it's proving to be unreliable.
Also, I would like to get or be able to simulate the multiple frames available in a cursor, such as what is possible when using DrawIconEx(), passing the cursorStep parameter.
Basically what I'm trying to emulate is part of the behavior of IDXGIOutputDuplication::GetFramePointerShape(), to get a buffer (byte[]) with the contents of the current cursor, just without having to rely on the DirectX library.
So, I want:
Get the cursor data as a byte[], even with the mask working (SOLVED).
Get the cursor data with the correct size, not a fixed 32x32px cursor.
Get the actual animation frame of the cursor.
Just as the DXGI method provides if I'm using OutputDuplication, but in my case, I'm not.
1) Get the cursor mask correctly (solved):
TL;DR: Wrong data interpretation on my part.
I'm calling GetDIBits() once, passing null as the buffer parameter to get the details of the color/mask images and a second time to get the image.
It works all right with the color image but returns a wrong data size and wrong image content for the mask image.
Here's a comparison and the code sample.
Files ending with "2" were created by using System.Drawing.Bitmap.FromHBitmap() while the others were obtained from GetDiBits().
For some reason, GetDIBits() returns this data (BitmapInfoHeader) for the mask. Also, only returns 128 bytes (256 if the mask has two frames, such as for the I-Beam/Text cursor), which is way too little for 32x32px or 32x64px masks.
Here's my code so far (it's C#):
WindowDeviceContext = User32.GetWindowDC(IntPtr.Zero);
//The parameter passed in the structs is just to be able to calculate the size of it.
var cursorInfo = new CursorInfo(false);
if (!User32.GetCursorInfo(out cursorInfo))
return;
if (cursorInfo.Flags != Native.Constants.CursorShowing)
{
Gdi32.DeleteObject(cursorInfo.CursorHandle);
return;
}
var iconHandle = User32.CopyIcon(cursorInfo.CursorHandle);
if (iconHandle == IntPtr.Zero)
{
User32.DestroyIcon(iconHandle);
Gdi32.DeleteObject(cursorInfo.CursorHandle);
return;
}
if (!User32.GetIconInfo(iconHandle, out var iconInfo))
{
Gdi32.DeleteObject(iconInfo.Color);
Gdi32.DeleteObject(iconInfo.Mask);
User32.DestroyIcon(iconHandle);
Gdi32.DeleteObject(cursorInfo.CursorHandle);
return;
}
//Color.
var colorHeader = new BitmapInfoHeader(false);
//Gets the color image details.
Gdi32.GetDIBits(WindowDeviceContext, iconInfo.Color, 0, 0, null, ref colorHeader, DibColorModes.RgbColors);
if (colorHeader.Height != 0)
{
colorHeader.Height *= -1;
var colorBuffer = new byte[colorHeader.SizeImage];
Gdi32.GetDIBits(WindowDeviceContext, iconInfo.Color, 0, (uint)(colorHeader.Height * -1), colorBuffer, ref colorHeader, DibColorModes.RgbColors);
//Converts a byte array to image (System.Windows.Media.Imaging.BitmapSource) just for testing.
var image = ImageUtil.ImageMethods.FromArray(colorBuffer, colorHeader.Width, colorHeader.Height * -1, 4); //4 channels
using var fileStream = new FileStream(#"Color.png", FileMode.Create);
var bmpEncoder = new PngBitmapEncoder();
bmpEncoder.Frames.Add(BitmapFrame.Create(image));
bmpEncoder.Save(fileStream);
//Converts the HBitmap to Bitmap, just for testing too.
var image2 = System.Drawing.Image.FromHbitmap(iconInfo.Color);
image2.Save(#"Color2.png");
}
//Mask.
var maskHeader = new BitmapInfoHeader(false);
//Gets the mask image details.
Gdi32.GetDIBits(WindowDeviceContext, iconInfo.Mask, 0, 0, null, ref maskHeader, DibColorModes.RgbColors);
if (maskHeader.Height != 0)
{
maskHeader.Height *= -1;
var maskBuffer = new byte[maskHeader.SizeImage];
Gdi32.GetDIBits(WindowDeviceContext, iconInfo.Mask, 0, (uint) maskHeader.Height, maskBuffer, ref maskHeader, DibColorModes.RgbColors);
//Converts a byte array to image (System.Windows.Media.Imaging.BitmapSource) just for testing.
var image = ImageUtil.ImageMethods.FromArray(maskBuffer.ToList(), maskHeader.Width, maskHeader.Height * -1, 1, 1);
using var fileStream = new FileStream(#"Mask.png", FileMode.Create);
var bmpEncoder = new PngBitmapEncoder();
bmpEncoder.Frames.Add(BitmapFrame.Create(image));
bmpEncoder.Save(fileStream);
//Converts the HBitmap to Bitmap, just for testing too.
var image2 = System.Drawing.Image.FromHbitmap(iconInfo.Mask);
image2.Save(#"Mask2.png");
}
Gdi32.DeleteObject(iconInfo.Color);
Gdi32.DeleteObject(iconInfo.Mask);
User32.DestroyIcon(iconHandle);
Gdi32.DeleteObject(cursorInfo.CursorHandle);
Solved:
With the proper interpretation of the bits per pixel of the mask image, this issue was solved.
public static BitmapSource FromArray(byte[] data, int width, int height, int channels, int bitsPerPixel = 32)
{
var format = PixelFormats.Default;
var stride = channels * ((bitsPerPixel * width + 31) / 32);
//Abridged channel and bits by pixel to format converter.
if (channels == 1)
{
format = PixelFormats.BlackWhite;
stride = width / 8;
}
else if (channels == 3)
format = PixelFormats.Bgr24; //RGB.
else if (channels == 4)
format = PixelFormats.Bgr32; //RGB + alpha.
var wbm = new WriteableBitmap(width, height, 96, 96, format, null);
wbm.WritePixels(new Int32Rect(0, 0, width, height), data, stride, 0);
return wbm;
}
2) Get the correct size of the cursor:
As noted above, I'm only getting cursor images of size 32x32 or 32x64 (for masks).
I've tried to increase the size being passed to GetDIBits(), but the image being returned is always the same size.
Even if the size in Windows 10/11 settings is set to a bigger cursor size.
The IDXGIOutputDuplication::GetFramePointerShape() method returns a correct size, so it looks like it's possible. What does this method do in order to get the correct image size?
3) Get the actual cursor frame being displayed (for animated cursors):
As for the animation frame (steps) of the cursor, I don't see a way of getting multiple frames based on an index the same way that I would be able to draw with DrawIconEx():
//If the cursor rate needs to be precisely captured, I could use this undocumented API:
//https://source.winehq.org/source/dlls/user32/cursoricon.c#2325
//int rate = 0, num = 0;
//var ok1 = User32.GetCursorFrameInfo(cursorInfo.hCursor, IntPtr.Zero, 17, ref rate, ref num);
//CursorStep
var ok = User32.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.XHotspot, frame.CursorY - iconInfo.YHotspot, cursorInfo.CursorHandle, 0, 0, CursorStep, IntPtr.Zero, 0x0003);
if (!ok)
{
CursorStep = 0;
User32.DrawIconEx(CompatibleDeviceContext, frame.CursorX - iconInfo.XHotspot, frame.CursorY - iconInfo.YHotspot, cursorInfo.CursorHandle, 0, 0, CursorStep, IntPtr.Zero, 0x0003);
}
else
CursorStep++;
Maybe I could draw the cursor color and masks separately using DrawIconEx() into two bitmaps (using the flags DI_IMAGE and DI_MASK in each call, passing the cursor step), and then getting the pixels using GetDIBits()?
But how to do that?
I searched all question about byte array but i always failed. I have never coded c# i am new in this side. Could you help me how to make image file from byte array.
Here is my function which stores byte in array named imageData
public void imageReady( byte[] imageData, int fWidth, int fHeight))
You'll need to get those bytes into a MemoryStream:
Bitmap bmp;
using (var ms = new MemoryStream(imageData))
{
bmp = new Bitmap(ms);
}
That uses the Bitmap(Stream stream) constructor overload.
UPDATE: keep in mind that according to the documentation, and the source code I've been reading through, an ArgumentException will be thrown on these conditions:
stream does not contain image data or is null.
-or-
stream contains a PNG image file with a single dimension greater than 65,535 pixels.
Guys thank you for your help. I think all of this answers works. However i think my byte array contains raw bytes. That's why all of those solutions didnt work for my code.
However i found a solution. Maybe this solution helps other coders who have problem like mine.
static byte[] PadLines(byte[] bytes, int rows, int columns) {
int currentStride = columns; // 3
int newStride = columns; // 4
byte[] newBytes = new byte[newStride * rows];
for (int i = 0; i < rows; i++)
Buffer.BlockCopy(bytes, currentStride * i, newBytes, newStride * i, currentStride);
return newBytes;
}
int columns = imageWidth;
int rows = imageHeight;
int stride = columns;
byte[] newbytes = PadLines(imageData, rows, columns);
Bitmap im = new Bitmap(columns, rows, stride,
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(newbytes, 0));
im.Save("C:\\Users\\musa\\Documents\\Hobby\\image21.bmp");
This solutions works for 8bit 256 bpp (Format8bppIndexed). If your image has another format you should change PixelFormat .
And there is a problem with colors right now. As soon as i solved this one i will edit my answer for other users.
*PS = I am not sure about stride value but for 8bit it should be equal to columns.
And also this function Works for me.. This function copies 8 bit greyscale image into a 32bit layout.
public void SaveBitmap(string fileName, int width, int height, byte[] imageData)
{
byte[] data = new byte[width * height * 4];
int o = 0;
for (int i = 0; i < width * height; i++)
{
byte value = imageData[i];
data[o++] = value;
data[o++] = value;
data[o++] = value;
data[o++] = 0;
}
unsafe
{
fixed (byte* ptr = data)
{
using (Bitmap image = new Bitmap(width, height, width * 4,
PixelFormat.Format32bppRgb, new IntPtr(ptr)))
{
image.Save(Path.ChangeExtension(fileName, ".jpg"));
}
}
}
}
Can be as easy as:
var ms = new MemoryStream(imageData);
System.Drawing.Image image = Image.FromStream(ms);
image.Save("c:\\image.jpg");
Testing it out:
byte[] imageData;
// Create the byte array.
var originalImage = Image.FromFile(#"C:\original.jpg");
using (var ms = new MemoryStream())
{
originalImage.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// Convert back to image.
using (var ms = new MemoryStream(imageData))
{
Image image = Image.FromStream(ms);
image.Save(#"C:\newImage.jpg");
}
In addition, you can simply convert byte array to Bitmap.
var bmp = new Bitmap(new MemoryStream(imgByte));
You can also get Bitmap from file Path directly.
Bitmap bmp = new Bitmap(Image.FromFile(filePath));
This was helpful to me: https://www.tek-tips.com/viewthread.cfm?qid=1264492 (Reference answer)
I understand the question as follows:
I have a byte array that contains pixel data e.g. in RGB format (24bit/pixel)
From this raw pixel data I want to create a Bitmap
This code worked for me:
int width = ...;
int height = ...;
byte[] pixelArray = new byte[] {
// Creation of the actual data is not in the scope of this answer
};
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
// Create a BitmapData and lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
// Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(pixelArray, 0, bmpData.Scan0, pixelArray.Length);
// Unlock the pixels
bmp.UnlockBits(bmpData);
// Do something with your image, e.g. save it to disc
bmp.Save("c:\\temp\\mybmp.bmp", ImageFormat.Bmp);
Based on the accepted answer the OP wanted to interpret imageData byte array as the pixel buffer, rather than an already encoded bitmap stream as the most upvoted answer suggests. And though it works, it contains a lot of copies, as well as palette issues ("And there is a problem with colors right now").
I actually happen to have a drawing library exactly for this purpose (among others). The platform-independent core library allows you to interpret any array of primitive types as a bitmap data:
// Unlike in the accepted answer, no extra buffer allocation or
// array copy happens in the background. Note that we can specify
// a palette for the indexed format so the colors will be interpreted correctly
using var myBitmap = BitmapDataFactory.CreateBitmapData(imageData, new Size(fWidth, fHeight),
stride: fWidth, // stride is same as width because of the 8bpp pixel format
pixelFormat: KnownPixelFormat.Format8bppIndexed,
palette: Palette.Grayscale256());
myBitmap is now an IReadWriteBitmapData instance, allowing a lot of operations (just see the available extension methods). It also offers a pretty fast SetPixel method, which respects the palette so in this particular case it turns any color to grayscale. But if you know the actual pixel format you can also can use the WriteRaw<T> method to access the pixels directly.
And if you use the technology-specific packages such as the one for GDI+ or WPF, then you can simply convert your buffer into known bitmap types such as System.Drawing.Bitmap or System.Windows.Media.WriteableBitmap:
// the accepted answer creates two bitmaps due to the color problems where
// the 2nd one is a 32 bpp image. This solution is much faster, simpler, it avoids
// unnecessary allocations and uses parallel processing internally if possible
var systemBitmap = myBitmap.ToBitmap(); // or ToBitmapAsync, ToWriteableBitmap, etc.
I searched all question about byte array but i always failed. I have never coded c# i am new in this side. Could you help me how to make image file from byte array.
Here is my function which stores byte in array named imageData
public void imageReady( byte[] imageData, int fWidth, int fHeight))
You'll need to get those bytes into a MemoryStream:
Bitmap bmp;
using (var ms = new MemoryStream(imageData))
{
bmp = new Bitmap(ms);
}
That uses the Bitmap(Stream stream) constructor overload.
UPDATE: keep in mind that according to the documentation, and the source code I've been reading through, an ArgumentException will be thrown on these conditions:
stream does not contain image data or is null.
-or-
stream contains a PNG image file with a single dimension greater than 65,535 pixels.
Guys thank you for your help. I think all of this answers works. However i think my byte array contains raw bytes. That's why all of those solutions didnt work for my code.
However i found a solution. Maybe this solution helps other coders who have problem like mine.
static byte[] PadLines(byte[] bytes, int rows, int columns) {
int currentStride = columns; // 3
int newStride = columns; // 4
byte[] newBytes = new byte[newStride * rows];
for (int i = 0; i < rows; i++)
Buffer.BlockCopy(bytes, currentStride * i, newBytes, newStride * i, currentStride);
return newBytes;
}
int columns = imageWidth;
int rows = imageHeight;
int stride = columns;
byte[] newbytes = PadLines(imageData, rows, columns);
Bitmap im = new Bitmap(columns, rows, stride,
PixelFormat.Format8bppIndexed,
Marshal.UnsafeAddrOfPinnedArrayElement(newbytes, 0));
im.Save("C:\\Users\\musa\\Documents\\Hobby\\image21.bmp");
This solutions works for 8bit 256 bpp (Format8bppIndexed). If your image has another format you should change PixelFormat .
And there is a problem with colors right now. As soon as i solved this one i will edit my answer for other users.
*PS = I am not sure about stride value but for 8bit it should be equal to columns.
And also this function Works for me.. This function copies 8 bit greyscale image into a 32bit layout.
public void SaveBitmap(string fileName, int width, int height, byte[] imageData)
{
byte[] data = new byte[width * height * 4];
int o = 0;
for (int i = 0; i < width * height; i++)
{
byte value = imageData[i];
data[o++] = value;
data[o++] = value;
data[o++] = value;
data[o++] = 0;
}
unsafe
{
fixed (byte* ptr = data)
{
using (Bitmap image = new Bitmap(width, height, width * 4,
PixelFormat.Format32bppRgb, new IntPtr(ptr)))
{
image.Save(Path.ChangeExtension(fileName, ".jpg"));
}
}
}
}
Can be as easy as:
var ms = new MemoryStream(imageData);
System.Drawing.Image image = Image.FromStream(ms);
image.Save("c:\\image.jpg");
Testing it out:
byte[] imageData;
// Create the byte array.
var originalImage = Image.FromFile(#"C:\original.jpg");
using (var ms = new MemoryStream())
{
originalImage.Save(ms, ImageFormat.Jpeg);
imageData = ms.ToArray();
}
// Convert back to image.
using (var ms = new MemoryStream(imageData))
{
Image image = Image.FromStream(ms);
image.Save(#"C:\newImage.jpg");
}
In addition, you can simply convert byte array to Bitmap.
var bmp = new Bitmap(new MemoryStream(imgByte));
You can also get Bitmap from file Path directly.
Bitmap bmp = new Bitmap(Image.FromFile(filePath));
This was helpful to me: https://www.tek-tips.com/viewthread.cfm?qid=1264492 (Reference answer)
I understand the question as follows:
I have a byte array that contains pixel data e.g. in RGB format (24bit/pixel)
From this raw pixel data I want to create a Bitmap
This code worked for me:
int width = ...;
int height = ...;
byte[] pixelArray = new byte[] {
// Creation of the actual data is not in the scope of this answer
};
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
// Create a BitmapData and lock all pixels to be written
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
// Copy the data from the byte array into BitmapData.Scan0
Marshal.Copy(pixelArray, 0, bmpData.Scan0, pixelArray.Length);
// Unlock the pixels
bmp.UnlockBits(bmpData);
// Do something with your image, e.g. save it to disc
bmp.Save("c:\\temp\\mybmp.bmp", ImageFormat.Bmp);
Based on the accepted answer the OP wanted to interpret imageData byte array as the pixel buffer, rather than an already encoded bitmap stream as the most upvoted answer suggests. And though it works, it contains a lot of copies, as well as palette issues ("And there is a problem with colors right now").
I actually happen to have a drawing library exactly for this purpose (among others). The platform-independent core library allows you to interpret any array of primitive types as a bitmap data:
// Unlike in the accepted answer, no extra buffer allocation or
// array copy happens in the background. Note that we can specify
// a palette for the indexed format so the colors will be interpreted correctly
using var myBitmap = BitmapDataFactory.CreateBitmapData(imageData, new Size(fWidth, fHeight),
stride: fWidth, // stride is same as width because of the 8bpp pixel format
pixelFormat: KnownPixelFormat.Format8bppIndexed,
palette: Palette.Grayscale256());
myBitmap is now an IReadWriteBitmapData instance, allowing a lot of operations (just see the available extension methods). It also offers a pretty fast SetPixel method, which respects the palette so in this particular case it turns any color to grayscale. But if you know the actual pixel format you can also can use the WriteRaw<T> method to access the pixels directly.
And if you use the technology-specific packages such as the one for GDI+ or WPF, then you can simply convert your buffer into known bitmap types such as System.Drawing.Bitmap or System.Windows.Media.WriteableBitmap:
// the accepted answer creates two bitmaps due to the color problems where
// the 2nd one is a 32 bpp image. This solution is much faster, simpler, it avoids
// unnecessary allocations and uses parallel processing internally if possible
var systemBitmap = myBitmap.ToBitmap(); // or ToBitmapAsync, ToWriteableBitmap, etc.
I want to start learning about how to tear images apart to find patterns in them but in order to do that I need to first see what makes it up. I want to take a png and convert it into a byte array so I can print it out and see if I can recognize simple patterns in the array values.
So far I have this
public MainWindow()
{
InitializeComponent();
System.Drawing.Image image;
image = System.Drawing.Image.FromFile("one.png");
byte[] imArray = imageToByteArray(image);
String bytes = "";
foreach (Char bite in imArray)
{
bytes += "-"+bite;
}
MessageBox.Show(bytes);
}
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
But it doesn't seem to be working. It gives me a null error when the conversion method is called. I have NO clue why this isn't working because my understanding of the compenents is nill.
If you can suggest an easier way to make this conversion feel free to post it. Im not stuck on this code I just want a working example so I have a starting point.
Thanks!
I'd recommend starting with Bitmap to look at binary data - most other formats store data compressed, so you have no chance to understand what is inside an image by looking at the bytes.
The method you want is Bitmap.LockBits. The article also includes complete sample how to read from file and look t bits, excerpt below:
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData =
bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
Marshal.Copy(bmpData.Scan0, rgbValues, 0, bytes);
you could try converting the image to a dataURI then converting it to a blob, heres an example of how you can convert dataURIs to blobs
Blob from DataURL?
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var bb = new BlobBuilder();
bb.append(ab); return bb.getBlob(mimeString);
}
Or you can just open the file in a binary editor.
Im currently trying to use writeablebitmap to take a IntPtr of a scan of images and turn each one into a Bitmap. Im wanting to use writeablebitmap because im having an issue with standard gdi
GDI+ System.Drawing.Bitmap gives error Parameter is not valid intermittently
There is a method on a WriteableBitmap that called WritePixels
http://msdn.microsoft.com/en-us/library/aa346817.aspx
Im not sure what I set for the buffer and the stride every example I find it shows the stride as 0 although that throws an error. When I set the stride to 5 the image appear black. I know this may not be the most efficient code but any help would be appreciated.
//create bitmap header
bmi = new BITMAPINFOHEADER();
//create initial rectangle
Int32Rect rect = new Int32Rect(0, 0, 0, 0);
//create duplicate intptr to use while in global lock
dibhand = dibhandp;
bmpptr = GlobalLock(dibhand);
//get the pixel sizes
pixptr = GetPixelInfo(bmpptr);
//create writeable bitmap
var wbitm = new WriteableBitmap(bmprect.Width, bmprect.Height, 96.0, 96.0, System.Windows.Media.PixelFormats.Bgr32, null);
//draw the image
wbitm.WritePixels(rect, dibhandp, 10, 0);
//convert the writeable bitmap to bitmap
var stream = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(wbitm));
encoder.Save(stream);
byte[] buffer = stream.GetBuffer();
var bitmap = new System.Drawing.Bitmap(new MemoryStream(buffer));
GlobalUnlock(dibhand);
GlobalFree(dibhand);
GlobalFree(dibhandp);
GlobalFree(bmpptr);
dibhand = IntPtr.Zero;
return bitmap;
An efficient way to work on Bitmaps in C# is to pass temporarily in unsafe mode (I know I don't answer the question exactly but I think the OP did not manage to use Bitmap, so this could be a solution anyway). You just have to lock bits and you're done:
unsafe private void GaussianFilter()
{
// Working images
using (Bitmap newImage = new Bitmap(width, height))
{
// Lock bits for performance reason
BitmapData newImageData = newImage.LockBits(new Rectangle(0, 0, newImage.Width,
newImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte* pointer = (byte*)newImageData.Scan0;
int offset = newImageData.Stride - newImageData.Width * 4;
// Compute gaussian filter on temp image
for (int j = 0; j < InputData.Height - 1; ++j)
{
for (int 0 = 1; i < InputData.Width - 1; ++i)
{
// You browse 4 bytes per 4 bytes
// The 4 bytes are: B G R A
byte blue = pointer[0];
byte green = pointer[1];
byte red = pointer[2];
byte alpha = pointer[3];
// Your business here by setting pointer[i] = ...
// If you don't use alpha don't forget to set it to 255 else your whole image will be black !!
// Go to next pixels
pointer += 4;
}
// Go to next line: do not forget pixel at last and first column
pointer += offset;
}
// Unlock image
newImage.UnlockBits(newImageData);
newImage.Save("D:\temp\OCR_gray_gaussian.tif");
}
}
This is really much more efficient than SetPixel(i, j), you just have to be careful about pointer limits (and not forget to unlock data when you're done).
Now to answer your question about stride: the stride is the length in bytes of a line, it is a multiple of 4. In my exemple I use the format Format32bppArgb which uses 4 bytes per pixel (R, G, B and alpha), so newImageData.Stride and newImageData.Width * 4 are always the same. I use the offset in my loops only to show where it would be necessary.
But if you use another format, for instance Format24bppRgb which uses 3 bytes per pixel (R, G and B only), then there may be an offset between stride and width. For an image 10 * 10 pixels in this format, you will have a stride of 10 * 3 = 30, + 2 to reach nearest multiple of 4, i.e. 32.