HangulSR
HangulSR

Reputation: 259

Unity flip texture using c#

How do I call the method FlipTextureVertically in MakePhoto ?

My picture currently taken is upside down in unity, and I came across this texture flipping code, but I do not know how to apply it.

Would really appreciate if someone could help me out here!

public static Texture2D FlipTextureVertically(Texture2D original)
{
    Texture2D flipped = new Texture2D(original.width, original.height, TextureFormat.ARGB32, false);

    int xN = original.width;
    int yN = original.height;

    for (int i = 0; i < xN; i++)
    {
        for (int j = 0; j < yN; j++)
        {
            flipped.SetPixel(i, yN - j - 1, original.GetPixel(i, j));
        }
    }

    flipped.Apply();

    return flipped;
}

public string MakePhoto(bool openIt)
{          
    int resWidth = Screen.width;
    int resHeight = Screen.height;

    Texture2D screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false); //Create new texture
    RenderTexture rt = new RenderTexture(resWidth, resHeight, 24);        

    // hide the info-text, if any
    if (infoText) 
    {
        infoText.text = string.Empty;
    }
    // render background and foreground cameras
    if (backroundCamera && backroundCamera.enabled) 
    {
        backroundCamera.targetTexture = rt;
        backroundCamera.Render();
        backroundCamera.targetTexture = null;
    }

    if (backroundCamera2 && backroundCamera2.enabled) 
    {
        backroundCamera2.targetTexture = rt;
        backroundCamera2.Render();
        backroundCamera2.targetTexture = null;
    }

    if (foreroundCamera && foreroundCamera.enabled) 
    {
        foreroundCamera.targetTexture = rt;
        foreroundCamera.Render();
        foreroundCamera.targetTexture = null;
    }

    // get the screenshot
    RenderTexture prevActiveTex = RenderTexture.active;
    RenderTexture.active = rt;

    screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);

    // clean-up
    RenderTexture.active = prevActiveTex;
    Destroy(rt);

    byte[] btScreenShot = screenShot.EncodeToJPG();
    Destroy(screenShot);


    // save the screenshot as jpeg file
    string sDirName = Application.persistentDataPath + "/Screenshots";
    if (!Directory.Exists(sDirName))
        Directory.CreateDirectory (sDirName);

    string sFileName = sDirName + "/" + string.Format ("{0:F0}", Time.realtimeSinceStartup * 10f) + ".jpg";
    File.WriteAllBytes(sFileName, btScreenShot);

    Debug.Log("Photo saved to: " + sFileName);
    if (infoText) 
    {
        infoText.text = "Saved to: " + sFileName;
    }

    // open file
    if(openIt)
    {
        System.Diagnostics.Process.Start(sFileName);
    }

    return sFileName;
}

Upvotes: 2

Views: 6423

Answers (4)

Evan Ruiz
Evan Ruiz

Reputation: 16

Thanks to https://stackoverflow.com/a/79090687/22588434 for pointing out using Graphics.Blit - it is much more efficient in the GPU.

I needed the following (for Unity 6 at least) to correctly vertically flip a RenderTexture (note both Vector2 parameters):

Graphics.Blit(srcTexture, destRenderTexture, new Vector2(1, -1), new Vector2(0, 1));

srcTexture is a Texture (RenderTexture, Texture2D, etc). destRenderTexture must be a RenderTexture, GraphicsTexture, or Material according to the latest docs.

If you need to get back a Texture2D, you can still use Graphics.Blit to do the flipping in the GPU to a RenderTexture, and then request an async gpu readback into a Texture2D. Such as:

AsyncGPUReadback.Request(destRenderTexture, 0, TextureFormat.RGBA32, request =>
    {
        // width/height must match destRenderTexture
        var texture2D = new Texture2D(width, height, TextureFormat.RGBA32, false);

        texture2D.LoadRawTextureData(request.GetData<float>());
        texture2D.Apply();
        
        // ... use texture2D
    });

Otherwise, if you just need a method to flip a given RenderTexture, I found this function on a github gist while researching the correct scale/offset parameters above: https://gist.github.com/mminer/816ff2b8a9599a9dd342e553d189e03f

/// <summary>
/// Vertically flips a render texture in-place.
/// </summary>
/// <param name="target">Render texture to flip.</param>
public static void VerticallyFlipRenderTexture(RenderTexture target)
{
    var temp = RenderTexture.GetTemporary(target.descriptor);
    Graphics.Blit(target, temp, new Vector2(1, -1), new Vector2(0, 1));
    Graphics.Blit(temp, target);
    RenderTexture.ReleaseTemporary(temp);
}

Upvotes: 0

Virgil Wylie
Virgil Wylie

Reputation: 21

Here is a GPU based Flipping technique. I just used Graphics.Blit. Check the code below. I kept this answer here so I or anyone else can find a faster way to do it. So the Vector2(-1, 1) is what does the trick. It Scales in X axis or Y axis. So I just set it to Negative to flip.

Graphics.Blit(mainTexture, renderTexture, new Vector2(-1, 1), Vector2.zero);

To understand more about Graphics.Blit, check below link. Graphics.Blit

Upvotes: 0

BISWAJIT SAHOO
BISWAJIT SAHOO

Reputation: 41

The reason your image is flipped is that you are swicthing the vertical pixels in your code.

public static Texture2D FlipTextureVertically(Texture2D original)
{
    Texture2D flipped = new Texture2D(original.width, original.height, TextureFormat.ARGB32, false);

    int xN = original.width;
    int yN = original.height;

    for (int i = 0; i < xN; i++)
    {
        for (int j = 0; j < yN; j++)
        {
            flipped.SetPixel(i, yN - j - 1, original.GetPixel(i, j));
        }
    }

    flipped.Apply();

    return flipped;
}

should be

public static Texture2D FlipTextureVertically(Texture2D original)
{
    Texture2D flipped = new Texture2D(original.width, original.height, TextureFormat.ARGB32, false);

    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, yN, original.GetPixel(i, j));
        }
    }

    flipped.Apply();

    return flipped;
}

Upvotes: 0

derHugo
derHugo

Reputation: 90649

I don't really see why the screenshot should be upside down but I guess you should call it e.g. after

screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);

screenShot = FlipTextureVertically(screenShot);

but there might be more efficient ways of doing that.


E.g. not creating a new Texture2D but instead alter only the pixels in the one you already have like

public static void FlipTextureVertically(Texture2D original)
{
    var originalPixels = original.GetPixels();

    var newPixels = new Color[originalPixels.Length];

    var width = original.width;
    var rows = original.height;

    for (var x = 0; x < width; x++)
    {
        for (var y = 0; y < rows; y++)
        {
            newPixels[x + y * width] = originalPixels[x + (rows - y -1) * width];
        }
    }

    original.SetPixels(newPixels);
    original.Apply();
}

public static void FlipTextureHorizontally(Texture2D original)
{
    var originalPixels = original.GetPixels();

    var newPixels = new Color[originalPixels.Length];

    var width = original.width;
    var rows = original.height;

    for (var x = 0; x < width; x++)
    {
        for (var y = 0; y < rows; y++)
        {
            newPixels[x + y * width] = originalPixels[(width - x - 1) + y * width];
        }
    }

    original.SetPixels(newPixels);
    original.Apply();
}

and use it like

screenShot.ReadPixels(new Rect(0, 0, resWidth, resHeight), 0, 0);

FlipTextureVertically(screenShot);

Upvotes: 5

Related Questions