[comp.lang.postscript] compressed bitmaps

jwz@spice.cs.cmu.edu (Jamie Zawinski) (02/10/89)

I wrote some code for sending run-length encoded bitmaps to our PS printer,
but it didn't work out very well - if you're sending a big bitmap, and a bit
gets inverted, it's only going to be one pixel, and you're not going to
notice it.  But if the bitmap is run-length encoded, you're going to lose
the rest of the image.  That's what kept happening - I'd print a file
multiple times, and the image would turn to static at a different point each
time it was printed.

But maybe it will be of use to someone with a more reliable data link, so
I've included the PostScript end of the code in this message.
(The host end was written in TI Explorer Lisp, and I doubt most of you will
find that useful...)

	Jamie Zawinski
	jwz@spice.cs.cmu.edu
	sunpitt!sun!eti!jwz

------ START IGNORING HERE -------

%!ps
%
% After this prolog, ship something of the form
%
% <width-in-points> <height-in-points> do-image
% <width-of-image-as-32-bit-hex-quantity>
% <height-of-image-as-32-bit-hex-quantity>
% <compressed-hex-data>
% showpage
%
% The compression is very simple, but also very effective.
% First there is a control byte in the range [-128,127].
% If this is negative, then that means replicate the next byte
% read -CONTROL+1 times.  If it is non-negative, then that means
% that the next CONTROL+1 bytes are literal.
% -128 is a no-op.
%

/controlbuf 1 string def
/repbuf 1 string def
/WHbuf 4 string def
/chunk-count 0 def

/read-w-and-h {
  currentfile WHbuf readhexstring pop   % read the width and height into /WHbuf as a 4 byte quantity.
  /w WHbuf 0 get 256 mul  WHbuf 1 get  add def   % set /w and /h to the values in this string, MSB first.
  /h WHbuf 2 get 256 mul  WHbuf 3 get  add def
  } def

/read-brun1-chunk {
  currentfile controlbuf readhexstring pop pop  % read one hex character from the stream.
  /code controlbuf 0 get def                    % set /code to the character read.
  code 127 gt {/code code 256 sub def} if       % If /code is >127, subtract 256, converting to be in the range [-128,127].
  /chunk-count chunk-count 1 add def

  code -128 eq  % -128 is illegal.  Print an error message and stop.
   { (\n%%[ Error: illegal BRUN1 control byte at chunk ) print
     chunk-count 1 sub (     ) cvs print
     ( - probably lost some bits.  Try re-printing this file. ]%%\n) print
     stop }
   { code 0 ge
     { lit }    % >=0 means copy next CODE+1 bytes literally.
     { rep }    % < 0 means replicate next byte -CODE+1 times.
     ifelse }
   ifelse
  } def

/lit {    % uses dynamic binding of /code - leaves a new string on the stack.
  currentfile
  code 1 add string
  readhexstring pop
  } def

/rep {    % uses dynamic binding of /code - leaves a new string on the stack.

  currentfile repbuf readhexstring pop pop
  /repchar repbuf 0 get def               % get next code.
  /newstring  code neg 1 add string  def  % build a string.
  
  0 1 newstring length 1 sub              % set every element of the new string to be the new char.
  { newstring exch repchar put }
  for
  newstring     % leave the new string on the stack.
  } def

/do-image {     % takes dest-w and dest-h on stack.  There must be image data textually after this.
  /dest-h exch def
  /dest-w exch def
  0 0 moveto
  20  20 translate
  dest-w dest-h scale
  read-w-and-h
  w h 1 [w 0 0 h neg 0 h] {read-brun1-chunk} image
  showpage
  } def
--