Sprite/Texture Atlas:GDI + Bitmap.MakeTransparent,用于带有OpenTK的颜色键 [英] Sprite/Texture Atlas: GDI+ Bitmap.MakeTransparent for color-key with OpenTK
问题描述
我正在使用C#和OpenTK编写一个sprite/texture地图集功能的支持类. 到目前为止,大多数功能都可以正常工作(正交视图上的简单2D切片).
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).
我的问题与调用GDI + Bitmap.MakeTransparent()方法来设置用作色键的颜色(洋红色/0xFFFF00FF)时出现意外的显示结果有关.
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.
似乎我对bitmap.LockBits()和GL.TexImage2D()调用使用了错误的像素格式参数.我的代码基于确实有效的示例,但是它们的共同点是传递给LockBits()的矩形适用于整个图像.
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);
我已经在不同的Boxen上使用上面的代码测试了一个小型演示,并观察了以下结果:
I have tested a small demo using the code above on different boxen, and observe these results:
- Windows 7框:洋红色像素起到透明作用(期望的结果)
- Windows XP框:洋红色像素呈现为黑色
- Ubuntu Linux框:洋红色像素呈现为洋红色
这让我感到惊讶,因为我预料到(GDI +和OpenGL以及OpenTK绑定)在不同的盒子上会起到相同的作用.
This surprises me, as I anticipated that (GDI+ and OpenGL and the OpenTK bindings) would act the same on different boxes.
就我已经吸收了GDI +和OpenGL/OpenTK API文档而言,我认为我的困惑与这两点有关:
To the extent that I have absorbed the GDI+ and OpenGL / OpenTK API documentation, I think my puzzlement relates to these two points:
-
调用MakeTransparent()+ LockBits()+ GL.TexImage2D()以便导致将指定的颜色呈现为透明的正确方法是什么?
What is a correct way of calling MakeTransparent() + LockBits() + GL.TexImage2D(), so as to result in the specified color being rendered as transparent?
为什么当调用LockBits()而不是整个图像的子矩形时,对于某些像素格式参数组合,为什么看到奇怪的显示结果(好像步幅"被错误地计算了?) /p>
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?
更新: 我将代码缩减为Github上的一个小项目: https://github.com/sglasby/OpenGL_Transparent_Sprite
Update: I have whittled down my code into a small project on Github: https://github.com/sglasby/OpenGL_Transparent_Sprite
此外,我偶然发现了一个有效的参数组合 (LockBits()的参数3为Format32bppPArgb), 尽管尚不清楚为什么起作用,但鉴于该文档暗示需要使用另一个pixelformat: http://msdn.microsoft.com/en-us/library/8517ckds.aspx (声明调用MakeTransparent后,位图将位于Format32bppArgb中.)
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).
推荐答案
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.
我在带有Intel 2000HD的Win7上运行了您的示例项目,并得到以下结果:
I ran your example project on Win7 with an Intel 2000HD and got the following results:
-
Format32bppPArgb
正常工作 -
Format32bppRgb
正常工作 -
Format32bppArgb
被加扰
Format32bppPArgb
works correctlyFormat32bppRgb
works correctlyFormat32bppArgb
is scrambled
在进一步研究中,这似乎与OpenGL无关,而是与Bitmap.LockBits
的工作方式有关.
On further investigation, this does not appear to be linked to OpenGL, but rather to the way Bitmap.LockBits
works.
针对每种方法,在调试器上检查data.Stride
的值:
Check the values of data.Stride
on the debugger for each approach:
-
Format32bppPArgb
的步幅为128(正确为位图宽度的4倍) -
Format32bppRgb
的步幅为128(是位图宽度的4倍,正确) -
Format32bppArgb
的步幅为512(是位图宽度的16倍,?)
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在这里没有提供有用的信息.在这一点上,我不知道为什么会这样.如果我能发现任何内容,我将更新此答案.
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.
瞧,如果在拆包数据时强行执行正确的步幅,输出看起来是正确的:
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
这篇关于Sprite/Texture Atlas:GDI + Bitmap.MakeTransparent,用于带有OpenTK的颜色键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!