On Mon, Jan 14, 2008 at 06:06:03PM +0000, place wrote:
(the ones that start out blurry and grow sharper)
- Added code for progressive display of, well, progressive jpegs because I was tired of waiting for full download before display. jpeglib uses the word "scan" for each loop through the scanlines of the image, so I did the same. I'm not sure it's a terribly descriptive term, though, at least to me (not a graphics person). Couldn't think of a better word.
- Turned out scaled ones didn't work. (And, incidentally, that was frustrating because it took some time to realize that the breaking ones were scaled ever so slightly.) Added some bit clearing to imgbuf code on the dw side so that scaled buffers would continue to copy from root buffers.
- Turned out that if there was more than one copy on a page, only the first copy worked. (dillo uses image decoders on the first one and pulls the scanlines out of Dicache for the rest.) More bit clearing and a scan number to help a_Dicache_callback with its copying.
- Discovered that interlaced pngs use progressive display, and that they hadn't been working for scaled or for multiple on a page.
This is cool! I haven't looked at the details, but it works and is stable here. Johannes
- Considered looking into animated gifs, but then my good sense prevailed :)
diff -pur dw2/dw/fltkimgbuf.cc dw2-cur/dw/fltkimgbuf.cc --- dw2/dw/fltkimgbuf.cc 2007-11-12 15:27:13.000000000 +0000 +++ dw2-cur/dw/fltkimgbuf.cc 2008-01-13 06:06:44.000000000 +0000 @@ -191,6 +191,16 @@ void FltkImgbuf::copyRow (int row, const } }
+void FltkImgbuf::newScan () +{ + if (isRoot()) { + for (Iterator<FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext(); ) { + FltkImgbuf *sb = it.getNext (); + sb->copiedRows->clear(); + } + } +} + core::Imgbuf* FltkImgbuf::getScaledBuf (int width, int height) { if (root) diff -pur dw2/dw/fltkimgbuf.hh dw2-cur/dw/fltkimgbuf.hh --- dw2/dw/fltkimgbuf.hh 2007-10-06 22:03:01.000000000 +0000 +++ dw2-cur/dw/fltkimgbuf.hh 2008-01-12 20:39:27.000000000 +0000 @@ -42,6 +42,7 @@ public:
void setCMap (int *colors, int num_colors); inline void scaleRow (int row, const core::byte *data); + void newScan (); void copyRow (int row, const core::byte *data); core::Imgbuf* getScaledBuf (int width, int height); void getRowArea (int row, dw::core::Rectangle *area); diff -pur dw2/dw/imgbuf.hh dw2-cur/dw/imgbuf.hh --- dw2/dw/imgbuf.hh 2007-10-06 22:03:01.000000000 +0000 +++ dw2-cur/dw/imgbuf.hh 2008-01-12 20:38:03.000000000 +0000 @@ -167,6 +167,7 @@ public:
virtual void setCMap (int *colors, int num_colors) = 0; virtual void copyRow (int row, const byte *data) = 0; + virtual void newScan () = 0;
/* * Methods called from dw::Image
diff -pur dillo2/src/bitvec.c dillo2-cur/src/bitvec.c --- dillo2/src/bitvec.c 2007-10-06 22:36:34.000000000 +0000 +++ dillo2-cur/src/bitvec.c 2008-01-14 00:40:48.000000000 +0000 @@ -30,6 +30,14 @@ bitvec_t *a_Bitvec_new(int num_bits) }
/* + * 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) diff -pur dillo2/src/bitvec.h dillo2-cur/src/bitvec.h --- dillo2/src/bitvec.h 2007-10-06 22:36:34.000000000 +0000 +++ dillo2-cur/src/bitvec.h 2008-01-14 00:40:48.000000000 +0000 @@ -21,6 +21,7 @@ bitvec_t *a_Bitvec_new(int bits); 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) \ diff -pur dillo2/src/dicache.c dillo2-cur/src/dicache.c --- dillo2/src/dicache.c 2007-11-07 05:03:53.000000000 +0000 +++ dillo2-cur/src/dicache.c 2008-01-14 02:26:26.000000000 +0000 @@ -84,6 +84,7 @@ static DICacheEntry *Dicache_entry_new(v 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 @@ void a_Dicache_callback(int Op, CacheCli
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 @@ void a_Dicache_callback(int Op, CacheCli 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 @@ void a_Dicache_set_cmap(DilloUrl *url, i }
/* + * 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 diff -pur dillo2/src/dicache.h dillo2-cur/src/dicache.h --- dillo2/src/dicache.h 2007-10-06 22:36:34.000000000 +0000 +++ dillo2-cur/src/dicache.h 2008-01-14 00:40:48.000000000 +0000 @@ -31,6 +31,7 @@ struct _DICacheEntry { 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_parms(DilloUrl *url, 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); diff -pur dillo2/src/image.cc dillo2-cur/src/image.cc --- dillo2/src/image.cc 2007-11-07 05:03:53.000000000 +0000 +++ dillo2-cur/src/image.cc 2008-01-14 00:40:48.000000000 +0000 @@ -55,6 +55,7 @@ DilloImage *a_Image_new(int width, 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 @@ void a_Image_set_cmap(DilloImage *Image, }
/* + * 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, diff -pur dillo2/src/image.hh dillo2-cur/src/image.hh --- dillo2/src/image.hh 2007-12-24 06:17:25.000000000 +0000 +++ dillo2-cur/src/image.hh 2008-01-14 00:40:48.000000000 +0000 @@ -44,6 +44,7 @@ struct _DilloImage {
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 @@ void a_Image_set_parms(DilloImage *Image 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); diff -pur dillo2/src/jpeg.c dillo2-cur/src/jpeg.c --- dillo2/src/jpeg.c 2007-11-07 05:03:53.000000000 +0000 +++ dillo2-cur/src/jpeg.c 2008-01-14 02:29:32.000000000 +0000 @@ -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 @@ void *a_Jpeg_image(const char *Type, voi 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 @@ static void Jpeg_write(DilloJpeg *jpeg, 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 @@ static void Jpeg_write(DilloJpeg *jpeg, /* 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; + } + } + + /* + * 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_READING) { + + 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 */ diff -pur dillo2/src/png.c dillo2-cur/src/png.c --- dillo2/src/png.c 2007-10-06 22:36:34.000000000 +0000 +++ dillo2-cur/src/png.c 2008-01-14 01:36:51.000000000 +0000 @@ -79,6 +79,7 @@ struct _DilloPng { 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 @@ static void
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 @@ static DilloPng *Png_new(DilloImage *Ima png->linebuf = NULL; png->image_data = NULL; png->row_pointers = NULL; + png->previous_row = 0;
return png; }
_______________________________________________ Dillo-dev mailing list Dillo-dev@dillo.org http://lists.auriga.wearlab.de/cgi-bin/mailman/listinfo/dillo-dev