0

I am trying to get the pixels a bit faster than one could when using .GetPixel() but I have found myself that it throws an exception with a 0,0 index with the right LockImageMode.

Exception

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

public async Task<IList<Color>> GetColorStream( )
    {
        (_Width, _Height, PixelFormat pixelFormat) = await GetDimensions(_Path);
        List<Color> pixels = new();
        if (pixelFormat is not PixelFormat.Format32bppRgb or not PixelFormat.Format24bppRgb or not PixelFormat.Format32bppRgb)
            throw new InvalidOperationException("Cant operate with ess than 24bpps");

         _Data = await GetBitmapData(_Width, _Height, pixelFormat, ImageLockMode.ReadWrite);

        if(pixelFormat is PixelFormat.Format24bppRgb or PixelFormat.Format32bppRgb)
        {
            for (int i = 0; i < _Width; i++)
                for (int j = 0; j < _Height; j++)
                {
                    int stride = i * _Data.Stride / 3 + j;
                    (byte R, byte G, byte B, byte A) = (Marshal.ReadByte(_Data.Scan0, stride), Marshal.ReadByte(_Data.Scan0, stride + 1),
                                                      Marshal.ReadByte(_Data.Scan0, stride + 2), 0);
                    pixels.Add(new Color(R, G, B, A));
                }
        }
        else if(pixelFormat == PixelFormat.Format32bppArgb )
        {
            for (int i = 0; i < _Width; i++)
                for (int j = 0; j < _Height; j++)
                {
                    int stride = i * _Data.Stride / 4 + j;
                    (byte R, byte G, byte B, byte A) = (Marshal.ReadByte(_Data.Scan0, stride), Marshal.ReadByte(_Data.Scan0, stride + 1),
                                                      Marshal.ReadByte(_Data.Scan0, stride + 2), Marshal.ReadByte(_Data.Scan0, stride + 3));
                    pixels.Add(new Color(R, G, B, A));
                }
        }
        image.UnlockBits(_Data);
        image = null;
        return pixels;
    }```
PontiacGTX
  • 185
  • 2
  • 15
  • The offset of the first of the three (four) bytes to read in that loop would be `_Data.Stride * j + i * 3` (`_Data.Stride * j + i * 4` respectively). If you want speed improvements, better read in bulk, not in bytes. And read by lines, not by columns, so that the memory access is sequential rather than all over the place. – GSerg May 29 '22 at 14:55
  • The code is not handling Format32bppRgb correctly, stride is then a multiple of 4. The simple solution is to handle only a single format, 32bppArgb, passing that to GetBitmapData(). You also greatly prefer Marshal.ReadInt32() to speed up the code, Color.FromArgb() to convert the int to Color. – Hans Passant May 29 '22 at 14:57
  • @HansPassant Stride is always a multiple of four... – GSerg May 29 '22 at 14:58
  • His stride variable, not the bitmap's stride. – Hans Passant May 29 '22 at 14:59
  • @GSerg is it always true for 24bpp? so you are telling me to copy the whole in array rather iterating through the BitmapData? – PontiacGTX May 29 '22 at 15:00
  • @HansPassant His `int stride` is the current offset relative to `Scan0` to read the next 3 (4) bytes from. A stride cannot be different for each read operation. – GSerg May 29 '22 at 15:01
  • @PontiacGTX Is what true? How the offset is calculated from Scan0 and Stride, by multiplying Stride to the number of passed lines (not columns)? Yes, that is true for all bpp. It is also true for all bpp that Stride is divisible by 4. – GSerg May 29 '22 at 15:03
  • @GSerg I assumed that the 24bpp were only 3 channels only with 8 bytes each. – PontiacGTX May 29 '22 at 16:23
  • @GSerg it doesnt work with the default value PixelFormat.Format24bppRgb but if I sent PixelFormat.Format32bppArgb then lockbits BitmapData works.... what could be the issue? – PontiacGTX May 29 '22 at 16:45
  • 1
    Because you are trying to divide the strive by 3? 24bpp is three channels only with 8 bytes each. That does not mean the stride is divisible by 3. The stride is always divisible by four. Sometimes by 3 as well, but only as a coincidence. You are not supposed to divide it in the first place. See the first comment. – GSerg May 29 '22 at 17:51
  • @GSerg I have updated my code and dividing by 3 it doesnt crash but PixelFormat should be 32bits otherwise LockBits wont work – PontiacGTX May 29 '22 at 22:49
  • it's much more efficient to just increase your read offset by `stride` on every `y` iteration, and copy that into a separate variable which you increase by the amount of bytes (3 or 4) on every `x` iteration. Then you don't need to do multiplications in every single loop. – Nyerguds Jun 09 '22 at 17:50
  • @Nyerguds should I divide oe get the mod for the most suitable ```stride```value in this loop I supose? – PontiacGTX Jun 12 '22 at 13:20
  • Most suitable stride? There is only one stride, and it's `_Data.Stride`. You can't _choose_ the stride. – Nyerguds Jun 13 '22 at 14:37
  • @Nyerguds but I mean the most suitable divider per pixelcount as you say reading in batch is much faster – PontiacGTX Jun 13 '22 at 21:31
  • There is no "divider". You increase your x per block of bytes corresponding to the pixel size, but every y-step, you just take the previous line's start and add the stride to get the next line's start. – Nyerguds Jun 14 '22 at 14:36

0 Answers0