I need to generate a grey scale bitmap from the alpha channel of a System.Drawing.Bitmap.
I tried using GetPixel and SetPixel but this does not work with bitmaps with a PixelFormat of Format16bppGrayScale.
For example, setting all the pixels in my greyscale image to black. SetPixel throws an exception.
var bitmap = new Bitmap(16, 16, PixelFormat.Format16bppGrayScale);
for (int x = 0; x < bitmap.Width; x++)
{
for (int y = 0; y < bitmap.Height; y++)
{
bitmap.SetPixel(x,y, Color.Black);
}
}
You can use BitmapData bitmapDataIn = bitmap.LockBits(... )
Then use use byte* pDataIn = (byte*)bitmapDataIn.Scan0; to get a pointer to raw bitmap data.
This is 32-bit bitmap data, though
pDataIn[y * iStrideSize + x * 4 + 0] //Blue
pDataIn[y * iStrideSize + x * 4 + 1] //Greed
pDataIn[y * iStrideSize + x * 4 + 2] //Red
pDataIn[y * iStrideSize + x * 4 + 3] //Alpha
You can modify or read pixel values with the pointer
This is copy-paste of my code,
unsafe
{
byte* pDataIn = (byte*)bitmapDataIn.Scan0;
int iStrideSize = bitmapDataIn.Stride; //one row size in bytes, iWidth * 4
int y, x;
byte B, G, R, A;
for (y = 0; y < iHeight; y++)
{
for (x = 0; x < iWidth; x++)
{
B = pDataIn[y * iStrideSize + x * 4 + 0];
G = pDataIn[y * iStrideSize + x * 4 + 1];
R = pDataIn[y * iStrideSize + x * 4 + 2];
A = pDataIn[y * iStrideSize + x * 4 + 3];
}
}
bitmap.UnlockBits(bitmapDataIn);
}
Related
I'm trying to copy an image to the windows clipboard but windows keeps removing my alpha. This is may latest attempt where i manually create the buffer and copy the BGRA values to the buffer, however still when i read the values later alpha is set to 255. If i copy my image from Paint.NET I get an InteropBitmap with the exact same values but alpha is set correct, so it should be possible. If i save my image to a PNG-file instead of the clipboard alpha is also correct.
var buffer = new byte[width * height * 4];
for (var x = 0; x < width; x++)
{
for (var y = 0; y < height; y++)
{
var pixel = surface[x, y];
var i = (y * width + x) * 4;
buffer[i + 0] = pixel.B;
buffer[i + 1] = pixel.G;
buffer[i + 2] = pixel.R;
buffer[i + 3] = pixel.A;
}
}
Clipboard.Clear();
Clipboard.SetImage(InteropBitmap.Create(
bounds.Width, bounds.Height,
96, 96,
PixelFormats.Bgra32,
null,
buffer,
width * 4));
I'm working on a simple drawing application where I can 'paint' on top of an existing image. I've made a little headway, but I've noticed a weird issue with the alpha channel of the bitmap I'm displaying that doesn't seem quite right. My draw brush function looks like this:
public unsafe void BitmapDrawBrush(double _x, double _y, double _radius, double _falloff, double _strength)
{
if (DisplayBmp == null)
{
DisplayBmp = new Bitmap(ImageWidth, ImageHeight, PixelFormat.Format32bppArgb);
}
const int pixelSize = 4; // 32 bits per pixel
Bitmap target = new Bitmap(DisplayBmp.Width, DisplayBmp.Height, PixelFormat.Format32bppArgb);
BitmapData sourceData = null, targetData = null;
try
{
sourceData = DisplayBmp.LockBits(new Rectangle(0, 0, DisplayBmp.Width, DisplayBmp.Height),ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
targetData = target.LockBits(new Rectangle(0, 0, target.Width, target.Height),ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
for (int y = 0; y < DisplayBmp.Height; ++y)
{
byte* sourceRow = (byte*)sourceData.Scan0 + (y * sourceData.Stride);
byte* targetRow = (byte*)targetData.Scan0 + (y * targetData.Stride);
for (int x = 0; x < DisplayBmp.Width; ++x)
{
byte b = sourceRow[x * pixelSize + 0];
byte g = sourceRow[x * pixelSize + 1];
byte r = sourceRow[x * pixelSize + 2];
byte a = sourceRow[x * pixelSize + 3];
double nx = x / (double)ImageWidth;
double ny = y/(double)ImageHeight;
double xDist = nx - _x;
double yDist = ny - _y;
if ((xDist * xDist) + (yDist * yDist) <= (_radius * _radius))
{
double pxDist = 1.0 - (((xDist * xDist) + (yDist * yDist)) / (_radius * _radius));
r = (byte)(255 * pxDist);
g = (byte)(255 * pxDist);
b = (byte)(255 * pxDist);
a = (byte)(255 * pxDist * _strength); // <-the alpha channel value
}
targetRow[x * pixelSize + 0] = b;
targetRow[x * pixelSize + 1] = g;
targetRow[x * pixelSize + 2] = r;
targetRow[x * pixelSize + 3] = a;
}
}
}
finally
{
if (sourceData != null)
DisplayBmp.UnlockBits(sourceData);
if (targetData != null)
target.UnlockBits(targetData);
}
DisplayBmp = target;
UpdateBitmap();
}
Yet, when I run my application and place a few brush strokes (actually the draw brush function is only called on mouse down at the moment so I'm only drawing dots really), you see that the alpha channel doesn't seem to be fading toward the edge. What I would expect is that as the 'dot' gets more black, so too does the alpha channel diminish. See below. Any ideas as to why this is happening?
I'm having a problem with writing to files using lock bits. I'm working on an edge detection software which has a strange distortion effect with most images. I've tried to isolate the problem, and it seems very random. It is not associated with format, but rather the only images that seem to work are pictures made for desktop wallpapers, and I don't really know why. I only switched to writing to files using lockbits recently, so I am sure the problem is with that (there were no problems when I was reading with lockbits and writing with set pixel). Here's a screenshot of the effect:
As you can see, the edge detection works, but the image is distorted horizontally, making the image into a parallelogram.
Here's a code snippet of the method that handles all this (in C#):
private void analyze()
{
//When the analyze button is pressed
percentageInt = float.Parse(textBox1.Text);
float scale = 1;
if (comboBox1.SelectedItem == "Auto")
{
scale = pic.Width / pictureBox1.Width;
}
else if (comboBox1.SelectedItem == "1/2")
{
scale = 2;
}
else if (comboBox1.SelectedItem == "1/4")
{
scale = 4;
}
else if (comboBox1.SelectedItem == "Original")
{
scale = 1;
}
else
{
scale = pic.Width / pictureBox1.Width;
}
int tempWidth = 1;
int tempHeight = 1;
if (scale >= 1)
{
tempWidth = (int)Math.Floor(pic.Width / scale);
tempHeight = (int)Math.Floor(pic.Height / scale);
}
else
{
tempWidth = pic.Width;
tempHeight = pic.Height;
}
width = pic.Width;
height = pic.Height;
edgeData = new Boolean[pic.Width, pic.Height];
img = (Bitmap)resizeImage(pic, new Size(tempWidth, tempHeight));
pic2 = new Bitmap(tempWidth, tempHeight);
Bitmap img2 = (Bitmap)pic2;
Color[] pixels = null;
BitmapData data = img.LockBits(new Rectangle(0, 0, img.Width, img.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int size = Math.Abs(data.Stride) * img.Height;
Byte[] bytes = new byte[size];
int scaledPercent = (int)(Math.Round(percentageInt * 255));
Debug.WriteLine("percent " + scaledPercent);
unsafe
{
Debug.WriteLine("Woah there, unsafe stuff");
byte* prevLine = (byte*)data.Scan0;
byte* currLine = prevLine + data.Stride;
byte* nextLine = currLine + data.Stride;
for (int y = 1; y < img.Height - 1; y++)
{
byte* pp = prevLine + 3;
byte* cp = currLine + 3;
byte* np = nextLine + 3;
for (int x = 1; x < img.Width - 1; x++)
{
if (IsEdgeOptimized(pp, cp, np, scaledPercent))
{
edgeData[x, y] = true;
//Debug.WriteLine("x " + x + "y " + y);
//img2.SetPixel(x, y, Color.Black);
//bytes[(y * img.Width + x) * 3 + 2] = 255;
}
else
{
bytes[(y * img.Width + x) * 3] = 255;
bytes[(y * img.Width + x) * 3 + 1] = 255;
bytes[(y * img.Width + x) * 3 + 2] = 255;
//img2.SetPixel(x, y, Color.White);
}
pp += 3; cp += 3; np += 3;
}
prevLine = currLine;
currLine = nextLine;
nextLine += data.Stride;
}
}
System.Runtime.InteropServices.Marshal.Copy(bytes, 0, data.Scan0, size);
img.UnlockBits(data);
pictureBox2.Image = img;
} // end analyze
So what is causing the problem, and how can I fix it? If you need more details, feel free to comment.
You're initializing your bytes buffer with stride x height bytes:
int size = Math.Abs(data.Stride) * img.Height;
Byte[] bytes = new byte[size];
But then using the width (instead of stride) when you write to it:
bytes[(y * img.Width + x) * 3] = 255;
I'm trying to add texture coordinates to each of the vertices so that a grass texture is added to each triangle. The code I have stretches the texture across the entire area which works but doesn't scale up very well. How do I correctly add (0,0), (0,1), (1,1), etc to the vertices?
Currently they're added in the SetUpVertices() method, should they be added in the SetUpIndices() method when the code can distinguish whether it's top left, bottom left, bottom right, etc. Any help would be greatly appreciated. The relevant methods are below and the full Game1.cs code is here http://pastebin.com/REd8QDZA
private void SetUpVertices()
{
vertices = new VertexPositionNormalTexture[terrainWidth * terrainHeight];
for (int x = 0; x < terrainWidth; x++)
{
for (int y = 0; y < terrainHeight; y++)
{
vertices[x + y * terrainWidth].Position = new Vector3(x, -y, heightData[x, y]);
vertices[x + y * terrainWidth].TextureCoordinate.X = x / (terrainWidth - 1.0);
vertices[x + y * terrainWidth].TextureCoordinate.Y = y / (terrainHeight - 1.0);
}
}
}
private void SetUpIndices()
{
indices = new short[(terrainWidth - 1) * (terrainHeight - 1) * 6];
int counter = 0;
for (int y = 0; y < terrainHeight - 1; y++)
{
for (int x = 0; x < terrainWidth - 1; x++)
{
int lowerLeft = x + y * terrainWidth;
int lowerRight = (x + 1) + y * terrainWidth;
int topLeft = x + (y + 1) * terrainWidth;
int topRight = (x + 1) + (y + 1) * terrainWidth;
indices[counter++] = (short)topLeft;
indices[counter++] = (short)lowerRight;
indices[counter++] = (short)lowerLeft;
indices[counter++] = (short)topLeft;
indices[counter++] = (short)topRight;
indices[counter++] = (short)lowerRight;
}
}
}
Just specify
vertices[x + y * terrainWidth].TextureCoordinate.X = x;
vertices[x + y * terrainWidth].TextureCoordinate.Y = y;
By default, texture coordinates greater than 1 will be wrapped and the texture is repeated.
Hello I have heightData in memory and sometimes (when I edit it) I wanna save it to jpg. This is my code:
float multi = 0.2f;
float[,] heightData = quadTree.HeightData;
Color[] heightMapColors = new Color[heightData.Length];
for (int x = 0; x < heightData.GetLength(0); x++)
{
for (int y = 0; y < heightData.GetLength(1); y++)
{
byte colorData = (byte)(heightData[x, y] / multi);
heightMapColors[x + y * heightData.GetLength(0)].R = colorData;
heightMapColors[x + y * heightData.GetLength(0)].G = colorData;
heightMapColors[x + y * heightData.GetLength(0)].B = colorData;
}
}
Texture2D heightMap = new Texture2D(device, heightData.GetLength(0), heightData.GetLength(1), false, SurfaceFormat.Color);
heightMap.SetData<Color>(heightMapColors);
using (System.IO.Stream stream = System.IO.File.OpenWrite(#"D:\test.jpg"))
{
heightMap.SaveAsJpeg(stream, heightData.GetLength(0), heightData.GetLength(1));
}
I am 100% sure that I have data in heightMapColors, but saved jpg is only black. :/ Is it a good way how to do it or something is wrong?
Alpha should not be zero
heightMapColors[x + y * heightData.GetLength(0)].R = colorData;
heightMapColors[x + y * heightData.GetLength(0)].G = colorData;
heightMapColors[x + y * heightData.GetLength(0)].B = colorData;
heightMapColors[x + y * heightData.GetLength(0)].A = 255;
A JPG is probably not a good format to store a heightmap into because it is a lossy format. You should be putting it into a BMP pr PNG. That said, what is the range of your "height"? It looks like your height is a float which means it is probably not in the right range to be displayed or even converted to discrete values.
If your allowed height range is Xf to Yf, convert that to a 0 - 255 range using
byteValue = (byte)(((OldValue - OldMin) * (255 - 0)) / (OldMax - OldMin)) + 0
and then give it a shot.