C# 如何在绘制8bpp位图时使调色板中的颜色透明
我有一个8bpp索引位图,带有自定义的256色调色板,调色板中的特定颜色(C# 如何在绘制8bpp位图时使调色板中的颜色透明,c#,winforms,bitmap,transparency,gdi+,C#,Winforms,Bitmap,Transparency,Gdi+,我有一个8bpp索引位图,带有自定义的256色调色板,调色板中的特定颜色(color.Pink和color.Green)表示透明度。 我可以在位图上使用MakeTransparent(color)方法(每种颜色两次),但它会将其转换为32bpp。所以我用的是: using var imageAttr = new ImageAttributes(); imageAttr.SetColorKey(pink, pink, ColorAdjustType.Default); 然后 g.DrawImag
color.Pink
和color.Green
)表示透明度。我可以在位图上使用
MakeTransparent(color)
方法(每种颜色两次),但它会将其转换为32bpp。所以我用的是:
using var imageAttr = new ImageAttributes();
imageAttr.SetColorKey(pink, pink, ColorAdjustType.Default);
然后
g.DrawImage(bitmap, destRect, X, Y, Width, Height, GraphicsUnit.Pixel, imageAttr);
它按原样绘制位图,但只将Color.Pink
更改为透明颜色。如何对第二种颜色(color.Green
)也这样做?当您需要重新映射位图调色板的索引颜色时,这两种颜色都不是首选:前者一次只能设置一种颜色,后者在32bpp图像中变换原始图像
您需要更改索引图像或使用该方法绘制新位图。此方法接受对象数组。ColorMap用于指定在绘制位图时替换旧颜色的新颜色
让我们制作一个8bpp图像示例,并应用部分调色板,然后使用解析并将这些颜色应用于一组3x3矩形:
var image = new Bitmap(12, 12, PixelFormat.Format8bppIndexed);
var palette = image.Palette; // Copy the Palette entries
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; // Sets back the modified palette
var data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, image.PixelFormat);
int rectsCount = 3; // Generate 3 Rectangles per line...
int step = data.Stride / rectsCount; // ...of this size (single dimension, since w=h here)
int colorIdx = 0, col = 0; // Color and Column positions
byte[] buffer = new byte[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);
然后:
- 用新映射的颜色替换图像调色板中的每种颜色:
(请注意,我不是直接传递
对象,而是使用以前创建的调色板副本([Image].palete
):如果直接传递图像调色板,则不会注册更改)var palete=Image.palete;
- 一个可用于更改索引图像格式的调色板
- 另一种方法是在设备上下文中使用重新映射的颜色显示图像(例如,将位图指定给控件的Image属性)
- [额外]另一个选项是使用TextureBrush,如下所示:
简单得多:从图像中获取调色板(自动复制调色板的操作),在其上找到
Color.Pink
和Color.Green
的索引,并将它们都设置为Color.Transparent,然后将修改后的调色板重新分配给图像。
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 };
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;
}
// [...]
var palette = image.Palette;
image.Palette = RemapImagePalette(palette, 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;
}
// [...]
// Draws the 12x12 indexed 8bpp Image to a new 300x300 32bpp Bitmap
Bitmap remappedImage = ImageRemapColors(image, new Size(300, 300), colorMap);