*** jpeg-6b-beta1/jpegtran.c	Wed Jul 23 19:37:26 1997
--- TMP/jpegtran.c	Thu Jun  4 16:44:42 1998
***************
*** 70,75 ****
--- 70,76 ----
    fprintf(stderr, "  -transpose     Transpose image\n");
    fprintf(stderr, "  -transverse    Transverse transpose image\n");
    fprintf(stderr, "  -trim          Drop non-transformable edge blocks\n");
+   fprintf(stderr, "  -cut WxH+X+Y   Cut out a subset of the image\n");
  #endif /* TRANSFORMS_SUPPORTED */
    fprintf(stderr, "Switches for advanced users:\n");
    fprintf(stderr, "  -restart N     Set restart interval in rows, or in blocks with B\n");
***************
*** 185,190 ****
--- 186,215 ----
        }
        cinfo->err->trace_level++;
  
+     } else if (keymatch(arg, "cut", 1)) {
+       /* Cut out a region of the image specified by an X geometry-like string */
+       char *p;
+ 
+       if (++argn >= argc)
+ 	usage();
+       select_transform(JXFORM_CUT);
+       p = argv[argn];
+       transformoption.newwidth = strtol(p, &p, 10);
+       if (*p++ != 'x')
+ 	usage();
+       transformoption.newheight = strtol(p, &p, 10);
+       if (*p != '+' && *p != '-')
+ 	usage();
+       transformoption.xoffs = strtol(p, &p, 10);
+       if (*p != '+' && *p != '-')
+ 	usage();
+       transformoption.yoffs = strtol(p, &p, 10);
+ 
+       if (!transformoption.newwidth || !transformoption.newheight) {
+         fprintf(stderr, "%s: degenerate -cut size in %s\n",
+ 	        progname, argv[argn]);
+         exit(EXIT_FAILURE);
+       }
      } else if (keymatch(arg, "flip", 1)) {
        /* Mirror left-right or top-bottom. */
        if (++argn >= argc)	/* advance to next argument */
***************
*** 325,330 ****
--- 350,362 ----
    return argn;			/* return index of next arg (file name) */
  }
  
+ 
+ GLOBAL(void)
+ jpegtran_error(char *str)
+ {
+   fprintf(stderr, "%s: %s\n", progname, str);
+   exit(EXIT_FAILURE);
+ }
  
  /*
   * The main program.
*** jpeg-6b-beta1/transupp.c	Sat Aug  9 17:15:26 1997
--- TMP/transupp.c	Thu Jun  4 17:09:37 1998
***************
*** 181,186 ****
--- 181,215 ----
    }
  }
  
+ LOCAL(void)
+ do_transform (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ 	      jvirt_barray_ptr *src_coef_arrays,
+ 	      jvirt_barray_ptr *dst_coef_arrays,
+ 	      JDIMENSION xoffs, JDIMENSION yoffs)
+ /* transform src_coef_arrays so that the xoffs,yoffs (rounded to an even
+  * dct block) are the new origin of the image.  copy rather than move because
+  * I'd never finish if I tried to understand the byzantine memory management.
+  */
+ {
+   int ci;
+   jpeg_component_info *compptr;
+   JBLOCKARRAY src_buffer, dst_buffer;
+   JDIMENSION dst_blk_x, dst_blk_y;
+ 
+   xoffs /= dstinfo->max_h_samp_factor * DCTSIZE;
+   yoffs /= dstinfo->max_v_samp_factor * DCTSIZE;
+ 
+   for (ci = 0; ci < dstinfo->num_components; ci++) {
+     compptr = dstinfo->comp_info + ci;
+     for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; dst_blk_y++) {
+       dst_buffer = (*srcinfo->mem->access_virt_barray)
+ 	((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, 1, TRUE);
+       src_buffer = (*srcinfo->mem->access_virt_barray)
+ 	((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y + yoffs * compptr->v_samp_factor, 1, FALSE);
+       jcopy_block_row(&src_buffer[0][xoffs * compptr->h_samp_factor], dst_buffer[0], compptr->width_in_blocks);
+     }
+   }
+ }
  
  LOCAL(void)
  do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
***************
*** 587,592 ****
--- 616,623 ----
    case JXFORM_FLIP_H:
      /* Don't need a workspace array */
      break;
+   case JXFORM_CUT:
+     /* really cut needs smaller arrays if you want to figure it out */
    case JXFORM_FLIP_V:
    case JXFORM_ROT_180:
      /* Need workspace arrays having same dimensions as source image.
***************
*** 716,721 ****
--- 747,822 ----
      dstinfo->image_height = MCU_rows * (max_v_samp_factor * DCTSIZE);
  }
  
+ /* For cropping, realize and constrain the target area, and reshape the
+  * dstinfo to hold the resulting image.
+  *
+  * Input was supplied as WxH[+-]X[+-]Y offsets.  Negative offsets are
+  * relative to the lower righthand corner of the image.  The region is
+  * expanded so that all boundaries fall on even MCU blocks by rounding
+  * the offsets *down* (at the do_transform() step) and the size *up*.
+  */
+ LOCAL(void)
+ set_dest_size(j_compress_ptr dstinfo, jpeg_transform_info *info)
+ {
+   int ci, max_samp_factor;
+   JDIMENSION MCU_size, newsize, offset, factor;
+ 
+   /* Initially the dstinfo is the same size as the srcinfo.
+    * Use it to constrain the offsets:
+    */
+   if (info->xoffs < 0)
+     info->xoffs += dstinfo->image_width;
+   if (info->yoffs < 0)
+     info->yoffs += dstinfo->image_height;
+   if (info->xoffs < 0 || info->xoffs >= dstinfo->image_width ||
+       info->yoffs < 0 || info->yoffs >= dstinfo->image_height) {
+     jpegtran_error("-cut offsets fall outside source image");
+   }
+ 
+   /* use it to constrain the size: */
+   if (info->newwidth + info->xoffs > dstinfo->image_width)
+     info->newwidth = dstinfo->image_width - info->xoffs;
+   if (info->newheight + info->yoffs > dstinfo->image_height)
+     info->newheight = dstinfo->image_height - info->yoffs;
+ 
+   /* We have to compute max_v/h_samp_factors ourselves,
+    * because it hasn't been set yet in the destination
+    * (and we don't want to use the source's value).
+    */
+   max_samp_factor = 1;
+   for (ci = 0; ci < dstinfo->num_components; ci++) {
+     int samp_factor = dstinfo->comp_info[ci].v_samp_factor;
+     max_samp_factor = MAX(max_samp_factor, samp_factor);
+   }
+   /* Find original (rounded down) and new (rounded up) heights in full
+    * dct blocks, choose the smaller of the two.
+    */
+   factor = max_samp_factor * DCTSIZE;
+   MCU_size = dstinfo->image_height / factor;
+   newsize = (info->newheight + (info->yoffs % factor) + factor - 1) / factor;
+   MCU_size = MIN(MCU_size, newsize);
+   if (MCU_size > 0)		/* can't trim to 0 pixels */
+     dstinfo->image_height = MCU_size * factor;
+   else
+     jpegtran_error("degenerate -cut height");
+ 
+   max_samp_factor = 1;
+   for (ci = 0; ci < dstinfo->num_components; ci++) {
+     int samp_factor = dstinfo->comp_info[ci].h_samp_factor;
+     max_samp_factor = MAX(max_samp_factor, samp_factor);
+   }
+   /* Find original (rounded down) and new (rounded up) heights in full
+    * dct blocks, choose the smaller of the two.
+    */
+   factor = max_samp_factor * DCTSIZE;
+   MCU_size = dstinfo->image_width / factor;
+   newsize = (info->newwidth + (info->xoffs % factor) + factor - 1) / factor;
+   MCU_size = MIN(MCU_size, newsize);
+   if (MCU_size > 0)		/* can't trim to 0 pixels */
+     dstinfo->image_width = MCU_size * factor;
+   else
+     jpegtran_error("degenerate -cut width");
+ }
  
  /* Adjust output image parameters as needed.
   *
***************
*** 762,767 ****
--- 863,871 ----
    case JXFORM_NONE:
      /* Nothing to do */
      break;
+   case JXFORM_CUT:
+     set_dest_size(dstinfo, info);
+     break;
    case JXFORM_FLIP_H:
      if (info->trim)
        trim_right_edge(dstinfo);
***************
*** 825,830 ****
--- 929,937 ----
  
    switch (info->transform) {
    case JXFORM_NONE:
+     break;
+   case JXFORM_CUT:
+     do_transform(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays, info->xoffs, info->yoffs);
      break;
    case JXFORM_FLIP_H:
      do_flip_h(srcinfo, dstinfo, src_coef_arrays);
*** jpeg-6b-beta1/transupp.h	Wed Jul 23 19:39:12 1997
--- TMP/transupp.h	Thu Jun  4 00:40:58 1998
***************
*** 39,44 ****
--- 39,45 ----
  
  typedef enum {
  	JXFORM_NONE,		/* no transformation */
+ 	JXFORM_CUT,		/* cut out part of the image */
  	JXFORM_FLIP_H,		/* horizontal flip */
  	JXFORM_FLIP_V,		/* vertical flip */
  	JXFORM_TRANSPOSE,	/* transpose across UL-to-LR axis */
***************
*** 88,93 ****
--- 89,96 ----
    JXFORM_CODE transform;	/* image transform operator */
    boolean trim;			/* if TRUE, trim partial MCUs as needed */
    boolean force_grayscale;	/* if TRUE, convert color image to grayscale */
+ 
+   JDIMENSION	xoffs, yoffs, newwidth, newheight;
  
    /* Internal workspace: caller should not touch these */
    int num_components;		/* # of components in workspace */
