Search code examples
c#image-processingbitmaplockingbitmapdata

Drawing a Bitmap onto another produces a distorted image


I am using a custom class to write an image onto another image of bigger size. Here is the full source code in DotNetFiddle.

My custom GetPixel() works fine. But the following SetPixel() is unable to produce the proper output. Probably, there has been some problems going on with the calculation of addresses. But, I am unable to detect it.

    public void SetPixel(int x, int y, Color color)
    {
        // Get color components count
        int cCount = ColorDepth / 8;
        // Get start index of the specified pixel
        int i = ((y * Width) + x) * cCount;
        //int i = ((x * Width) + y) * cCount;
        if (ColorDepth == 32) // For 32 bpp set Red, Green, Blue and Alpha
        {
            _imageData[i] = color.B;
            _imageData[i + 1] = color.G;
            _imageData[i + 2] = color.R;
            _imageData[i + 3] = color.A;
        }

        if (ColorDepth == 24) // For 24 bpp set Red, Green and Blue
        {
            _imageData[i] = color.B;
            _imageData[i + 1] = color.G;
            _imageData[i + 2] = color.R;
        }
        if (ColorDepth == 8)
        {
            // For 8 bpp set color value (Red, Green and Blue values are the same)
            _imageData[i] = color.B;

            string str = string.Empty;
        }
    }

This is is generating a distorted image:

enter image description here

.

P.S. Here is the input image:

enter image description here

.

.


Solution

  •     // Get start index of the specified pixel
        int i = ((y * Width) + x) * cCount;
    

    That is not correct, neither in GetPixel nor SetPixel. You get the skew because you are ignoring Stride. Which are the number of bytes in a single scanline of the image. It is a multiple of 4 to align the pixel data in memory, helps the processor read the data faster. Fix:

        int i = y * Stride + x * cCount;
    

    There is another bug that's hidden in your code, scanlines are stored upside down. In other words, the data for the last scanline is stored first. But only if BitmapData.Height is not negative. Since that bug occurs both in your GetPixel and your SetPixel methods they cancel each other out. Correct code would be (Height - y - 1) * Stride + x * cCount.

    This code is not faster than Graphics.DrawImage(), the method you should always prefer.