繁体   English   中英

将 Bitmap 转换为字节数组时出现不必要的黑条

[英]Unnecessary black bar when convert a Bitmap to Byte Array

我编写了一个简单的程序,将单色 bitmap 数据转换为字节数组,然后将其反转(0->1、1->0),以便将其作为 ZPL 发送到 label 打印机。

Bitmap bmp = (Bitmap)pictureBox1.Image;
Rectangle rectangle = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bd = bmp.LockBits(rectangle, ImageLockMode.ReadOnly, bmp.PixelFormat);
IntPtr ptr = bd.Scan0;
byte[] b = new byte[Math.Abs(bd.Stride) * bmp.Height];
Marshal.Copy(ptr, b, 0, b.Length);
bmp.UnlockBits(bd);
StringBuilder sb = new StringBuilder();
foreach (byte i in b)
{
    sb.Append((255 - i).ToString("X2"));

}
textBox1.Text = string.Format(@"^XA^FO100,0^GFA,{0},{0},{1},{2}^FS^XZ", b.Length, Math.Abs(bd.Stride), sb.ToString());

但是它画了一个原本没有的不必要的黑条!

(由 Line 图片提供) 在此处输入图像描述

我首先想到可能是因为我分配给 BitmapData 的矩形太大,所以其中包含一些“未使用”区域,但显然我错了,07FFFFs 仍然在那里!

我什至试图用空字符串替换它来破解它!

但这是错误的,因为它弄乱了 ZPL 输出!

当然,我可以只存储替换的字符串并重新计算^GFA命令的长度,但是如果图片“恰好”本身有一些07FFFF 我这样做基本上搞砸了图片本身!

07FFFF被我的代码“反转”了,所以它在原始字节数组中是F80000 ,所以我认为它可能类似于 bitmap 数据中的“\n”,告诉图片“继续从新行绘制“!

而且我在网上找不到任何解释为什么 bitmap 文件中有F80000的内容。

这对图片来说都很好,但我怎么能“摆脱它”呢?

我“只”想要“图片本身”的数据。

有人可以这么好心帮帮我吗??

非常感激!

原因

Stride属性为您提供图像的每条扫描线的长度(以字节为单位),向上舍入下一个 4 字节边界。 如果图像中的一条线没有填满整个扫描线,它将用 0 填充。您的代码处理并在^GFA命令中包含此填充数据,从而在右侧产生黑条。 如果不反转图像,您将不会注意到问题。

您需要反转图像,因为每个像素的值不仅是黑白的。 它是双色表的索引。 并且颜色表中的每个条目都定义了 RGB 中像素的实际颜色。 在你的情况下Color[0]=blackColor[1]=white

使用Format1bppIndexed的像素格式,如果图像宽度不是 8 的倍数,您将获得额外的复杂性。然后在图像中每行的最后使用的字节中有填充位。

让我们以您的图像为例。 您的图像宽度为 173。每行至少需要 22 (=ceil(173 / 8)) 字节来存储一行的数据。 在最后一个字节中,只有前 5 位 (=173 % 8) 有真正的像素数据。 22 以上的 4 的下一个倍数是 24。所以每一行占用 memory 的 24 个字节。另外的 2 个字节是用零填充的填充字节。

ZPL 命令^GFA

格式: ^GFa,b,c,d,data其中 a、b、c、d 和数据是命令的参数。

  • a: A表示 ASCII-HEX (Base16), B表示二进制
  • b:二进制字节数; 如果使用 ASCII,b 必须等于 c
  • c:图形字段数; 包含图像的字节数(宽度[以字节为单位]*高度)
  • d:每行字节数
  • data:指定格式的单色图像数据

据我了解该命令的文档,它只支持 8 的倍数的图像宽度,因为您只能指定行/行的字节数。 如果源图像的宽度不正确,您可以扩展所有填充位设置为 0/白色/不活动的图像。

解决方案

我已经实施了两种方法来解决这个问题。 第一个读取单色 bitmap 并使用适当的^GFA命令返回StringBuilder object。

private static StringBuilder ZPLCommandFromMonochromeFile(string pFileName)
{
    var sb = new StringBuilder();

    using (var bmp = new Bitmap(pFileName))
    {
        if (bmp.PixelFormat != PixelFormat.Format1bppIndexed)
            throw new InvalidOperationException($"We only suppoert monochrome images, file {pFileName} is invalid.");

        var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
        byte[] data = null;
        int stride = 0;
        var bd = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
        try
        {
            stride = Math.Abs(bd.Stride);
            data = new byte[stride * bmp.Height];
            System.Runtime.InteropServices.Marshal.Copy(bd.Scan0, data, 0, data.Length);
        }
        finally
        {
            bmp.UnlockBits(bd);
        }

        ZPLCommandFromArray(sb, data, stride, bmp.Width, bmp.Height);
    }

    return sb;
}

ZPLCommandFromArray将字节数组转换为^GFA命令的图像数据。

private static void ZPLCommandFromArray(StringBuilder dest, byte[] data, int stride, int width, int height)
{
    // calc the length of the destination line in bytes
    // this is the nr of bytes in a line where all 8 bits are used for the picture
    var len8 = width / 8;
    // this is the number of trailing pixels for a line if the width is not a multiple of 8
    var bits = width % 8; 
    // the minimum nr of bytes needed for a line 
    var len = len8 + (bits > 0 ? 1 : 0);

    // the number of bits we have to mask out if the width is not a multiple
    // of 8. e.g if the width is 19, we only may change 3 bits of the last byte
    var mask = (byte)(~(0xFF >> bits));

    // in the output graphics, the width will always be a multiple of 8, because
    // in the ZPL command ^GFA you can only specify the width of the imagen in 
    // number bytes.
    dest.AppendFormat("^GFA,{0},{1},{2},", len * height, len * height, len);
    dest.AppendLine();

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < len8; x++)
            dest.AppendFormat("{0:X2}", (byte)~data[y * stride + x]);

        if (bits > 0)
            dest.AppendFormat("{0:X2}", (byte)((~data[y * stride + len8]) & mask));

        dest.AppendLine();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM