erkyrath / UnitySkiaDemo

A demonstration of embedding a SkiaSharp canvas in a Unity project
6 stars 3 forks source link

RawImageDraw imports the bitmap upside down! #1

Open The-MAZZTer opened 4 years ago

The-MAZZTer commented 4 years ago

When I first started using this code I ran into some oddities, such as the y axis of the image seeming to start from the bottom instead of the top. I wasn't familiar with Skia so this didn't bother me much and I adjusted. Later on I realized some of my images were flipped when I tried to add text, but I assumed it was due to alterations I made or other problems with my own code. I worked around these problems.

However the other day I used SKBitmap.Decode to load existing images (I was not using Unity's own loaders since I was loading from Streams provided by third-party libraries) so I could then do some minor manipulation on them and load them into a Texture2D. I ended up with something like this to get my SKBitmap into a Texture2D, based off of the RawImageDraw example with my own tweaks:

    TextureFormat format = (x.Info.ColorType == SKColorType.Rgba8888) ? TextureFormat.RGBA32 : TextureFormat.BGRA32;
    Texture2D texture = new Texture2D(x.Info.Width, x.Info.Height, format, false, true);
    texture.wrapMode = TextureWrapMode.Clamp;
    using (SKPixmap pixmap = x.PeekPixels()) {
      texture.LoadRawTextureData(pixmap.GetPixels());
    }
    texture.Apply(false, true);

However when I applied my texture to a Quad's MeshRenderer like so:

this.GetComponent<MeshRenderer>().material.SetTexture("_MainTex", t);

The texture was flippeed!

I assume this has something to do with BMP traditionally storing data in memory and files from the bottom up. Either Skia or Unity expects the data stored in this format while the other expects it from the top down.

There are many potential fixes such as having Skia flip the image intentionally or doing something on the Unity side such as scaling an object by -1 veritcally to force the texture to render flipped.

My fix was to reorder the rows of pixels to swap them around:

    TextureFormat format = (x.Info.ColorType == SKColorType.Rgba8888) ? TextureFormat.RGBA32 : TextureFormat.BGRA32;
    Texture2D texture = new Texture2D(x.Info.Width, x.Info.Height, format, false, true);
    texture.wrapMode = TextureWrapMode.Clamp;

    byte[] buffer;
    using (SKPixmap pixmap = x.PeekPixels()) {
      buffer = new byte[pixmap.RowBytes * pixmap.Height];
      IntPtr pixels = pixmap.GetPixels();
      for (int y = 0; y < pixmap.Height; y++) {
        Marshal.Copy(pixels + (y * pixmap.RowBytes), buffer, (pixmap.Height - y - 1) * pixmap.RowBytes, pixmap.RowBytes);
      }

      texture.LoadRawTextureData(buffer);
      buffer = null;
    }
    texture.Apply(false, true);
erkyrath commented 4 years ago

Thanks for the note. The Y-axis will always be a thorn in the side of graphics APIs. (Yeah, yeah, "but which side?")

SrejonKhan commented 3 years ago

The following solution works perfectly. I found another solution by flipping canvas.

SrejonKhan commented 3 years ago

[THIS EXPLANATION IS BIAS, CHECK THE LAST COMMENT]

Alright, this flipping issue is happening for Endianness I believe.

Before trying out Skia, I was trying ImageSharp. Where I need to read pixels from the end.

Before trying ImageSharp, I was tried to Read PNG Chunks and understand how PNG files are structured. Where I had to reverse the chunk's length part (4 bytes) to get the accurate length of chunk data. I had to reverse it because I'm running my application in Little Endian Architecture.

Most probably, those APIs are reading in Big Endian manner and Unity reading in the actual architecture manner.

erkyrath commented 3 years ago

I don't believe that is right.

SrejonKhan commented 3 years ago

Alright. Any idea why? I'm just being curious here. I tried in OpenGL after reading this, but the same result.

The-MAZZTer commented 3 years ago

Endianness affects the order of individual bytes within numerical/pointer data types. It's not going to affect the order of lines in a bitmap. Individual 16/24/32 bit pixel values might be affected, not sure. But this would only affect, for example, ARGB vs BGRA formats, if it does happen

Not sure why bitmaps are stored from the bottom up. I think on Windows it's a legacy thing from the format of native video buffers.