代码改变世界

Winform的Bitmap调色板的一个问题

2011-03-27 01:07  贼寇在何方  阅读(2397)  评论(7编辑  收藏  举报

帮人做一个遥感数字图像处理的小功能,其中的数据源是Landset图像,八位灰度。
然而Winform真的太弱了,System.Drawing下的PixelFormat(像素格式)枚举居然没有提供八位灰度模式,无奈只好用

PixelFormat.Format8bppIndexed(八位颜色索引)替代,可是问题又来了···

 

要使颜色索引模式生效,必须在Bitmap的调色板中设置每个索引到具体的颜色的映射:

for (int i = 0; i < 256; i++)
{
    // 每一个灰度映射到一种颜色
    bmp.Palette.Entries[i] = Color.FromArgb(i, i, i);
}

运行结果,图像呈现了一种蓝色色调,没有在灰度模式下显示。
我找到了bmp.Palette的定义,Bitmap的Palette属性可读可写,是一个ColorPalette(调色板)对象(MSDN链接)。
那么是否能够直接对这个属性赋值呢?
似乎有难度,因为“调色板”没有一个公开的构造方法。于是谷歌了一下解决方法,有一位网友是这么做的:

Bitmap bmpTemp = newBitmap(1, 1, PixelFormat.Format8bppIndexed);
ColorPalette palette = bmpTemp.Palette;
for (int i = 0; i < 256; i++)
{
   palette.Entries[i] = Color.FromArgb(i, i, i);
}
bmp.Palette = palette;

特别构造了一个临时的Bitmap,为的就是取他的调色板。运行后,确实有效,灰度图像正常显示。
有没有不用更容易的办法呢?有!我在后面的尝试当中,发现了另一种方法:

ColorPalette palette = bmp.Palette;
for (int i = 0; i < 256; i++)
{
   palette.Entries[i] = Color.FromArgb(i, i, i);
}
bmp.Palette = palette;

 

和第一段代码没有什么差别啊,只是定义一个新的变量来保存bmp.Palette。这段代码如何能够起作用呢?
但确实起作用了···

 

按照正常的思维,ColorPalette是引用类型,所以palette只是复制了bmp.Palette的引用,最后一句把自己的引用重新赋值回来应当没有用处,因此效果和第一段代码应当一样!!
既然MSDN不能解决疑惑,那只有求助于Reflector了。
在Bitmap的父类Image中,找到了Palette属性的实现:

public ColorPalette get_Palette()
{
    return this._GetColorPalette();
}

private ColorPalette _GetColorPalette()
{
    ······
    ColorPalette palette = new ColorPalette(size);
    IntPtr ptr = Marshal.AllocHGlobal(size);
    ······
   
    return palette;
}

很明显,Bitmap.Palette并没有像普通的属性一样,返回对应字段的引用,而是复制了一份新的。这就好的解释了为什么第一段代码完全没有效果。

 

当然,这样的类用起来实在不爽,不了解他的内部构造,就没有办法正常使用。能改用WPF的,还是改了吧。