I have some c# code that works fine on Vista & Windows 7 but throws a GDI+ Error on Windows XP (with service pack 3 installed).
Error thrown on XP
System.Runtime.INteropServices.ExternalException(0x80004005): A generic error occured in GDI+
at System.Drawing.Graphics.CheckErrorStatus(Int32Status)
at System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y)
At System.Drawing.Graphics.DrawImageUnscaled(Image image, Int32 x,Int 32 y)
at mysolution.Core.ImageTools.ConvertToBitonal(Bitmap orginal, Int32 threshold)
Code breaks on this line:
using (var g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0); // Error Is Thrown Here
}
WpFAppTestingConvertToBitonalCS
....
Below is the full function I'm calling.
public static Bitmap ConvertToBitonal(Bitmap original, int threshold)
{
Bitmap source;
// If original bitmap is not already in 32 BPP, ARGB format, then convert
if (original.PixelFormat != PixelFormat.Format32bppArgb)
{
source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (var g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0);
}
}
else
{
source = original;
}
// Lock source bitmap in memory
var sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
// Copy image data to binary array
var imageSize = sourceData.Stride * sourceData.Height;
var sourceBuffer = new byte[imageSize];
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);
// Unlock source bitmap
source.UnlockBits(sourceData);
// Create destination bitmap
var destination = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);
destination.SetResolution(original.HorizontalResolution, original.VerticalResolution);
// Lock destination bitmap in memory
var destinationData = destination.LockBits(new Rectangle(0, 0, destination.Width, destination.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
// Create destination buffer
imageSize = destinationData.Stride * destinationData.Height;
var destinationBuffer = new byte[imageSize];
var sourceIndex = 0;
var destinationIndex = 0;
var pixelTotal = 0;
byte destinationValue = 0;
var pixelValue = 128;
var height = source.Height;
var width = source.Width;
// Iterate lines
for (var y = 0; y < height; y++)
{
sourceIndex = y * sourceData.Stride;
destinationIndex = y * destinationData.Stride;
destinationValue = 0;
pixelValue = 128;
// Iterate pixels
for (var x = 0; x < width; x++)
{
// Compute pixel brightness (i.e. total of Red, Green, and Blue values) - Thanks murx
// B G R
pixelTotal = sourceBuffer[sourceIndex] + sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2];
if (pixelTotal > threshold)
{
destinationValue += (byte)pixelValue;
}
if (pixelValue == 1)
{
destinationBuffer[destinationIndex] = destinationValue;
destinationIndex++;
destinationValue = 0;
pixelValue = 128;
}
else
{
pixelValue >>= 1;
}
sourceIndex += 4;
}
if (pixelValue != 128)
{
destinationBuffer[destinationIndex] = destinationValue;
}
}
// Copy binary image data to destination bitmap
Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);
// Unlock destination bitmap
destination.UnlockBits(destinationData);
// Dispose of source if not originally supplied bitmap
if (source != original)
{
source.Dispose();
}
// Return
return destination;
}
I hate GDI+ exception handling, the only error it fires is A generic error occured in GDI+, Try this
Bitmap source = new Bitmap(0, 0);
using (Graphics g = Graphics.FromImage(source))
{ g.Clear(Color.White);
g.Graphics.DrawImageUnscaled(your original source,0,0);
}
Related
I am making a video recorder, The app works by taking a lot of screenshots and putting them together into one video. Also, I am trying to make something like screen motion detection. I need the app to take screenshots only when a difference in the screen is detected. I was thinking about how to do that, and I believe I need to make it still take screenshots while comparing them to the previous one. Is there a way to do that?
The code:
//Record video:
public void RecordVideo()
{
//Keep track of time:
watch.Start();
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
//Add screen to bitmap:
g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
}
//Save screenshot:
string name = tempPath + "//screenshot-" + fileCount + ".png";
bitmap.Save(name, ImageFormat.Png);
inputImageSequence.Add(name);
fileCount++;
//Dispose of bitmap:
bitmap.Dispose();
}
}
I have something that may be useful for you. The idea is save only the differences between the images and, with that, recreate later all images from starting image and saved changes.
To do this, you only need make a XOR operation in the image bytes. This method allow you get the difference (the array parameter) between two images:
protected void ApplyXor(Bitmap img1, Bitmap img2, byte[] array)
{
const ImageLockMode rw = ImageLockMode.ReadWrite;
const PixelFormat argb = PixelFormat.Format32bppArgb;
var locked1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), rw, argb);
var locked2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), rw, argb);
try
{
ApplyXor(locked2, locked1, array);
}
finally
{
img1.UnlockBits(locked1);
img2.UnlockBits(locked2);
}
}
With the previous img1 bitmap and the array returned, you can get the img2 with this method:
protected void ApplyXor(Bitmap img1, byte[] array, Bitmap img2)
{
const ImageLockMode rw = ImageLockMode.ReadWrite;
const PixelFormat argb = PixelFormat.Format32bppArgb;
var locked1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), rw, argb);
var locked2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), rw, argb);
try
{
ApplyXor(locked1, array, locked2);
}
finally
{
img1.UnlockBits(locked1);
img2.UnlockBits(locked2);
}
}
And here the other required methods:
private unsafe void ApplyXor(BitmapData img1, BitmapData img2, byte[] array)
{
byte* prev0 = (byte*)img1.Scan0.ToPointer();
byte* cur0 = (byte*)img2.Scan0.ToPointer();
int height = img1.Height;
int width = img1.Width;
int halfwidth = width / 2;
fixed (byte* target = array)
{
ulong* dst = (ulong*)target;
for (int y = 0; y < height; ++y)
{
ulong* prevRow = (ulong*)(prev0 + img1.Stride * y);
ulong* curRow = (ulong*)(cur0 + img2.Stride * y);
for (int x = 0; x < halfwidth; ++x)
{
if (curRow[x] != prevRow[x])
{
int a = 0;
}
*(dst++) = curRow[x] ^ prevRow[x];
}
}
}
}
private unsafe void ApplyXor(BitmapData img1, byte[] array, BitmapData img2)
{
byte* prev0 = (byte*)img1.Scan0.ToPointer();
byte* cur0 = (byte*)img2.Scan0.ToPointer();
int height = img1.Height;
int width = img1.Width;
int halfwidth = width / 2;
fixed (byte* target = array)
{
ulong* dst = (ulong*)target;
for (int y = 0; y < height; ++y)
{
ulong* prevRow = (ulong*)(prev0 + img1.Stride * y);
ulong* curRow = (ulong*)(cur0 + img2.Stride * y);
for (int x = 0; x < halfwidth; ++x)
{
curRow[x] = *(dst++) ^ prevRow[x];
}
}
}
}
NOTE: You must configure your project to allow unsafe.
With previous methods, you can do:
Save a img1 bitmap
Get img2 bitmap, do XOR and get the array (array2, for example)
With img3, get the XOR with img2 (array3, for example). Now, img2 isn't needed
With img4, get the XOR with img3 (array4). Now, img3 isn't needed
...
You have img1 and array2, array3, array4... and you can recreate all images:
Make XOR between img1 and array2 to get img2
Make XOR between img2 and array3 to get img3
...
If you need send video over TCP, you can send the images sending one image and the XOR arrays (the differences). Or better yet, compress the XOR arrays using K4os.Compression.LZ4.
I'm trying to convert a Matlab MWNumericArray to a black and white picture (not grayscale). Initially I tried to transform the 2D byte array to a 1D one, then copy that to a BitmapData object and transform it in a Bitmap, but did not work, because I must be doing several mistakes trying to manipulate this data.
The method that works is a mix of code I got from other answers and online documentation. Below is what works and what does not work.
Question:
Is there a simple way to convert a 8bpp bitmap to 1bpp, one shot or byte by byte, starting from a byte[,] array and without using GetPixel or Clone methods? Unsafe code is allowed.
What I tried (works):
// Most of this code is based on a Jon Skeet's answer.
// resultOut[0] is a 2D 356x356 MWArray
var numArray = (MWNumericArray)resultOut[0];
var data = numArray.ToArray(MWArrayComponent.Real) as byte[,];
var w = data.GetLength(0);
var h = data.GetLength(1);
unsafe
{
fixed (byte* ptr = data)
{
var scan0 = new IntPtr(ptr);
var bpm1 = new Bitmap(
w, h, // image size
w, // scan size
PixelFormat.Format8bppIndexed, scan0);
// works but is grayscale, not B&W
var palette = bpm1.Palette;
palette.Entries[0] = Color.Black;
for (var i = 1; i < 256; i++)
{
palette.Entries[i] = Color.FromArgb((i * 7) % 256, (i * 7) % 256, 255);
}
bpm1.Palette = palette;
bpm1.Save("output.grayscale.png");
// works but with additional helper method call
// based on a Hans Passant answer
var bpm2 = BitmapTo1Bpp(bpm1);
bpm2.Save("output.helper.png");
// works but uses Clone
var bpm3 = bpm1.Clone(new Rectangle(0, 0, bpm1.Width, bpm1.Height), PixelFormat.Format1bppIndexed);
bpm3.Save("output.clone.png");
}
}
What I tried (did not work):
if (numArray.ToArray(MWArrayComponent.Real) is byte[,] data)
{
// Convert to 1D byte array
var h = data.GetLength(1);
var w = data.GetLength(0);
var byteArray1D = new byte[h * w];
for (var i = 0; i < h; i++)
{
for (var j = 0; j < w; j++)
{
byteArray1D[i + j] = data[i, j];
}
}
// Create Bitmap object
var bmp = new Bitmap(w, h, PixelFormat.Format8bppIndexed);
//// Set palette
// var cp = bmp.Palette;
// for (var i = 0; i < 256; ++i)
// {
// cp.Entries[i] = Color.FromArgb(255, i, i, i);
// }
// bmp.Palette = cp;
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
Marshal.Copy(byteArray1D,
0,
bmpData.Scan0,
byteArray1D.Length);
bmp.UnlockBits(bmpData);
// Does not work: graphic object cannot be created from an image that has an indexed pixel format
using (var g = Graphics.FromImage(bmp))
{
g.Clear(Color.White);
}
// Does not work: saves all black image
bmp.Save("output.raw.png");
using (var ms = new MemoryStream())
{
ms.Write(byteArray1D, 0, byteArray1D.Length);
ms.Seek(0, SeekOrigin.Begin); // ms.Position = 0;
// Does not work: parameter is not valid exception
var bm = new Bitmap(ms);
bm.Save("output.ms.png");
// Does not work: parameter is not valid exception
var newImage = Image.FromStream(ms);
newImage.Save("output.fromstream.png");
}
}
I am trying to generate 16bit grayscale Bitmap in C# from a random data.But it crashed on Marshal.Copy.
Here is my code:
Bitmap b16bpp;
private void GenerateDummy16bitImage()
{
b16bpp = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
var rect = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * IMAGE_HEIGHT * 2;
var bitmapBytes = new short[numberOfBytes];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < IMAGE_WIDTH; x++)
{
for (int y = 0; y < IMAGE_HEIGHT; y++)
{
var i = ((y * IMAGE_WIDTH) + x) * 2; // 16bpp
// Generate the next random pixel color value.
var value = (short)random.Next(5);
bitmapBytes[i] = value; // BLUE
bitmapBytes[i + 1] = value; // GREEN
bitmapBytes[i + 2] = value; // RED
// bitmapBytes[i + 3] = 0xFF; // ALPHA
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, numberOfBytes);//crashes here
// Unlock the bitmap, we're all done.
b16bpp.UnlockBits(bitmapData);
b16bpp.Save("random.bmp", ImageFormat.Bmp);
Debug.WriteLine("saved");
}
The exception is:
An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
This is not my code.I found it in relation to 32bit Bitmaps and modified.But I guess I have missed something as I am pretty new to C#.
Basically,all I need is to wrap into BitmapData an arrays of shorts.
This works for System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
private static void SaveBmp(Bitmap bmp, string path)
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
var pixelFormats = ConvertBmpPixelFormat(bmp.PixelFormat);
BitmapSource source = BitmapSource.Create(bmp.Width,
bmp.Height,
bmp.HorizontalResolution,
bmp.VerticalResolution,
pixelFormats,
null,
bitmapData.Scan0,
bitmapData.Stride * bmp.Height,
bitmapData.Stride);
bmp.UnlockBits(bitmapData);
FileStream stream = new FileStream(path, FileMode.Create);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
stream.Close();
}
private static System.Windows.Media.PixelFormat ConvertBmpPixelFormat(System.Drawing.Imaging.PixelFormat pixelformat)
{
System.Windows.Media.PixelFormat pixelFormats = System.Windows.Media.PixelFormats.Default;
switch (pixelformat)
{
case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
pixelFormats = PixelFormats.Bgr32;
break;
case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
pixelFormats = PixelFormats.Gray8;
break;
case System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
pixelFormats = PixelFormats.Gray16;
break;
}
return pixelFormats;
}
I have corrected some of your mistakes (mostly wrong sizes). But it will still crash on b16bpp.Save(), because GDI+ does not support saving 16bit grayscale images.
Bitmap b16bpp;
private void GenerateDummy16bitImage()
{
b16bpp = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
var rect = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * IMAGE_HEIGHT;
var bitmapBytes = new short[IMAGE_WIDTH * IMAGE_HEIGHT];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < IMAGE_WIDTH; x++)
{
for (int y = 0; y < IMAGE_HEIGHT; y++)
{
var i = ((y * IMAGE_WIDTH) + x); // 16bpp
// Generate the next random pixel color value.
var value = (short)random.Next(5);
bitmapBytes[i] = value; // GRAY
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, bitmapBytes.Length);
// Unlock the bitmap, we're all done.
b16bpp.UnlockBits(bitmapData);
b16bpp.Save("random.bmp", ImageFormat.Bmp);
Debug.WriteLine("saved");
}
Explanation of my changes:
bitmapData.Stride is already IMAGE_WIDTH * BytesPerPixel so you don't need to multiply by 2
as you declared bitmapBytes as short[] it has to have the size of the image in pixels not in bytes
that means you also do not need to multiply i by 2
since you have a grayscale image it does not have a blue, green and red channel, but one single 16bit gray channel
Marshal.Copy takes the length in "array units" not in bytes
All in all you tried to copy an array 8 times to large into the bitmap.
I have to draw an image from integer values. They are stored in List<int>[].
The list has 5081 arrays with 2048 values each. Each value is between 0-1000 and one int is the color for one pixel, so it's grayscale.
I know how to do it with setpixel but this is too slow.
for (int y = 0; y < channelId.Length; y++) {
for (int x = 0; x < channelId[y].Count; x++) {
int myColor = (channelId[y].ElementAt(x) * 255) / 1000;
if (myColor > 255) {
myColor = 255;
} else if (myColor < 0) {
myColor = 0;
}
bmp.SetPixel(x, y, Color.FromArgb(myColor, myColor, myColor));
}
}
I know that there are similar questions here how to draw bitmaps faster but they already have a bitmap. I have to draw the image from my values.
If you don't want to do it unsafely, you could do this (assumes a 32bppArgb pixelformat):
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
for (var y = 0; y < channelId.Length; y++)
{
var scanLineSize = channelId[y].Count*4;
var rgb = new byte[scanLineSize];
int idx = 0;
// Convert the whole scanline
foreach (var id in channelId[y])
{
rgb[idx] = rgb[idx+1] = rgb[idx+2] = (byte)(id*255/1000);
rgb[idx+3] = 255;
idx += 4;
}
// And copy it in one pass
System.Runtime.InteropServices.Marshal.Copy(rgb, 0, ptr, scanLineSize);
ptr += bmpData.Stride;
}
bmp.UnlockBits(bmpData);
If it's not fast enough for you, you could do the whole conversion of the bitmap in-memory and copy the whole array in one pass. This would not be very memory-efficient though, and this should be "fast enough" for the amount of data you are moving.
Update
Just for fun, in one pass:
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int idx = 0;
var rgb = new byte[channelId[0].Count * 4 * channelId.Length];
foreach (var id in channelId.SelectMany(t => t))
{
rgb[idx] = rgb[idx+1] = rgb[idx+2] = (byte)(id*255/1000);
rgb[idx+3] = 255;
idx += 4;
}
System.Runtime.InteropServices.Marshal.Copy(rgb, 0, ptr, rgb.Length);
bmp.UnlockBits(bmpData);
Apart from the memory-efficiency, you'll need to make sure all List<int> in the array contain the same number of elements and that the Stride of the bitmap is the same as width*4. It should be for 2048 elements, but you never know.
BitmapData bitmapData = bmp.LockBits(
new Rectangle(0, 0, channelId[0].Count, channelId.Length),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb
);
unsafe{
ColorARGB* startingPosition = (ColorARGB*) bitmapData.Scan0;
for (int y = 0; y < channelId.Length; y++) {
for (int x = 0; x < channelId[y].Count; x++) {
int myColor = (channelId[y].ElementAt(x) * 255) / 1000;
if (myColor > 255) {
myColor = 255;
} else if (myColor < 0) {
myColor = 0;
}
ColorARGB* position = startingPosition + j + i * channelId[0].Count;
position->A = 255;
position->R = myColor;
position->G = myColor;
position->B = myColor;
}
}
bmp.UnlockBits(bitmapData);
}
I am trying to combine 3 grayscale bitmaps into one color bitmap. All three grayscale images are the same size (this is based off of data from the Hubble). My logic is:
Load "blue" image and convert to PixelFormat.Format24bppRgb. Based off of that create a new byte array that is 4 times as large as the blue data array length/3 (so it will be one byte for blue, one byte for green, one byte for red, one byte for alpha per pixel since my system is little endian). Populate the "blue" bytes of the array from the "blue" bytes of the blue image (and in this first loop set the alpha byte to 255). I then load the green and red bitmaps, convert them to PixelFormat.Format24bppRgb, and pull the g/r value and add it to the correct place in the data array. The final data array then has the bgra bytes set correctly from what I can tell.
When I have the data array populated, I have used it to:
Create a PixelFormats.Bgra32 BitmapSource then convert that to a Bitmap.
Create a PixelFormat.Format32bppArgb Bitmap using the Bitmap constructor (width, height, stride, PixelForma, IntPtr)
Create a PixelFormat.Format32bppArgb Bitmap using pointers
All three ways of creating a return bitmap result in the image being "skewed" (sorry, I don't know of a better word).
The actual output (of all three ways of generating the final bitmap) is: Actual output
The desired output is something like (this was done in photoshop so it is slightly different): Desired output
The three file names (_blueFileName, _greenFileName, _redFileName) are set in the constructor and I check to make sure the files exist before creating the class. I can post that code if anyone wants it.
Can anyone tell me what I am doing wrong? I am guessing that is is due to the stride or something like that?
Note: I can't post the links to the images I am using as input as I don't have 10 reputation points. Maybe I could send the links via email or something if someone wants them as well.
Here is my code (with some stuff commented out, the comments describe what happens if each commented out block is used instead):
public Bitmap Merge()
{
// Load original "blue" bitmap.
Bitmap tblueBitmap = (Bitmap)Image.FromFile(_blueFileName);
int width = tblueBitmap.Width;
int height = tblueBitmap.Height;
// Convert to 24 bpp rgb (which is bgr on little endian machines)
Bitmap blueBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(blueBitmap))
{
gr.DrawImage(tblueBitmap, 0, 0, width, height);
}
tblueBitmap.Dispose();
// Lock and copy to byte array.
BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly,
blueBitmap.PixelFormat);
int numbBytes = blueData.Stride*blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
Marshal.Copy(blueData.Scan0, blueBytes, 0, numbBytes);
blueBitmap.UnlockBits(blueData);
blueData = null;
blueBitmap.Dispose();
int mult = 4;
byte[] data = new byte[(numbBytes/3)*mult];
int count = 0;
// Copy every third byte starting at 0 to the final data array (data).
for (int i = 0; i < data.Length / mult; i++)
{
// Check for overflow
if (blueBytes.Length <= count*3 + 2)
{
continue;
}
// First pass, set Alpha channel.
data[i * mult + 3] = 255;
// Set blue byte.
data[i*mult] = blueBytes[count*3];
count++;
}
// Cleanup.
blueBytes = null;
int generation = GC.GetGeneration(this);
GC.Collect(generation);
Bitmap tgreenBitmap = (Bitmap)Image.FromFile(_greenFileName);
Bitmap greenBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(greenBitmap))
{
gr.DrawImage(tgreenBitmap, 0, 0, width, height);
}
tgreenBitmap.Dispose();
BitmapData greenData = greenBitmap.LockBits(new Rectangle(0, 0, greenBitmap.Width, greenBitmap.Height), ImageLockMode.ReadOnly,
greenBitmap.PixelFormat);
numbBytes = greenData.Stride * greenBitmap.Height;
byte[] greenBytes = new byte[numbBytes];
Marshal.Copy(greenData.Scan0, greenBytes, 0, numbBytes);
greenBitmap.UnlockBits(greenData);
greenData = null;
greenBitmap.Dispose();
count = 0;
for (int i = 0; i < data.Length / mult; i++)
{
if (greenBytes.Length <= count * 3 + 1)
{
continue;
}
// Set green byte
data[i * mult + 1] = greenBytes[count * 3 + 1];
count++;
}
greenBytes = null;
generation = GC.GetGeneration(this);
GC.Collect(generation);
Bitmap tredBitmap = (Bitmap)Image.FromFile(_redFileName);
Bitmap redBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(redBitmap))
{
gr.DrawImage(tredBitmap, 0, 0, width, height);
}
tredBitmap.Dispose();
BitmapData redData = redBitmap.LockBits(new Rectangle(0, 0, redBitmap.Width, redBitmap.Height), ImageLockMode.ReadOnly,
redBitmap.PixelFormat);
numbBytes = redData.Stride * redBitmap.Height;
byte[] redBytes = new byte[numbBytes];
Marshal.Copy(redData.Scan0, redBytes, 0, numbBytes);
redBitmap.UnlockBits(redData);
redData = null;
redBitmap.Dispose();
count = 0;
for (int i = 0; i < data.Length / mult; i++)
{
if (redBytes.Length <= count * 3+2)
{
count++;
continue;
}
// set red byte
data[i * mult + 2] = redBytes[count * 3 + 2];
count++;
}
redBytes = null;
generation = GC.GetGeneration(this);
GC.Collect(generation);
int stride = (width*32 + 7)/8;
var bi = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, data, stride);
// uncomment out below to see what a bitmap source to bitmap does. So far, it is exactly the same as
// the uncommented out lines below.
// ---------------------------------------------------------------------------------------------------
//return BitmapImage2Bitmap(bi);
unsafe
{
fixed (byte* p = data)
{
IntPtr ptr = (IntPtr)p;
// Trying the commented out lines returns the same bitmap as the uncommented out lines.
// ------------------------------------------------------------------------------------
byte* p2 = (byte*)ptr;
Bitmap retBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
BitmapData fData = retBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);
unsafe
{
for (int i = 0; i < fData.Height; i++)
{
byte* imgPtr = (byte*)(fData.Scan0 + (fData.Stride * i));
for (int x = 0; x < fData.Width; x++)
{
for (int ii = 0; ii < 4; ii++)
{
*imgPtr++ = *p2++;
}
//*imgPtr++ = 255;
}
}
}
retBitmap.UnlockBits(fData);
//Bitmap retBitmap = new Bitmap(width, height, GetStride(width, PixelFormat.Format32bppArgb),
// PixelFormat.Format32bppArgb, ptr);
return retBitmap;
}
}
}
private Bitmap BitmapImage2Bitmap(BitmapSource bitmapSrc)
{
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSrc));
enc.Save(outStream);
Bitmap bitmap = new Bitmap(outStream);
return new Bitmap(bitmap);
}
}
private int GetStride(int width, PixelFormat pxFormat)
{
int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
int validBitsPerLine = width * bitsPerPixel;
int stride = ((validBitsPerLine + 31) / 32) * 4;
return stride;
}
You are missing the gap between the lines. The Stride value is not the amount of data in a line, it's the distance between the start of one line to the next. There may be a gap at the end of each line to align the next line on an even address boundary.
The Stride value can even be negative, then the image is stored upside down in memory. To get the data without the gaps and to handle all cases you need to copy one line at a time:
BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly, blueBitmap.PixelFormat);
int lineBytes = blueBitmap.Width * 3;
int numbBytes = lineBytes * blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
for (int y = 0; y < blueBitmap.Height; y++) {
Marshal.Copy(blueData.Scan0 + y * blueData.Stride, blueBytes, y * lineBytes, lineBytes);
}
blueBitmap.UnlockBits(blueData);
blueBitmap.Dispose();