Question

I am printing the mono chorme bit map image on thermal printer where i am able to print the image but at rightmost, one vertical line is getting printed. (The line is from Top right to bottom right with nearly 2mm thick)

Bitmap image = new Bitmap(imagePath, false);
 int imageDepth = System.Drawing.Bitmap.GetPixelFormatSize(image.PixelFormat);

 Rectangle monoChromeBitmapRectangle = new Rectangle(0, 0, image.Width, image.Height);
 BitmapData monoChromebmpData = null;
 int stride = 0;

 monoChromebmpData = image.LockBits(monoChromeBitmapRectangle, ImageLockMode.ReadOnly, resizedImage.PixelFormat);
 IntPtr ptr = monoChromebmpData.Scan0;
 stride = monoChromebmpData.Stride;
 int numbytes = stride * image.Height;
 byte[] bitmapFileData = new byte[numbytes];
 Marshal.Copy(ptr, bitmapFileData, 0, numbytes);

 image.UnlockBits(monoChromebmpData);

 //Invert bitmap colors
  for (int i = 0; i < bitmapFileData.Length; i++)
  {
      bitmapFileData[i] ^= 0xFF;
  }

 StringBuilder hexaDecimalImageDataString = new StringBuilder(bitmapFileData.Length * 2);
 foreach (byte b in bitmapFileData)
    hexaDecimalImageDataString.AppendFormat("{0:X2}", b);

return hexaDecimalImageDataString;

Here i am converting the mono chrome bitmap image to byte array and from byte array to hexadecimal string. i googled in forums but this kind of error is not discussed. (May be i am doing silly mistake) Can any one suggest where exactly i am making the mistake.

Thanks in advance.

Cheers, Siva.

Was it helpful?

Solution 3

The comments from MiMo and MyItchyChin helped me alot on resolving the issue.

The problem is getting the extra line at the end. So technically, on printing the each row of image, last few byte information is incorrect.

The reason for getting this problem is, the image size could be anything, but whne it sends to printer, the byte width should be divisible by eight. In my case my printer expects the bytewidth as input so i must be careful on passing the image.

Assume i have image 168x168 size.

byteWidth = Math.Ceiling(bitmapDataWidth / 8.0);

so the byteWidth is 21 here, As per printer expectation i did Left shift operation to 24 which is diviseble by 8, so virtually i increased the size of image by 3bytes and then started reading the byte information. The line i am talking about is that extra 3 bytes. Since no data is there, the black line is getting printed.

I wrote the logic in such a way, where byte array doesnot effect with shift operations hence it worked for me.

Early days for me in image processing, So please ignore, if i made a silly mistake and explaining the solution here.

OTHER TIPS

Your are returning monoChromebmpData.Stride * image.Height bytes, i.e. each line in the image will be exactly monoChromebmpData.Stride * 8 pixels wide - but probably the original image has a pixel width that is less than that, hence the extra vertical line on the right.

Try something like this:

byte[] masks = new byte[]{0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
int byteWidth = (image.Width+7)/8;
int nBits = imageWidth % 8;
byte[] actualBitmapFileData = new byte[byteWidth*image.Height];
int yFrom = 0;
for (int y=0; y<image.Height; y++) {
  for (int x=0; x<byteWidth-1; x++) {
    actualBitmapFileData[y*byteWidth + x] = (bitmapFileData[yFrom + x] ^ 0xFF);
  }
  int lastX = byteWidth - 1;
  actualBitmapFileData[y*byteWidth + lastX] = (bitmapFileData[yFrom + lastX] ^ 0xFF) & masks[nBits];
  yFrom += stride;
}

it creates an actualBitmapFileData array for bitmapFileData with of the correct size.

Note that the last byte of every line would contain only nBits pixels - and so needs to be 'masked' to clear out the extra bit not corresponding with any pixel. This is done by & masks[nBits], where masks is an array of 8 bytes with the 8 masks to use. The actual values of the mask depend on how the printer works: you might need to set the extra bits to 0 or to 1, and the extra bits can be the most-significant or the least-significant ones. The mask values used above assume that the most significant bits are rendered to the right, and that the masked bits should be set to 0. Depending on how the printer works it might be necessary to swap the bits and/or set the masked bits to 1 instead than zero (complementing the mask and using | instead than &)

For performance reasons each horizontal row in a Bitmap is buffered to a DWORD boundary (see this answer for more details). So if your Bitmap's width multiplied by it's bits-per-pixel(bpp) is not divisible by 32 (DWORD = 32bits) then it's padded with extra bits. So a 238x40 1bpp Bitmap has a memory foot print of 8 DWORDs per row or 256 bits.

The BitmapData object's Stride property is the number of bytes that each row of your bitmap consumes in memory. When you capture the Byte Array, you're capturing that padding as well.

Before you convert the byte array to hex you need to trim the buffer off the end. The following function should do that nicely.

public static byte[] TruncatePadding(byte[] PaddedImage, int Width, int Stride, int BitsPerPixel)
{
    //Stride values can be negative
    Stride = Math.Abs(Stride);
    //Get the actual number of bytes each row contains.
    int shortStride = (int)Math.Ceiling((double)(Width*BitsPerPixel/8));
    //Figure out the height of the image from the array data
    int height = PaddedImage.Length / Stride;

    if (height < 1)
        return null;

    //Allocate the new array based on the image width
    byte[] truncatedImage = new byte[shortStride * height];

    //Copy the data minus the padding to a new array
    for(int i = 0; i < height; i++)
        Buffer.BlockCopy(PaddedImage,i*Stride,truncatedImage,i*shortStride,shortStride);

    return truncatedImage;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top