I need a Delphi function to convert .BMP pixel data to dot matrix APA graphics.

Hello...

For reference, we are using Delphi 7 for this project.

I need help with printing graphics to a 9 pin POS printer. Actually I am looking for a universal function to read in a bitmap from a file and return a stream of data in
APA(All-Points-Addressible) format so we can use it with all of our 9 pin POS printers.  
The only info I could find on the net about APA graphics format was in an old ESC/P
manual.  APA format is a vertical slice of bitmap data printed as the head moves
across the paper which is much different than the way the bitmap data is stored
in a .bmp file.  All of the bitmaps we will be sending to the printer are 1 bit black and white.  We also need the function to scale the bitmap using  x0, y0, x1, y1 arguments.

My current test printer is a Cognitive TPG-A760.
Programming Doc can be found here...
A760 Programmers Manual

If I can get it working with the A760, I can
get it working on our others.

The A760 programmers manual has a command:
      ESC (+*.BMP file)
but we can't use this since its not offered in our other printers.

We prefer to use Command:
      ESC * m n1 n2 d1...dn

Where m = 0
and n1 + (256 * n2) bytes of data
 

Here is my function so far:


function BMPtoAPA(bmp:TBitmap; x0, y0, x1, y1:Integer ): String;
var iRow,iCol:Integer;
    byteArray: array of array of byte; 
    Row:  PByteArray;
    pixel: byte;
begin
  result := '';
  
  if Not Assigned(bmp) then exit;
  
  SetLength(byteArray, bmp.Height, bmp.Width);
  
  for iRow := 0 to bmp.Height-1 do
      begin
        Row := bmp.Scanline[iRow];

        for iCol := 0 to bmp.Width - 1 do
          begin
            byteArray[iRow,iCol] := Row[iCol];

            
                
          end;

      end;

  end;
	

  
  byteArray := nil;
end;	

Open in new window

cmcclennyAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

cmcclennyAuthor Commented:
I just came across this question that is very similar:
http://www.experts-exchange.com/Programming/Languages/C/Q_10100995.html
0
ThommyCommented:
This technical white paper describes principles of bitmap dot matrix printing and also the procedure of converting a bitmap to directly send it to the printer.
Sending a bit image to an Epson TM-T88III receipt printer using C# and ESC/POS

Description is for an EPSON printer and C# as programming language, but I think it will help you with your problem anyhow...
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
cmcclennyAuthor Commented:
Thanks for the info Thommy. That was a huge help.  I used that blog to translate it into delphi but I seem to be getting gibberish instead of a bitmap printed out.  I think maybe I am missing something in my translation?   If anyone knows, I would appreciate the help.



Code Translation:


function BitmapToAPAGraphics(const bmp:TBitmap;
                              const RowHeightEscapeStr:String;
                              const SliceEscapeStr:String;
                              const PrintHeadPinCount:byte = 9):String;

const threshhold = 127;
      CRLF = #13#10;
TYPE
    TRGB32 = packed record
        B, G, R, A: Byte;
     end;

    TRGB32Array = packed array[0..MaxInt div SizeOf(TRGB32)-1] of TRGB32;
    PRGB32Array = ^TRGB32Array;
    TBitArray = array of boolean;
var
    iCol,iRow,index, sliceIndex, bytePos, bitPos, offset,luminance: integer;
    Line: PRGB32Array;
    Pixel: TRGB32;
    tmpFile: TextFile;

    dots: TBitArray;
    slice,bit,tmpBit,bytesPerSlice, hold: byte;
    bVal: Boolean;

begin
  result := '';


  if not Assigned(bmp) then exit;
  if SliceEscapeStr = '' then exit;



  try

    bmp.PixelFormat := pf1bit;

    SetLength(dots, bmp.Height * bmp.Width);

    index := 0;

    //1) Loop the bitmap scanlines and build a bitArray from each pixel
    for iRow := 0 to bmp.Height-1 do
      begin
        Line := bmp.Scanline[iRow];

        for iCol := 0 to bmp.Width - 1 do
          begin
            Pixel := line[iCol];

            luminance := Trunc((Pixel.R * 0.3) + (Pixel.G * 0.59) + (Pixel.B * 0.11));
            dots[index] := (luminance < threshhold);

            inc(index);
          end;
      end;


    offset := 0;

    //Convert pin count to Count of bytes in each slice
    bytesPerSlice := (PrintHeadPinCount div 8);

    //if there is a remainder, we need another byte added (9 pin is 2 bytes, etc...)
    if PrintHeadPinCount mod 8 > 0 then
      inc(bytesPerSlice);


    result := result + RowHeightEscapeStr;

    //Loop height steping down by offset
    while offset < bmp.Height do
      begin
        result := result + SliceEscapeStr;

        //Loop width of bitmap
        for iCol := 0 to bmp.Width-1 do
          begin

            //Loop each byte from current slice
            for sliceIndex := 0 to bytesPerSlice -1 do
              begin
                slice := 0;

                //Loop each bit from current byte from current slice
                for bit := 0 to 7 do
                  begin
                    bytePos := (((offset div 8) + sliceIndex) * 8) + bit;
                    bitPos := (bytePos * bmp.Width) + iCol;

                    bVal := FALSE;
                    if bitPos < Length(dots) then
                      bVal := dots[bitPos];


                    //Delphi has no ternary operator so we have to compensate
                    if (bVal) then
                      tmpBit := 1
                    else
                      tmpBit := 0;


                    slice := slice or (tmpBit shl (7 - bit));

                  end; //end for bit := 0 to 7 do

                result := result + chr(slice);
             
             end; //end for sliceIndex := 0 to bytesPerSlice -1 do 
          end; //end for iCol := 0 to bmp.Width-1 do

        //Step down to next slice
        inc(offset,PrintHeadPinCount);

        result := result + CRLF;
     
     end;  //end while offset < bmp.Height do

  finally
     dots := nil;
  end;


end;

Open in new window



Useage:
    function PrintBitmap: Boolean
    var prnBuffer, RowHeightEscapeStr, SliceEscapeStr :String; 
          nL, nH: byte;  
          myBMP :TBitmap;
    begin
         myBMP := TBitmap.create;
         
         try
             myBMP.LoadFromFile('someBitmap.bmp');
 
             RowHeightEscapeStr := #27+#51+#9; // 1b 3 <n>   Line Height

             nL := (myBMP.Width + 1) div 256;
             nH := (myBMP.Width + 1) mod 256;
             SliceEscapeStr := #27+#42+#48 + chr(nL)+ chr(nH);   // 1b * 0 nL nH 

             prnBuffer := prnBuffer + #13+ BitmapToAPAGraphics(myBMP,RowHeightEscapeStr,SliceEscapeStr,9);

        finally
            myBMP.Free;
        end;

       //***** send prnBuffer to printer here...

      end;

Open in new window

0
cmcclennyAuthor Commented:
Its been a long time since I have dug through hardware code. I finally got it working thanks to Thommy.  i am posting my final code below. Hope this helps others out there.

Thanks Thommy!!


Final Translation:
function TGlobals.BitmapToAPAGraphics(const bmp:TBitmap;
                                      const RowHeightEscapeStr:String;
                                      const SliceEscapeStr:String;
                                      const BitsPerSlice:byte = 8):String;
const threshhold = 127;
      CR = #13;
      LF = #10;
TYPE
    TBitArray = array of boolean;

    TRGBTripleArray = ARRAY[Word] of TRGBTriple;
    pRGBTripleArray = ^TRGBTripleArray; // Use a PByteArray for pf8bit color.
var
    iCol,iRow,index, sliceIndex, bytePos, bitPos, offset,luminance: integer;
    line: pRGBTripleArray;
    Pixel: TRGBTriple;
    tmpFile: TextFile;

    dots: TBitArray;
    slice,bit,tmpBit,bytesPerSlice, hold: byte;
    bVal: Boolean;

    p:pRGBTripleArray;
begin
  result := '';


  if not Assigned(bmp) then exit;
  if SliceEscapeStr = '' then exit;



  try
    bmp.PixelFormat := pf24bit;

    SetLength(dots, (bmp.Height * bmp.Width));

    index := 0;

    //1) Loop the bitmap scanlines and build a bitArray from each pixel
    for iRow := 0 to bmp.Height-1 do
      begin
        line := bmp.Scanline[iRow];

        for iCol := 0 to bmp.Width-1 do
          begin
            Pixel := line[iCol];
            luminance := Trunc((Pixel.rgbtRed * 0.3) + (Pixel.rgbtGreen * 0.59) + (Pixel.rgbtBlue * 0.11));
            dots[index] := (luminance < threshhold);
            inc(index);
          end;

      end;



    offset := 0;

    //Convert BitsPerSlice to byte count in each slice
    bytesPerSlice := (BitsPerSlice div 8);

    //if there is a remainder, we need another byte added (9 bits is 2 bytes, etc...)
    if BitsPerSlice mod 8 > 0 then
      inc(bytesPerSlice);

    result := result + RowHeightEscapeStr;

    //Loop height steping down by offset
    while offset < bmp.Height do
      begin

        result := result + SliceEscapeStr;

        //Loop width of bitmap
        for iCol := 0 to bmp.Width-1 do
          begin

            //Loop each byte from current slice
            for sliceIndex := 0 to bytesPerSlice -1 do
              begin
                slice := 0;

                //Loop each bit from current byte from current slice
                for bit := 0 to 7 do
                  begin
                    bytePos := (((offset div 8) + sliceIndex) * 8) + bit;
                    bitPos := (bytePos * bmp.Width) + iCol;

                    bVal := FALSE;
                    if bitPos < Length(dots) then
                      bVal := dots[bitPos];


                    //Delphi has no ternary operator so we have to compensate
                    if (bVal) then
                      tmpBit := 1
                    else
                      tmpBit := 0;


                    slice := slice or (tmpBit shl (7 - bit));

                  end;

                result := result + chr(slice);    
              end;
          end;


        //Step down to next slice
        inc(offset,BitsPerSlice);

        result := result + CR+LF;

      end;


  finally
     dots := nil;
  end;


end;

Open in new window

0
cmcclennyAuthor Commented:
Thanks Thommy!!!!
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Algorithms

From novice to tech pro — start learning today.