changeset 104:ed71c7b5ea92

- Added support for progressive display of progressive jpegs. - Fixed progressive display of interlaced pngs.
author jcid
date Wed, 16 Jan 2008 01:17:20 +0100
parents 41dfbc23e98f
children 73cc1bdeb695
files ChangeLog src/bitvec.c src/bitvec.h src/dicache.c src/dicache.h src/image.cc src/image.hh src/jpeg.c src/png.c
diffstat 9 files changed, 156 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jan 14 22:54:28 2008 +0100
+++ b/ChangeLog	Wed Jan 16 01:17:20 2008 +0100
@@ -72,6 +72,8 @@
  - Added a MSG_HTTP for HTTP/1.1's warning headers.
  - Added support for multi-line header fields.
  - Added support for "charset" in the HTTP header field for Content-Type.
+ - Added support for progressive display of progressive jpegs.
+ - Fixed progressive display of interlaced pngs.
    Patches: place
 +- Fixed a problem with locally-installed dpis.
  - Added code for optional image loading (nice interface) very advanced!
--- a/src/bitvec.c	Mon Jan 14 22:54:28 2008 +0100
+++ b/src/bitvec.c	Wed Jan 16 01:17:20 2008 +0100
@@ -30,6 +30,14 @@
 }
 
 /*
+ * Clear a bitvec
+ */
+void a_Bitvec_clear(bitvec_t *bvec)
+{
+   memset(bvec->vec, 0, sizeof(uchar_t) * bvec->len/BVEC_SIZE + 1);
+}
+
+/*
  * Free a bitvec
  */
 void a_Bitvec_free(bitvec_t *bvec)
--- a/src/bitvec.h	Mon Jan 14 22:54:28 2008 +0100
+++ b/src/bitvec.h	Wed Jan 16 01:17:20 2008 +0100
@@ -21,6 +21,7 @@
 void a_Bitvec_free(bitvec_t *bvec);
 int a_Bitvec_get_bit(bitvec_t *bvec, int pos);
 void a_Bitvec_set_bit(bitvec_t *bvec, int pos);
+void a_Bitvec_clear(bitvec_t *bvec);
 
 /*
 #define a_Bitvec_get_bit(bvec,pos) \
--- a/src/dicache.c	Mon Jan 14 22:54:28 2008 +0100
+++ b/src/dicache.c	Wed Jan 16 01:17:20 2008 +0100
@@ -84,6 +84,7 @@
    entry->RefCount = 1;
    entry->TotalSize = 0;
    entry->Y = 0;
+   entry->ScanNumber = 0;
    entry->BitVec = NULL;
    entry->State = DIC_Empty;
    entry->version = 0;
@@ -263,7 +264,7 @@
 
    dReturn_if_fail ( DicEntry != NULL );
 
-   /* when the data stream is not an image 'v_imgbuf' keeps NULL */
+   /* when the data stream is not an image 'v_imgbuf' remains NULL */
    if (Op == CA_Send && DicEntry->v_imgbuf) {
       if (Image->height == 0 && DicEntry->State >= DIC_SetParms) {
          /* Set parms */
@@ -273,11 +274,25 @@
             DicEntry->type);
       }
       if (DicEntry->State == DIC_Write) {
-         for (i = 0; i < DicEntry->height; ++i)
-            if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) &&
-                !a_Bitvec_get_bit(Image->BitVec, (int)i) )
-               a_Image_write(Image, DicEntry->v_imgbuf,
-                             DicEntry->linebuf, i, FALSE);
+         if (DicEntry->ScanNumber == Image->ScanNumber) {
+            for (i = 0; i < DicEntry->height; ++i)
+               if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) &&
+                   !a_Bitvec_get_bit(Image->BitVec, (int)i) )
+                  a_Image_write(Image, DicEntry->v_imgbuf,
+                                DicEntry->linebuf, i, FALSE);
+         } else {
+            for (i = 0; i < DicEntry->height; ++i) {
+               if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) ||
+                   !a_Bitvec_get_bit(Image->BitVec, (int)i)   ||
+                   DicEntry->ScanNumber > Image->ScanNumber + 1) {
+                  a_Image_write(Image, DicEntry->v_imgbuf,
+                                DicEntry->linebuf, i, FALSE);
+               }
+               if (!a_Bitvec_get_bit(DicEntry->BitVec, (int)i))
+                  a_Bitvec_clear_bit(Image->BitVec, (int)i);
+            }
+            Image->ScanNumber = DicEntry->ScanNumber;
+         }
       }
    } else if (Op == CA_Close || Op == CA_Abort) {
       a_Image_close(Web->Image);
@@ -353,6 +368,22 @@
 }
 
 /*
+ * Reset for a new scan from a multiple-scan image.
+ */
+void a_Dicache_new_scan(DilloImage *image, const DilloUrl *url, int version)
+{
+   DICacheEntry *DicEntry;
+
+   dReturn_if_fail ( url != NULL );
+   DicEntry = Dicache_get_entry_version(url, version);
+   dReturn_if_fail ( DicEntry != NULL );
+
+   a_Bitvec_clear(DicEntry->BitVec);
+   DicEntry->ScanNumber++;
+   a_Image_new_scan(image, DicEntry->v_imgbuf);
+}
+
+/*
  * Implement the write method
  * (Write a scan line into the Dicache entry)
  * buf: row buffer
--- a/src/dicache.h	Mon Jan 14 22:54:28 2008 +0100
+++ b/src/dicache.h	Wed Jan 16 01:17:20 2008 +0100
@@ -31,6 +31,7 @@
    void *v_imgbuf;         /* Void pointer to an Imgbuf object */
    size_t TotalSize;       /* Amount of memory the image takes up */
    int Y;                  /* Current decoding row */
+   uint_t ScanNumber;      /* Current decoding scan */
    bitvec_t *BitVec;       /* Bit vector for decoded rows */
    DicEntryState State;    /* Current status for this entry */
    int RefCount;           /* Reference Counter */
@@ -53,6 +54,7 @@
 void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
                         const uchar_t *cmap, uint_t num_colors,
                         int num_colors_max, int bg_index);
+void a_Dicache_new_scan(DilloImage *image, const DilloUrl *url, int version);
 void a_Dicache_write(DilloImage *Image, DilloUrl *url, int version,
                      const uchar_t *buf, int x, uint_t Y);
 void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client);
--- a/src/image.cc	Mon Jan 14 22:54:28 2008 +0100
+++ b/src/image.cc	Wed Jan 16 01:17:20 2008 +0100
@@ -55,6 +55,7 @@
    Image->in_type = DILLO_IMG_TYPE_NOTSET;
    Image->bg_color = bg_color;
    Image->ProcessedBytes = 0;
+   Image->ScanNumber = 0;
    Image->BitVec = NULL;
    Image->State = IMG_Empty;
 
@@ -155,6 +156,16 @@
 }
 
 /*
+ * Begin a new scan for a multiple-scan image
+ */
+void a_Image_new_scan(DilloImage *Image, void *v_imgbuf)
+{
+   a_Bitvec_clear(Image->BitVec);
+   Image->ScanNumber++;
+   ((Imgbuf*)v_imgbuf)->newScan();
+}
+
+/*
  * Implement the write method
  */
 void a_Image_write(DilloImage *Image, void *v_imgbuf,
--- a/src/image.hh	Mon Jan 14 22:54:28 2008 +0100
+++ b/src/image.hh	Wed Jan 16 01:17:20 2008 +0100
@@ -44,6 +44,7 @@
 
    int ProcessedBytes;      /* Amount of bytes already decoded */
    bitvec_t *BitVec;        /* Bit vector for decoded rows */
+   uint_t ScanNumber;       /* Current decoding scan */
    ImageState State;        /* Processing status */
 
    int RefCount;            /* Reference counter */
@@ -62,6 +63,7 @@
                        int version, uint_t width, uint_t height,
                        DilloImgType type);
 void a_Image_set_cmap(DilloImage *Image, const uchar_t *cmap);
+void a_Image_new_scan(DilloImage *image, void *v_imgbuf);
 void a_Image_write(DilloImage *Image, void *v_imgbuf,
                    const uchar_t *buf, uint_t y, int decode);
 void a_Image_close(DilloImage *Image);
--- a/src/jpeg.c	Mon Jan 14 22:54:28 2008 +0100
+++ b/src/jpeg.c	Wed Jan 16 01:17:20 2008 +0100
@@ -42,7 +42,9 @@
 typedef enum {
    DILLO_JPEG_INIT,
    DILLO_JPEG_STARTING,
-   DILLO_JPEG_READING,
+   DILLO_JPEG_READ_BEGIN_SCAN,
+   DILLO_JPEG_READ_IN_SCAN,
+   DILLO_JPEG_READ_END_SCAN,
    DILLO_JPEG_DONE,
    DILLO_JPEG_ERROR
 } DilloJpegState;
@@ -139,10 +141,7 @@
 static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client)
 {
    a_Dicache_close(jpeg->url, jpeg->version, Client);
-
-   if (jpeg->state != DILLO_JPEG_DONE) {
-      jpeg_destroy_decompress(&(jpeg->cinfo));
-   }
+   jpeg_destroy_decompress(&(jpeg->cinfo));
    dFree(jpeg);
 }
 
@@ -288,6 +287,15 @@
          else
             DEBUG_MSG(5, "jpeg: can't handle %d component images\n",
                       jpeg->cinfo.num_components);
+
+         /*
+          * TODO: The multiple-scan jpeg code is valuable at download time
+          * when an image arrives slowly, but should not be used to redisplay
+          * cached images.
+          */
+         if (jpeg_has_multiple_scans(&jpeg->cinfo))
+            jpeg->cinfo.buffered_image = TRUE;
+
          a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image,
                              (uint_t)jpeg->cinfo.image_width,
                              (uint_t)jpeg->cinfo.image_height,
@@ -301,34 +309,94 @@
       /* decompression step 5 (see libjpeg.doc) */
       if (jpeg_start_decompress(&(jpeg->cinfo))) {
          jpeg->y = 0;
-         jpeg->state = DILLO_JPEG_READING;
+         jpeg->state = jpeg_has_multiple_scans(&jpeg->cinfo) ?
+                          DILLO_JPEG_READ_BEGIN_SCAN : DILLO_JPEG_READ_IN_SCAN;
       }
    }
-   if (jpeg->state == DILLO_JPEG_READING) {
+
+   /*
+    * A progressive jpeg contains multiple scans that can be used to display
+    * an increasingly sharp image as it is being received. The reading of each
+    * scan must be surrounded by jpeg_start_output()/jpeg_finish_output().
+    */
+
+   if (jpeg->state == DILLO_JPEG_READ_END_SCAN) {
+      if (jpeg_finish_output(&jpeg->cinfo)) {
+         if (jpeg_input_complete(&jpeg->cinfo)) {
+            jpeg->state = DILLO_JPEG_DONE;
+         } else {
+            jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN;
+         }
+      }
+   }
+
+   if (jpeg->state == DILLO_JPEG_READ_BEGIN_SCAN) {
+      if (jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number)) {
+         a_Dicache_new_scan(jpeg->Image, jpeg->url, jpeg->version);
+         jpeg->state = DILLO_JPEG_READ_IN_SCAN;
+      }
+   }
+
+   if (jpeg->state == DILLO_JPEG_READ_IN_SCAN) {
       linebuf = dMalloc(jpeg->cinfo.image_width *
                          jpeg->cinfo.num_components);
       array[0] = linebuf;
-      while (jpeg->y < jpeg->cinfo.image_height) {
+
+      while (1) {
          num_read = jpeg_read_scanlines(&(jpeg->cinfo), array, 1);
-         if (num_read == 0)
+         if (num_read == 0) {
+            /* out of input */
             break;
+         }
          a_Dicache_write(jpeg->Image, jpeg->url, jpeg->version,
                          linebuf, 0, jpeg->y);
 
          jpeg->y++;
-      }
-      if (jpeg->y == jpeg->cinfo.image_height) {
-         DEBUG_MSG(5, "height achieved\n");
 
-         jpeg_destroy_decompress(&(jpeg->cinfo));
-         jpeg->state = DILLO_JPEG_DONE;
+         if (jpeg->y == jpeg->cinfo.image_height) {
+            /* end of scan */
+            if (!jpeg_has_multiple_scans(&jpeg->cinfo)) {
+               jpeg->state = DILLO_JPEG_DONE;
+               break;
+            } else {
+               jpeg->y = 0;
+               if (jpeg_input_complete(&jpeg->cinfo)) {
+                  if (jpeg->cinfo.input_scan_number ==
+                      jpeg->cinfo.output_scan_number) {
+                     jpeg->state = DILLO_JPEG_DONE;
+                     break;
+                  } else {
+                       /* one final loop through the scanlines */
+                       jpeg_finish_output(&jpeg->cinfo);
+                       jpeg_start_output(&jpeg->cinfo,
+                                         jpeg->cinfo.input_scan_number);
+                       continue;
+                  }
+               }
+               jpeg->state = DILLO_JPEG_READ_END_SCAN;
+               if (!jpeg_finish_output(&jpeg->cinfo)) {
+                  /* out of input */
+                  break;
+               } else {
+                  if (jpeg_input_complete(&jpeg->cinfo)) {
+                     jpeg->state = DILLO_JPEG_DONE;
+                     break;
+                  } else {
+                     jpeg->state = DILLO_JPEG_READ_BEGIN_SCAN;
+                  }
+               }
+               if (!jpeg_start_output(&jpeg->cinfo,
+                                      jpeg->cinfo.input_scan_number)) {
+                  /* out of input */
+                  break;
+               }
+               a_Dicache_new_scan(jpeg->Image, jpeg->url, jpeg->version);
+               jpeg->state = DILLO_JPEG_READ_IN_SCAN;
+            }
+         }
       }
       dFree(linebuf);
    }
-   if (jpeg->state == DILLO_JPEG_ERROR) {
-      jpeg_destroy_decompress(&(jpeg->cinfo));
-      jpeg->state = DILLO_JPEG_DONE;
-   }
 }
 
 #endif /* ENABLE_JPEG */
--- a/src/png.c	Mon Jan 14 22:54:28 2008 +0100
+++ b/src/png.c	Wed Jan 16 01:17:20 2008 +0100
@@ -79,6 +79,7 @@
    uchar_t **row_pointers;       /* pntr to row starts    */
    jmp_buf jmpbuf;              /* png error processing */
    int error;                  /* error flag */
+   png_uint_32 previous_row;
    int rowbytes;               /* No. bytes in image row */
    short passes;
    short channels;              /* No. image channels */
@@ -230,6 +231,11 @@
 
    png_progressive_combine_row(png_ptr, png->row_pointers[row_num], new_row);
 
+   if (row_num < png->previous_row) {
+      a_Dicache_new_scan(png->Image, png->url, png->version);
+   }
+   png->previous_row = row_num;
+
    switch (png->channels) {
    case 3:
       a_Dicache_write(png->Image, png->url, png->version,
@@ -419,6 +425,7 @@
    png->linebuf = NULL;
    png->image_data = NULL;
    png->row_pointers = NULL;
+   png->previous_row = 0;
 
    return png;
 }