about summary refs log tree commit diff
path: root/converter/ppm
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2007-03-24 23:23:48 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2007-03-24 23:23:48 +0000
commit45a832a32e169288d60d4b2ff4cf21f60bc7a56c (patch)
tree19453c130621be301875c9703fc51fa9cca78015 /converter/ppm
parentfb819c28f60adf5d2a2cb4d64b1ec584f2bda497 (diff)
downloadnetpbm-mirror-45a832a32e169288d60d4b2ff4cf21f60bc7a56c.tar.gz
netpbm-mirror-45a832a32e169288d60d4b2ff4cf21f60bc7a56c.tar.xz
netpbm-mirror-45a832a32e169288d60d4b2ff4cf21f60bc7a56c.zip
Fix 'too many pixels in row' failure due to padding when there are multiple pixels per byte
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@253 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/ppm')
-rw-r--r--converter/ppm/picttoppm.c718
1 files changed, 481 insertions, 237 deletions
diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c
index cfc5760e..0b5549b2 100644
--- a/converter/ppm/picttoppm.c
+++ b/converter/ppm/picttoppm.c
@@ -94,16 +94,16 @@ struct rgbPlanes {
     word * blu;
 };
 
+struct canvas {
+    struct rgbPlanes planes;
+};
+
 typedef void (*transfer_func) (struct RGBColor* src, struct RGBColor* dst);
 
 static const char* stage;
 static struct Rect picFrame;
-static word* red;
-static word* green;
-static word* blue;
 static word rowlen;
 static word collen;
-static longword planelen;
 static int verbose;
 static int fullres;
 static int recognize_comment;
@@ -142,10 +142,12 @@ 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;
-    void (*impl) (int);
+    drawFn * impl;
     const char* description;
 };
 
@@ -589,15 +591,23 @@ picComment(word const type,
 
 
 
+static drawFn ShortComment;
+
 static void
-ShortComment(int const version) {
+ShortComment(struct canvas * const canvasP,
+             int             const version) {
+
     picComment(read_word(), 0);
 }
 
 
 
+static drawFn LongComment;
+
 static void
-LongComment(int const version) {
+LongComment(struct canvas * const canvasP,
+            int             const version) {
+
     word type;
 
     type = read_word();
@@ -606,8 +616,12 @@ LongComment(int const version) {
 
 
 
+static drawFn skip_poly_or_region;
+
 static void
-skip_poly_or_region(int const version) {
+skip_poly_or_region(struct canvas * const canvasP,
+                    int             const version) {
+
     stage = "skipping polygon or region";
     skip(read_word() - 2);
 }
@@ -685,10 +699,11 @@ load_fontdir(const char * const dirfile) {
 
 static void
 read_rect(struct Rect * const r) {
-    r->top = read_word();
-    r->left = read_word();
+
+    r->top    = read_word();
+    r->left   = read_word();
     r->bottom = read_word();
-    r->right = read_word();
+    r->right  = read_word();
 }
 
 
@@ -1422,6 +1437,7 @@ 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, 
@@ -1462,9 +1478,9 @@ generalBlit(struct Rect       const srcRect,
 
     dstoff = (clipdst.top - dstBounds.top) * dstwid +
         (clipdst.left - dstBounds.left);
-    dst.red = red + dstoff;
-    dst.grn = green + dstoff;
-    dst.blu = blue + dstoff;
+    dst.red = planes.red + dstoff;
+    dst.grn = planes.grn + dstoff;
+    dst.blu = planes.blu + dstoff;
 
     /* get rid of Text mask mode bit, if (erroneously) set */
     if ((mode & ~64) == 0)
@@ -1491,6 +1507,7 @@ static int
 blit(struct Rect       const srcRect, 
      struct Rect       const srcBounds, 
      struct raster     const srcplane,
+     struct canvas *   const canvasP,
      int               const pixSize, 
      struct Rect       const dstRect, 
      struct Rect       const dstBounds, 
@@ -1534,7 +1551,7 @@ blit(struct Rect       const srcRect,
 
             retval = 0;
         } else {
-            generalBlit(srcRect, srcBounds, srcplane, pixSize,
+            generalBlit(srcRect, srcBounds, srcplane, canvasP->planes, pixSize,
                         dstRect, dstBounds, dstwid, color_map, mode,
                         clipsrc, clipdst);
 
@@ -1553,17 +1570,15 @@ blit(struct Rect       const srcRect,
  */
 
 static void 
-allocPlanes(struct rgbPlanes * const planesP) {
+allocPlanes(unsigned int       const width,
+            unsigned int       const height,
+            struct rgbPlanes * const planesP) {
 
-    struct rgbPlanes planes;
+    unsigned int const planelen = width * height;
 
-    rowlen = picFrame.right - picFrame.left;
-    collen = picFrame.bottom - picFrame.top;
-
-    clip_rect = picFrame;
+    struct rgbPlanes planes;
 
-    planelen = rowlen * collen;
-    MALLOCARRAY(planes.red,  planelen);
+    MALLOCARRAY(planes.red, planelen);
     MALLOCARRAY(planes.grn, planelen);
     MALLOCARRAY(planes.blu, planelen);
     if (planes.red == NULL || planes.grn == NULL || planes.blu == NULL)
@@ -1574,13 +1589,7 @@ allocPlanes(struct rgbPlanes * const planesP) {
     memset(planes.grn, 255, planelen * sizeof(word));
     memset(planes.blu, 255, planelen * sizeof(word));
 
-    /* Until we wean this program off of global variables, we have to
-       set these:
-    */
-
-    red   = planes.red;
-    green = planes.grn;
-    blue  = planes.blu;
+    *planesP = planes;
 }
 
 
@@ -1604,7 +1613,7 @@ compact(word const input) {
 
 
 static void
-do_blits(struct rgbPlanes * const planesP) {
+do_blits(struct canvas * const canvasP) {
 
     struct blit_info* bi;
     int srcwidth, dstwidth, srcheight, dstheight;
@@ -1702,10 +1711,15 @@ do_blits(struct rgbPlanes * const planesP) {
         rectscale(&picFrame, xscale, yscale);
     }
 
-    allocPlanes(planesP);
+    rowlen = picFrame.right  - picFrame.left;
+    collen = picFrame.bottom - picFrame.top;
+
+    allocPlanes(rowlen, collen, &canvasP->planes);
+
+    clip_rect = picFrame;
 
     for (bi = blit_list; bi; bi = bi->next) {
-        blit(bi->srcRect, bi->srcBounds, bi->srcplane,
+        blit(bi->srcRect, bi->srcBounds, bi->srcplane, canvasP,
              bi->pixSize,
              bi->dstRect, picFrame, rowlen,
              bi->color_map,
@@ -1716,7 +1730,8 @@ do_blits(struct rgbPlanes * const planesP) {
 
 
 static void
-outputPpm(struct rgbPlanes const planes) {
+outputPpm(FILE *           const ofP,
+          struct rgbPlanes const planes) {
 
     unsigned int width;
     unsigned int height;
@@ -1732,7 +1747,7 @@ outputPpm(struct rgbPlanes const planes) {
     width  = picFrame.right  - picFrame.left;
     height = picFrame.bottom - picFrame.top;
 
-    ppm_writeppminit(stdout, width, height, PPM_MAXMAXVAL, 0);
+    ppm_writeppminit(ofP, width, height, PPM_MAXMAXVAL, 0);
     pixelrow = ppm_allocrow(width);
     srcCursor = 0;
     for (row = 0; row < height; ++row) {
@@ -1745,9 +1760,8 @@ outputPpm(struct rgbPlanes const planes) {
                 );
             ++srcCursor;
         }
-        ppm_writeppmrow(stdout, pixelrow, width, PPM_MAXMAXVAL, 0);
+        ppm_writeppmrow(ofP, pixelrow, width, PPM_MAXMAXVAL, 0);
     }
-    pm_close(stdout);
 }
 
 
@@ -1773,8 +1787,12 @@ get_op(int const version) {
 
 
 
+static drawFn Clip;
+
 static void
-Clip(int const version) {
+Clip(struct canvas * const canvasP,
+     int             const version) {
+
     word len;
 
     len = read_word();
@@ -1791,8 +1809,12 @@ Clip(int const version) {
 
 
 
+static drawFn OpColor;
+
 static void
-OpColor(int const version) {
+OpColor(struct canvas * const canvasP,
+        int             const version) {
+
     op_color.red = read_word();
     op_color.grn = read_word();
     op_color.blu = read_word();
@@ -1806,17 +1828,17 @@ read_pixmap(struct pixMap * const p) {
     stage = "getting pixMap header";
 
     read_rect(&p->Bounds);
-    p->version = read_word();
-    p->packType = read_word();
-    p->packSize = read_long();
-    p->hRes = read_long();
-    p->vRes = read_long();
-    p->pixelType = read_word();
-    p->pixelSize = read_word();
-    p->cmpCount = read_word();
-    p->cmpSize = read_word();
+    p->version    = read_word();
+    p->packType   = read_word();
+    p->packSize   = read_long();
+    p->hRes       = read_long();
+    p->vRes       = read_long();
+    p->pixelType  = read_word();
+    p->pixelSize  = read_word();
+    p->cmpCount   = read_word();
+    p->cmpSize    = read_word();
     p->planeBytes = read_long();
-    p->pmTable = read_long();
+    p->pmTable    = read_long();
     p->pmReserved = read_long();
 
     if (verbose) {
@@ -1824,6 +1846,8 @@ read_pixmap(struct pixMap * const p) {
         pm_message("pixelSize: %d", p->pixelSize);
         pm_message("cmpCount:  %d", p->cmpCount);
         pm_message("cmpSize:   %d", p->cmpSize);
+        if (verbose)
+            dumpRect("Bounds:", p->Bounds);
     }
 
     if (p->pixelType != 0)
@@ -2115,11 +2139,22 @@ expandRun(unsigned char * const block,
 
         if (verbose > 1)
             pm_message("Block: run of %u pixels or plane samples", runLength);
-        
+
         unpackBuf(&block[1], pkpixsize, bitsPerPixel,
                   &bytePixels, &expandedByteCount);
+
+        /* I assume in a legal PICT the run never has padding for the
+           case that the run is at the right edge of a row and the
+           remaining columns in the row don't fill whole bytes.
+           E.g. if there are 25 columns left in the row and 1 bit per
+           pixel, we won't see a run of 4 bytes and have to ignore the
+           last 7 pixels.  Instead, we'll see a run of 3 bytes
+           followed by a non-run block for the remaining pixel.
+
+           That is what I saw in a test image.
+        */
         
-        if (expandedByteCount > expandedSize)
+        if (expandedByteCount * runLength > expandedSize)
             pm_error("Invalid PICT image.  It contains a row with more pixels "
                      "than the width of the image");
         
@@ -2146,9 +2181,23 @@ copyPixelGroup(unsigned char * const block,
                unsigned int *  const rasterBytesGeneratedP) {
 /*----------------------------------------------------------------------------
    Copy a group of pixels (the data says, "take the following N pixels").
+
+   Copy them (unpacked) from block block[] to dest[].
+
+   block[] self-describes its length.  Return that length as
+   *blockLengthP.
+
+   block[] contains at most 'blockLimit' valid array elements, so if
+   the length information in block[] indicates the block is larger
+   than that, the block is corrupt.
+
+   Return the number of pixels placed in dest[] as *rasterBytesGeneratedP.
+
+   The output array dest[] has 'destSize' elements of space.  Ignore
+   any pixels on the right that won't fit in that.
 -----------------------------------------------------------------------------*/
-    unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1;
-    unsigned int const groupLen = block[0] + 1;
+    unsigned int const pkpixsize   = bitsPerPixel == 16 ? 2 : 1;
+    unsigned int const groupLen    = block[0] + 1;
     unsigned int const blockLength = 1 + groupLen * pkpixsize;
 
     if (blockLength > blockLimit)
@@ -2159,6 +2208,7 @@ copyPixelGroup(unsigned char * const block,
         unsigned int i;
         unsigned char * bytePixels;  /* Points to static storage */
         unsigned int bytePixelLen;
+        unsigned int rasterBytesGenerated;
         
         assert(blockLimit >= 1);  /* block[0] exists */
         assert((block[0] & 0x80) == 0);  /* It's not a run */
@@ -2169,16 +2219,24 @@ copyPixelGroup(unsigned char * const block,
         
         unpackBuf(&block[1], groupLen * pkpixsize, bitsPerPixel,
                   &bytePixels, &bytePixelLen);
-        
-        if (bytePixelLen > destSize)
-            pm_error("Invalid PICT image.  It contains a row with more pixels "
-                     "than the width of the image");
-        
-        for (i = 0; i < bytePixelLen; ++i)
+
+        /* It is normal for the above to return more pixels than there
+           are left in the row, because of padding.  E.g. there is one
+           pixel left in the row, at one bit per pixel.  But a block
+           contains full bytes, so it must contain at least 8 pixels.
+           7 of them are padding, which we should ignore.
+
+           BUT: I saw an image in which the block had _two_ data bytes
+           (16 pixels) when only 1 pixel remained in the row.  I don't
+           understand why, but ignoring the 15 extra seemed to work.
+        */
+        rasterBytesGenerated = MIN(bytePixelLen, destSize);
+
+        for (i = 0; i < rasterBytesGenerated; ++i)
             dest[i] = bytePixels[i];
         
         *blockLengthP = blockLength;
-        *rasterBytesGeneratedP = bytePixelLen;
+        *rasterBytesGeneratedP = rasterBytesGenerated;
     }
 }
 
@@ -2222,6 +2280,49 @@ static unsigned int const maxPixelBytesPerBlock = 1024;
 
 
 static void
+interpretCompressedLine(unsigned char * const linebuf,
+                        unsigned int    const linelen,
+                        unsigned char * const rowRaster,
+                        unsigned int    const rowSize,
+                        unsigned int    const bitsPerPixel) {
+/*----------------------------------------------------------------------------
+   linebuf[] contains 'linelen' bytes from the PICT image that represents
+   one row of the image, in compressed format.  Return the
+   uncompressed pixels of that row as rowRaster[].
+
+   rowRaster[] has 'rowSize' bytes of space.  Caller ensures that
+   linebuf[] does not contain more pixels than that, unless the PICT
+   image from which it comes is corrupt.
+-----------------------------------------------------------------------------*/
+    unsigned int lineCursor;
+    unsigned int rasterCursor;
+
+    for (lineCursor = 0, rasterCursor = 0; lineCursor < linelen; ) {
+        unsigned int blockLength, rasterBytesGenerated;
+        
+        assert(lineCursor <= linelen);
+            
+        if (verbose > 1)
+            pm_message("At Byte %u of line, Column %u of row",
+                       lineCursor, rasterCursor);
+
+        interpretOneRasterBlock(
+            &linebuf[lineCursor], linelen - lineCursor,
+            bitsPerPixel,
+            &rowRaster[rasterCursor], rowSize - rasterCursor,
+            &blockLength, &rasterBytesGenerated);
+        
+        lineCursor += blockLength;
+        rasterCursor += rasterBytesGenerated;
+        assert(rasterCursor <= rowSize);
+    }
+    if (verbose > 1)
+        pm_message("Got %u pixels for row", rasterCursor);
+}
+
+
+
+static void
 unpackCompressedBits(FILE *          const ifP,
                      struct raster   const raster,
                      unsigned int    const rowBytes,
@@ -2253,8 +2354,6 @@ unpackCompressedBits(FILE *          const ifP,
         unsigned char * const rowRaster =
             &raster.bytes[rowOfRect * raster.rowSize];
         unsigned int linelen;
-        unsigned int lineCursor;
-        unsigned int rasterCursor;
 
         if (llsize == 2)
             linelen = read_word();
@@ -2262,7 +2361,7 @@ unpackCompressedBits(FILE *          const ifP,
             linelen = read_byte();
 
         if (verbose > 1)
-            pm_message("linelen: %u", linelen);
+            pm_message("Row %u: %u-byte line", rowOfRect, linelen);
 
         if (linelen > linebufSize) {
             linebufSize = linelen;
@@ -2272,23 +2371,8 @@ unpackCompressedBits(FILE *          const ifP,
         }
         readBytes(ifP, linelen, linebuf);
 
-        for (lineCursor = 0, rasterCursor = 0; lineCursor < linelen; ) {
-            unsigned int blockLength, rasterBytesGenerated;
-
-            assert(lineCursor <= linelen);
-            
-            interpretOneRasterBlock(
-                &linebuf[lineCursor], linelen - lineCursor,
-                bitsPerPixel,
-                &rowRaster[rasterCursor], raster.rowSize - rasterCursor,
-                &blockLength, &rasterBytesGenerated);
-
-            lineCursor += blockLength;
-            rasterCursor += rasterBytesGenerated;
-            assert(rasterCursor <= raster.rowSize);
-        }
-        if (verbose > 1)
-            pm_message("row %u: got %u", rowOfRect, rasterCursor);
+        interpretCompressedLine(linebuf, linelen, rowRaster, raster.rowSize,
+                                bitsPerPixel);
     }
     free(linebuf);
 }
@@ -2390,21 +2474,39 @@ read_pattern(void) {
 
 /* these 3 do nothing but skip over their data! */
 
+static drawFn BkPixPat;
+
 static void
-BkPixPat(int const version) {
+BkPixPat(struct canvas * const canvasP,
+         int             const version) {
+
     read_pattern();
 }
 
+
+
+static drawFn PnPixPat;
+
 static void
-PnPixPat(int const version) {
+PnPixPat(struct canvas * const canvasP,
+         int             const version) {
+
     read_pattern();
 }
 
+
+
+static drawFn FillPixPat;
+
 static void
-FillPixPat(int const version) {
+FillPixPat(struct canvas * const canvasP,
+           int             const version) {
+
     read_pattern();
 }
 
+
+
 static void
 read_8x8_pattern(struct Pattern * const pat) {
     unsigned char buf[8];
@@ -2428,29 +2530,45 @@ read_8x8_pattern(struct Pattern * const pat) {
 
 
 
+static drawFn BkPat;
+
 static void 
-BkPat(int const version) {
+BkPat(struct canvas * const canvasP,
+      int             const version) {
+
     read_8x8_pattern(&bkpat);
 }
 
 
 
+static drawFn PnPat;
+
 static void 
-PnPat(int const version) {
+PnPat(struct canvas * const canvasP,
+      int             const version) {
+
     read_8x8_pattern(&pen_pat);
 }
 
 
 
+static drawFn FillPat;
+
 static void 
-FillPat(int const version) {
+FillPat(struct canvas * const canvasP,
+        int             const version) {
+
     read_8x8_pattern(&fillpat);
 }
 
 
 
+static drawFn PnSize;
+
 static void 
-PnSize(int const version) {
+PnSize(struct canvas * const canvasP,
+       int             const version) {
+
     pen_height = read_word();
     pen_width = read_word();
     if (verbose)
@@ -2459,8 +2577,11 @@ PnSize(int const version) {
 
 
 
+static drawFn PnSize;
+
 static void 
-PnMode(int const version) {
+PnMode(struct canvas * const canvasP,
+       int             const version) {
 
     pen_mode = read_word();
 
@@ -2484,8 +2605,12 @@ read_rgb(struct RGBColor * const rgb) {
 
 
 
+static drawFn RGBFgCol;
+
 static void 
-RGBFgCol(int const v) {
+RGBFgCol(struct canvas * const canvasP,
+         int             const version) {
+
     read_rgb(&foreground);
     if (verbose)
         pm_message("foreground now [%d,%d,%d]", 
@@ -2494,8 +2619,12 @@ RGBFgCol(int const v) {
 
 
 
+static drawFn RGBBkCol;
+
 static void 
-RGBBkCol(int const v) {
+RGBBkCol(struct canvas * const canvasP,
+         int             const version) {
+
     read_rgb(&background);
     if (verbose)
         pm_message("background now [%d,%d,%d]", 
@@ -2507,34 +2636,37 @@ RGBBkCol(int const v) {
 #define PIXEL_INDEX(x,y) ((y) - picFrame.top) * rowlen + (x) - picFrame.left
 
 static void 
-draw_pixel(int                const x, 
-           int                const y, 
+draw_pixel(struct canvas *   const canvasP,
+           int               const x, 
+           int               const y, 
            struct RGBColor * const clr, 
-           transfer_func            trf) {
-
-    int i;
-    struct RGBColor dst;
+           transfer_func           trf) {
 
     if (x < clip_rect.left || x >= clip_rect.right ||
-        y < clip_rect.top || y >= clip_rect.bottom)
-    {
-        return;
-    }
+        y < clip_rect.top || y >= clip_rect.bottom) {
+    } else {
+        unsigned int const i = PIXEL_INDEX(x, y);
 
-    i = PIXEL_INDEX(x, y);
-    dst.red = red[i];
-    dst.grn = green[i];
-    dst.blu = blue[i];
-    (*trf)(clr, &dst);
-    red[i] = dst.red;
-    green[i] = dst.grn;
-    blue[i] = dst.blu;
+        struct RGBColor dst;
+
+        dst.red = canvasP->planes.red[i];
+        dst.grn = canvasP->planes.grn[i];
+        dst.blu = canvasP->planes.blu[i];
+ 
+        (*trf)(clr, &dst);
+
+        canvasP->planes.red[i] = dst.red;
+        canvasP->planes.grn[i] = dst.grn;
+        canvasP->planes.blu[i] = dst.blu;
+    }
 }
 
 
 
 static void 
-draw_pen_rect(struct Rect * const r) {
+draw_pen_rect(struct canvas * const canvasP,
+              struct Rect *   const r) {
+
     int const rowadd = rowlen - (r->right - r->left);
 
     int i;
@@ -2545,16 +2677,18 @@ draw_pen_rect(struct Rect * const r) {
     
     for (y = r->top; y < r->bottom; y++) {
         for (x = r->left; x < r->right; x++) {
-            dst.red = red[i];
-            dst.grn = green[i];
-            dst.blu = blue[i];
+            dst.red = canvasP->planes.red[i];
+            dst.grn = canvasP->planes.grn[i];
+            dst.blu = canvasP->planes.blu[i];
+
             if (pen_pat.pix[(x & 7) + (y & 7) * 8])
                 (*pen_trf)(&black, &dst);
             else
                 (*pen_trf)(&white, &dst);
-            red[i] = dst.red;
-            green[i] = dst.grn;
-            blue[i] = dst.blu;
+
+            canvasP->planes.red[i] = dst.red;
+            canvasP->planes.grn[i] = dst.grn;
+            canvasP->planes.blu[i] = dst.blu;
 
             i++;
         }
@@ -2565,8 +2699,10 @@ draw_pen_rect(struct Rect * const r) {
 
 
 static void 
-draw_pen(int const x, 
-         int const y) {
+draw_pen(struct canvas * const canvasP,
+         int             const x, 
+         int             const y) {
+
     struct Rect penrect;
 
     penrect.left = x;
@@ -2576,7 +2712,7 @@ draw_pen(int const x,
 
     rectinter(penrect, clip_rect, &penrect);
 
-    draw_pen_rect(&penrect);
+    draw_pen_rect(canvasP, &penrect);
 }
 
 /*
@@ -2593,10 +2729,12 @@ draw_pen(int const x,
  * Paul Heckbert    3 Sep 85
  */
 static void 
-scan_line(short const x1, 
-          short const y1, 
-          short const x2, 
-          short const y2) {
+scan_line(struct canvas * const canvasP,
+          short           const x1, 
+          short           const y1, 
+          short           const x2, 
+          short           const y2) {
+
     int d, x, y, ax, ay, sx, sy, dx, dy;
 
     if (!(pen_width == 0 && pen_height == 0)) {
@@ -2609,7 +2747,7 @@ scan_line(short const x1,
         if (ax>ay) {        /* x dominant */
             d = ay-(ax>>1);
             for (;;) {
-                draw_pen(x, y);
+                draw_pen(canvasP, x, y);
                 if (x==x2) return;
                 if ((x > rowlen) && (sx > 0)) return;
                 if (d>=0) {
@@ -2623,7 +2761,7 @@ scan_line(short const x1,
         else {          /* y dominant */
             d = ax-(ay>>1);
             for (;;) {
-                draw_pen(x, y);
+                draw_pen(canvasP, x, y);
                 if (y==y2) return;
                 if ((y > collen) && (sy > 0)) return;
                 if (d>=0) {
@@ -2639,70 +2777,88 @@ scan_line(short const x1,
 
 
 
+static drawFn Line;
+
 static void 
-Line(int const v) {
+Line(struct canvas * const canvasP,
+     int             const version) {
+
   struct Point p1;
   read_point(&p1);
   read_point(&current);
   if (verbose)
     pm_message("(%d,%d) to (%d, %d)",
            p1.x,p1.y,current.x,current.y);
-  scan_line(p1.x,p1.y,current.x,current.y);
+  scan_line(canvasP, p1.x,p1.y,current.x,current.y);
 }
 
 
 
+static drawFn LineFrom;
+
 static void 
-LineFrom(int const v) {
-  struct Point p1;
-  read_point(&p1);
-  if (verbose)
-    pm_message("(%d,%d) to (%d, %d)",
-           current.x,current.y,p1.x,p1.y);
+LineFrom(struct canvas * const canvasP,
+         int             const version) {
 
-  if (!fullres)
-      scan_line(current.x,current.y,p1.x,p1.y);
+    struct Point p1;
+    read_point(&p1);
+    if (verbose)
+        pm_message("(%d,%d) to (%d, %d)", current.x, current.y, p1.x, p1.y);
 
-  current.x = p1.x;
-  current.y = p1.y;
+    if (!fullres)
+        scan_line(canvasP, current.x, current.y, p1.x, p1.y);
+
+    current.x = p1.x;
+    current.y = p1.y;
 }
 
 
 
+static drawFn ShortLine;
+
 static void 
-ShortLine(int const v) {
-  struct Point p1;
-  read_point(&p1);
-  read_short_point(&current);
-  if (verbose)
-    pm_message("(%d,%d) delta (%d, %d)",
-           p1.x,p1.y,current.x,current.y);
-  current.x += p1.x;
-  current.y += p1.y;
+ShortLine(struct canvas * const canvasP,
+          int             const version) {
 
-  if (!fullres)
-      scan_line(p1.x,p1.y,current.x,current.y);
+    struct Point p1;
+    read_point(&p1);
+    read_short_point(&current);
+    if (verbose)
+        pm_message("(%d,%d) delta (%d, %d)", p1.x, p1.y, current.x, current.y);
+    current.x += p1.x;
+    current.y += p1.y;
+    
+    if (!fullres)
+        scan_line(canvasP, p1.x, p1.y, current.x, current.y);
 }
 
 
 
+static drawFn ShortLineFrom;
+
 static void 
-ShortLineFrom(int const v) {
-  struct Point p1;
-  read_short_point(&p1);
-  if (verbose)
-    pm_message("(%d,%d) delta (%d, %d)",
-               current.x,current.y,p1.x,p1.y);
-  p1.x += current.x;
-  p1.y += current.y;
-  if (!fullres)
-      scan_line(current.x,current.y,p1.x,p1.y);
-  current.x = p1.x;
-  current.y = p1.y;
+ShortLineFrom(struct canvas * const canvasP,
+              int             const version) {
+
+    struct Point p1;
+    read_short_point(&p1);
+    if (verbose)
+        pm_message("(%d,%d) delta (%d, %d)",
+                   current.x,current.y,p1.x,p1.y);
+    p1.x += current.x;
+    p1.y += current.y;
+    if (!fullres)
+        scan_line(canvasP, current.x, current.y, p1.x, p1.y);
+    current.x = p1.x;
+    current.y = p1.y;
 }
 
+
+
 static void 
-do_paintRect(struct Rect const prect) {
+do_paintRect(struct canvas * const canvasP,
+             struct Rect     const prect) {
+
     struct Rect rect;
   
     if (fullres)
@@ -2713,28 +2869,37 @@ do_paintRect(struct Rect const prect) {
 
     rectinter(clip_rect, prect, &rect);
 
-    draw_pen_rect(&rect);
+    draw_pen_rect(canvasP, &rect);
 }
 
 
 
+static drawFn paintRect;
+
 static void 
-paintRect(int const v) {
+paintRect(struct canvas * const canvasP,
+          int             const version) {
+
     read_rect(&cur_rect);
-    do_paintRect(cur_rect);
+    do_paintRect(canvasP, cur_rect);
 }
 
 
 
+static drawFn paintSameRect;
+
 static void 
-paintSameRect(int const v) {
-    do_paintRect(cur_rect);
+paintSameRect(struct canvas * const canvasP,
+              int             const version) {
+
+    do_paintRect(canvasP, cur_rect);
 }
 
 
 
 static void 
-do_frameRect(struct Rect const rect) {
+do_frameRect(struct canvas * const canvasP,
+             struct Rect     const rect) {
     int x, y;
 
     if (fullres)
@@ -2747,29 +2912,37 @@ do_frameRect(struct Rect const rect) {
         return;
 
     for (x = rect.left; x <= rect.right - pen_width; x += pen_width) {
-        draw_pen(x, rect.top);
-        draw_pen(x, rect.bottom - pen_height);
+        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(rect.left, y);
-        draw_pen(rect.right - pen_width, y);
+        draw_pen(canvasP, rect.left, y);
+        draw_pen(canvasP, rect.right - pen_width, y);
     }
 }
 
 
 
+static drawFn frameRect;
+
 static void 
-frameRect(int const v) {
+frameRect(struct canvas * const canvasP,
+          int             const version) {
+
     read_rect(&cur_rect);
-    do_frameRect(cur_rect);
+    do_frameRect(canvasP, cur_rect);
 }
 
 
 
+static drawFn frameSameRect;
+
 static void 
-frameSameRect(int const v) {
-    do_frameRect(cur_rect);
+frameSameRect(struct canvas * const canvasP,
+              int             const version) {
+
+    do_frameRect(canvasP, cur_rect);
 }
 
 
@@ -2810,8 +2983,10 @@ poly_sort(int const sort_index, struct Point points[]) {
 /* Watch out for the lack of error checking in the next two functions ... */
 
 static void 
-scan_poly(int          const np, 
-          struct Point       pts[]) {
+scan_poly(struct canvas * const canvasP,
+          int             const np, 
+          struct Point          pts[]) {
+
   int dx,dy,dxabs,dyabs,i,scan_index,j,k,px,py;
   int sdx,sdy,x,y,toggle,old_sdy,sy0;
 
@@ -2864,7 +3039,7 @@ scan_poly(int          const np,
         scan_index++;
       }
       px += sdx;
-      draw_pen(px, py);
+      draw_pen(canvasP, px, py);
     }
       }
     else
@@ -2880,7 +3055,7 @@ scan_poly(int          const np,
         old_sdy = sdy;
         if (sdy != 0) scan_index--;
       }
-      draw_pen(px,py);
+      draw_pen(canvasP, px,py);
       coord[scan_index].x = px;
       coord[scan_index].y = py;
       scan_index++;
@@ -2900,7 +3075,7 @@ scan_poly(int          const np,
     if ((coord[i].y == coord[i+1].y) && (toggle == 0))
       {
     for (j = coord[i].x; j <= coord[i+1].x; j++)
-      draw_pen(j, coord[i].y);
+      draw_pen(canvasP, j, coord[i].y);
     toggle = 1;
       }
     else
@@ -2909,9 +3084,13 @@ scan_poly(int          const np,
 }
   
 
+
+static drawFn paintPoly;
   
 static void 
-paintPoly(int const v) {
+paintPoly(struct canvas * const canvasP,
+          int             const version) {
+
   struct Rect bb;
   struct Point pts[100];
   int i, np = (read_word() - 10) >> 2;
@@ -2922,13 +3101,17 @@ paintPoly(int const v) {
 
   /* scan convert poly ... */
   if (!fullres)
-      scan_poly(np, pts);
+      scan_poly(canvasP, np, pts);
 }
 
 
 
+static drawFn PnLocHFrac;
+
 static void 
-PnLocHFrac(int const version) {
+PnLocHFrac(struct canvas * const canvasP,
+           int             const version) {
+
     word frac = read_word();
 
     if (verbose)
@@ -2937,8 +3120,12 @@ PnLocHFrac(int const version) {
 
 
 
+static drawFn TxMode;
+
 static void 
-TxMode(int const version) {
+TxMode(struct canvas * const canvasP,
+       int             const version) {
+
     text_mode = read_word();
 
     if (text_mode >= 8 && text_mode < 15)
@@ -2953,8 +3140,12 @@ TxMode(int const version) {
 
 
 
+static drawFn TxFont;
+
 static void 
-TxFont(int const version) {
+TxFont(struct canvas * const canvasP,
+       int             const version) {
+
     text_font = read_word();
     if (verbose)
         pm_message("text font %s", const_name(font_name, text_font));
@@ -2962,8 +3153,12 @@ TxFont(int const version) {
 
 
 
+static drawFn TxFace;
+
 static void 
-TxFace(int const version) {
+TxFace(struct canvas * const canvasP,
+       int             const version) {
+
     text_face = read_byte();
     if (verbose)
         pm_message("text face %d", text_face);
@@ -2971,8 +3166,12 @@ TxFace(int const version) {
 
 
 
+static drawFn TxSize;
+
 static void 
-TxSize(int const version) {
+TxSize(struct canvas * const canvasP,
+       int             const version) {
+
     text_size = read_word();
     if (verbose)
         pm_message("text size %d", text_size);
@@ -3062,8 +3261,10 @@ rotate(int * const x,
 
 
 static void
-do_ps_text(word const tx, 
-           word const ty) {
+do_ps_text(struct canvas * const canvasP,
+           word            const tx, 
+           word            const ty) {
+
     int len, width, i, w, h, x, y, rx, ry, o;
     byte str[256], ch;
     struct glyph* glyph;
@@ -3114,9 +3315,9 @@ do_ps_text(word const tx,
                 {
                     o = PIXEL_INDEX(rx, ry);
                     if (glyph->bmap[h * glyph->width + w]) {
-                        red[o] = foreground.red;
-                        green[o] = foreground.grn;
-                        blue[o] = foreground.blu;
+                        canvasP->planes.red[o] = foreground.red;
+                        canvasP->planes.grn[o] = foreground.grn;
+                        canvasP->planes.blu[o] = foreground.blu;
                     }
                 }
             }
@@ -3129,8 +3330,10 @@ do_ps_text(word const tx,
 
 
 static void
-do_text(word const startx, 
-        word const starty) {
+do_text(struct canvas *  const canvasP,
+        word             const startx, 
+        word             const starty) {
+
     if (fullres)
         skip_text();
     else {
@@ -3138,7 +3341,7 @@ do_text(word const startx,
             tfont = pbm_defaultfont("bdf");
 
         if (ps_text)
-            do_ps_text(startx, starty);
+            do_ps_text(canvasP, startx, starty);
         else {
             int len;
             word x, y;
@@ -3158,7 +3361,8 @@ do_text(word const startx,
                             struct RGBColor * const colorP = 
                                 glyph->bmap[h * glyph->width + w] ?
                                 &black : &white;
-                            draw_pixel(x + w + glyph->x, dy, colorP, text_trf);
+                            draw_pixel(canvasP,
+                                       x + w + glyph->x, dy, colorP, text_trf);
                         }
                     }
                     x += glyph->xadd;
@@ -3172,34 +3376,50 @@ do_text(word const startx,
 
 
 
+static drawFn LongText;
+
 static void
-LongText(int const version) {
+LongText(struct canvas * const canvasP,
+         int             const version) {
+
     struct Point p;
 
     read_point(&p);
-    do_text(p.x, p.y);
+
+    do_text(canvasP, p.x, p.y);
 }
 
 
 
+static drawFn DHText;
+
 static void
-DHText(int const version) {
+DHText(struct canvas * const canvasP,
+       int             const version) {
+
     current.x += read_byte();
-    do_text(current.x, current.y);
+    do_text(canvasP, current.x, current.y);
 }
 
 
 
+static drawFn DVText;
+
 static void
-DVText(int const version) {
+DVText(struct canvas * const canvasP,
+       int             const version) {
+
     current.y += read_byte();
-    do_text(current.x, current.y);
+    do_text(canvasP, current.x, current.y);
 }
 
 
 
+static drawFn DHDVText;
+
 static void
-DHDVText(int const version) {
+DHDVText(struct canvas * const canvasP,
+         int             const version) {
     byte dh, dv;
 
     dh = read_byte();
@@ -3210,7 +3430,7 @@ DHDVText(int const version) {
 
     current.x += dh;
     current.y += dv;
-    do_text(current.x, current.y);
+    do_text(canvasP, current.x, current.y);
 }
 
 
@@ -3220,8 +3440,9 @@ DHDVText(int const version) {
  */
 
 static void
-directBits(unsigned int const pictVersion, 
-           bool         const skipRegion) {
+directBits(struct canvas * const canvasP,
+           unsigned int    const pictVersion, 
+           bool            const skipRegion) {
 
     struct pixMap   p;
     struct Rect     srcRect;
@@ -3261,11 +3482,11 @@ directBits(unsigned int const pictVersion,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (skipRegion) 
-        skip_poly_or_region(pictVersion);
+        skip_poly_or_region(canvasP, pictVersion);
 
     unpackbits(ifp, &p.Bounds, 0, p.pixelSize, &raster);
 
-    blit(srcRect, p.Bounds, raster, p.pixelSize,
+    blit(srcRect, p.Bounds, raster, canvasP, p.pixelSize,
          dstRect, picFrame, rowlen, NULL, mode);
 
     freeRaster(raster);
@@ -3276,26 +3497,33 @@ directBits(unsigned int const pictVersion,
 #define SKIP_REGION_TRUE TRUE
 #define SKIP_REGION_FALSE FALSE
 
+static drawFn DirectBitsRect;
+
 static void
-DirectBitsRect(int const version) {
+DirectBitsRect(struct canvas * const canvasP,
+               int             const version) {
 
-    directBits(version, SKIP_REGION_FALSE);
+    directBits(canvasP, version, SKIP_REGION_FALSE);
 }
 
 
 
+static drawFn DirectBitsRgn;
+
 static void
-DirectBitsRgn(int const version) {
+DirectBitsRgn(struct canvas * const canvasP,
+              int             const version) {
 
-    directBits(version, SKIP_REGION_TRUE);
+    directBits(canvasP, version, SKIP_REGION_TRUE);
 }
 
 
 
 static void
-do_pixmap(int  const version, 
-          word const rowBytes, 
-          int  const is_region) {
+do_pixmap(struct canvas * const canvasP,
+          int             const version, 
+          word            const rowBytes, 
+          int             const is_region) {
 /*----------------------------------------------------------------------------
    Do a paletted image.
 -----------------------------------------------------------------------------*/
@@ -3331,13 +3559,13 @@ do_pixmap(int  const version,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (is_region)
-        skip_poly_or_region(version);
+        skip_poly_or_region(canvasP, version);
 
     stage = "unpacking rectangle";
 
     unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster);
 
-    blit(srcRect, p.Bounds, raster, 8,
+    blit(srcRect, p.Bounds, raster, canvasP, 8,
          dstRect, picFrame, rowlen, color_table, mode);
 
     free(color_table);
@@ -3347,9 +3575,10 @@ do_pixmap(int  const version,
 
 
 static void
-do_bitmap(int const version, 
-          int const rowBytes, 
-          int const is_region) {
+do_bitmap(struct canvas * const canvasP,
+          int             const version, 
+          int             const rowBytes, 
+          int             const is_region) {
 /*----------------------------------------------------------------------------
    Do a bitmap.  That's one bit per pixel, 0 is white, 1 is black.
 -----------------------------------------------------------------------------*/
@@ -3369,13 +3598,13 @@ do_bitmap(int const version,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (is_region)
-        skip_poly_or_region(version);
+        skip_poly_or_region(canvasP, version);
 
     stage = "unpacking rectangle";
 
     unpackbits(ifp, &Bounds, rowBytes, 1, &raster);
 
-    blit(srcRect, Bounds, raster, 8,
+    blit(srcRect, Bounds, raster, canvasP, 8,
          dstRect, picFrame, rowlen, color_table, mode);
 
     freeRaster(raster);
@@ -3383,8 +3612,11 @@ do_bitmap(int const version,
 
 
 
+static drawFn BitsRect;
+
 static void
-BitsRect(int const version) {
+BitsRect(struct canvas * const canvasP,
+         int             const version) {
 
     word rowBytesWord;
     bool pixMap;
@@ -3396,16 +3628,19 @@ BitsRect(int const version) {
     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
 
     if (pixMap)
-        do_pixmap(version, rowBytes, 0);
+        do_pixmap(canvasP, version, rowBytes, 0);
     else
-        do_bitmap(version, rowBytes, 0);
+        do_bitmap(canvasP, version, rowBytes, 0);
 }
 
 
 
-static void
-BitsRegion(int const version) {
+static drawFn BitsRegion;
 
+static void
+BitsRegion(struct canvas * const canvasP,
+           int             const version) {
+    
     word rowBytesWord;
     bool pixMap;
     unsigned int rowBytes;
@@ -3416,9 +3651,9 @@ BitsRegion(int const version) {
     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
 
     if (pixMap)
-        do_pixmap(version, rowBytes, 1);
+        do_pixmap(canvasP, version, rowBytes, 1);
     else
-        do_bitmap(version, rowBytes, 1);
+        do_bitmap(canvasP, version, rowBytes, 1);
 }
 
 
@@ -3601,14 +3836,15 @@ static struct opdef const optable[] = {
 
 
 static void
-interpret_pict(void) {
+interpret_pict(FILE * const ofP) {
+
     byte ch;
     word picSize;
     word opcode;
     word len;
     unsigned int version;
     int i;
-    struct rgbPlanes planes;
+    struct canvas canvas;
 
     for (i = 0; i < 64; i++)
         pen_pat.pix[i] = bkpat.pix[i] = fillpat.pix[i] = 1;
@@ -3634,8 +3870,14 @@ interpret_pict(void) {
             picFrame.bottom - picFrame.top);
     }
 
-    if (!fullres)
-        allocPlanes(&planes);
+    if (!fullres) {
+        rowlen = picFrame.right  - picFrame.left;
+        collen = picFrame.bottom - picFrame.top;
+
+        allocPlanes(rowlen, collen, &canvas.planes);
+
+        clip_rect = picFrame;
+    }
 
     while ((ch = read_byte()) == 0)
         ;
@@ -3672,7 +3914,7 @@ interpret_pict(void) {
             }
 
             if (optable[opcode].impl != NULL)
-                (*optable[opcode].impl)(version);
+                (*optable[opcode].impl)(&canvas, version);
             else if (optable[opcode].len >= 0)
                 skip(optable[opcode].len);
             else switch (optable[opcode].len) {
@@ -3721,11 +3963,11 @@ interpret_pict(void) {
     }
     
     if (fullres)
-        do_blits(&planes);
+        do_blits(&canvas);
 
-    outputPpm(planes);
+    outputPpm(ofP, canvas.planes);
 
-    freePlanes(planes);
+    freePlanes(canvas.planes);
 }
 
 
@@ -3782,7 +4024,9 @@ main(int argc, char * argv[]) {
         skip(512);
     }
 
-    interpret_pict();
+    interpret_pict(stdout);
+
+    pm_close(stdout);
 
     return 0;
 }