[英]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());
但是它画了一个原本没有的不必要的黑条!
我首先想到可能是因为我分配给 BitmapData 的矩形太大,所以其中包含一些“未使用”区域,但显然我错了,07FFFFs 仍然在那里!
我什至试图用空字符串替换它来破解它!
但这是错误的,因为它弄乱了 ZPL 输出!
当然,我可以只存储替换的字符串并重新计算^GFA
命令的长度,但是如果图片“恰好”本身有一些07FFFF
? 我这样做基本上搞砸了图片本身!
07FFFF
被我的代码“反转”了,所以它在原始字节数组中是F80000
,所以我认为它可能类似于 bitmap 数据中的“\n”,告诉图片“继续从新行绘制“!
而且我在网上找不到任何解释为什么 bitmap 文件中有F80000
的内容。
这对图片来说都很好,但我怎么能“摆脱它”呢?
我“只”想要“图片本身”的数据。
有人可以这么好心帮帮我吗??
非常感激!
Stride
属性为您提供图像的每条扫描线的长度(以字节为单位),向上舍入下一个 4 字节边界。 如果图像中的一条线没有填满整个扫描线,它将用 0 填充。您的代码处理并在^GFA
命令中包含此填充数据,从而在右侧产生黑条。 如果不反转图像,您将不会注意到问题。
您需要反转图像,因为每个像素的值不仅是黑白的。 它是双色表的索引。 并且颜色表中的每个条目都定义了 RGB 中像素的实际颜色。 在你的情况下Color[0]=black
和Color[1]=white
。
使用Format1bppIndexed
的像素格式,如果图像宽度不是 8 的倍数,您将获得额外的复杂性。然后在图像中每行的最后使用的字节中有填充位。
让我们以您的图像为例。 您的图像宽度为 173。每行至少需要 22 (=ceil(173 / 8)) 字节来存储一行的数据。 在最后一个字节中,只有前 5 位 (=173 % 8) 有真正的像素数据。 22 以上的 4 的下一个倍数是 24。所以每一行占用 memory 的 24 个字节。另外的 2 个字节是用零填充的填充字节。
^GFA
格式: ^GFa,b,c,d,data
其中 a、b、c、d 和数据是命令的参数。
A
表示 ASCII-HEX (Base16), B
表示二进制据我了解该命令的文档,它只支持 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.