views:

379

answers:

3

I'm trying to use a bitmap in an unsafe context, and am seeing instability in that, e.g., the program runs the first time round but fails the second. Here is the code:

private static void RenderBitmap(Graphics g)
{
  const int width = 150, height = 150;
  using (Bitmap bmp = new Bitmap(width, height, 
    System.Drawing.Imaging.PixelFormat.Format24bppRgb))
  {
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    System.Drawing.Imaging.BitmapData bmpData =
      bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, 
        bmp.PixelFormat);
    NativeMethods.RenderText(Graphics.FromImage(bmp).GetHdc(), bmpData.Scan0,
      "This works only first time round", "Segoe", 10, 
      new RGBA(255, 0, 0, 255), width, height);
    bmp.UnlockBits(bmpData);
    g.DrawImage(bmp, new Rectangle(width, height, width, -height));
  }
}

Seeing how this isn't working, I have a few questions. Is what I'm doing safe and correct, provided the native RenderText method manipulates the bitmap memory directly? Is my way of getting HDC from the bitmap correct, or should I use the parameter g that was passed from a drawing method?

The error I'm getting is this:

System.AccessViolationException was unhandled Message="Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

+1  A: 

Maybe this is a silly question, but why don't you use the TextRenderer class that comes with .NET instead of using p/invoke?

TextRenderer::DrawText Method (IDeviceContext, String, Font, Point, Color)

http://msdn.microsoft.com/en-us/library/4ftkekek.aspx

-Oisin

x0n
I'd like to use my own text rendering algorithm.
Dmitri Nesteruk
+1  A: 

The NativeMethods.RenderRext method can't safely work with the bitmap data, as it doesn't know how wide the scan lines of the bitmap is, and if it is stored upside down in memory or not. The symptoms suggests that the method is writing to memory outside the bitmap, overwriting something else that you need in your application.

The BitmapData.Stride property has the information that the method needs to work with the data. It contains the scan line width in bytes, and if it's negative it means that the bitmap is stored upside down in memory. Simply Scan0 is the address of the first scan line, and Scan0 + Stride is the address of the second scan line.

Guffa
I'm using the following expression to get the stride: (width * 3 + 3) / 4 * 4 -- is this wrong?
Dmitri Nesteruk
That gives you the absolute value of the stride, you still don't know if it should be negative or not.
Guffa
A: 

Well, after much pain and suffering, I found a solution: instead of passing in a memory buffer to be filled in, I passed a device context (HDC) to be rendered into. Seems to be working so far!

Thanks to all who answered.

Dmitri Nesteruk