【问题标题】:Unnecessary black bar when convert a Bitmap to Byte Array将位图转换为字节数组时出现不必要的黑条
【发布时间】:2020-11-15 17:19:49
【问题描述】:

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

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

但它画了一个原本不存在的不必要的黑条!

(行图像的礼貌)

我最初认为这可能是因为我分配给 BitmapData 的矩形太大所以包含了一些“未使用”的区域,但显然我错了,07FFFFs 仍然存在!

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

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

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

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

我在网上找不到任何解释为什么位图文件中有F80000s。

对画面都好,但我怎么能“摆脱它”!?

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

有人能帮帮我吗!?

非常感谢!

【问题讨论】:

  • PixelFormat 属性有什么值?图片的宽度是4*8的倍数吗?因为步幅向上舍入到 4 字节边界。见docs
  • @GWimpassinger 我认为这正是问题所在!我检查了图片大小,它的宽度是 173,Stride 是 24,所以 24*8-173 = 19,如果我在 0 后面加上 19 个 1,它是“完全”7FFFF!所以我想我只需要将图片的宽度设为 32 的倍数!谢谢你的帮助!如果可以,请将其发布为答案,以便我将其标记为最佳答案!非常感谢!
  • 可能还有其他解决方案。但是你的位图宽度必须是 8 的倍数。试试...^GFA,{0},{1},{2},{3}...", b.Length, bmp.Width / 8 * bmp.Height, bmp.Width / 8, sb.ToString()。我不确定{1} 的论点。见ZPL docs
  • 您不必调整图片大小,而是逐行读取并跳过每行的步幅字节,然后将内容写入您的构建器。

标签: c# zpl bitmapdata


【解决方案1】:

原因

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

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

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

让我们以您的图像为例。 您的图像宽度为 173。每行至少需要 22 (=ceil(173 / 8)) 字节来存储一行的数据。在最后一个字节中,只有前 5 位 (=173 % 8) 具有真实像素数据。 22 之上 4 的下一个倍数是 24。所以每行占用 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/white/inactive 的图像展开。

解决方案

我已经实现了两种方法来解决这个问题。第一个读取单色位图并返回带有适当^GFAcommand 的StringBuilder 对象。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-26
    • 2018-05-06
    • 2013-01-14
    • 2013-07-29
    • 2012-11-09
    • 2011-06-26
    相关资源
    最近更新 更多