【问题标题】:Using Graphics.FillRectangle Method on 8-bit image在 8 位图像上使用 Graphics.FillRectangle 方法
【发布时间】:2019-05-12 05:58:02
【问题描述】:

我正在尝试在图像的顶部和底部添加黑色横幅。我可以添加横幅,但结果位图的像素格式更改为 32 位。有什么办法可以得到一个 8 位的位图。 如here 所述,如果我在构造函数中设置了 8 位 pixelFormat,则创建图形会引发异常。 我读到如果我从 32 转换为 8,像素值可能会与原始值不同。不知道我是否可以创建一个具有所需高度的新位图并使用 for 循环添加黑色横幅的像素。谁有更好更简单的方法?

我的代码如下:

            Bitmap img = image as Bitmap;

            using (Bitmap bitmap = new Bitmap(img.Width, img.Height + 200*2)) // create blank bitmap of desired size
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                SolidBrush brush = new SolidBrush(Color.White);
                graphics.FillRectangle(brush, 0, 0, img.Width,200);
                // draw existing image onto new blank bitmap
                graphics.DrawImage(img, 0, 200, img.Width, img.Height);

                // draw your rectangle below the original image
                graphics.FillRectangle(brush, 0, 200 + img.Height, img.Width, 200);
               // bitmap.Save(@"c:\Test1.bmp");
            }

【问题讨论】:

  • 您引用的链接向您展示了要走的路..-我很想以重复的形式关闭..

标签: c# graphics bitmap


【解决方案1】:

这是一个在bmp8bpp 位图上执行FillRectangle 的例程。

你传入一个索引,如果是肯定的,它将用于将颜色放入调色板。如果你传入一个负索引,它会尝试找到颜色,如果没有找到,它将把颜色放在索引的末尾。这将覆盖之前的任何颜色!

void FillIndexedRectangle(Bitmap bmp8bpp, Rectangle rect, Color col, int index)
{
    var pal = bmp8bpp.Palette;
    int idx = -1;
    if (index >= 0) idx = index;
    else
    {
        if (pal.Entries.Where(x => x.ToArgb() == col.ToArgb()).Any())
            idx = pal.Entries.ToList().FindIndex(x => x.ToArgb() == col.ToArgb());
        if (idx < 0) idx = pal.Entries.Length - 1;
    }

    pal.Entries[idx] = col;
    bmp8bpp.Palette = pal;

    var bitmapData = 
        bmp8bpp.LockBits(new Rectangle(Point.Empty, bmp8bpp.Size),
                         ImageLockMode.ReadWrite, bmp8bpp.PixelFormat);
    byte[] buffer=new byte[bmp8bpp.Width*bmp8bpp.Height];

    Marshal.Copy(bitmapData.Scan0, buffer,0, buffer.Length);

    for (int y = rect.Y; y < rect.Bottom; y++)
    for (int x = rect.X; x < rect.Right; x++)
    {
            buffer[x + y * bmp8bpp.Width] = (byte)idx;
    }

    Marshal.Copy(buffer, 0, bitmapData.Scan0,buffer.Length);
    bmp8bpp.UnlockBits(bitmapData);
}

示例调用:

Bitmap img = new Bitmap(200, 200, PixelFormat.Format8bppIndexed);
FillIndexedRectangle(img, new Rectangle(0,0,200, 200), Color.Silver, 21);
FillIndexedRectangle(img, new Rectangle(23, 23, 55, 99), Color.Red, 22);
FillIndexedRectangle(img, new Rectangle(123, 123, 55, 33), Color.Black, 23);
FillIndexedRectangle(img, new Rectangle(1, 1, 123, 22), Color.Orange, 34);
FillIndexedRectangle(img, new Rectangle(27, 27, 16, 12), Color.Black, -1);
img.Save("D:\\__bmp8bpp.png");

结果:

还有改进的余地:

  • lockbits 中的所有错误检查都缺失,包括像素格式和矩形数据
  • 使用更具动态性的方案添加颜色可能会更好,而不是添加到末尾
  • 在调色板中找到最接近颜色的方案也很不错
  • 具有透明度的绘图方案也不错。为此,必须首先确定所有必要的新颜色;还有透明模式。
  • 也许应该返回方法中使用的index,以便下一次调用可以引用它..

对于矩形以外的其他形状,可以使用复制例程,首先将它们绘制到“正常”32bpp 位图上,然后将像素传输到缓冲区。..

更新:这里有几行要添加 (**) 或更改 (*) 以允许绘制 未填充 矩形; stroke 0 填充矩形..:

 void FillIndexedRectangle(Bitmap bmp8bpp, Rectangle rect, 
                           Color col, int index, int stroke)  // * 

...
...
Marshal.Copy(bitmapData.Scan0, buffer,0, buffer.Length);
Rectangle ri = rect;                               //**
if (stroke > 0) ri.Inflate(-stroke, -stroke);      //**
for (int y = rect.Y; y < rect.Bottom; y++)
for (int x = rect.X; x < rect.Right; x++)
{
        if (ri == rect || !ri.Contains(x,y))      //**
            buffer[x + y * bmp8bpp.Width] = (byte)idx;
}

Marshal.Copy(buffer, 0, bitmapData.Scan0,buffer.Length);

【讨论】:

  • 感谢您没有结束这个问题。我认为将这个答案保留在网络上是件好事,因为这正是我所需要的。我刚刚添加了一个类似的方法来在目标位图中添加源位图
  • 是的,这就是我写它的原因。我添加了 3 行来更改以允许 DrawRectangle。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-31
相关资源
最近更新 更多