about summary refs log tree commit diff
path: root/converter/ppm/picttoppm.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/ppm/picttoppm.c')
-rw-r--r--converter/ppm/picttoppm.c632
1 files changed, 414 insertions, 218 deletions
diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c
index b7da0c79..9a7d8e7c 100644
--- a/converter/ppm/picttoppm.c
+++ b/converter/ppm/picttoppm.c
@@ -142,15 +142,6 @@ static int ps_cent_x;
 static int ps_cent_y;
 static int ps_cent_set;
 
-typedef void (drawFn)(struct canvas *, int);
-
-struct opdef {
-    const char* name;
-    int len;
-    drawFn * impl;
-    const char* description;
-};
-
 struct raster {
 /*----------------------------------------------------------------------------
    An image raster.  May be either truecolor or paletted.
@@ -265,13 +256,30 @@ struct blit_info {
     struct raster      srcplane;
     int                pixSize;
     struct Rect        dstRect;
-    struct RGBColor *  color_map;
+    struct RGBColor *  colorMap;
     int                mode;
     struct blit_info * next;
 };
 
-static struct blit_info* blit_list = 0;
-static struct blit_info** last_bl = &blit_list;
+typedef struct {
+    struct blit_info * firstP;
+    struct blit_info ** connectorP;
+    bool unblittableText;
+        /* The image contains text opcodes, and we don't know how to put that
+           in a blit list (I really don't even know what a blit _is_), so
+           the image information here is incomplete.
+        */
+} blitList;
+
+
+typedef void (drawFn)(struct canvas *, blitList *, int);
+
+struct opdef {
+    const char* name;
+    int len;
+    drawFn * impl;
+    const char* description;
+};
 
 #define WORD_LEN (-1)
 
@@ -610,6 +618,7 @@ static drawFn ShortComment;
 
 static void
 ShortComment(struct canvas * const canvasP,
+             blitList *      const blitListP,
              int             const version) {
 
     picComment(read_word(), 0);
@@ -621,6 +630,7 @@ static drawFn LongComment;
 
 static void
 LongComment(struct canvas * const canvasP,
+            blitList *      const blitListP,
             int             const version) {
 
     word type;
@@ -635,6 +645,7 @@ static drawFn skip_poly_or_region;
 
 static void
 skip_poly_or_region(struct canvas * const canvasP,
+                    blitList *      const blitListP,
                     int             const version) {
 
     stage = "skipping polygon or region";
@@ -765,24 +776,33 @@ load_fontdir(const char * const dirfile) {
 
 
 static void
+dumpRect(const char * const label,
+         struct Rect  const rectangle) {
+
+    pm_message("%s (%u,%u) (%u,%u)",
+               label,
+               rectangle.left,  rectangle.top,
+               rectangle.right, rectangle.bottom);
+}
+
+
+
+static void
 read_rect(struct Rect * const r) {
 
     r->top    = read_word();
     r->left   = read_word();
     r->bottom = read_word();
     r->right  = read_word();
-}
-
 
+    if (r->top > r->bottom || r->right < r->left)
+        dumpRect("Invalid rectangle", *r);
 
-static void
-dumpRect(const char * const label,
-         struct Rect  const rectangle) {
-
-    pm_message("%s (%u,%u) (%u,%u)",
-               label,
-               rectangle.left,  rectangle.top,
-               rectangle.right, rectangle.bottom);
+    if (r->top > r->bottom)
+        pm_error("Invalid PICT: a rectangle has a top below its bottom");
+    if (r->right < r->left)
+        pm_error("Invalid PICT: a rectangle has a right edge "
+                 "left of its left edge");
 }
 
 
@@ -802,10 +822,10 @@ rectheight(const struct Rect * const r) {
 
 
 static bool
-rectsamesize(const struct Rect * const r1, 
-             const struct Rect * const r2) {
-    return r1->right - r1->left == r2->right - r2->left &&
-           r1->bottom - r1->top == r2->bottom - r2->top ;
+rectsamesize(struct Rect const r1, 
+             struct Rect const r2) {
+    return r1.right - r1.left == r2.right - r2.left &&
+           r1.bottom - r1.top == r2.bottom - r2.top ;
 }
 
 
@@ -835,20 +855,45 @@ rectscale(struct Rect * const r,
 
 
 
-static struct blit_info* 
-add_blit_list(void) {
+static void
+    initBlitList(blitList * const blitListP) {
 
-    struct blit_info * bi;
+    blitListP->firstP          = NULL;
+    blitListP->connectorP      = &blitListP->firstP;
+    blitListP->unblittableText = false;
+}
+
+
+
+static void
+addBlitList(blitList *        const blitListP,
+            struct Rect       const srcRect,
+            struct Rect       const srcBounds,
+            struct raster     const srcplane,
+            int               const pixSize,
+            struct Rect       const dstRect,
+            struct RGBColor * const colorMap,
+            int               const mode) {
+
+    struct blit_info * biP;
     
-    MALLOCVAR(bi);
-    if (bi == NULL)
+    MALLOCVAR(biP);
+    if (biP == NULL)
         pm_error("out of memory for blit list");
-    
-    bi->next = 0;
-    *last_bl = bi;
-    last_bl = &bi->next;
-    
-    return bi;
+    else {
+        biP->srcRect   = srcRect;
+        biP->srcBounds = srcBounds;
+        biP->srcplane  = srcplane;
+        biP->pixSize   = pixSize;
+        biP->dstRect   = dstRect;
+        biP->colorMap  = colorMap;
+        biP->mode      = mode;
+
+        biP->next = NULL;
+
+        *blitListP->connectorP = biP;
+        blitListP->connectorP = &biP->next;
+    }
 }
 
 
@@ -1327,8 +1372,6 @@ doSameSize(transfer_func           trf,
            struct rgbPlanes  const dst,
            unsigned int      const dstwid) {
 /*----------------------------------------------------------------------------
-   Generalized (but slow) blit.
-
    Transfer pixels from 'src' to 'dst', applying the transfer function
    'trf'.
 
@@ -1501,19 +1544,30 @@ blitIdempotent(unsigned int          const pixSize,
 
 
 static void
-generalBlit(struct Rect       const srcRect, 
-            struct Rect       const srcBounds, 
-            struct raster     const srcplane,
-            struct rgbPlanes  const planes,
-            int               const pixSize, 
-            struct Rect       const dstRect, 
-            struct Rect       const dstBounds, 
-            int               const dstwid, 
-            struct RGBColor * const color_map, 
-            int               const mode,
-            struct Rect       const clipsrc,
-            struct Rect       const clipdst) {
-    
+doBlit(struct Rect       const srcRect, 
+       struct Rect       const dstRect, 
+       struct Rect       const srcBounds, 
+       struct raster     const srcplane,
+       struct Rect       const dstBounds, 
+       struct rgbPlanes  const canvasPlanes,
+       int               const pixSize, 
+       int               const dstwid, 
+       struct RGBColor * const color_map, 
+       int               const mode) {
+/*----------------------------------------------------------------------------
+   Transfer some pixels from 'srcplane' to 'canvasPlanes', applying the
+   transfer function 'trf'.
+
+   'srcplane' contains the rectangle 'srcBounds' of the image.
+   'canvasPlanes' contains the rectangle 'dstRect' of the image.
+
+   Take the rectangle 'srcRect' of the source image and copy it to the
+   rectangle 'dstRec' of the destination image.
+
+   Each plane of 'canvasPlanes' is one word per pixel and contains actual
+   colors, never a palette index.  It is an array in row-major order
+   with 'dstwid' words per row.
+-----------------------------------------------------------------------------*/
     unsigned char * src;
     struct rgbPlanes dst;
     int dstoff;
@@ -1523,31 +1577,31 @@ generalBlit(struct Rect       const srcRect,
     transfer_func trf;
 
     if (verbose) {
-        dumpRect("copying from:", clipsrc);
-        dumpRect("to:          ", clipdst);
+        dumpRect("copying from:", srcRect);
+        dumpRect("to:          ", dstRect);
         pm_message("a %u x %u area to a %u x %u area",
-                   rectwidth(&clipsrc), rectheight(&clipsrc),
-                   rectwidth(&clipdst), rectheight(&clipdst));
+                   rectwidth(&srcRect), rectheight(&srcRect),
+                   rectwidth(&dstRect), rectheight(&dstRect));
     }
 
     {
         unsigned int const pkpixsize = pixSize == 16 ? 2 : 1;
-        unsigned int const srcRowNumber = clipsrc.top - srcBounds.top;
+        unsigned int const srcRowNumber = srcRect.top - srcBounds.top;
         unsigned int const srcRowOffset =
-            (clipsrc.left - srcBounds.left) * pkpixsize;
+            (srcRect.left - srcBounds.left) * pkpixsize;
         assert(srcRowNumber < srcplane.rowCount);
         assert(srcRowOffset < srcplane.rowSize);
         src = srcplane.bytes + srcRowNumber * srcplane.rowSize + srcRowOffset;
-        xsize = clipsrc.right - clipsrc.left;
-        ysize = clipsrc.bottom - clipsrc.top;
+        xsize = rectwidth(&srcRect);
+        ysize = rectheight(&srcRect);
         srcadd = srcplane.rowSize - xsize * pkpixsize;
     }
 
-    dstoff = (clipdst.top - dstBounds.top) * dstwid +
-        (clipdst.left - dstBounds.left);
-    dst.red = planes.red + dstoff;
-    dst.grn = planes.grn + dstoff;
-    dst.blu = planes.blu + dstoff;
+    dstoff = (dstRect.top - dstBounds.top) * dstwid +
+        (dstRect.left - dstBounds.left);
+    dst.red = canvasPlanes.red + dstoff;
+    dst.grn = canvasPlanes.grn + dstoff;
+    dst.blu = canvasPlanes.blu + dstoff;
 
     /* get rid of Text mask mode bit, if (erroneously) set */
     if ((mode & ~64) == 0)
@@ -1555,8 +1609,8 @@ generalBlit(struct Rect       const srcRect,
     else
         trf = transfer(mode & ~64);
 
-    if (!rectsamesize(&clipsrc, &clipdst))
-        doDiffSize(clipsrc, clipdst, pixSize, xsize, ysize,
+    if (!rectsamesize(srcRect, dstRect))
+        doDiffSize(srcRect, dstRect, pixSize, xsize, ysize,
                    trf, color_map, src, srcplane.rowSize, dst, dstwid);
     else {
         if (trf == NULL)
@@ -1575,12 +1629,21 @@ blit(struct Rect       const srcRect,
      struct Rect       const srcBounds, 
      struct raster     const srcplane,
      struct canvas *   const canvasP,
+     blitList *        const blitListP,
      int               const pixSize, 
      struct Rect       const dstRect, 
      struct Rect       const dstBounds, 
      int               const dstwid, 
      struct RGBColor * const color_map, 
      int               const mode) {
+/*----------------------------------------------------------------------------
+   'srcplane' contains the rectangle 'srcBounds' of the image.
+
+   We transfer rectangle 'srcRect' from that.
+
+   if 'blitListP' is non-null, we don't draw anything on 'canvasP'; instead,
+   we add to the list *blitlistP a description of what needs to be drawn.
+-----------------------------------------------------------------------------*/
 
     /* I can't tell what the result value of this function is supposed to mean,
        but I found several return statements that did not set it to anything,
@@ -1595,9 +1658,7 @@ blit(struct Rect       const srcRect,
         retval = 1;
     else {
         /* Almost got it.  Clip source rect with source bounds.
-           clip dest rect with dest bounds.  If source and
-           destination are not the same size, use Pnmscale
-           to get a nicely sized rectangle.
+           clip dest rect with dest bounds.
         */
         struct Rect clipsrc;
         struct Rect clipdst;
@@ -1605,22 +1666,16 @@ blit(struct Rect       const srcRect,
         rectinter(srcBounds, srcRect, &clipsrc);
         rectinter(dstBounds, dstRect, &clipdst);
 
-        if (fullres) {
-            struct blit_info * bi;
-            bi = add_blit_list();
-            bi->srcRect   = clipsrc;
-            bi->srcBounds = srcBounds;
-            bi->srcplane  = srcplane;
-            bi->pixSize   = pixSize;
-            bi->dstRect   = clipdst;
-            bi->color_map = color_map;
-            bi->mode      = mode;
+        if (blitListP) {
+            addBlitList(blitListP,
+                        clipsrc, srcBounds, srcplane, pixSize,
+                        clipdst, color_map, mode);
 
             retval = 0;
         } else {
-            generalBlit(srcRect, srcBounds, srcplane, canvasP->planes, pixSize,
-                        dstRect, dstBounds, dstwid, color_map, mode,
-                        clipsrc, clipdst);
+            doBlit(clipsrc, clipdst,
+                   srcBounds, srcplane, dstBounds, canvasP->planes,
+                   pixSize, dstwid, color_map, mode);
 
             retval = 1;
         }
@@ -1680,9 +1735,31 @@ compact(word const input) {
 
 
 static void
-do_blits(struct canvas * const canvasP) {
+reportBlitList(blitList * const blitListP) {
+
+    if (verbose) {
+        unsigned int count;
+        struct blit_info * biP;
+
+        for (count = 0, biP = blitListP->firstP; biP; biP = biP->next)
+            ++count;
+
+        pm_message("# blits: %u", count);
+    }
+}
+
 
-    struct blit_info* bi;
+
+static void
+doBlitList(struct canvas * const canvasP,
+           blitList *      const blitListP) {
+/*----------------------------------------------------------------------------
+   Do the list of blits *blitListP, drawing on canvas *canvasP.
+
+   We allocate new plane data structures in *canvasP.  We assume it doesn't
+   have them already.
+-----------------------------------------------------------------------------*/
+    struct blit_info * bi;
     int srcwidth, dstwidth, srcheight, dstheight;
     double  scale, scalelow, scalehigh;
     double  xscale = 1.0;
@@ -1690,9 +1767,11 @@ do_blits(struct canvas * const canvasP) {
     double  lowxscale, highxscale, lowyscale, highyscale;
     int     xscalecalc = 0, yscalecalc = 0;
 
+    reportBlitList(blitListP);
+
     fullres = 0;
 
-    for (bi = blit_list; bi; bi = bi->next) {
+    for (bi = blitListP->firstP; bi; bi = bi->next) {
         srcwidth = rectwidth(&bi->srcRect);
         dstwidth = rectwidth(&bi->dstRect);
         srcheight = rectheight(&bi->srcRect);
@@ -1768,11 +1847,12 @@ do_blits(struct canvas * const canvasP) {
     }
 
     if (xscale != 1.0 || yscale != 1.0) {
-        for (bi = blit_list; bi; bi = bi->next)
-            rectscale(&bi->dstRect, xscale, yscale);
+        struct blit_info * biP;
+        
+        for (biP = blitListP->firstP; biP; biP = biP->next)
+            rectscale(&biP->dstRect, xscale, yscale);
 
-        pm_message("Scaling output by %f in X and %f in Y",
-                   xscale, yscale);
+        pm_message("Scaling output by %f in X and %f in Y", xscale, yscale);
         rectscale(&picFrame, xscale, yscale);
     }
 
@@ -1783,12 +1863,10 @@ do_blits(struct canvas * const canvasP) {
 
     clip_rect = picFrame;
 
-    for (bi = blit_list; bi; bi = bi->next) {
-        blit(bi->srcRect, bi->srcBounds, bi->srcplane, canvasP,
-             bi->pixSize,
-             bi->dstRect, picFrame, rowlen,
-             bi->color_map,
-             bi->mode);
+    for (bi = blitListP->firstP; bi; bi = bi->next) {
+        doBlit(bi->srcRect, bi->dstRect,
+               bi->srcBounds, bi->srcplane, picFrame, canvasP->planes,
+               bi->pixSize, rowlen, bi->colorMap, bi->mode);
     }
 }
 
@@ -1856,6 +1934,7 @@ static drawFn Clip;
 
 static void
 Clip(struct canvas * const canvasP,
+     blitList *      const blitListP,
      int             const version) {
 
     word len;
@@ -1878,6 +1957,7 @@ static drawFn OpColor;
 
 static void
 OpColor(struct canvas * const canvasP,
+        blitList *      const blitListP,
         int             const version) {
 
     op_color.red = read_word();
@@ -1967,7 +2047,7 @@ read_color_table(void) {
         color_table[val].blu = read_word();
 
         if (verbose > 1)
-            pm_message("%d: [%d,%d,%d]", val,
+            pm_message("Color %3u: [%u,%u,%u]", val,
                 color_table[val].red,
                 color_table[val].grn,
                 color_table[val].blu);
@@ -2173,6 +2253,42 @@ unpackUncompressedBits(FILE *          const ifP,
 
 
 static void
+reportValidateCompressedLineLen(unsigned int const row,
+                                unsigned int const linelen,
+                                unsigned int const rowSize) {
+/*----------------------------------------------------------------------------
+   'row' is a row number in the raster.
+
+   'linelen' is the number of bytes of PICT that the PICT says hold the
+   compressed version of that row.
+
+   'rowSize' is the number of bytes we expect the uncompressed line to
+   be (includes pad pixels on the right).
+-----------------------------------------------------------------------------*/
+    if (verbose > 1)
+        pm_message("Row %u: %u-byte compressed line", row, linelen);
+
+    /* When the line length value is garbage, it often causes the program to
+       try to read beyond EOF.  To make that failure easier to diagnose,
+       we sanity check the line length now.
+    */
+
+    /* In the worst case, a pixel is represented by two bytes: a one byte
+       repeat count of one followed by a one byte pixel value (the byte could
+       be up to 8 pixels) or a one byte block length of one followed by the
+       pixel value.  So expansion factor two.
+    */
+
+    if (linelen > rowSize * 2)
+        pm_error("Invalid PICT: compressed line of %u bytes for Row %u "
+                 "is too big "
+                 "to represent a %u-byte padded row, even with worse case "
+                 "compression.", linelen, row, rowSize);
+}
+
+
+
+static void
 expandRun(unsigned char * const block,
           unsigned int    const blockLimit,
           unsigned int    const bitsPerPixel,
@@ -2375,7 +2491,7 @@ interpretCompressedLine(unsigned char * const linebuf,
         
         assert(lineCursor <= linelen);
             
-        if (verbose > 1)
+        if (verbose > 2)
             pm_message("At Byte %u of line, Column %u of row",
                        lineCursor, rasterCursor);
 
@@ -2394,6 +2510,32 @@ interpretCompressedLine(unsigned char * const linebuf,
 }
 
 
+/* There is some confusion about when, in PICT, a line length is one byte and
+  when it is two.  An Apple document says it is two bytes when the number of
+  pixels in the row, padded, is > 250.  Ppmtopict generated PICTs that way
+  until January 2009.  Picttoppm assumed something similar until March 2004:
+  It assumed the line length is two bytes when the number of pixels > 250 _or_
+  bits per pixel > 8.  But in March 2004, Steve Summit did a bunch of
+  experiments on existing PICT files and found that they all worked with the
+  rule "pixels per row > 200 => 2 byte line length" and some did not work
+  with the original rule.
+
+  So in March 2004, Picttoppm changed to pixels per row > 200.  Ppmtopict
+  didn't catch up until January 2009.
+
+  http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-460.html#HEADING460-0
+
+  Of course, neither 200 nor 250 make any logical sense.  In the worst case,
+  you can represent 254 pixels of 8 bpp or less in a 255 byte line.
+  In the worst case, you can represent 127 16bpp pixels in a 255 byte line.
+  So with 200 being the cutoff, it's actually impossible to represent some 
+  16 bpp images with 200 pixels per row.
+
+  We have not been able to find an offical spec for PICT.
+
+  Some day, we may have to make a user option for this.
+*/
+
 
 static void
 unpackCompressedBits(FILE *          const ifP,
@@ -2410,14 +2552,13 @@ unpackCompressedBits(FILE *          const ifP,
    "packing" and I don't know what packing is called.  But we don't
    use that confusing terminology in this program, except when talking
    to the user.
-
-   *boundsP describes the rectangle.
 -----------------------------------------------------------------------------*/
     unsigned int const llsize = rowBytes > 200 ? 2 : 1;
         /* Width in bytes of the field at the beginning of a line that tells
-           how long (in bytes) the line is.
+           how long (in bytes) the line is.  See notes above about this
+           computation.
         */
-    unsigned int rowOfRect;
+    unsigned int row;
     unsigned char * linebuf;
     unsigned int linebufSize;
 
@@ -2426,9 +2567,9 @@ unpackCompressedBits(FILE *          const ifP,
     if (linebuf == NULL)
         pm_error("can't allocate memory for line buffer");
 
-    for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) {
+    for (row = 0; row < raster.rowCount; ++row) {
         unsigned char * const rowRaster =
-            &raster.bytes[rowOfRect * raster.rowSize];
+            &raster.bytes[row * raster.rowSize];
         unsigned int linelen;
 
         if (llsize == 2)
@@ -2436,8 +2577,7 @@ unpackCompressedBits(FILE *          const ifP,
         else
             linelen = read_byte();
 
-        if (verbose > 1)
-            pm_message("Row %u: %u-byte line", rowOfRect, linelen);
+        reportValidateCompressedLineLen(row, linelen, raster.rowSize);
 
         if (linelen > linebufSize) {
             linebufSize = linelen;
@@ -2554,6 +2694,7 @@ static drawFn BkPixPat;
 
 static void
 BkPixPat(struct canvas * const canvasP,
+         blitList *      const blitListP,
          int             const version) {
 
     read_pattern();
@@ -2565,6 +2706,7 @@ static drawFn PnPixPat;
 
 static void
 PnPixPat(struct canvas * const canvasP,
+         blitList *      const blitListP,
          int             const version) {
 
     read_pattern();
@@ -2576,6 +2718,7 @@ static drawFn FillPixPat;
 
 static void
 FillPixPat(struct canvas * const canvasP,
+           blitList *      const blitListP,
            int             const version) {
 
     read_pattern();
@@ -2610,6 +2753,7 @@ static drawFn BkPat;
 
 static void 
 BkPat(struct canvas * const canvasP,
+      blitList *      const blitListP,
       int             const version) {
 
     read_8x8_pattern(&bkpat);
@@ -2621,6 +2765,7 @@ static drawFn PnPat;
 
 static void 
 PnPat(struct canvas * const canvasP,
+      blitList *      const blitListP,
       int             const version) {
 
     read_8x8_pattern(&pen_pat);
@@ -2632,6 +2777,7 @@ static drawFn FillPat;
 
 static void 
 FillPat(struct canvas * const canvasP,
+        blitList *      const blitListP,
         int             const version) {
 
     read_8x8_pattern(&fillpat);
@@ -2643,6 +2789,7 @@ static drawFn PnSize;
 
 static void 
 PnSize(struct canvas * const canvasP,
+       blitList *      const blitListP,
        int             const version) {
 
     pen_height = read_word();
@@ -2657,6 +2804,7 @@ static drawFn PnSize;
 
 static void 
 PnMode(struct canvas * const canvasP,
+       blitList *      const blitListP,
        int             const version) {
 
     pen_mode = read_word();
@@ -2685,6 +2833,7 @@ static drawFn RGBFgCol;
 
 static void 
 RGBFgCol(struct canvas * const canvasP,
+         blitList *      const blitListP,
          int             const version) {
 
     read_rgb(&foreground);
@@ -2699,6 +2848,7 @@ static drawFn RGBBkCol;
 
 static void 
 RGBBkCol(struct canvas * const canvasP,
+         blitList *      const blitListP,
          int             const version) {
 
     read_rgb(&background);
@@ -2857,6 +3007,7 @@ static drawFn Line;
 
 static void 
 Line(struct canvas * const canvasP,
+     blitList *      const blitListP,
      int             const version) {
 
   struct Point p1;
@@ -2874,6 +3025,7 @@ static drawFn LineFrom;
 
 static void 
 LineFrom(struct canvas * const canvasP,
+         blitList *      const blitListP,
          int             const version) {
 
     struct Point p1;
@@ -2881,7 +3033,7 @@ LineFrom(struct canvas * const canvasP,
     if (verbose)
         pm_message("(%d,%d) to (%d, %d)", current.x, current.y, p1.x, p1.y);
 
-    if (!fullres)
+    if (!blitListP)
         scan_line(canvasP, current.x, current.y, p1.x, p1.y);
 
     current.x = p1.x;
@@ -2894,6 +3046,7 @@ static drawFn ShortLine;
 
 static void 
 ShortLine(struct canvas * const canvasP,
+          blitList *      const blitListP,
           int             const version) {
 
     struct Point p1;
@@ -2904,7 +3057,7 @@ ShortLine(struct canvas * const canvasP,
     current.x += p1.x;
     current.y += p1.y;
     
-    if (!fullres)
+    if (!blitListP)
         scan_line(canvasP, p1.x, p1.y, current.x, current.y);
 }
 
@@ -2914,6 +3067,7 @@ static drawFn ShortLineFrom;
 
 static void 
 ShortLineFrom(struct canvas * const canvasP,
+              blitList *      const blitListP,
               int             const version) {
 
     struct Point p1;
@@ -2923,7 +3077,7 @@ ShortLineFrom(struct canvas * const canvasP,
                    current.x,current.y,p1.x,p1.y);
     p1.x += current.x;
     p1.y += current.y;
-    if (!fullres)
+    if (!blitListP)
         scan_line(canvasP, current.x, current.y, p1.x, p1.y);
     current.x = p1.x;
     current.y = p1.y;
@@ -2937,9 +3091,6 @@ do_paintRect(struct canvas * const canvasP,
 
     struct Rect rect;
   
-    if (fullres)
-        return;
-
     if (verbose)
         dumpRect("painting", prect);
 
@@ -2954,10 +3105,12 @@ static drawFn paintRect;
 
 static void 
 paintRect(struct canvas * const canvasP,
+          blitList *      const blitListP,
           int             const version) {
 
     read_rect(&cur_rect);
-    do_paintRect(canvasP, cur_rect);
+    if (!blitListP)
+        do_paintRect(canvasP, cur_rect);
 }
 
 
@@ -2966,9 +3119,11 @@ static drawFn paintSameRect;
 
 static void 
 paintSameRect(struct canvas * const canvasP,
+              blitList *      const blitListP,
               int             const version) {
 
-    do_paintRect(canvasP, cur_rect);
+    if (!blitListP)
+        do_paintRect(canvasP, cur_rect);
 }
 
 
@@ -2976,25 +3131,22 @@ paintSameRect(struct canvas * const canvasP,
 static void 
 do_frameRect(struct canvas * const canvasP,
              struct Rect     const rect) {
-    int x, y;
 
-    if (fullres)
-        return;
-  
     if (verbose)
         dumpRect("framing", rect);
 
-    if (pen_width == 0 || pen_height == 0)
-        return;
-
-    for (x = rect.left; x <= rect.right - pen_width; x += pen_width) {
-        draw_pen(canvasP, x, rect.top);
-        draw_pen(canvasP, x, rect.bottom - pen_height);
-    }
+    if (pen_width > 0 && pen_height > 0) {
+        unsigned int x, y;
 
-    for (y = rect.top; y <= rect.bottom - pen_height ; y += pen_height) {
-        draw_pen(canvasP, rect.left, y);
-        draw_pen(canvasP, rect.right - pen_width, y);
+        for (x = rect.left; x <= rect.right - pen_width; x += pen_width) {
+            draw_pen(canvasP, x, rect.top);
+            draw_pen(canvasP, x, rect.bottom - pen_height);
+        }
+        
+        for (y = rect.top; y <= rect.bottom - pen_height ; y += pen_height) {
+            draw_pen(canvasP, rect.left, y);
+            draw_pen(canvasP, rect.right - pen_width, y);
+        }
     }
 }
 
@@ -3004,10 +3156,12 @@ static drawFn frameRect;
 
 static void 
 frameRect(struct canvas * const canvasP,
+          blitList *      const blitListP,
           int             const version) {
 
     read_rect(&cur_rect);
-    do_frameRect(canvasP, cur_rect);
+    if (!blitListP)
+        do_frameRect(canvasP, cur_rect);
 }
 
 
@@ -3016,9 +3170,11 @@ static drawFn frameSameRect;
 
 static void 
 frameSameRect(struct canvas * const canvasP,
+              blitList *      const blitListP,
               int             const version) {
 
-    do_frameRect(canvasP, cur_rect);
+    if (!blitListP)
+        do_frameRect(canvasP, cur_rect);
 }
 
 
@@ -3165,6 +3321,7 @@ static drawFn paintPoly;
   
 static void 
 paintPoly(struct canvas * const canvasP,
+          blitList *      const blitListP,
           int             const version) {
 
   struct Rect bb;
@@ -3176,7 +3333,7 @@ paintPoly(struct canvas * const canvasP,
     read_point(&pts[i]);
 
   /* scan convert poly ... */
-  if (!fullres)
+  if (!blitListP)
       scan_poly(canvasP, np, pts);
 }
 
@@ -3186,6 +3343,7 @@ static drawFn PnLocHFrac;
 
 static void 
 PnLocHFrac(struct canvas * const canvasP,
+           blitList *      const blitListP,
            int             const version) {
 
     word frac = read_word();
@@ -3200,6 +3358,7 @@ static drawFn TxMode;
 
 static void 
 TxMode(struct canvas * const canvasP,
+       blitList *      const blitListP,
        int             const version) {
 
     text_mode = read_word();
@@ -3220,6 +3379,7 @@ static drawFn TxFont;
 
 static void 
 TxFont(struct canvas * const canvasP,
+       blitList *      const blitListP,
        int             const version) {
 
     text_font = read_word();
@@ -3233,6 +3393,7 @@ static drawFn TxFace;
 
 static void 
 TxFace(struct canvas * const canvasP,
+       blitList *      const blitListP,
        int             const version) {
 
     text_face = read_byte();
@@ -3246,6 +3407,7 @@ static drawFn TxSize;
 
 static void 
 TxSize(struct canvas * const canvasP,
+       blitList *      const blitListP,
        int             const version) {
 
     text_size = read_word();
@@ -3256,11 +3418,11 @@ TxSize(struct canvas * const canvasP,
 
 
 static void
-skip_text(void) {
+skip_text(blitList * const blitListP) {
 
-    pm_message("Warning: text is omitted from the output because "
-               "we don't know how to do text with -fullres.");
     skip(read_byte());
+
+    blitListP->unblittableText = true;
 }
 
 
@@ -3279,6 +3441,7 @@ static struct font*
 get_font(int const font, 
          int const size, 
          int const style) {
+
     int closeness, bestcloseness;
     struct fontinfo* fi, *best;
 
@@ -3410,11 +3573,12 @@ do_ps_text(struct canvas * const canvasP,
 
 static void
 do_text(struct canvas *  const canvasP,
+        blitList *       const blitListP,
         word             const startx, 
         word             const starty) {
 
-    if (fullres)
-        skip_text();
+    if (blitListP)
+        skip_text(blitListP);
     else {
         if (!(tfont = get_font(text_font, text_size, text_face)))
             tfont = pbm_defaultfont("bdf");
@@ -3459,13 +3623,14 @@ static drawFn LongText;
 
 static void
 LongText(struct canvas * const canvasP,
+         blitList *      const blitListP,
          int             const version) {
 
     struct Point p;
 
     read_point(&p);
 
-    do_text(canvasP, p.x, p.y);
+    do_text(canvasP, blitListP, p.x, p.y);
 }
 
 
@@ -3474,10 +3639,12 @@ static drawFn DHText;
 
 static void
 DHText(struct canvas * const canvasP,
+       blitList *      const blitListP,
        int             const version) {
 
     current.x += read_byte();
-    do_text(canvasP, current.x, current.y);
+
+    do_text(canvasP, blitListP, current.x, current.y);
 }
 
 
@@ -3486,10 +3653,12 @@ static drawFn DVText;
 
 static void
 DVText(struct canvas * const canvasP,
+       blitList *      const blitListP,
        int             const version) {
 
     current.y += read_byte();
-    do_text(canvasP, current.x, current.y);
+
+    do_text(canvasP, blitListP, current.x, current.y);
 }
 
 
@@ -3498,6 +3667,7 @@ static drawFn DHDVText;
 
 static void
 DHDVText(struct canvas * const canvasP,
+         blitList *      const blitListP,
          int             const version) {
     byte dh, dv;
 
@@ -3509,7 +3679,8 @@ DHDVText(struct canvas * const canvasP,
 
     current.x += dh;
     current.y += dv;
-    do_text(canvasP, current.x, current.y);
+
+    do_text(canvasP, blitListP, current.x, current.y);
 }
 
 
@@ -3520,6 +3691,7 @@ DHDVText(struct canvas * const canvasP,
 
 static void
 directBits(struct canvas * const canvasP,
+           blitList *      const blitListP,
            unsigned int    const pictVersion, 
            bool            const skipRegion) {
 
@@ -3561,11 +3733,11 @@ directBits(struct canvas * const canvasP,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (skipRegion) 
-        skip_poly_or_region(canvasP, pictVersion);
+        skip_poly_or_region(canvasP, blitListP, pictVersion);
 
     unpackbits(ifp, &p.Bounds, 0, p.pixelSize, &raster);
 
-    blit(srcRect, p.Bounds, raster, canvasP, p.pixelSize,
+    blit(srcRect, p.Bounds, raster, canvasP, blitListP, p.pixelSize,
          dstRect, picFrame, rowlen, NULL, mode);
 
     freeRaster(raster);
@@ -3580,9 +3752,10 @@ static drawFn DirectBitsRect;
 
 static void
 DirectBitsRect(struct canvas * const canvasP,
+               blitList *      const blitListP,
                int             const version) {
 
-    directBits(canvasP, version, SKIP_REGION_FALSE);
+    directBits(canvasP, blitListP, version, SKIP_REGION_FALSE);
 }
 
 
@@ -3591,15 +3764,17 @@ static drawFn DirectBitsRgn;
 
 static void
 DirectBitsRgn(struct canvas * const canvasP,
+              blitList *      const blitListP,
               int             const version) {
 
-    directBits(canvasP, version, SKIP_REGION_TRUE);
+    directBits(canvasP, blitListP, version, SKIP_REGION_TRUE);
 }
 
 
 
 static void
 do_pixmap(struct canvas * const canvasP,
+          blitList *      const blitListP,
           int             const version, 
           word            const rowBytes, 
           int             const is_region) {
@@ -3638,13 +3813,13 @@ do_pixmap(struct canvas * const canvasP,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (is_region)
-        skip_poly_or_region(canvasP, version);
+        skip_poly_or_region(canvasP, blitListP, version);
 
     stage = "unpacking rectangle";
 
     unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster);
 
-    blit(srcRect, p.Bounds, raster, canvasP, 8,
+    blit(srcRect, p.Bounds, raster, canvasP, blitListP, 8,
          dstRect, picFrame, rowlen, color_table, mode);
 
     free(color_table);
@@ -3656,6 +3831,7 @@ do_pixmap(struct canvas * const canvasP,
 static void
 do_bitmap(FILE *          const ifP,
           struct canvas * const canvasP,
+          blitList *      const blitListP,
           int             const version, 
           int             const rowBytes, 
           int             const is_region) {
@@ -3683,13 +3859,13 @@ do_bitmap(FILE *          const ifP,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (is_region)
-        skip_poly_or_region(canvasP, version);
+        skip_poly_or_region(canvasP, blitListP, version);
 
     stage = "unpacking rectangle";
 
     unpackbits(ifP, &Bounds, rowBytes, 1, &raster);
 
-    blit(srcRect, Bounds, raster, canvasP, 8,
+    blit(srcRect, Bounds, raster, canvasP, blitListP, 8,
          dstRect, picFrame, rowlen, color_table, mode);
 
     freeRaster(raster);
@@ -3701,6 +3877,7 @@ static drawFn BitsRect;
 
 static void
 BitsRect(struct canvas * const canvasP,
+         blitList *      const blitListP,
          int             const version) {
 
     word rowBytesWord;
@@ -3713,9 +3890,9 @@ BitsRect(struct canvas * const canvasP,
     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
 
     if (pixMap)
-        do_pixmap(canvasP, version, rowBytes, 0);
+        do_pixmap(canvasP, blitListP, version, rowBytes, 0);
     else
-        do_bitmap(ifp, canvasP, version, rowBytes, 0);
+        do_bitmap(ifp, canvasP, blitListP, version, rowBytes, 0);
 }
 
 
@@ -3724,6 +3901,7 @@ static drawFn BitsRegion;
 
 static void
 BitsRegion(struct canvas * const canvasP,
+           blitList *      const blitListP,
            int             const version) {
     
     word rowBytesWord;
@@ -3736,9 +3914,9 @@ BitsRegion(struct canvas * const canvasP,
     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
 
     if (pixMap)
-        do_pixmap(canvasP, version, rowBytes, 1);
+        do_pixmap(canvasP, blitListP, version, rowBytes, 1);
     else
-        do_bitmap(ifp, canvasP, version, rowBytes, 1);
+        do_bitmap(ifp, canvasP, blitListP, version, rowBytes, 1);
 }
 
 
@@ -3921,15 +4099,85 @@ static struct opdef const optable[] = {
 
 
 static void
+processOpcode(word const opcode, 
+              struct canvas * const canvasP,
+              blitList *      const blitListP,
+              unsigned int    const version) {
+
+    if (opcode < 0xa2) {
+        stage = optable[opcode].name;
+        if (verbose) {
+            if (streq(stage, "reserved"))
+                pm_message("reserved opcode=0x%x", opcode);
+            else
+                pm_message("Opcode: %s", optable[opcode].name);
+        }
+
+        if (optable[opcode].impl != NULL)
+            (*optable[opcode].impl)(canvasP, blitListP, version);
+        else if (optable[opcode].len >= 0)
+            skip(optable[opcode].len);
+        else {
+            switch (optable[opcode].len) {
+            case WORD_LEN: {
+                word const len = read_word();
+                skip(len);
+                } break;
+            default:
+                pm_error("can't do length %u", optable[opcode].len);
+            }
+        }
+    } else if (opcode == 0xc00) {
+        if (verbose)
+            pm_message("HeaderOp");
+        stage = "HeaderOp";
+        skip(24);
+    } else if (opcode >= 0xa2 && opcode <= 0xaf) {
+        stage = "skipping reserved";
+        if (verbose)
+            pm_message("%s 0x%x", stage, opcode);
+        skip(read_word());
+    } else if (opcode >= 0xb0 && opcode <= 0xcf) {
+        /* just a reserved opcode, no data */
+        if (verbose)
+            pm_message("reserved 0x%x", opcode);
+    } else if (opcode >= 0xd0 && opcode <= 0xfe) {
+        stage = "skipping reserved";
+        if (verbose)
+            pm_message("%s 0x%x", stage, opcode);
+        skip(read_long());
+    } else if (opcode >= 0x100 && opcode <= 0x7fff) {
+        stage = "skipping reserved";
+        if (verbose)
+            pm_message("%s 0x%x", stage, opcode);
+        skip((opcode >> 7) & 255);
+    } else if (opcode >= 0x8000 && opcode <= 0x80ff) {
+        /* just a reserved opcode */
+        if (verbose)
+            pm_message("reserved 0x%x", opcode);
+    } else if (opcode >= 0x8100) {
+        stage = "skipping reserved";
+        if (verbose)
+            pm_message("%s 0x%x", stage, opcode);
+        skip(read_long());
+    } else
+        pm_error("This program does not understand opcode 0x%04x", opcode);
+}
+
+
+
+static void
 interpret_pict(FILE * const ofP) {
 
     byte ch;
     word picSize;
     word opcode;
-    word len;
     unsigned int version;
     int i;
     struct canvas canvas;
+    blitList blitList;
+
+    initBlitList(&blitList);
 
     for (i = 0; i < 64; i++)
         pen_pat.pix[i] = bkpat.pix[i] = fillpat.pix[i] = 1;
@@ -3943,16 +4191,16 @@ interpret_pict(FILE * const ofP) {
     picSize = read_word();
 
     if (verbose)
-        pm_message("picture size = %d (0x%x)", picSize, picSize);
+        pm_message("picture size = %u (0x%x)", picSize, picSize);
 
     stage = "reading picture frame";
     read_rect(&picFrame);
 
     if (verbose) {
         dumpRect("Picture frame:", picFrame);
-        pm_message("Picture size is %d x %d",
-            picFrame.right - picFrame.left,
-            picFrame.bottom - picFrame.top);
+        pm_message("Picture size is %u x %u",
+                   picFrame.right - picFrame.left,
+                   picFrame.bottom - picFrame.top);
     }
 
     if (!fullres) {
@@ -3988,68 +4236,15 @@ interpret_pict(FILE * const ofP) {
     if (verbose)
         pm_message("PICT version %u", version);
 
-    while((opcode = get_op(version)) != 0xff) {
-        if (opcode < 0xa2) {
-            stage = optable[opcode].name;
-            if (verbose) {
-                if (streq(stage, "reserved"))
-                    pm_message("reserved opcode=0x%x", opcode);
-                else
-                    pm_message("Opcode: %s", optable[opcode].name);
-            }
-
-            if (optable[opcode].impl != NULL)
-                (*optable[opcode].impl)(&canvas, version);
-            else if (optable[opcode].len >= 0)
-                skip(optable[opcode].len);
-            else switch (optable[opcode].len) {
-            case WORD_LEN:
-                len = read_word();
-                skip(len);
-                break;
-            default:
-                pm_error("can't do length %u", optable[opcode].len);
-            }
-        } else if (opcode == 0xc00) {
-            if (verbose)
-                pm_message("HeaderOp");
-            stage = "HeaderOp";
-            skip(24);
-        } else if (opcode >= 0xa2 && opcode <= 0xaf) {
-            stage = "skipping reserved";
-            if (verbose)
-                pm_message("%s 0x%x", stage, opcode);
-            skip(read_word());
-        } else if (opcode >= 0xb0 && opcode <= 0xcf) {
-            /* just a reserved opcode, no data */
-            if (verbose)
-                pm_message("reserved 0x%x", opcode);
-        } else if (opcode >= 0xd0 && opcode <= 0xfe) {
-            stage = "skipping reserved";
-            if (verbose)
-                pm_message("%s 0x%x", stage, opcode);
-            skip(read_long());
-        } else if (opcode >= 0x100 && opcode <= 0x7fff) {
-            stage = "skipping reserved";
-            if (verbose)
-                pm_message("%s 0x%x", stage, opcode);
-            skip((opcode >> 7) & 255);
-        } else if (opcode >= 0x8000 && opcode <= 0x80ff) {
-            /* just a reserved opcode */
-            if (verbose)
-                pm_message("reserved 0x%x", opcode);
-        } else if (opcode >= 0x8100) {
-            stage = "skipping reserved";
-            if (verbose)
-                pm_message("%s 0x%x", stage, opcode);
-            skip(read_long());
-        } else
-            pm_error("This program does not understand opcode 0x%04x", opcode);
-    }
+    while((opcode = get_op(version)) != 0xff)
+        processOpcode(opcode, &canvas, fullres ? &blitList : NULL, version);
     
-    if (fullres)
-        do_blits(&canvas);
-
+    if (fullres) {
+        if (blitList.unblittableText)
+            pm_message("Warning: text is omitted from the output because "
+                       "we don't know how to do text with -fullres.");
+        doBlitList(&canvas, &blitList);
+    }
     outputPpm(ofP, canvas.planes);
 
     freePlanes(canvas.planes);
@@ -4123,6 +4318,7 @@ main(int argc, char * argv[]) {
 
     if (header) {
         stage = "Reading 512 byte header";
+        /* Note that the "header" in PICT is entirely comment! */
         skip(512);
     }