Unity 180 rotation for a Texture2D, or maybe flip both - c#

I could use just a little help. I am loading a png into a Texture2D, and have managed to flip it over the y axis using the following script I found. I need to flip it over the x axis now. I know a small modification should do it, but I have not managed to get the results I want.
Texture2D FlipTexture(Texture2D original){
Texture2D flipped = new Texture2D(original.width,original.height);
int xN = original.width;
int yN = original.height;
for(int i=0;i<xN;i++){
for(int j=0;j<yN;j++){
flipped.SetPixel(xN-i-1, j, original.GetPixel(i,j));
}
}
flipped.Apply();
return flipped;
}

say "pix" is a png,
Texture2D photo;
Color[] pix = photo.GetPixels(startAcross,0, 256,256);
// (256 is just an example size)
this ENTIRELY ROTATES a png 180 degrees
System.Array.Reverse(pix, 0, pix.Length);
this mirrors a PNG just around the upright axis
for(int row=0;row<256;++row)
System.Array.Reverse(pix, row*256, 256);

Texture2D FlipTexture(Texture2D original, bool upSideDown = true)
{
Texture2D flipped = new Texture2D(original.width, original.height);
int xN = original.width;
int yN = original.height;
for (int i = 0; i < xN; i++)
{
for (int j = 0; j < yN; j++)
{
if (upSideDown)
{
flipped.SetPixel(j, xN - i - 1, original.GetPixel(j, i));
}
else
{
flipped.SetPixel(xN - i - 1, j, original.GetPixel(i, j));
}
}
}
flipped.Apply();
return flipped;
}
To call it:
FlipTexture(camTexture, true); //Upside down
FlipTexture(camTexture, false); //Sideways

This flips the texture upside down:
int width = texture.width;
int height = texture.height;
Texture2D snap = new Texture2D(width, height);
Color[] pixels = texture.GetPixels();
Color[] pixelsFlipped = new Color[pixels.Length];
for (int i = 0; i < height; i++)
{
Array.Copy(pixels, i*width, pixelsFlipped, (height-i-1) * width , width);
}
snap.SetPixels(pixelsFlipped);
snap.Apply();

Related

Merging two different texture into one in unity3d

i am trying to merge two textures into one in unity
the first texture is from a webcamTexture
the second is from a sprite using : gameobject.getComponent<SpriteRenderer>().sprite.texture as Texture2D
I'm having problem in writing the function this is what i did so far :
public static Texture2D CombineTextures(GameObject obj, Texture2D background, Texture2D TodrawLogo)
{
Vector3 v = obj.transform.position;// obj is TodrawLogo gameobject
int width = TodrawLogo.width;
int height = TodrawLogo.height;
for (int x =(int)v.x; x < width; x++){
background.SetPixel(x,(int)v.y,TodrawLogo.GetPixel(x,(int)v.y));
}
background.Apply();
return background;
}
this what i am trying to do :
WebcamTexture
the result Texture should be like this
the webcamTexture is a 3dplane and the logo is a single sprite
but sadly my function doesn't work
does anyone know how to fix this
I know that i should find the exact coordinate of the todraw image and set the pixels but i can't figure out how
Much appreciation
EDIT:
i tried to use #nexx code :
public static Texture2D CombineTexture(Texture2D background, Texture2D TodrawLogo)
{
int width = TodrawLogo.width;
int height = TodrawLogo.height;
int backWidth = background.width;
int backHeight = background.height;
// bottom right corner
int startX = backWidth - width;
int startY = backHeight - height;
// loop through texture
int y = 0;
while (y < backHeight) {
int x = 0;
while (x < backWidth) {
// set normal pixels
background.SetPixel(x,y,background.GetPixel(x,y));
// if we are at bottom right apply logo
//TODO also check alpha, if there is no alpha apply it!
if(x >= startX && y < backHeight- startY)
background.SetPixel(x,y,TodrawLogo.GetPixel(x-startX,y-startY));
++x;
}
++y;
}
background.Apply();
return background;
}
but this is the resulting image i get :
i am stuck at this can someone please tell me what am i doing wrong ?
Here's a working example. I tested!
public Texture2D AddWatermark(Texture2D background, Texture2D watermark)
{
int startX = 0;
int startY = background.height - watermark.height;
for (int x = startX; x < background.width; x++)
{
for (int y = startY; y < background.height; y++)
{
Color bgColor = background.GetPixel(x, y);
Color wmColor = watermark.GetPixel(x - startX, y - startY);
Color final_color = Color.Lerp(bgColor, wmColor, wmColor.a / 1.0f);
background.SetPixel(x, y, final_color);
}
}
background.Apply();
return background;
}
This code works perfectly with two images that are not the same size
public static Texture2D AddWatermark(Texture2D background, Texture2D watermark, int startX, int startY)
{
Texture2D newTex = new Texture2D(background.width, background.height, background.format, false);
for (int x = 0; x < background.width; x++)
{
for (int y = 0; y < background.height; y++)
{
if (x >= startX && y >= startY && x < watermark.width && y < watermark.height)
{
Color bgColor = background.GetPixel(x, y);
Color wmColor = watermark.GetPixel(x - startX, y - startY);
Color final_color = Color.Lerp(bgColor, wmColor, wmColor.a / 1.0f);
newTex.SetPixel(x, y, final_color);
}
else
newTex.SetPixel(x, y, background.GetPixel(x, y));
}
}
newTex.Apply();
return newTex;
}
I made some changes for your CombineTextures method,
public static Texture2D CombineTexture(GameObject obj, Texture2D background, Texture2D TodrawLogo)
{
int width = TodrawLogo.width;
int height = TodrawLogo.height;
int backWidth = background.width;
int backHeight = background.height;
// bottom right corner
int startX = backWidth - width;
int startY = backHeight - height;
// loop through texture
int y = 0;
while (y < backHeight) {
int x = 0;
while (x < backWidth) {
// set normal pixels
background.SetPixel(x,y,background.GetPixel(x,y));
// if we are at bottom right apply logo
//TODO also check alpha, if there is no alpha apply it!
if(x >= startX && y < backHeight- startY)
background.SetPixel(x,y,TodrawLogo.GetPixel(x-startX,y-startY));
++x;
}
++y;
}
background.Apply();
return background;
}
You can change the values inside while loop to place your texture where you want.
I don't know how you would like to do it but you can use unity's OnGUI method to draw the logo where needed.
http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnGUI.html
Its as simple as something like this:
var aTexture : Texture;
function OnGUI() {
if(!aTexture){
Debug.LogError("Assign a Texture in the inspector.");
return;
}
GUI.DrawTexture(Rect(10,10,60,60), aTexture, ScaleMode.ScaleToFit, true, 10.0f);
}
I hope i was helpful.
Try this (its a modification of nexx's code.) It combines two textures into a new texture. The new texture is guarenteed to be writable (otherwise SetPixel/GetPixel won't work.)
Also, it assumes that the watermark texture is smaller than the background texture.
Please note: I've not tested this.
public static Texture2D AddWatermark(Texture2D background, Texture2D watermark)
{
// Create a new writable texture.
Texture2D result = new Texture2D(background.width, background.height);
// Draw watermark at bottom right corner.
int startX = background.width - watermark.width;
int startY = background.height - watermark.height;
for (int x = 0; x < background.width; x++) {
for (int y = 0; y < background.height; y++) {
Color bgColor = background.GetPixel(x, y);
Color wmColor = new Color(0, 0, 0, 0);
// Change this test if no longer drawing at the bottom right corner.
if (x >= startX && y >= startY) {
wmColor = watermark.GetPixel(x, y);
}
// Alpha-blend background and watermark color.
Color bended = bgColor * (1.0f - wmColor.a) + wmColor;
blended.a = 1.0f;
result.SetPixel(x, y, blended);
}
}
result.Apply();
return result;
}
A slightly more optimized solution. Reading and writing only the area required for the watermark.
public static Texture2D AddWatermark(Texture2D background, Texture2D watermark, int startPositionX, int startPositionY)
{
//only read and rewrite the area of the watermark
for (int x = startPositionX; x < background.width; x++)
{
for (int y = startPositionY; y < background.height; y++)
{
if (x - startPositionX < watermark.width && y - startPositionY < watermark.height)
{
var bgColor = background.GetPixel(x, y);
var wmColor = watermark.GetPixel(x - startPositionX, y - startPositionY);
var finalColor = Color.Lerp(bgColor, wmColor, wmColor.a / 1.0f);
background.SetPixel(x, y, finalColor);
}
}
}
background.Apply();
return background;
}

ArgumentException was unhandled. The rectangle is too large or too small for this resource

I'm trying to create a collision detection method for a simple XNA racing game, for which I am using this tutorial on how to extract texture data. What I'm trying to do is to check if any of the colors in that area of the texture are blue (which is the color of the walls on my racing track). However, I keep getting the error in the title. Can anyone explain to me why this happens?
code:
public bool Collision()
{
int width = arrow.Width; //arrow is the name of my "car" texture (it's an arrow)
int height = arrow.Height;
int xr = (int)x; // x is the x position of my arrow
int yr = (int)y; // y is the y position of my arrow
Color[] rawData = new Color[width * height];
Rectangle extractRegion = new Rectangle(xr, yr, width, height);
track.GetData<Color>(0, extractRegion, rawData, 0, width * height); //error occurs here
Color[,] rawDataAsGrid = new Color[height, width];
for (int row = 0; row < height; row++)
{
for (int column = 0; column < width; column++)
{
rawDataAsGrid[row, column] = rawData[row * width + column];
}
}
for (int x1 = (int)x; x1 < width; x1++)
{
for (int y1 = (int)y; y1 < height; y1++)
{
if (rawDataAsGrid[x1, y1] == Color.Blue)
{
return true;
}
}
}
return false;
}
edit: I got it working!
Your rawData is not of sufficient length to receive the data you attempt to get with the GetData() method.
Change this line:
Color[] rawData = new Color[width * height];
into:
Color[] rawData = new Color[track.Width * track.Height];
And that should do it. Hope it helps!

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;

Get information from a graph image

In this image black colour graph is in the white background. I want to get the pixel length between the two peak waves in the graph and the average amplitude (height of the peak) of the peak waves.
I'm stuck with the logic to implement this code.can anyone help me to implement this. I'm using C#
public void black(Bitmap bmp)
{
Color col;
for (int i = 0; i < bmp.Height; i++)
{
for (int j = 0; j < bmp.Width; j++)
{
col = bmp.GetPixel(j, i);
if (col.R == 0) //check whether black pixel
{
y = i; //assign black pixel x,y positions to a variable
x = j;
}
}
}
}
my supervisor told i have to use a 2D array to store increments and decrements(start point pixel value and end point pixel value of each increment and decrement) of the line to get these values.But i haven't sufficient coding skills to apply that logic to this code.
Bitmap img = new Bitmap(pictureBox1.Image);
int width = img.Width;
int height = img.Height;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Color pixelColor = img.GetPixel(x, y);
if (pixelColor.R == 0 && pixelColor.G == 0 && pixelColor.B == 0)
//listBox1.Items.Add(String.Format("x:{0} y:{1}", x, y));
textBox1.Text = (String.Format("x:{0} y:{1}", x, y));
}
}

Texture2D is turning black

I have a Texture2D that I'm loading from the Content Pipeline. That's working fine, but as soon as I try to use SetData on a completely different Texture2D all of the textures in my game go completely black:
This is in my HUDMeter class, the class that I want to be just red
Texture2D colorGrad = Content.Load<Texture2D>(GradientAsset);
Color[,] pixels = new Color[colorGrad.Width, colorGrad.Height];
Color[] pixels1D = new Color[colorGrad.Width * colorGrad.Height];
pixels = GetRedChannel(colorGrad);
pixels1D = Color2DToColor1D(pixels, colorGrad.Width);
System.Diagnostics.Debug.WriteLine(pixels[32,32]);
Gradient = colorGrad;
Gradient.SetData<Color>(pixels1D);
These are using Riemers tutorial
protected Color[,] GetRedChannel(Texture2D texture)
{
Color[,] pixels = TextureTo2DArray(texture);
Color[,] output = new Color[texture.Width, texture.Height];
for (int x = 0; x < texture.Width; x++)
{
for (int y = 0; y < texture.Height; y++)
{
output[x,y] = new Color(pixels[x,y].G, 0, 0);
}
}
return output;
}
protected Color[,] TextureTo2DArray(Texture2D texture)
{
Color[] colors1D = new Color[texture.Width * texture.Height];
texture.GetData(colors1D);
Color[,] colors2D = new Color[texture.Width, texture.Height];
for (int x = 0; x < texture.Width; x++)
for (int y = 0; y < texture.Height; y++)
colors2D[x, y] = colors1D[x + y * texture.Width];
return colors2D;
}
private Color[] Color2DToColor1D (Color[,] colors, int width)
{
Color[] output = new Color[colors.Length];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < colors.Length / width; y++)
{
output[x + y * width] = colors[x % width, y % (colors.Length/width)];
}
}
return output;
}
And here is the code to draw the sprites, this works fine and is how I always draw sprites:
batch.Draw(meter.Gradient, new Vector2(X, Y), Color.White);
Update:
I've actually found that the sprites that don't use the same file are not black. Does Texture2D.SetData<>() actually change the file itself? what is the use of that?
Update:
I just tried to use the Alpha as well as RGB and it's working. I'm thinking that there's something wrong with one of the conversion methods.
If you do this:
Texture2D textureA = Content.Load<Texture2D>("MyTexture");
Texture2D textureB = Content.Load<Texture2D>("MyTexture");
Both textureA and textureB refer to the same object. So if you call SetData on one of them, it will affect both of them. This is because ContentManager keeps an internal list of resources already loaded, so it doesn't have to keep reloading the same resource.
The solution would be to create a new Texture2D object of the same size, call GetData on the one loaded by ContentManager, and then SetData on the new texture.
Example (not tested):
Color[] buffer = new Color[textureA.Width * textureA.Height];
Texture2D textureB = new Texture2D(textureA.GraphicsDevice,
textureA.Width,
textureA.Height);
textureA.GetData(buffer);
textureB.SetData(buffer);
Dispose() of the new texture when you are finished with it (eg: in your Game.UnloadContent method). But never dispose of the one loaded by ContentManager (because, like I said, it is a shared object; use ContentManager.Unload instead).

Categories

Resources