[comp.lang.postscript] Bitmap decompression in PostScript?

wcs@skep2.ATT.COM (Bill.Stewart.[ho95c]) (01/30/89)

Does anyone have a Postscript program that can  be  downloaded to a
Postscript printer to uncompress bitmaps?  I know it's possible (Glenn
Reid periodically alludes to it.)  The objective is to download a
compressed picture and decompress on the printer, trading printer CPU
for transmission time, which can be significant on large pictures.
For some pictures, a simple run-length encoding would work well, but for
complex grayscale you need something more sophisticated to be worth using.

			Thanks;  Bill
-- 
#				Thanks;
# Bill Stewart, AT&T Bell Labs 2G218 Holmdel NJ 201-949-0705 ho95c.att.com!wcs
#
#	News.  Don't talk to me about News.

pokey@well.UUCP (Jef Poskanzer) (01/30/89)

In the referenced message, wcs@skep2.UUCP (Bill.Stewart) wrote:
>Does anyone have a Postscript program that can  be  downloaded to a
>Postscript printer to uncompress bitmaps?

The pbmtops filter in recent versions of the PBM package does run-length
encoding.  However, for all but the slowest printer links, this is slightly
slower than sending the raw bitmap.  The decrease in transmission time
is slightly more than compensated for by the increase in processing time.

Perhaps the decoding routine I wrote is suboptimal; perhaps the slowness
of current PostScript interpreters makes it a hard problem.  As far as I
know, everyone who has tried this compression trick has come up with the
same slight increase in time.  But if anyone wants to prove me wrong and
send me a faster decoding routine, I'll be glad to use it.
---
Jef

            Jef Poskanzer   jef@helios.ee.lbl.gov   ...well!pokey
   There is no substitute for good manners, except, perhaps, fast reflexes.

kgdykes@watmath.waterloo.edu (Ken Dykes) (01/31/89)

In article <10529@well.UUCP> Jef Poskanzer <jef@helios.ee.lbl.gov> writes:
>encoding.  However, for all but the slowest printer links, this is slightly
>slower than sending the raw bitmap.  The decrease in transmission time
>is slightly more than compensated for by the increase in processing time.
>
The thing is, with me, I dont care how long the PRINTER takes, but
i dont like the LINE tied-up, ie: I am willing to have the printer glow
red hot doing computing I just want quicker transmit times.
This is because my gutless pc cannot DO ANYTHING ELSE while doing transmission
(well it can, then it slows transmission to the point of "time-out errors")
 ..Just a viewpoint on the value of marginal compression.
 -ken
-- 
          - Ken Dykes,   Software Development Group, U.of.Waterloo
                         Waterloo, Ontario, Canada  N2L 3G1
kgdykes@watmath.uucp     kgdykes@water.bitnet     kgdykes@waterloo.csnet
kgdykes@watmath.uwaterloo.ca   kgdykes@watmath.edu  {backbone}!watmath!kgdykes

jhm+@andrew.cmu.edu (Jim Morris) (02/01/89)

Here is a program that works for me. It seems to compress a business letter by a
factor of 5 and sometimes the printing time as well. Your mileage may vary...
------------------------CUT HERE-----------------------------
/*      pssqw.c Compression of postscript image files
              just  squeezes the white space    */

/*  Written Jim Morris (james.morris@andrew.cmu.edu) 7/19/88
        derived from a version (pscomp.c) by
     Ganapathy Krishnan (krishnan@cs.buffalo.edu) (716-636-3197)
  Morris's work was supported by the NSF EXPRES project.
   */


#include <stdio.h>
#ifdef VMS
#include <string.h>
#else
#include <strings.h>
#endif
#define BSIZE 10000    /* PS internal buffer size */
#define PSLineSize 72  /* Limit on output lines */
#define LINEBUF 300
#define MinimunF 22    /*default shortest run of f's to encode */
                       /*  This number seems to minimize processing time on a
Laserwriter
                                       If your printer is different, hopefully
faster,
                                       revising this number may improve
performance. */

#define dig(c) ((c) >= '0' && (c) <= '9' )
#define hdig(c) (((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
#define hexdig(c) ( dig(c) || hdig (c))
#define white(c) (c == ' ' || c == '\n' || c=='\t')
/*
  Usage   pssqw < file.ps | lpr

  cc pssqw.c  -o pssqw

      This program accepts a PostScript file and writes out a new one
      in which all the image commands have been altered to use compressed
      rasters. Each image command  must appear on a line by itself and
      the image data can contain only hex digits and white space. It is possible
      for the program to malfunction either by missing an image command or
      mistaking some innocent text for one. If the next command after the
      image data starts with a hex digit, e.g. endpage, put a PS comment
     before it, e.g. %end of image

      Here is an example of the kind of input expected:

      %!  To be embedded, delete the '!'
      %  Bits are stored left-to-right, bottom-to-top;  1=white, 0=black
       /width 1016 def  /height 1321 def                % dimensions in pixels
      /xScale 0.60 def /yScale 0.60 def % scaling factors
      width xScale mul height yScale mul scale  % set scaling
      /picstr width 7 add 8 idiv string def     % define place to read lines
      % the  "image" operator has five parameters:
      width height 1                    % dimensions
      [width    0    0    height    0    0]     % transform matrix
      {currentfile picstr readhexstring pop}    % bits source
        image                                   % PRINT IT
        fff fff fffff   ffff
        fffff ffffffff
        aab034  f09b
             .......
             showpage

 Here is the PS program that replaces image:

  pop % discard standard proc
 /bz 10000 def % co-ordinate with BSIZE above
 /bf bz string def %place to build lines
 /ff bz string def
 ff 0 255 put
 /hbz bz 1 add 2 idiv def
 /i 1 def
 %fill ff with 255
 {
   % [0...i) contains 255, i < bz
   ff i  % start putting at i
   ff 0  % start taking from 0
   i  hbz ge
     {bz i sub getinterval putinterval exit}
     {i getinterval putinterval
     /i i i add def
     }
   ifelse
 } loop

{ % new proc
   /len currentfile token pop def
   len 0 lt
    {ff 0 len neg getinterval}
    {currentfile bf 0 len getinterval readhexstring pop}
   ifelse
  } bind
image

A squeezed version appears immediately below.

        */

static char pscommand[] = {
"pop/bz 10000 def/bf bz string def/ff bz string def ff 0 255 put /hbz\n\
bz 1 add 2 idiv def/i 1 def{ff i ff 0 i hbz ge{bz i sub getinterval\n\
putinterval exit}{i getinterval putinterval/i i i add def}ifelse}loop\n\
{/len currentfile token pop def  len 0 lt{ff 0 len neg getinterval}\n\
{currentfile bf 0 len getinterval readhexstring pop}ifelse}bind image\n"
};

static char line[LINEBUF];
static int nch; /* next character from file */
static int minF = MinimunF;

main(argc, argv)
int argc;
char **argv;
{
    int j;

    /* get the argument */
    while (--argc > 0 && (*++argv)[0] == '-')
    { while (*++(*argv))
    { switch (**argv)
    {   case 'f': minF = atoi (*argv+1);
        if (minF<6) minF = 6;
        while (*++(*argv)); --(*argv); break;
    default:  fprintf (stderr,
                        "Usage: pssqw [-f<value>] \n");
        exit (1);
    }
    }
    }

    nch = getchar();

    for(; ;)
    {
        if (nch == EOF) exit(0);

        readline();

        /* look for line starting with "image" */
        for (j=0; white(line[j]); j++) ;
        if (strncmp(&(line[j]), "image", 5) == 0 )
        {
            printf("%s", pscommand);
            runlength_encode();
        }
        else
        {
            printf("%s\n", line);
            nch = getchar();
        }

    }
}

readline()
{
    int i;
    i=0;
    /* nch contains the next item of input */
    for (;;)
    {
        if (i>=LINEBUF)
        {
            fprintf(stderr, "Line buffer length exceeded\n");
            exit(1);
        }
        if(nch =='\n' || nch == EOF)
        {
            line[i] = '\0';
            return(i);
        }
        line[i++] = nch;
        nch = getchar();
    }
}


runlength_encode()
{   char duffer[2*BSIZE]; /* double sized buffer */
    int size = 2*BSIZE;
    int done = 0;
    int i, j, lout;
    for(;!done;)
    {
        /* read in a buffer's worth */
        for (size=0;size!=2*BSIZE ;)
        {
            nch = getchar();
            if white(nch) continue;

            if (hexdig(nch)) duffer[size++] = nch;
            else {done = 1; break; }

        }

        /* write out  duffer[0...size) */

        size = (size/2)*2; /* ignore odd character */

        for(lout=0;lout!=size;)
        {
            /* duffer[0...lout) has been put out */
            /* find a string of ff's */

            for (i=lout; i!=size; i+=2)
            {
                /* duffer[lout..i) contains non F-string */
                if(duffer[i] != 'f' && duffer[i] != 'F') continue;
                for (j=i+1; j!=size; j++)
                {
                    if (duffer[j] != 'f' && duffer[j] != 'F') break;
                }

                /* duffer[i...j) are f's */
                j = (j/2)*2; /* must be even */

                if(j-i >= minF) break;
            }

            /* duffer[lout...i) is non-F stuff */
            /* duffer[i...j) are f's if i!=size */

            if(i>lout)
            {
                PNum((i-lout)/2);
                for (;lout!=i; lout++) PC(duffer[lout]);
            }

            if (lout!=size)
            {
                PNum(-(j-i)/2);
                lout = j;
            }
        }
    }
    printf("\n");
}

#define PSLength 72

static  int tlcount = 0;
/* there are tlcount chars sent since the last cr */
PC(c)
char c;
{
    if (++tlcount >= PSLength)
    {
        putchar('\n');
        tlcount = 1;
    }
    putchar(c);
}

PNum(n)
int n;
{
    int an = n;
    int length = 0;
    if (n<0) {an = -n; length = 1; }
    length += (an>9999) ? 6 :
      (an>999) ? 5 :
      (an>99) ? 4 :
      (an>9) ? 3 :
      2;
    if( (tlcount += length) >= PSLength)
    {
        printf("\n");
        tlcount = length;
    }
    printf("%d ", n);
}

jos@idca.tds.PHILIPS.nl (Jos Vos) (02/01/89)

In article <10529@well.UUCP> Jef Poskanzer <jef@helios.ee.lbl.gov> writes:

>In the referenced message, wcs@skep2.UUCP (Bill.Stewart) wrote:
>>Does anyone have a Postscript program that can  be  downloaded to a
>>Postscript printer to uncompress bitmaps?

>The pbmtops filter in recent versions of the PBM package does run-length
>encoding.  However, for all but the slowest printer links, this is slightly
>slower than sending the raw bitmap.  The decrease in transmission time
>is slightly more than compensated for by the increase in processing time.

What's the PBM package? Is it available for free?

Anyhow, can anybody mail me a working example of PostScript decompression.
I still want to try it for rather simple scanned documents.

-- 
-- ######   Jos Vos   ######   Internet   jos@idca.tds.philips.nl   ######
-- ######             ######   UUCP         ...!mcvax!philapd!jos   ######