Home>

I want to create a desktop mascot based on Unity.
I want the mouse behavior in the transparent part to respond to the bottom window.
Although it was implemented, I want to make it as light as possible because the conversion process from RenderTexture to System.Drawing.Image is heavy.
Also, please let me know if there is any other way to have the same behavior in a Unity-based desktop mascot.

Implementation

Call TransParentForm.cs in FormCaller.cs, convert RenderTexture updated in OnCallRender in FormCaller.cs once to Texture2D, then convert it to .png in EncodeToPNG, and convert it to Bitmap in System.Drawing.Image doing.
If i look at Profiler, EncodeToPNG seems to be the heaviest processing and you don't want to use it. ReadPixels is also used for conversion to Texture2D. I don't want to use it because it's heavy.
If possible, I want to convert RenderTexture to Bitmap in one shot.
Below is a list of FormCaller.cs, TransParentForm.cs and Profiler.

using System.Drawing;
using System.IO;
using UnityEngine;
public class FormCaller: MonoBehaviour
{
    [SerializeField] Vector2 Size;
    [SerializeField] RenderTexture renderTexture;
    [SerializeField] Camera mainCam;
    public System.Drawing.Bitmap Image2paint;
    TransparentForm f;
    void Start ()
    {
        f = new TransparentForm (renderTexture, mainCam);
    }
    void OnPostRender ()
    {
        Texture2D Tex2Paint = new Texture2D (renderTexture.width, renderTexture.height);
        Tex2Paint.ReadPixels (new Rect (0, 0, renderTexture.width, renderTexture.height), 0, 0);
        Tex2Paint.Apply ();
        var img2Paint = new Bitmap (renderTexture.width, renderTexture.height);
        MemoryStream memoryStream = new MemoryStream ();
        var img = Tex2Paint.EncodeToPNG ();
        memoryStream.Write (img, 0, img.Length);
        Image2paint = new System.Drawing.Bitmap (memoryStream);
        f.Image2paint = Image2paint;
    }
    void OnDisable ()
    {
        f.Close ();
    }
}
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using UnityEngine;
public class TransparentForm: Form
{
    RenderTexture tex;
    public System.Drawing.Bitmap Image2paint;
    Point mousePoint;
    public TransparentForm (RenderTexture Rendertex, Camera main)
    {
        tex = Rendertex;
        main.targetTexture = tex;
        this.SetStyle (ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);
        this.SetBounds (0, 0, tex.width, tex.height);
        this.BackColor = System.Drawing.Color.Black;
        this.TransparencyKey = System.Drawing.Color.Black;
        this.FormBorderStyle = FormBorderStyle.None;
        this.TopMost = true;
        this.MouseDown + = new MouseEventHandler (Form1_MouseDown);
        this.MouseMove + = new MouseEventHandler (Form1_MouseMove);
        this.Show ();
    }
    protected override void OnPaint (PaintEventArgs e)
    {
        base.OnPaint (e);
        e.Graphics.DrawImage (Image2paint, 0, 0, tex.width, tex.height);
        this.Invalidate ();
    }
    private void Form1_MouseDown (object sender,
        System.Windows.Forms.MouseEventArgs e)
    {
        if ((e.Button&MouseButtons.Left) == MouseButtons.Left)
        {
            mousePoint = new Point (e.X, e.Y);
        }
    }
    private void Form1_MouseMove (object sender,
        System.Windows.Forms.MouseEventArgs e)
    {
        if ((e.Button&MouseButtons.Left) == MouseButtons.Left)
        {
            this.Left + = e.X-mousePoint.X;
            this.Top + = e.Y-mousePoint.Y;
        }
    }
}


]

I changed EncodeToPNG to EncodeToJPG, but it was rejected due to noise.

  • Answer # 1

    Since self-solved.
    I tried many things, but it is fastest to write the byte array on my own after all.
    Since each byte of RGBA is required for each pixel, prepare byte [] that is 4 times the size of width * height.
    Since bitmap is BGRA, write byte [] like that.

    Texture2D Tex2Paint = new Texture2D (renderTexture.width, renderTexture.Tex2Paint.ReadPixels (new Rect (0, 0, renderTexture.width, renderTexture.height), 0, 0, false );
    Tex2Paint.Apply ();
    Color32 [] pixels = Tex2Paint.GetPixels32 ();
    Bitmap bitmap = new Bitmap (renderTexture.width, renderTexture.height);
    BitmapData data = bitmap.LockBits (
        new Rectangle (0, 0, bitmap.Width, bitmap.Height),
        ImageLockMode.ReadWrite,
        PixelFormat.Format32bppArgb);
    byte [] colorbytes = new byte [renderTexture.width * renderTexture.height * 4];
    Marshal.Copy (data.Scan0, colorbytes, 0, colorbytes.Length);
    for (int i = 0;i<pixels.Length;i ++)
    {
        colorbytes [4 * i] = pixels [i] .b;
        colorbytes [4 * i + 1] = pixels [i] .g;
        colorbytes [4 * i + 2] = pixels [i] .r;
        colorbytes [4 * i + 3] = pixels [i] .a;
    }
    Marshal.Copy (colorbytes, 0, data.Scan0, colorbytes.Length);
    bitmap.UnlockBits (data);
    bitmap.RotateFlip (RotateFlipType.Rotate180FlipX);
    f.Image2paint = bitmap;