Link to home
Start Free TrialLog in
Avatar of Chris McClenny
Chris McClennyFlag for United States of America

asked on

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

Avatar of Chris McClenny
Chris McClenny
Flag of United States of America image

ASKER

I just came across this question that is very similar:
https://www.experts-exchange.com/questions/10100995/Printing-graphics.html
ASKER CERTIFIED SOLUTION
Avatar of Thommy
Thommy
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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

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

Thanks Thommy!!!!