Sprite/Texture Atlas: GDI+ Bitmap.MakeTransparent for color-key with OpenTK

风格不统一 提交于 2019-12-12 11:24:08

问题


I am writing a support class for sprite/texture atlas functionality, using C# with OpenTK. Most functionality is working fine thus far (simple 2D tiles on an orthographic view).

My problem relates to unexpected display results when calling the GDI+ Bitmap.MakeTransparent() method to set a color (Magenta / 0xFFFF00FF) for use as a color-key.

It would seem that I am using incorrect pixel format parameters for the bitmap.LockBits() and GL.TexImage2D() calls. My code was based on examples which indeed worked, but which had in common that the rectangle passed to LockBits() was for the entire image.

The calls which pertain to this process are:

<!-- language: C# -->
Bitmap bitmap = new Bitmap(filename);
bitmap.MakeTransparent(Color.Magenta);

GL.GenTextures(1, out texture_id);
GL.BindTexture(TextureTarget.Texture2D, texture_id);

// "rect" is initialized for one of:
//   - the dimensions of the entire image 
//     (0, 0, bitmap.width, bitmap.height)
//   - the dimensions for a sub-rectangle within the image (for one tile)
//     (tile_x * tile_width, tile_y * tile_height, tile_width, tile_height)
// I observe different behaviors for a sub-rectangle, 
// as compared to the entire image, when in combination with 
// the .MakeTransparent() call.
//
// This code is in a load_tile() method, and the plan was to make 
// multiple calls per image file, one per tile to extract as a GL texture.  
// Without transparency, that worked fine.

Rectangle  rect = new Rectangle(xx, yy, width, height);
BitmapData data = bitmap.LockBits(rect,
                                  ImageLockMode.ReadOnly, 
                                  System.Drawing.Imaging.PixelFormat.Format32bppRgb);
// In the absence of calling bitmap.MakeTransparent(),
// images loaded and displayed as expected with Format24bppRgb.
// With MakeTransparent() and Format32bppRgb, the results seem to be OS-dependent.
//     (At first I thought the "correct" combination to be found, 
//     but then found that the results were "right" only under Windows 7.)

GL.TexImage2D(
        OpenTK.Graphics.OpenGL.TextureTarget.Texture2D,   // texture_target,
        0,                                                // level,
        OpenTK.Graphics.OpenGL.PixelInternalFormat.Rgba,  // internal_format
        data.Width, data.Height,                          // width, height, 
        0,                                                // border,
        OpenTK.Graphics.OpenGL.PixelFormat.Bgra,          // pixel_format
        OpenTK.Graphics.OpenGL.PixelType.UnsignedByte,    // pixel_type
        data.Scan0                                        // pixels
        );
// Certainly the internal_format and pixel_format arguments are pertinent,
// but other combinations I have tried produced various undesired display results.
// After reading various (OpenGL, OpenTK, and GDI+) docs, still I am not enlightened..

bitmap.UnlockBits(data);

I have tested a small demo using the code above on different boxen, and observe these results:

  • Windows 7 box: magenta pixels act as transparent (the desired result)
  • Windows XP box: magenta pixels rendered as black
  • Ubuntu Linux box: magenta pixels rendered as magenta

This surprises me, as I anticipated that (GDI+ and OpenGL and the OpenTK bindings) would act the same on different boxes.

To the extent that I have absorbed the GDI+ and OpenGL / OpenTK API documentation, I think my puzzlement relates to these two points:

  • What is a correct way of calling MakeTransparent() + LockBits() + GL.TexImage2D(), so as to result in the specified color being rendered as transparent?

  • Why do I see strange display results (as if the "stride" was mis-calculated) for certain pixel format parameter combinations, when LockBits() is called for a sub-rectangle rather than the entire image?

Update: I have whittled down my code into a small project on Github: https://github.com/sglasby/OpenGL_Transparent_Sprite

Also, I stumbled upon a parameter combination that works (arg 3 of LockBits() is Format32bppPArgb), though it is not clear why it works, given that the documentation implies another pixelformat is wanted: http://msdn.microsoft.com/en-us/library/8517ckds.aspx (which states that the bitmap will be in Format32bppArgb after calling MakeTransparent).


回答1:


While this is a separate issue to your question, in most cases you should actually use premultiplied-alpha (Format32bppPArgb). If this format is working correctly, then understanding why Format32bppArgb does not work is mostly an academic exercise.

I ran your example project on Win7 with an Intel 2000HD and got the following results:

  • Format32bppPArgb works correctly
  • Format32bppRgb works correctly
  • Format32bppArgb is scrambled

On further investigation, this does not appear to be linked to OpenGL, but rather to the way Bitmap.LockBits works.

Check the values of data.Stride on the debugger for each approach:

  • Format32bppPArgb has a stride of 128 (4x the bitmap width, correct)
  • Format32bppRgb has a stride of 128 (4x the bitmap width, correct)
  • Format32bppArgb has a stride of 512 (16x the bitmap width, ?)

MSDN does not turn up something useful here. At this point, I cannot tell why this is happening. I'll update this answer if I manage to uncover anything.

Edit: lo and behold, if you force the correct stride when unpacking the data, the output looks correct:

GL.PixelStore(PixelStoreParameter.UnpackRowLength, data.Width * 4); // 4x for 32bpp


来源:https://stackoverflow.com/questions/12417946/sprite-texture-atlas-gdi-bitmap-maketransparent-for-color-key-with-opentk

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!