hello! How I can convert a Bitmap to a PCX file to print in PrintAbout? This process should be done in a 7505 Ikon with "Windows Mobile 6" an compact framework 3.5. Thanks!
I found a sample that works! convert a bitmap to a 1 or 8 bit per pixel. The problem now is that the converted image is mostly black, beacose if the pixel is white, then the converted pixel is white, but if the pixel have some color (some rgb color) then the resultant pixel is black so the converted image have more black pixel that white.
I guess somewhere in the code should be able to change this behavior so that not all pixels with any color will become black.
The sample:
namespace ImageEditor
{
public class BitmapFile
static int SRCCOPY = 0x00CC0020;
static uint BI_RGB = 0;
static uint DIB_RGB_COLORS = 0;
/// <summary>
/// Copies a bitmap into a 1bpp/8bpp bitmap of the same dimensions, fast
/// </summary>
/// <param name="b">original bitmap</param>
/// <param name="bpp">1 or 8, target bpp</param>
/// <returns>a 1bpp copy of the bitmap</returns>
public static System.Drawing.Bitmap CopyToBpp(System.Drawing.Bitmap b, int bpp)
if (bpp != 1 && bpp != 8) throw new System.ArgumentException("1 or 8", "bpp");
// Plan: built into Windows GDI is the ability to convert
// bitmaps from one format to another. Most of the time, this
// job is actually done by the graphics hardware accelerator card
// and so is extremely fast. The rest of the time, the job is done by
// very fast native code.
// We will call into this GDI functionality from C#. Our plan:
// (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
// (2) Create a GDI monochrome hbitmap
// (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
// (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)
int w = b.Width, h = b.Height;
IntPtr hbm = b.GetHbitmap(); // this is step (1)
//
// Step (2): create the monochrome bitmap.
// "BITMAPINFO" is an interop-struct which we define below.
// In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
BITMAPINFO bmi = new BITMAPINFO();
bmi.biSize = 40; // the size of the BITMAPHEADERINFO struct
bmi.biWidth = w;
bmi.biHeight = h;
bmi.biPlanes = 1; // "planes" are confusing. We always use just 1. Read MSDN for more info.
bmi.biBitCount = (short)bpp; // ie. 1bpp or 8bpp
bmi.biCompression = BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
bmi.biSizeImage = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8);
bmi.biXPelsPerMeter = 1000000; // not really important
bmi.biYPelsPerMeter = 1000000; // not really important
// Now for the colour table.
uint ncols = (uint)1 << bpp; // 2 colours for 1bpp; 256 colours for 8bpp
bmi.biClrUsed = ncols;
bmi.biClrImportant = ncols;
bmi.cols = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours
if (bpp == 1) { bmi.cols[0] = MAKERGB(0, 0, 0); bmi.cols[1] = MAKERGB(255, 255, 255); }
else { for (int i = 0; i < ncols; i++) bmi.cols = MAKERGB(i, i, i); }
// For 8bpp we've created an palette with just greyscale colours.
// You can set up any palette you want here. Here are some possibilities:
// greyscale: for (int i=0; i<256; i++) bmi.cols=MAKERGB(i,i,i);
// rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int{0,51,102,153,204,255};
// for (int i=0; i<216; i++) bmi.cols=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
// optimal: a difficult topic: en.wikipedia.org/.../Color_quantization
// Now create the indexed bitmap "hbm0"
IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
IntPtr hbm0 = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
// Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
// GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
IntPtr sdc = GetDC(IntPtr.Zero); // First we obtain the DC for the screen
// Next, create a DC for the original hbitmap
IntPtr hdc = CreateCompatibleDC(sdc);
SelectObject(hdc, hbm);
// and create a DC for the monochrome hbitmap
IntPtr hdc0 = CreateCompatibleDC(sdc);
SelectObject(hdc0, hbm0);
// Now we can do the BitBlt:
BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
DeleteDC(hdc);
DeleteDC(hdc0);
ReleaseDC(IntPtr.Zero, sdc);
DeleteObject(hbm);
GC.Collect();
// Step (4): convert this monochrome hbitmap back into a Bitmap:
System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);
// Finally some cleanup.
DeleteObject(hbm0);
return b0;
}
[DllImport( "coredll.dll" )] public static extern bool DeleteObject( IntPtr hObject );
[DllImport( "coredll.dll" )] public static extern int InvalidateRect( IntPtr hwnd, IntPtr rect, int bErase );
[DllImport( "coredll.dll" )] public static extern IntPtr GetDC( IntPtr hwnd );
[DllImport( "coredll.dll" )] public static extern IntPtr CreateCompatibleDC( IntPtr hdc );
[DllImport( "coredll.dll" )] public static extern int ReleaseDC( IntPtr hwnd, IntPtr hdc );
[DllImport( "coredll.dll" )] public static extern int DeleteDC( IntPtr hdc );
[DllImport( "coredll.dll" )] public static extern IntPtr SelectObject( IntPtr hdc, IntPtr hgdiobj );
[DllImport( "coredll.dll" )] public static extern int BitBlt( IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop );
[DllImport( "coredll.dll" )] private static extern IntPtr LocalAlloc( uint flags, uint cb );
[DllImport( "coredll.dll" )] private static extern IntPtr LocalFree( IntPtr hMem );
[DllImport( "coredll.dll" )] private static extern IntPtr CreateDIBSection( IntPtr hdc, IntPtr hdr, uint colors, ref IntPtr pBits, IntPtr hFile, uint offset );
[DllImport("coredll.dll")] private static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct BITMAPINFO
public uint biSize;
public int biWidth, biHeight;
public short biPlanes, biBitCount;
public uint biCompression, biSizeImage;
public int biXPelsPerMeter, biYPelsPerMeter;
public uint biClrUsed, biClrImportant;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 256)]
public uint[] cols;
static uint MAKERGB(int r, int g, int b)
return ((uint)(b & 255)) | ((uint)((r & 255) << 8)) | ((uint)((g & 255) << 16));
Have a look at this:
http://www.codeproject.com/KB/graphics/cximage.aspx
look nice, but my project is c # and CxImage is in C++ :s. I read that there is a wrapper to use the libraries .. but I can not find it.
There another solution?, Since it is typical use for PCX images in this type of printers?
..and CxImage is a Desktop application! and i need WM6 based
Hello,
Take a look at the below sample code:
public void printTo(Stream stream) throws IOException { // DiagsPanelShow(); // tbDiags.Text = "Converting BMP to PCX ..." + CR_LF; int bytesPerLine = (width+7)/8; int xEnd = width-1; int yEnd = height-1; byte[] header = { 0x0A, // "PCX File" 0x05, // "Version 5" 0x01, // RLE Encoding 0x01, // 1 bit per pixel 0x00, 0x00, // XStart at 0 0x00, 0x00, // YStart at 0 (byte)(xEnd&0xFF), (byte)((xEnd>>8) & 0xFF), // Xend (byte)(yEnd&0xFF), (byte)((yEnd>>8) & 0xFF), // Yend (byte)(xEnd&0xFF), (byte)((xEnd>>8) & 0xFF), // Xend (byte)(yEnd&0xFF), (byte)((yEnd>>8) & 0xFF), // Yend 0x0F, 0x0F, 0x0F, 0x0E, 0x0E, 0x0E, 0x0D, 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, //48-byte EGA palette info 0x0B, 0x0B, 0x0B, 0x0A, 0x0A, 0x0A, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, // Reserved byte, always x00 0x01, // 1 bit plane (byte)(bytesPerLine&0xFF), (byte)((bytesPerLine>>8) & 0xFF), // Bytes per scan line: (XEnd - XStart, 1) / 8 0x01, 0x00, // Palette type: 1 means color or monochrome 0x00, 0x00, // Horizontal screen size (not used) 0x00, 0x00 // Vertical screen size (not used) }; stream.writeBytes(header); // Write most of header data. stream.writeBytes(new byte[54]); // pad the 128-byte header byte rowIn[] = new byte[width*3]; int []bits = {128, 64, 32, 16, 8, 4, 2, 1}; byte[] bytes = new byte[2]; int last = 0; int count = 0; for(int y=0; y < height; y++) { getPixelRow(rowIn,y); for (int x=0; x < width; x+=8) { int n = x+8; if (n > width) n = width; int b = 0; for (int j=x; j < n; j++) if (rowIn[j+j+j] != 0) b |= bits[j-x]; if (last==b && count < 63) count++; else { if (count > 0) { bytes[0] = (byte)(count | 0xC0); bytes[1] = (byte)last; stream.writeBytes(bytes,0,2); } last = b; count = 1; } } if (count > 0) { bytes[0] = (byte)(count | 0xC0); bytes[1] = (byte)last; stream.writeBytes(bytes,0,2); count = 0; last = 0; } } }
I saw this example but there is a function "getPixelRow" that is not implemented. Furthermore it is not clear where you get the image data. The input parameter of the "printto (Stream stream)" seems to be used to return the image PCX, but no sign of where you get the bitmap to be processed.
It is quite incomplete example. : S
Another example in VB
Private Function GetControlImageArrayBlackAndWhite(ByVal i_oControl As Control, ByVal i_nMaxRed As Integer, ByVal i_nMaxGreen As Integer, ByVal i_nMaxBlue As Integer) As Byte() '1. The images are stored upside down. The first line in the file is the bottom line of the image and the ' last line in the file is the first line of the image. This means that if you are computing an image with ' resolution of 256 x 192, to be displayed in a system where the (0, 0) pixel is the top left, then the ' first pixel to be computed and written to the BMP file is for pixel (191, 0). ' '2. In 24-bit images RGB values are stored in Blue Green Red order. ' '3. Bitmap images if not a multiple of for will contain garbage data and to speed up coding ' this function limits the width to a multiple of 4
Dim oBitmapData() As Byte Dim oSigData() As Byte Dim nPixsPerRow As Long Dim nNumberofRows As Long Dim nRow As Integer Dim nPixel As Integer Dim nIndex As Integer Dim bPixelRed As Byte Dim bPixelGreen As Byte Dim bPixelBlue As Byte Dim nFirstPixelColorLocation As Long
nPixsPerRow = i_oControl.Width If ((nPixsPerRow * 3) Mod 4) > 0 Then 'times 3 because of rgb MsgBox("Please make your signiture control width is a multiple of 4") Return New Byte() {} End If
nNumberofRows = i_oControl.Height oBitmapData = ImageEditor.BitmapFile.GetControlBitmapArray(i_oControl, 24) ReDim oSigData((nPixsPerRow * nNumberofRows) - 1)
nIndex = 0 For nRow = nNumberofRows - 1 To 0 Step -1 For nPixel = 0 To nPixsPerRow - 1 nFirstPixelColorLocation = (nRow * nPixsPerRow * 3) + (nPixel * 3) bPixelBlue = oBitmapData(nFirstPixelColorLocation) bPixelGreen = oBitmapData(nFirstPixelColorLocation + 1) bPixelRed = oBitmapData(nFirstPixelColorLocation + 2) If bPixelBlue <= i_nMaxBlue And bPixelGreen <= i_nMaxGreen And bPixelRed <= i_nMaxRed Then oSigData(nIndex) = 1 Else oSigData(nIndex) = 0 End If nIndex += 1 Next Next
Return oSigData
End Function Public Sub PrintControl(ByVal i_oControl As Control) PrintControl(i_oControl, 0, 0, 0) End Sub
Public Sub PrintControl(ByVal i_oControl As Control, ByVal i_nMaxRed As Integer, ByVal i_nMaxGreen As Integer, ByVal i_nMaxBlue As Integer) Dim bSigniture() As Byte Dim bOutput() As Byte
Dim nWidthBytes As Integer = (i_oControl.Width / 8) Dim nHeightLines As Integer = i_oControl.Height Dim nNumberOfdividers As Integer = Math.Ceiling(CType(nWidthBytes * nHeightLines, Double) / 128.0)
Debug.Assert(i_oControl.Width Mod 8 = 0, "Only supports images with width divisible by 8") Debug.Assert(i_oControl.Width / 8 <= 104, "Only supports images with width/8 <= 104") 'for MtP300 the value is 72 Debug.Assert(i_oControl.Height <= 255, "Only supports images with height <= 255")
bSigniture = GetControlImageArrayBlackAndWhite(i_oControl, i_nMaxRed, i_nMaxGreen, i_nMaxBlue) ReDim bOutput(4 + (nWidthBytes * nHeightLines) + nNumberOfdividers - 1)
bOutput(0) = &H1B bOutput(1) = &H76 bOutput(2) = nHeightLines bOutput(3) = nWidthBytes
Dim nCurrentPixel As Integer = 0 Dim i As Integer = 4 Dim nAmoutToProcessBytes As Byte Dim nTotalBytes As Integer = bSigniture.Length / 8 Dim nAmountRemainingBytes As Integer = nTotalBytes
Do While nAmountRemainingBytes > 0 If nAmountRemainingBytes > 128 Then nAmoutToProcessBytes = 128 Else nAmoutToProcessBytes = nAmountRemainingBytes End If
bOutput(i) = nAmoutToProcessBytes - 1 i += 1
For h As Integer = 0 To nAmoutToProcessBytes - 1 For nCurBit As Integer = 0 To 7 bOutput(i) += bSigniture(nCurrentPixel) * (2 ^ (7 - nCurBit)) nCurrentPixel += 1 Next i += 1 Next
nAmountRemainingBytes -= nAmoutToProcessBytes Loop
SetEmulationBackToPrintek() moPort.Write(bOutput, 0, bOutput.Length) System.Threading.Thread.Sleep(1000) 'give time to print
End Sub
Public Sub Print2bitBMP(ByVal i_sFile As String) Dim bBMPFile() As Byte Dim oFile As System.IO.BinaryReader Dim oStream As System.IO.FileStream Dim nWidthByte As Integer Dim nHeightInRows As Integer Dim nBytePerRowBMP As Integer Dim bImageToPrint() As Byte Dim nBMPOffset As Integer Dim nIndexImageToPrint As Integer Dim nIndexBMP As Integer Dim nBitCount As Integer
'open the bmp and load it into memory oStream = System.IO.File.Open(i_sFile, IO.FileMode.Open) oFile = New System.IO.BinaryReader(oStream) bBMPFile = oFile.ReadBytes(oFile.BaseStream.Length) oFile.Close() oStream.Close()
'Check the file type , bmp files always start with the first 2 bytes being BM If bBMPFile(0) <> Asc("B") And bBMPFile(1) <> Asc("M") Then MsgBox("File is not a bmp") Return End If
'gather some information from the bitmap header and verify it nWidthByte = System.BitConverter.ToInt32(bBMPFile, 18) / 8 nHeightInRows = System.BitConverter.ToInt32(bBMPFile, 22) nBMPOffset = System.BitConverter.ToInt32(bBMPFile, 10) nBitCount = System.BitConverter.ToInt16(bBMPFile, 28) If nBitCount <> 1 Then MsgBox("File is wrong bitcount") Return End If
'In a bmp file each row should be divisible by 4 bytes 'if not extra bytes are added If (nWidthByte) Mod 4 > 0 Then nBytePerRowBMP = (nWidthByte) + (4 - ((nWidthByte) Mod 4)) Else nBytePerRowBMP = nWidthByte End If 'this array is used to store the trimmed array ReDim bImageToPrint(nHeightInRows * nWidthByte - 1)
'loop through each byte, convert and copy nIndexImageToPrint = 0 nIndexBMP = nBMPOffset For nRow As Integer = nHeightInRows - 1 To 0 Step -1 For nCol As Integer = 0 To nWidthByte - 1 nIndexImageToPrint = (nRow * nWidthByte) + nCol bImageToPrint(nIndexImageToPrint) = bBMPFile(nIndexBMP) 'Xor &HFF 'xor the byte because bmp stores the pixel as zero for on nIndexBMP += 1 'nIndexImageToPrint += 1 Next 'skip any extra bytes added to the row nIndexBMP += (nBytePerRowBMP - nWidthByte) Next
'print the image 'Debug.Assert(i_oControl.Width Mod 8 = 0, "Only supports images with width divisible by 8") Debug.Assert(nWidthByte <= 104, "Only supports images with width/8 <= 104") 'for MtP300 the value is 72 Debug.Assert(nHeightInRows <= 255, "Only supports images with height <= 255")
Dim nNumberOfdividers As Integer = Math.Ceiling(CType(nWidthByte * nHeightInRows, Double) / 128.0) Dim bOutput(4 + (nWidthByte * nHeightInRows) + nNumberOfdividers - 1) As Byte bOutput(0) = &H1B bOutput(1) = &H76 bOutput(2) = nHeightInRows bOutput(3) = nWidthByte
Dim nCurrentPixel As Integer = 0 Dim i As Integer = 4 Dim nAmoutToProcessBytes As Byte Dim nTotalBytes As Integer = bImageToPrint.Length Dim nAmountRemainingBytes As Integer = nTotalBytes
For h As Integer = 0 To nAmoutToProcessBytes - 1 'For nCurBit As Integer = 0 To 7 bOutput(i) = bImageToPrint(nCurrentPixel) '* (2 ^ (7 - nCurBit)) nCurrentPixel += 1 'Next i += 1 Next
SetEmulationBackToPrintek() moPort.Write(bOutput, 0, bOutput.Length)
'For nTest As Integer = 0 To 31 ' i_oPort.Write(bOutput, nTest * 143, 143) ' System.Threading.Thread.Sleep(150) 'Next End Sub
after translate this to c#, dont work for me.
Try the attached C# sample
6175.SampleBMP_PCX.zip
Hi Scott ...
I already use this code to take a signature (through InkPad from Intermec) from which I get a bitmap of a "bit per pixel," then this example, converts it to PCX and then sent to the printer. This works.
The problem is:
How to get a bitmap of one bit per pixel (in "compact framework") for use with this example?
I tried several examples and all have a problem. So my original question on this issue was how to convert a JPG taken by the camera and sent to the printer. Thinking that there was any application (even for a console) where you pass a path to a JPG file and the application creates a file with extension PCX, corresponding to the original image.
I tried several options (convert from one JPG to a bitmap "one bit per pixel" and then convert it to PCX), but always end up with a problem .. black images, OutOfMemory exceptions or PCX completely flawed.
Examples of conversion are quite complicated to modify them without thinking that adding some bug.
Help!
Sorry, I assumed you were trying to print out a signature capture.
I am not aware of anything that will convert an actual image captured from a camera and converting that down to a pcx file to print to a mobile printer. I will check around.