about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2007-03-01 03:23:07 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2007-03-01 03:23:07 +0000
commit82972ce89087dbb30496a67ea6dc6be4c7a5f1d7 (patch)
treedee5e485d76a66b1523a6c71684dde968dfdc58a
parent1afcbfb7de6fef5c052766f258bde74d845253df (diff)
downloadnetpbm-mirror-82972ce89087dbb30496a67ea6dc6be4c7a5f1d7.tar.gz
netpbm-mirror-82972ce89087dbb30496a67ea6dc6be4c7a5f1d7.tar.xz
netpbm-mirror-82972ce89087dbb30496a67ea6dc6be4c7a5f1d7.zip
Add jpegtopnm -repair
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@240 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--converter/other/jpegdatasource.c36
-rw-r--r--converter/other/jpegdatasource.h3
-rw-r--r--converter/other/jpegtopnm.c126
-rw-r--r--doc/HISTORY6
4 files changed, 114 insertions, 57 deletions
diff --git a/converter/other/jpegdatasource.c b/converter/other/jpegdatasource.c
index 5c1070e4..1f53c2a4 100644
--- a/converter/other/jpegdatasource.c
+++ b/converter/other/jpegdatasource.c
@@ -45,6 +45,11 @@ struct sourceManager {
     */
     struct jpeg_source_mgr jpegSourceMgr;
     FILE * ifP;
+    bool prematureEof;
+        /* We have been asked for data and were unable to comply because
+           the file had no more to give (so we supplied EOI markers
+           instead).
+        */
     JOCTET * currentBuffer;
     JOCTET * nextBuffer;
     unsigned int bytesInNextBuffer;
@@ -66,6 +71,10 @@ dsInitSource(j_decompress_ptr const cinfoP) {
 
 
 
+static const JOCTET jfifEoiMarker[] = {0xff, JPEG_EOI};
+    /* An EOI (end of image) marker */
+
+
 static boolean
 dsFillInputBuffer(j_decompress_ptr const cinfoP) {
 /*----------------------------------------------------------------------------
@@ -74,9 +83,19 @@ dsFillInputBuffer(j_decompress_ptr const cinfoP) {
 -----------------------------------------------------------------------------*/
     struct sourceManager * const srcP = (struct sourceManager *) cinfoP->src;
 
-    if (srcP->bytesInNextBuffer == 0) 
-        pm_error("End-of-file encountered in the middle of JPEG image.");
-    else {
+    if (srcP->bytesInNextBuffer == 0) {
+        /* The decompressor expects more bytes, but there aren't any, so
+           the file is corrupted -- probably truncated.  We want the
+           decompressor to decompress whatever it's read so far, so we
+           synthesize an EOI marker here, but we also set error state
+           in the source manager.  The decompressor will recognize the
+           truncation and pad out the image with gray.
+        */
+        srcP->prematureEof = TRUE;
+        
+        srcP->jpegSourceMgr.next_input_byte = jfifEoiMarker;
+        srcP->jpegSourceMgr.bytes_in_buffer = sizeof(jfifEoiMarker);
+    } else {
         /* Rotate the buffers */
         srcP->jpegSourceMgr.next_input_byte = srcP->nextBuffer;
         srcP->jpegSourceMgr.bytes_in_buffer = srcP->bytesInNextBuffer;
@@ -87,7 +106,7 @@ dsFillInputBuffer(j_decompress_ptr const cinfoP) {
             srcP->currentBuffer = tmp;
         }
 
-        /* Fill the new 'next' buffer */
+        /* Fill the new "next" buffer */
         srcP->bytesInNextBuffer = 
             fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);
     }
@@ -139,6 +158,14 @@ dsDataLeft(struct sourceManager * const srcP) {
 
 
 
+bool
+dsPrematureEof(struct sourceManager * const srcP) {
+
+    return srcP->prematureEof;
+}
+
+
+
 struct sourceManager * 
 dsCreateSource(const char * const fileName) {
 
@@ -156,6 +183,7 @@ dsCreateSource(const char * const fileName) {
     srcP->jpegSourceMgr.resync_to_restart = jpeg_resync_to_restart;
     srcP->jpegSourceMgr.term_source = dsTermSource;
     
+    srcP->prematureEof = FALSE;
     srcP->currentBuffer = srcP->buffer1;
     srcP->nextBuffer = srcP->buffer2;
     srcP->jpegSourceMgr.bytes_in_buffer = 
diff --git a/converter/other/jpegdatasource.h b/converter/other/jpegdatasource.h
index 07f17389..58648fe4 100644
--- a/converter/other/jpegdatasource.h
+++ b/converter/other/jpegdatasource.h
@@ -12,6 +12,9 @@ dsDestroySource(struct sourceManager * const srcP);
 bool
 dsDataLeft(struct sourceManager * const srcP);
 
+bool
+dsPrematureEof(struct sourceManager * const srcP);
+
 struct jpeg_source_mgr *
 dsJpegSourceMgr(struct sourceManager * const srcP);
 
diff --git a/converter/other/jpegtopnm.c b/converter/other/jpegtopnm.c
index d0a465e3..180ef3d4 100644
--- a/converter/other/jpegtopnm.c
+++ b/converter/other/jpegtopnm.c
@@ -109,6 +109,7 @@ struct cmdlineInfo {
     unsigned int comments;
     unsigned int dumpexif;
     unsigned int multiple;
+    unsigned int repair;
 };
 
 
@@ -116,9 +117,9 @@ static bool displayComments;
     /* User wants comments from the JPEG to be displayed */
 
 static void 
-interpret_maxmemory (bool         const maxmemorySpec,
-                     const char * const maxmemory, 
-                     long int *   const max_memory_to_use_p) { 
+interpret_maxmemory(bool         const maxmemorySpec,
+                    const char * const maxmemory, 
+                    long int *   const max_memory_to_use_p) { 
 /*----------------------------------------------------------------------------
    Interpret the "maxmemory" command line option.
 -----------------------------------------------------------------------------*/
@@ -207,6 +208,7 @@ parseCommandLine(int                  const argc,
             &exifSpec, 0);
     OPTENT3(0, "dumpexif",    OPT_FLAG,   NULL, &cmdlineP->dumpexif,      0);
     OPTENT3(0, "multiple",    OPT_FLAG,   NULL, &cmdlineP->multiple,      0);
+    OPTENT3(0, "repair",      OPT_FLAG,   NULL, &cmdlineP->repair,        0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -460,32 +462,34 @@ read_rgb(JSAMPLE *ptr, const enum colorspace color_space,
    copy_pixel_row().  But it would be impractical to allocate and free
    the storage with every call to copy_pixel_row().
 */
-static xel *pnmbuffer;      /* Output buffer.  Input to pnm_writepnmrow() */
+static xel * pnmbuffer;      /* Output buffer.  Input to pnm_writepnmrow() */
 
 static void
-copy_pixel_row(const JSAMPROW jpegbuffer, const int width, 
-               const unsigned int samples_per_pixel, 
-               const enum colorspace color_space,
-               const unsigned int maxval,
-               FILE * const output_file, const int output_type) {
-  JSAMPLE *ptr;
-  unsigned int output_cursor;     /* Cursor into output buffer 'pnmbuffer' */
-
-  ptr = jpegbuffer;  /* Start at beginning of input row */
-
-  for (output_cursor = 0; output_cursor < width; output_cursor++) {
-      xel current_pixel;
-      if (samples_per_pixel >= 3) {
-          const rgb_type * const rgb_p = read_rgb(ptr, color_space, maxval);
-          PPM_ASSIGN(current_pixel, rgb_p->r, rgb_p->g, rgb_p->b);
-      } else {
-          PNM_ASSIGN1(current_pixel, GETJSAMPLE(*ptr));
-      }
-      ptr += samples_per_pixel;  /* move to next pixel of input */
-      pnmbuffer[output_cursor] = current_pixel;
-  }
-  pnm_writepnmrow(output_file, pnmbuffer, width,
-                  maxval, output_type, FALSE);
+copyPixelRow(JSAMPROW        const jpegbuffer,
+             unsigned int    const width, 
+             unsigned int    const samplesPerPixel, 
+             enum colorspace const colorSpace,
+             FILE *          const ofP,
+             int             const format,
+             xelval          const maxval) {
+
+    JSAMPLE * ptr;
+    unsigned int outputCursor;     /* Cursor into output buffer 'pnmbuffer' */
+
+    ptr = &jpegbuffer[0];  /* Start at beginning of input row */
+    
+    for (outputCursor = 0; outputCursor < width; ++outputCursor) {
+        xel currentPixel;
+        if (samplesPerPixel >= 3) {
+            const rgb_type * const rgb_p = read_rgb(ptr, colorSpace, maxval);
+            PPM_ASSIGN(currentPixel, rgb_p->r, rgb_p->g, rgb_p->b);
+        } else {
+            PNM_ASSIGN1(currentPixel, GETJSAMPLE(*ptr));
+        }
+        ptr += samplesPerPixel;  /* move to next pixel of input */
+        pnmbuffer[outputCursor] = currentPixel;
+    }
+    pnm_writepnmrow(ofP, pnmbuffer, width, maxval, format, FALSE);
 }
 
 
@@ -798,17 +802,42 @@ computeColorSpace(struct jpeg_decompress_struct * const cinfoP,
 
 
 static void
+convertRaster(struct jpeg_decompress_struct * const cinfoP,
+              enum colorspace                 const color_space,
+              FILE *                          const ofP,
+              xelval                          const format,
+              unsigned int                    const maxval) {
+              
+    JSAMPROW jpegbuffer;  /* Input buffer.  Filled by jpeg_scanlines() */
+
+    jpegbuffer = ((*cinfoP->mem->alloc_sarray)
+                  ((j_common_ptr) cinfoP, JPOOL_IMAGE,
+                   cinfoP->output_width * cinfoP->output_components, 
+                   (JDIMENSION) 1)
+        )[0];
+
+    while (cinfoP->output_scanline < cinfoP->output_height) {
+        jpeg_read_scanlines(cinfoP, &jpegbuffer, 1);
+        if (ofP)
+            copyPixelRow(jpegbuffer, cinfoP->output_width, 
+                         cinfoP->out_color_components,
+                         color_space, ofP, format, maxval);
+    }
+}
+
+
+
+static void
 convertImage(FILE *                          const ofP, 
              struct cmdlineInfo              const cmdline,
              struct jpeg_decompress_struct * const cinfoP) {
 
-    int output_type;
+    int format;
         /* The type of output file, PGM or PPM.  Value is either PPM_TYPE
            or PGM_TYPE, which conveniently also pass as format values
            PPM_FORMAT and PGM_FORMAT.
         */
-    JSAMPROW jpegbuffer;  /* Input buffer.  Filled by jpeg_scanlines() */
-    unsigned int maxval;  
+    xelval maxval;  
         /* The maximum value of a sample (color component), both in the input
            and the output.
         */
@@ -819,43 +848,30 @@ convertImage(FILE *                          const ofP,
                    cmdline.dct_method, 
                    cmdline.max_memory_to_use, cmdline.nosmooth);
                    
-    set_color_spaces(cinfoP->jpeg_color_space, &output_type, 
+    set_color_spaces(cinfoP->jpeg_color_space, &format,
                      &cinfoP->out_color_space);
 
-    maxval = (1 << cinfoP->data_precision) - 1;
+    maxval = pm_bitstomaxval(cinfoP->data_precision);
 
     if (cmdline.verbose) 
-        tellDetails(*cinfoP, maxval, output_type);
+        tellDetails(*cinfoP, maxval, format);
 
     /* Calculate output image dimensions so we can allocate space */
     jpeg_calc_output_dimensions(cinfoP);
 
-    jpegbuffer = ((*cinfoP->mem->alloc_sarray)
-                  ((j_common_ptr) cinfoP, JPOOL_IMAGE,
-                   cinfoP->output_width * cinfoP->output_components, 
-                   (JDIMENSION) 1)
-        )[0];
-
     /* Start decompressor */
     jpeg_start_decompress(cinfoP);
 
     if (ofP)
         /* Write pnm output header */
         pnm_writepnminit(ofP, cinfoP->output_width, cinfoP->output_height,
-                         maxval, output_type, FALSE);
+                         maxval, format, FALSE);
 
     pnmbuffer = pnm_allocrow(cinfoP->output_width);
     
     color_space = computeColorSpace(cinfoP, cmdline.inklevel);
-
-    /* Process data */
-    while (cinfoP->output_scanline < cinfoP->output_height) {
-        jpeg_read_scanlines(cinfoP, &jpegbuffer, 1);
-        if (ofP)
-            copy_pixel_row(jpegbuffer, cinfoP->output_width, 
-                           cinfoP->out_color_components,
-                           color_space, maxval, ofP, output_type);
-    }
+    
+    convertRaster(cinfoP, color_space, ofP, format, maxval);
 
     if (cmdline.comments)
         print_comments(*cinfoP);
@@ -907,17 +923,25 @@ convertImages(FILE *                          const ofP,
             convertImage(ofP, cmdline, cinfoP);
         }
     } else {
-        if (dsDataLeft(sourceManagerP))
+        if (dsDataLeft(sourceManagerP)) {
             convertImage(ofP, cmdline, cinfoP);
-        else
+        } else
             pm_error("Input stream is empty");
     }
+    if (dsPrematureEof(sourceManagerP)) {
+        if (cmdline.repair)
+            pm_message("Premature EOF on input; repaired by padding end "
+                       "of image.");
+        else
+            pm_error("Premature EOF on input.  Use -repair to salvage.");
+    }
 }
 
 
 
 int
 main(int argc, char **argv) {
+
     FILE * ofP;
     struct cmdlineInfo cmdline;
     struct jpeg_decompress_struct cinfo;
diff --git a/doc/HISTORY b/doc/HISTORY
index 78d167c5..d5e79c5c 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -6,14 +6,14 @@ CHANGE HISTORY
 
 not yet  BJH  Release 10.38.0
 
+              Add pamfixtrunc.
+
               pammixinterlace: Add -filter and fir and ffmpeg filters.
               Thanks Bruce Guenter <bruce@untroubled.org>.
 
               pammixinterlace: Add -adaptive.
               Thanks Bruce Guenter <bruce@untroubled.org>.
 
-              Add pamfixtrunc.
-
               pambackground: recognize mid-row background.
 
               ppm3d: Change default offset to zero columns.
@@ -22,6 +22,8 @@ not yet  BJH  Release 10.38.0
 
               ppm3d: Add -offset option as alternative to offset argument.
 
+              jpegtopnm: Add -repair option.
+
               giftopnm: Add -repair option.
 
               libnetpbm: shhopt: reject signed number as value for