ImageAttributes.SetColorKey()和Bitmap.MakeTransparent都不是在需要重新映射位图调色板的索引颜色时的首选方法:前者每次只能设置一个颜色,后者会将原始图像转换为32bpp图像。
您需要更改索引图像ColorPalette或使用ImageAttributes.SetRemapTable()方法绘制新的位图。该方法接受一个ColorMap对象数组。当绘制位图时,ColorMap用于指定替换旧颜色的新颜色。
让我们创建一个示例8bpp图像并应用部分调色板,然后使用Image.LockBits解析BitmapData并将这些颜色应用于一组3x3矩形:
var image = new Bitmap(12, 12, PixelFormat.Format8bppIndexed);
var palette = image.Palette;
palette.Entries[0] = Color.SteelBlue;
palette.Entries[1] = Color.Pink;
palette.Entries[2] = Color.Red;
palette.Entries[3] = Color.Orange;
palette.Entries[4] = Color.YellowGreen;
palette.Entries[5] = Color.Khaki;
palette.Entries[6] = Color.Green;
palette.Entries[7] = Color.LightCoral;
palette.Entries[8] = Color.Maroon;
image.Palette = palette;
var data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, image.PixelFormat);
int rectsCount = 3;
int step = data.Stride / rectsCount;
int colorIdx = 0, col = 0;
byte[] buffer = new byte[Math.Abs(data.Stride) * image.Height];
for (int i = 1; i <= rectsCount; i++) {
for (int y = 0; y < data.Height; y++) {
for (int x = col; x < (step * i); x++) {
buffer[x + y * data.Stride] = (byte)colorIdx;
}
colorIdx += (y + 1) % step == 0 ? 1 : 0;
}
col += step;
}
Marshal.Copy(buffer, 0, data.Scan0, buffer.Length);
image.UnlockBits(data);
以下是翻译的结果:
这条代码生成了一张有趣的图片(放大25倍):
![ImageAttributes SetColorMap Original Colors](https://istack.dev59.com/i2AfV.gif)
现在,您想将调色板中的两种颜色变为透明: Color.Pink
和 Color.Green
。
您可以构建一个ColorMap
对象数组,指定新颜色替换旧颜色的方式:
var mapPink = new ColorMap() { OldColor = Color.Pink, NewColor = Color.Transparent };
var mapGreen = new ColorMap() { OldColor = Color.Green, NewColor = Color.Transparent };
var colorMap = new ColorMap[] { mapPink, mapGreen };
然后:
- 要么将图像调色板中的每个颜色替换为新映射的颜色:
(请注意,我不是直接传递[Image].Palette
对象,而是使用先前创建的调色板副本var palette = image.Palette;
:如果您直接传递图像调色板,更改不会被注册)
var palette = image.Palette;
image.Palette = RemapImagePalette(palette, colorMap);
private ColorPalette RemapImagePalette(ColorPalette palette, ColorMap[] colorMaps)
{
for (int i = 0; i < palette.Entries.Length; i++) {
foreach (ColorMap map in colorMaps) {
if (palette.Entries[i] == map.OldColor) {
palette.Entries[i] = map.NewColor;
}
}
}
return palette;
}
或使用
ImageAttributes.SetRemapTable()
方法生成一个新的位图,并使用新颜色映射绘制索引图像,使用接受
ImageAttributes
参数的
Graphics.DrawImage()
方法:
Bitmap remappedImage = ImageRemapColors(image, new Size(300, 300), colorMap);
private Bitmap ImageRemapColors(Image image, Size newSize, ColorMap[] map)
{
var bitmap = new Bitmap(newSize.Width, newSize.Height);
using (var g = Graphics.FromImage(bitmap))
using (var attributes = new ImageAttributes()) {
if (map != null) attributes.SetRemapTable(map);
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = PixelOffsetMode.Half;
g.DrawImage(image, new Rectangle(Point.Empty, newSize),
0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
}
return bitmap;
}
这些方法生成相同的输出。
- 其中一种方法可以用来更改索引图像格式的调色板。
- 另一种方法是在设备上下文中以重新映射颜色的形式呈现图像(例如将位图分配给控件的图像属性)。
- [附加] 另一种选项是使用纹理画刷,如此所示:
如何在图像上绘制透明形状
![ImageAttributes SetColorMap Remapped Colors](https://istack.dev59.com/hjqvE.gif)
Color.Pink
和Color.Green
的索引,并将它们都设置为Color.Transparent
,然后将修改后的调色板重新分配给图像。 - Nyerguds