C# Draw image implemention

2019-08-12 00:55发布

问题:

Im trying to make kind of Grahpics.DrawImage implemention using unsafe code and pointers of course.

In this case im trying to draw a small bitmap on a bigger width(both 32bppArgb).

This is my code

   private static unsafe void Draw(Bitmap bmp, Bitmap bmp2, int xPoint, int yPoint)
    {

        BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat);
        BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);

        IntPtr scan0 = bmData.Scan0;
        IntPtr scan02 = bmData2.Scan0;

        int stride = bmData.Stride;
        int stride2 = bmData2.Stride;

        int nWidth = bmp2.Width;
        int nHeight = bmp2.Height;

        int sourceX = 0;
        int sourceY = 0;
        byte* p = (byte*)scan0.ToPointer();
        p += yPoint * stride + xPoint * 4;
        byte* p2 = (byte*)scan02.ToPointer();
        p2 += sourceY * stride2 + sourceX * 4;
        int bytes = nWidth * 4;

        for (int y = 0; y < nHeight; y++)
        {

            for (int x = 0; x <nWidth; x++)
            {


                p[0] = p2[0];
                p[1] = p2[1];
                p[2] = p2[2];
                p[3] = p2[3];

            }

            p += 4;
            p2 += 4;
        }

        bmp.UnlockBits(bmData);
        bmp2.UnlockBits(bmData2);
    }

this is the updated code

回答1:

I made some changes to make it work:

  • Use ImageLockMode.WriteOnly in the LockBits call for the image that you want to write to.
  • Don't move p2 according to xPoint and yPoint.

I saw that you set the pointers inside the outer loop, so then you would not need to move the pointers at the end of the loop. I recommend that you calculcate the starting points outside the outer loop, and move the pointers inside it.

I also recommend these changes that I did:

  • Add checks for the bounds of the images, so that you don't accidentally try to draw outside the image.

  • Make the method void. Returning a bitmap suggests that it creates a new bitmap rather than changing one of the bitmap that you pass into it.

I added a pixelBytes parameter to make it usable for different pixel formats. (Mostly because I happened to have JPEGs, not PNGs to test with.)

Code:

private static unsafe void Draw(Bitmap bmp, Bitmap bmp2, int xPoint, int yPoint, int pixelBytes) {

  BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat);
  BitmapData bmData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);

  IntPtr scan0 = bmData.Scan0;
  IntPtr scan02 = bmData2.Scan0;

  int stride = bmData.Stride;
  int stride2 = bmData2.Stride;

  int nWidth = bmp2.Width;
  int nHeight = bmp2.Height;

  int sourceX = 0;
  int sourceY = 0;

  if (xPoint < 0) {
    sourceX = -xPoint;
    nWidth -= sourceX;
    xPoint = 0;
  }
  if (yPoint < 0) {
    sourceY = -yPoint;
    nHeight -= sourceY;
    yPoint = 0;
  }
  if (xPoint + nWidth > bmp.Width) {
    nWidth = bmp.Width - xPoint;
  }
  if (yPoint + nHeight > bmp.Height) {
    nHeight = bmp.Height - yPoint;
  }

  if (nWidth > 0 && nHeight > 0) {

    byte* p = (byte*)scan0.ToPointer();
    p += yPoint * stride + xPoint * pixelBytes;
    byte* p2 = (byte*)scan02.ToPointer();
    p2 += sourceY * stride2 + sourceX * pixelBytes;

    int bytes = nWidth * pixelBytes;

    for (int y = 0; y < nHeight; y++) {
      for (int x = 0; x < bytes; x++) {
        p[0] = p2[0];
        p++;
        p2++;
      }
      p += stride - nWidth * pixelBytes;
      p2 += stride2 - nWidth * pixelBytes;
    }

  }

  bmp.UnlockBits(bmData);
  bmp2.UnlockBits(bmData2);
}