0

So I'm using System.Drawing Bitmap implementation. It looks like when setting format to PixelFormat.Format24bppRgb its actually interpreting it as 24 Bgr and not Rgb

latestImage = new Bitmap(bitmap.Width, bitmap.Height, bitmap.Stride, PixelFormat.Format24bppRgb, bitmap.Scan0);
for (int x = 0; x < _latestImage.Width; x++)
{
    for (int y = 0; y < _latestImage.Height; y++)
    {
        var c = _latestImage.GetPixel(x, y);
        byte r = Marshal.ReadByte((bitmap.Scan0 + (x * 3) + 0) + (y * bitmap.Stride));
        byte g = Marshal.ReadByte((bitmap.Scan0 + (x * 3) + 1) + (y * bitmap.Stride));
        byte b = Marshal.ReadByte((bitmap.Scan0 + (x * 3) + 2) + (y * bitmap.Stride));
        _latestImage.SetPixel(x, y, System.Drawing.Color.FromArgb(r, g, b));
    }
}
_latestImage.Save(temp3, ImageFormat.Jpeg);

vs

_latestImage = new Bitmap(bitmap.Width, bitmap.Height, bitmap.Stride, PixelFormat.Format24bppRgb, bitmap.Scan0);
_latestImage.Save(temp3, ImageFormat.Jpeg);

In the first example were I loop over everything and manually set the colors it works fine but takes for ever. In the second one the red and blue colors are swapped in the image.

Am I crazy or is the .net implementation wrong here?

I've tried different formats but the rest are even worse. Been looking for another library to handle my Bitmap data but haven't found a solution yet.

Clemens
  • 123,504
  • 12
  • 155
  • 268
  • https://stackoverflow.com/a/8104553/17034 – Hans Passant Mar 15 '23 at 16:33
  • How would I apply this? unsafe { var ptr = (int*)bitmap.Scan0; latestImage = new Bitmap(bitmap.Width, bitmap.Height, bitmap.Stride, PixelFormat.Format24bppRgb, (nint)ptr); } latestImage.Save(temp3, ImageFormat.Jpeg); If I just cast it to (int*) I get Argument 5 cannot convert from 'int*' to 'nint'. – Mattias Kallsäby Mar 15 '23 at 17:36
  • _"Been looking for another library to handle my Bitmap data"_ - [here](https://github.com/koszeggy/KGySoft.Drawing#fast-bitmap-manipulation) is such a library (written by me). Cures both the performance and the pixel format misery issues. – György Kőszeg Mar 20 '23 at 18:26
  • Amazing! Will try it out. – Mattias Kallsäby Mar 21 '23 at 22:17

1 Answers1

0

In the first example were I loop over everything and manually set the colors it works fine but takes for ever

Of course it does, you're calling GetPixel and SetPixel which are both extremely slow. You have access to the raw data in Scan0, just write them through a Span<byte> directly.

Funnily enough, you don't even use the result of GetPixel, you're just calling it for fun? I suppose?

Am I crazy or is the .net implementation wrong here?

You are, definitely, because you're not thinking of the environment you're programming for. Specifically, your machine's endianness (which may or may not the be same as the endianness other people will run your code on).

Also as a note, your WPF tag is the polar opposite of the GDI+ code you're writing. Your code is about as ancient as it gets, running fully on the CPU. Modern frameworks like WPF do image processing, storing and displaying on the GPU.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • Also lastly your `for`s are in the wrong order, Y then X is what you want. But considering all the other problems, it's par to the course. – Blindy Mar 15 '23 at 16:47
  • Okey maybe I formulated the question in a bad way. – Mattias Kallsäby Mar 15 '23 at 16:57
  • I know the performance sux as with that solution. My only point with looping over like that was to show that when I manually read byte by byte and interpret it as RGB it becomes a correct image. – Mattias Kallsäby Mar 15 '23 at 16:59
  • However when I just use the built in constructor from Drawings and tell it to interpret the image as 24bit RGB it flips the R and B values. Also sorry for the spam I'm on my phone now and apparently "enter" posts the message and doesn't add a new line... – Mattias Kallsäby Mar 15 '23 at 17:00
  • But do you have anything to add about the issue I tried to ask about? When Bitmap is created with that pixelformat it looks like it read like this BGR: byte 0 as blue byte 1 as green byte 2 as red byte 3 as blue byte 4 as green byte 5 as red Instead of RBB as it should: byte 0 as red byte 1 as green byte 2 as blue byte 3 as red byte 4 as green byte 5 as blue – Mattias Kallsäby Mar 15 '23 at 17:26
  • The endian thing could be a thing though. I'll try it out. – Mattias Kallsäby Mar 15 '23 at 17:29
  • "Instead of RBB as it should" -- again, that is just your assumptions about how computers in general and GDI+ in particular work. In practice, it is quite natural to worry about endianness when reading files stored on disk, because again everyone has a different endianness. At best you could ask why isn't there a constructor that takes the data you have formatted wrongly and swaps it around, and the reason for that is that GDI+ has been dead for over 10 years, you just chose to use it. OpenGL for example has such swizzling as it calls it built-in. – Blindy Mar 15 '23 at 19:05
  • "My only point with looping over like that was to show that when I manually read byte by byte and interpret it as RGB" -- and *my* point is that your way of doing it is what "sux" as you put it. You can definitely swap bytes around trivially and quickly in C#, but not with your code. – Blindy Mar 15 '23 at 19:06
  • I wasnt aware this was old libraries. Would System.Windows.Interop.Imaging be a better library to use. Idk, each value is 8 bits, so byte endian shouldnt matter. But as you say apparently Drawings "has been dead" for 10 years. So what I really need is find the modern equivalence for mapping that bitmap. The memory is unmanaged and comes from another library written in C/C++. So I don't really have control over that. I just need to take that data and interpret it as an image correctly. In memory the data is RGBRGBRGB etc. starting from lowest address going to highest address for each byte – Mattias Kallsäby Mar 16 '23 at 07:54
  • "In memory the data is RGBRGBRGB etc. starting from lowest address going to highest address for each byte" -- that's fine if you were programming on an ARM computer for example, but it's backwards on an x86. x86 is little endian, and the layout is `BGR`. For the third and last time, it's perfectly normal to swap your bytes due to this, the only problem is specifically the code you used to do it. Use spans and regular increments and swaps. – Blindy Mar 16 '23 at 14:42
  • As mentioned, an "ARGB" colour is ARGB in little-endian Int32. This means that while in _value_ it's indeed AARRGGBB (for example, 0xFF800040), but in _actual individual bytes_ that's [BB, GG, RR, AA] (or, for that value, [40, 00, 80, FF]). – Nyerguds May 30 '23 at 14:31