IndexOutOfRangeException where there is no chance to get it - c#

I'm writing a game in XNA, created simple method to get subImages from textures, but everytime I use it, it throws an exception. I checked the variables and there is no chance to get out of bounds. Code for this two methods below:
public Color[] GetSubImage(Color[] colorData, int width, Rectangle rec)
{
Color[] color = new Color[rec.Width * rec.Height];
for (int x = 0; x < rec.Width; x++)
{
for (int y = 0; y < rec.Height; y++)
{
color[x + y * rec.Width] = colorData[x + rec.X + (y + rec.Y) * width]; // Exception is thrown there
}
}
return color;
}
public void LoadSubImages(Texture2D sourceSpritesheet, List<Texture2D[]> destinationSprites)
{
int count = 0;
Color[] imageData = new Color[sourceSpritesheet.Width * sourceSpritesheet.Height];
Texture2D subImage;
Rectangle sourceRec;
destinationSprites = new List<Texture2D[]>();
for (int i = 0; i < this.NUMFRAMES.Length; i++)
{
Texture2D[] bi = new Texture2D[this.NUMFRAMES[i]];
for (int j = 0; j < this.NUMFRAMES[i]; j++)
{
sourceRec = new Rectangle(j * this.FRAMEWIDTHS[i], count, this.FRAMEWIDTHS[i], this.FRAMEHEIGHTS[i]);
Color[] imagePiece = this.GetSubImage(imageData, sourceSpritesheet.Width, sourceRec);
subImage = new Texture2D(Game1.Instance.GraphicsDevice, sourceRec.Width, sourceRec.Height);
subImage.SetData<Color>(imagePiece);
bi[j] = subImage;
}
destinationSprites.Add(bi);
count += this.FRAMEHEIGHTS[i];
}
}
sourceSpritesheet is 368*550 big, FRAMEWIDTHS = 46, FRAMEHEIGTHS = 50, NUMFRAMES.Length = 11 (with values between 1-8)
Is there something that I can't see?

colorData has indices starting from 0 to width * height. You're accessing indices starting from rec.X + rec.Y * width to (rec.X + width) + (height + rec.Y) * height. If rec.X or rec.Y is greater than 0 (which will happen, given how you construct your rectangles), this will go out of bounds. .NET Framework arrays are luckily working correctly, universe is safe...

colorData is 202,400 in size
In the worst case scenario :
colorData[x + rec.X + (y + rec.Y) * width];
x = 45
rec.x = 7*46 = 322
y = 50
rec.y = 11*50 = 550
width = 368
due to the order of operations your formula would execute like so:
x + rec.X + ((y + rec.Y) * width)
45 + 322 + ((50 + 550) * 368)
367 + (600 * 368)
221,167
and 221,167 is greater then colorData size of 202,400. So in conclusion it is most definitely possible to go out of bounds with your function. I would recommend you rewrite it as it seems to be a horrid case of spaghetti code.

Related

How do I change the colors of triangles inside a procedurally generated mesh during generating?

I'm using unity, c#, and I cannot figure out how I can generate a landscape with all different kinds of height colors. For instance, I want sand near the water and snow on mountain peaks. But I cannot manage to make this happen in code where the mesh is being generated.
Just to make clear, The mesh generates perfectly fine, but I cannot manage to change the colors of each separate triangle.
void CreateMesh()
{
planeMesh = new Mesh();
List<Vector3> vertices = new List<Vector3>();
List<Vector2> uvs = new List<Vector2>();
List<int> indices = new List<int>();
List<Color> colors = new List<Color>();
for (int x = 0; x < gridSize + 1; x++)
{
for (int y = 0; y < gridSize + 1; y++)
{
float height = Mathf.PerlinNoise(y / (float)gridSize, x / (float)gridSize) * 15.0f;
vertices.Add(new Vector3(y, height, x));
if(height > 12)//mountain tops
{
uvs.Add(new Vector2(0, 1/3));//down to up, x first
colors.Add(Color.white);
}
else if(height > 3 && height < 13)//grassy fields
{
uvs.Add(new Vector2(1/3,2/3));
colors.Add(Color.green);
}
else if(height > 0 && height < 4)//sand
{
uvs.Add(new Vector2(2/3, 2/3));
colors.Add(Color.yellow);
}
}
}
//making the indices
for (int b = 0; b < gridSize; b++)
{
for (int c = 0; c < gridSize; c++)
{
indices.Add(b + ((gridSize + 1) * c));
indices.Add(b + gridSize + 1 + ((gridSize + 1) * c));
indices.Add(b + 1 + ((gridSize + 1) * c));
indices.Add(b + gridSize + 1 + ((gridSize + 1) * c));
indices.Add(b + gridSize + 2 + ((gridSize + 1) * c));
indices.Add(b + 1 + ((gridSize + 1) * c));
}
}
planeMesh.SetVertices(vertices);
planeMesh.SetIndices(indices.ToArray(), MeshTopology.Triangles, 0);
planeMesh.SetUVs(0, uvs);
planeMesh.RecalculateNormals();
planeMesh.colors = colors.ToArray();
GetComponent<MeshFilter>().mesh = planeMesh;
GetComponent<MeshFilter>().mesh.name = "Environment";
}
Could you guys help me with that?
Solved it! It seems the numbers I was using for the uvs were being rounded down to 0! I had to make them floats to use them properly.

Why still not coloring the pixels in acolor using LockBits?

I'm doing a loop over a List pointtocolor contain over 50000 pixels coordinates then i'm trying to color them on the Bitmap bmpBackClouds.
First time i tried with the line:
if ((int)p + (int)(x + y) < bD.Stride * bD.Height)
But then it never step in and never did the p[1] = p[2] = (byte)255; lines.
So now i'm not using this IF and it's doing all the lines.
But in the end i'm getting the same Bitmap as original as it was nothing colored.
float x, y;
bD = bmpBackClouds.LockBits(
new System.Drawing.Rectangle(0, 0, bmpBackClouds.Width, bmpBackClouds.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
IntPtr s0 = bD.Scan0;
unsafe
{
byte* p;
byte* pBU = (byte*)(void*)s0;
for (int i = 0; i < pointtocolor.Count; i++)
{
p = (byte*)(void*)s0;
x = pointtocolor[i].X * (float)currentFactor;
y = pointtocolor[i].Y * (float)currentFactor;
if ((int)x >= bmpBackClouds.Width || (int)y >= bmpBackClouds.Height)
{
continue;
}
x = (int)(y * (float)bD.Stride);
y = (int)(x * 4F);
p += (int)(x + y);
if ((int)p + (int)(x + y) < bD.Stride * bD.Height)
{
if (x + y > 3)
p -= (p - pBU) % 4;
p[1] = p[2] = (byte)255;
p[0] = (byte)0;
p[3] = (byte)255;
}
}
}
bmpBackClouds.UnlockBits(bD);
This is the unsafe code part:
unsafe
{
byte* p;
byte* pBU = (byte*)(void*)s0;
for (int i = 0; i < pointtocolor.Count; i++)
{
//set pointer to the beggining
p = (byte*)(void*)s0;
x = pointtocolor[i].X * (float)currentFactor;
y = pointtocolor[i].Y * (float)currentFactor;
//check if point is inside bmp
if ((int)x >= bmpBackClouds.Width || (int)y >= bmpBackClouds.Height)
{
continue;
}
//Add offset where the point is. The formula: position = Y * stride + X * 4
x = (int)(y * (float)bD.Stride);
y = (int)(x * 4F);
p += (int)(x + y);
//here check, whether the pointer's at a correct position
if (x + y > 3)
p -= (p - pBU) % 4;
//set yellow color
p[1] = p[2] = (byte)255;
p[0] = (byte)0;
p[3] = (byte)255;
}
}
Hans solution is working i just have a small quation the image i get on the hard disk is colored on the right shape but it's opposite or reversed not sure how to call to it what it should be:
On the screenshot on the left is whaht i got on the har disk the bitmap i wrote to with the LockBits.
On the right it's my program and a rectangle i drawed in red and this rectangle should be the colored area on the Bitmap. But on the Bitmap i see this rectangle are but it seems like opposite or reversed.
The question is if there is something wrong with the LockBits code ? Or it seems more like something with my other code ?
(p - pBU) % 4 produces always zero. The difference is always multiple of 4 because each pixel has 4bytes of information. The p pointer jumbs to multiple of 4 from the beginning where pBU points. If you want to test if the points are in the rectangle do this:
GraphicsPath gp = new GraphicsPath();
gp.AddRectangle(new RectangleF(?, ?, ?, ?)); //fill the values of your rectangle
for (int i = 0; i < pointtocolor.Count; i++)
{
if(gp.IsVisible (pointtocolor[i].X * (float)currentFactor, pointtocolor[i].Y * (float)currentFactor)
{
//is inside
}
}
EDIT
When scanning a bitmap and use bD.Stride to see the width using an int pointer is wrong because stride gives color byte count not pixel count
int *p;
p = (int*)(void*)s0;
//x, y the position coordinates
p += y * bD.Stride + x * 4; //wrong
byte *p;
p = (byte*)(void*)s0;
//x, y the position coordinates
p += y * bD.Stride + x * 4; //correct
The error is here
x = (int)(y * (float)bD.Stride);
y = (int)(x * 4F);
You are using previous x to set y!
The correct code is:
float fx, fy;
int x, y;
bD = bmpBackClouds.LockBits(
new System.Drawing.Rectangle(0, 0, bmpBackClouds.Width, bmpBackClouds.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
IntPtr s0 = bD.Scan0;
unsafe
{
byte* p;
//byte* pBU = (byte*)(void*)s0;
for (int i = 0; i < pointtocolor.Count; i++)
{
p = (byte*)(void*)s0;
fx = pointtocolor[i].X * (float)currentFactor;
fy = pointtocolor[i].Y * (float)currentFactor;
if ((int)fx >= bmpBackClouds.Width || (int)fy >= bmpBackClouds.Height)
{
continue;
}
x = (int)fy * bD.Stride;
y = (int)fx * 4;
p += (x + y);
p[1] = p[2] = (byte)255;
p[0] = (byte)0;
p[3] = (byte)255;
}
}
bmpBackClouds.UnlockBits(bD);
valter

Getting vertices and indices from a List<Vector3>

I am trying to achieve drawing triangles from a list of Vector3 Elements.
Previously I have used a heightmap to create vertices and indices however this worked out well because it was a rectangle in a 2d array but not a list.
How would I go about (or modify) my existing code to deal with a list instead of a 2d array.
My existing code for Vertices:
public VertexPositionNormalTexture[] getVerticies(float[,] heightData)
{
VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[terrainLength * terrainWidth];
for (int y = 0; y < terrainLength; y++)
{
for (int x = 0; x < terrainWidth; x++)
{
// position the vertices so that the heightfield is centered
// around x=0,z=0
vertices[x + y * terrainWidth].Position.X = terrainScale * (x - ((terrainWidth - 1) / 2.0f));
vertices[x + y * terrainWidth].Position.Z = terrainScale * (y - ((terrainLength - 1) / 2.0f));
vertices[x + y * terrainWidth].Position.Y = (heightData[x, y] - 1);
vertices[x + y * terrainWidth].TextureCoordinate.X = (float)x / terrainScale;
vertices[x + y * terrainWidth].TextureCoordinate.Y = (float)y / terrainScale;
}
}
return vertices;
}
Here is the code for indices:
public int[] getIndicies()
{
int counter = 0;
int [] indices = new int[(terrainWidth - 1) * (terrainLength - 1) * 6];
for (int y = 0; y < terrainLength - 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++] = topLeft;
indices[counter++] = lowerRight;
indices[counter++] = lowerLeft;
indices[counter++] = topLeft;
indices[counter++] = topRight;
indices[counter++] = lowerRight;
}
}
return indices;
}
You'd be looking at List<List<float> or whichever type you're working with here.
Syntax might change slightly.

Placing a Smaller Texture2D into a larger one

Similar to many programs that take a tiled map, like that in the game Terraria, and turn the map into a single picture of the entire map, I am trying to do something similar. The problem is, my block textures are in a single large texture atlas and are referenced by index, and I am having trouble taking the color data from a single block and placing it into the correct place in the larger texture.
This is my code so far.
Getting the source from the index (this code works):
public static Rectangle GetSourceForIndex(int index, Texture2D tex)
{
int dim = tex.Width / TEXTURE_MAP_DIM;
int startx = index % TEXTURE_MAP_DIM;
int starty = index / TEXTURE_MAP_DIM;
return new Rectangle(startx * dim, starty * dim, dim, dim);
}
Getting the texture at the index (Where the problems start):
public static Texture2D GetTextureAtIndex(int index, Texture2D tex)
{
Rectangle source = GetSourceForIndex(index, tex);
Texture2D texture = new Texture2D(_device, source.Width, source.Height);
Color[] colors = new Color[tex.Width * tex.Height];
tex.GetData<Color>(colors);
Color[] colorData = new Color[source.Width * source.Height];
for (int x = 0; x < source.Width; x++)
{
for (int y = 0; y < source.Height; y++)
{
colorData[x + y * source.Width] = colors[x + source.X + (y + source.Y) * tex.Width];
}
}
texture.SetData<Color>(colorData);
return texture;
}
Putting the texture into the larger picture (this is completely wrong I'm sure):
private void doSave()
{
int texWidth = this._rWidth * Region.REGION_DIM * 16;
int texHeight = this._rHeight * Region.REGION_DIM * 16;
Texture2D picture = new Texture2D(Game.GraphicsDevice, texWidth, texHeight);
Color[] pictureData = new Color[picture.Width * picture.Height];
for (int blockX = 0; blockX < texWidth / 16; blockX++)
{
for (int blockY = 0; blockY < texHeight / 16; blockY++)
{
Block b = this.GetBlockAt(blockX, blockY);
Texture2D toCopy = TextureManager.GetTextureAtIndex(b.GetIndexBasedOnMetadata(b.GetMetadataForSurroundings(this, blockX, blockY)), b.GetTextureFile());
Color[] copyData = new Color[toCopy.Width * toCopy.Height];
Rectangle source = new Rectangle(blockX * 16, blockY * 16, 16, 16);
toCopy.GetData<Color>(copyData);
for (int x = 0; x < source.Width; x++)
{
for (int y = 0; y < source.Height; y++)
{
pictureData[x + source.X + (y + source.Y) * picture.Width] = copyData[x + y * source.Width];
}
}
}
}
picture.SetData<Color>(pictureData);
string fileName = "picture" + DateTime.Now.ToString(#"MM\-dd\-yyyy-h\-mm-tt");
FileStream stream = File.Open(this.GetSavePath() + #"Pictures\" + fileName, FileMode.OpenOrCreate);
picture.SaveAsPng(stream, picture.Width, picture.Height);
I can't find any good descriptions on how to properly convert between the texture and a one dimensional color array. It would be much easier if I knew how to easily and properly place a square of colors into a larger two dimensional texture.
TL;DR: How do you put a smaller Texture into a larger texture?
Create a RenderTarget2D the size of you largest texture and set it to active. Draw the large texture then draw the smaller one. Set the reference to the original texture to the RenderTarget2D you just drew to.
int texWidth = this._rWidth * Region.REGION_DIM * 16;
int texHeight = this._rHeight * Region.REGION_DIM * 16;
_renderTarget = new RenderTarget2D(GraphicsDevice, texWidth, texHeight);
GraphicsDevice.SetRenderTarget(_renderTarget);
GraphicsDevice.Clear(Color.Transparent);
_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque);
for (int blockX = 0; blockX < texWidth / 16; blockX++)
{
for (int blockY = 0; blockY < texHeight / 16; blockY++)
{
_spriteBatch.Draw(
TextureManager.GetTextureAtIndex(b.GetIndexBasedOnMetadata(b.GetMetadataForSurroundings(this, blockX, blockY)), b.GetTextureFile()),
new Rectangle(blockX * 16, blockY * 16, 16, 16),
Color.White);
}
}
_spriteBatch.End()
GraphicsDevice.SetRenderTarget(null);
var picture = _renderTarget;

Inward spiral algorithm not working

I have this c# code to iterate through a grid in an inward spiral like this:
1 2 3
8 9 4
7 6 5
Here is the code, but there is something wrong with it, for some reason it is taking much longer than expected to compute. Does anyone know why this is happening?
static void create_spiral_img(int width, int height)
{
Bitmap img = new Bitmap(width, height);
Graphics graph = Graphics.FromImage(img);
int x = 0;
int y = 0;
int size = width * height;
int max = size;
int count = 1;
int i, j;
while (size > 0)
{
for (i = y; i <= y + size - 1; i++)
{
draw_pixel(count++, x, i, graph);
}
for (j = x + 1; j <= x + size - 1; j++)
{
draw_pixel(count++, j, y + size - 1, graph);
}
for (i = y + size - 2; i >= y; i--)
{
draw_pixel(count++, x + size - 1, i, graph);
}
for (i = x + size - 2; i >= x + 1; i--)
{
draw_pixel(count++, i, y, graph);
}
x = x + 1;
y = y + 1;
size = size - 2;
Console.Write(100 * ((float)(count) / (float)max) + "% ");
}
graph.Dispose();
img.Save("./" + width + "x" + height + "_spiril.png", System.Drawing.Imaging.ImageFormat.Png);
img.Dispose();
}
Assuming a square (width=height) it looks like you've got an O(x^4) implementation - that's going to be hideously slow.
I would recommend trying to drop it down to O(x^2). Instead of drawing it spirally, rewrite your algorithm to draw it rectangularly - that is, go by rows & columns, calculating what each pixel should be.
Assuming that
draw_pixel(c,x,y,g)
draws a point in color c at (x,y) coordinates in the graph g, you're going way too far. You're doing
for (i = y; i <= y + size - 1; i++)
to print a line that should have length width, but you're printing a line of length size.
I'm thinking I didn't understand your algorithm. If this doesn't make sense, can you explain the semantics of draw_pixel please ?

Categories

Resources