首页 文章

在8位图像上使用Graphics.FillRectangle方法

提问于
浏览
1

我想在图像的顶部和底部添加黑色 Banner . 我可以添加 Banner ,但结果位图的像素格式更改为32位 . 有没有办法得到一个8位位图 . 如上所述here,如果我在构造函数中设置了8位pixelFormat,创建图形将引发异常 . 我读到,如果我从32转换为8,可能像素值将不同于原始像素值 . 使用for循环使用't know if I can create a new bitmap with the desired height and add the black banners'像素 . 任何人都有更好更简单的方法吗?

我的代码如下:

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");
            }

1 回答

  • 0

    这是一个在 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");
    

    结果:

    __bmp8bpp

    还有改进的余地:

    • 缺少lockbits中的所有错误检查,包括wrt pixelformat和rectangle data

    • 使用更动态的方案添加颜色可能不错,而不是添加到最后

    • 用于找到调色板中已有的最接近颜色的方案也可能很好

    • 用透明度绘图的方案也很不错 . 为此,必须首先确定所有必要的新颜色;也是透明模式 .

    • 也许应该返回方法中使用的 index ,以便下次调用可以引用它 .

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

    Update: 这里有几行要添加(**)或更改(*)以允许绘制 unfilled 矩形; stroke 0 fills 矩形..:

    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);
    

相关问题