The Independent JPEG Group's JPEG software v6
diff --git a/jmemmgr.c b/jmemmgr.c
index 76fb486..dc3e1c7 100644
--- a/jmemmgr.c
+++ b/jmemmgr.c
@@ -1,7 +1,7 @@
/*
* jmemmgr.c
*
- * Copyright (C) 1991-1994, Thomas G. Lane.
+ * Copyright (C) 1991-1995, Thomas G. Lane.
* This file is part of the Independent JPEG Group's software.
* For conditions of distribution and use, see the accompanying README file.
*
@@ -151,10 +151,12 @@
JSAMPARRAY mem_buffer; /* => the in-memory buffer */
JDIMENSION rows_in_array; /* total virtual array height */
JDIMENSION samplesperrow; /* width of array (and of memory buffer) */
- JDIMENSION unitheight; /* # of rows accessed by access_virt_sarray */
+ JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */
JDIMENSION rows_in_mem; /* height of memory buffer */
JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */
JDIMENSION cur_start_row; /* first logical row # in the buffer */
+ JDIMENSION first_undef_row; /* row # of first uninitialized row */
+ boolean pre_zero; /* pre-zero mode requested? */
boolean dirty; /* do current buffer contents need written? */
boolean b_s_open; /* is backing-store data valid? */
jvirt_sarray_ptr next; /* link to next virtual sarray control block */
@@ -165,10 +167,12 @@
JBLOCKARRAY mem_buffer; /* => the in-memory buffer */
JDIMENSION rows_in_array; /* total virtual array height */
JDIMENSION blocksperrow; /* width of array (and of memory buffer) */
- JDIMENSION unitheight; /* # of rows accessed by access_virt_barray */
+ JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */
JDIMENSION rows_in_mem; /* height of memory buffer */
JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */
JDIMENSION cur_start_row; /* first logical row # in the buffer */
+ JDIMENSION first_undef_row; /* row # of first uninitialized row */
+ boolean pre_zero; /* pre-zero mode requested? */
boolean dirty; /* do current buffer contents need written? */
boolean b_s_open; /* is backing-store data valid? */
jvirt_barray_ptr next; /* link to next virtual barray control block */
@@ -481,21 +485,17 @@
/*
* About virtual array management:
*
- * To allow machines with limited memory to handle large images, all
- * processing in the JPEG system is done a few pixel or block rows at a time.
* The above "normal" array routines are only used to allocate strip buffers
- * (as wide as the image, but just a few rows high).
- * In some cases multiple passes must be made over the data. In these
- * cases the virtual array routines are used. The array is still accessed
- * a strip at a time, but the memory manager must save the whole array
- * for repeated accesses. The intended implementation is that there is
- * a strip buffer in memory (as high as is possible given the desired memory
- * limit), plus a backing file that holds the rest of the array.
+ * (as wide as the image, but just a few rows high). Full-image-sized buffers
+ * are handled as "virtual" arrays. The array is still accessed a strip at a
+ * time, but the memory manager must save the whole array for repeated
+ * accesses. The intended implementation is that there is a strip buffer in
+ * memory (as high as is possible given the desired memory limit), plus a
+ * backing file that holds the rest of the array.
*
* The request_virt_array routines are told the total size of the image and
- * the unit height, which is the number of rows that will be accessed at once;
- * the in-memory buffer should be made a multiple of this height for best
- * efficiency.
+ * the maximum number of rows that will be accessed at once. The in-memory
+ * buffer must be at least as large as the maxaccess value.
*
* The request routines create control blocks but not the in-memory buffers.
* That is postponed until realize_virt_arrays is called. At that time the
@@ -506,30 +506,23 @@
* area accessible (after reading or writing the backing file, if necessary).
* Note that the access routines are told whether the caller intends to modify
* the accessed strip; during a read-only pass this saves having to rewrite
- * data to disk.
+ * data to disk. The access routines are also responsible for pre-zeroing
+ * any newly accessed rows, if pre-zeroing was requested.
*
- * The typical access pattern is one top-to-bottom pass to write the data,
- * followed by one or more read-only top-to-bottom passes. However, other
- * access patterns may occur while reading. For example, translation of image
- * formats that use bottom-to-top scan order will require bottom-to-top read
- * passes. The memory manager need not support multiple write passes nor
- * funny write orders (meaning that rearranging rows must be handled while
- * reading data out of the virtual array, not while putting it in). THIS WILL
- * PROBABLY NEED TO CHANGE ... will need multiple write passes for progressive
- * JPEG decoding.
- *
- * In current usage, the access requests are always for nonoverlapping strips;
- * that is, successive access start_row numbers always differ by exactly the
- * unitheight. This allows fairly simple buffer dump/reload logic if the
- * in-memory buffer is made a multiple of the unitheight. The code below
- * would work with overlapping access requests, but not very efficiently.
+ * In current usage, the access requests are usually for nonoverlapping
+ * strips; that is, successive access start_row numbers differ by exactly
+ * num_rows = maxaccess. This means we can get good performance with simple
+ * buffer dump/reload logic, by making the in-memory buffer be a multiple
+ * of the access height; then there will never be accesses across bufferload
+ * boundaries. The code will still work with overlapping access requests,
+ * but it doesn't handle bufferload overlaps very efficiently.
*/
METHODDEF jvirt_sarray_ptr
-request_virt_sarray (j_common_ptr cinfo, int pool_id,
+request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero,
JDIMENSION samplesperrow, JDIMENSION numrows,
- JDIMENSION unitheight)
+ JDIMENSION maxaccess)
/* Request a virtual 2-D sample array */
{
my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
@@ -539,9 +532,6 @@
if (pool_id != JPOOL_IMAGE)
ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */
- /* Round array size up to a multiple of unitheight */
- numrows = (JDIMENSION) jround_up((long) numrows, (long) unitheight);
-
/* get control block */
result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id,
SIZEOF(struct jvirt_sarray_control));
@@ -549,7 +539,8 @@
result->mem_buffer = NULL; /* marks array not yet realized */
result->rows_in_array = numrows;
result->samplesperrow = samplesperrow;
- result->unitheight = unitheight;
+ result->maxaccess = maxaccess;
+ result->pre_zero = pre_zero;
result->b_s_open = FALSE; /* no associated backing-store object */
result->next = mem->virt_sarray_list; /* add to list of virtual arrays */
mem->virt_sarray_list = result;
@@ -559,9 +550,9 @@
METHODDEF jvirt_barray_ptr
-request_virt_barray (j_common_ptr cinfo, int pool_id,
+request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero,
JDIMENSION blocksperrow, JDIMENSION numrows,
- JDIMENSION unitheight)
+ JDIMENSION maxaccess)
/* Request a virtual 2-D coefficient-block array */
{
my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
@@ -571,9 +562,6 @@
if (pool_id != JPOOL_IMAGE)
ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */
- /* Round array size up to a multiple of unitheight */
- numrows = (JDIMENSION) jround_up((long) numrows, (long) unitheight);
-
/* get control block */
result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id,
SIZEOF(struct jvirt_barray_control));
@@ -581,7 +569,8 @@
result->mem_buffer = NULL; /* marks array not yet realized */
result->rows_in_array = numrows;
result->blocksperrow = blocksperrow;
- result->unitheight = unitheight;
+ result->maxaccess = maxaccess;
+ result->pre_zero = pre_zero;
result->b_s_open = FALSE; /* no associated backing-store object */
result->next = mem->virt_barray_list; /* add to list of virtual arrays */
mem->virt_barray_list = result;
@@ -595,67 +584,67 @@
/* Allocate the in-memory buffers for any unrealized virtual arrays */
{
my_mem_ptr mem = (my_mem_ptr) cinfo->mem;
- long space_per_unitheight, maximum_space, avail_mem;
- long unitheights, max_unitheights;
+ long space_per_minheight, maximum_space, avail_mem;
+ long minheights, max_minheights;
jvirt_sarray_ptr sptr;
jvirt_barray_ptr bptr;
- /* Compute the minimum space needed (unitheight rows in each buffer)
+ /* Compute the minimum space needed (maxaccess rows in each buffer)
* and the maximum space needed (full image height in each buffer).
* These may be of use to the system-dependent jpeg_mem_available routine.
*/
- space_per_unitheight = 0;
+ space_per_minheight = 0;
maximum_space = 0;
for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) {
if (sptr->mem_buffer == NULL) { /* if not realized yet */
- space_per_unitheight += (long) sptr->unitheight *
- (long) sptr->samplesperrow * SIZEOF(JSAMPLE);
+ space_per_minheight += (long) sptr->maxaccess *
+ (long) sptr->samplesperrow * SIZEOF(JSAMPLE);
maximum_space += (long) sptr->rows_in_array *
(long) sptr->samplesperrow * SIZEOF(JSAMPLE);
}
}
for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) {
if (bptr->mem_buffer == NULL) { /* if not realized yet */
- space_per_unitheight += (long) bptr->unitheight *
- (long) bptr->blocksperrow * SIZEOF(JBLOCK);
+ space_per_minheight += (long) bptr->maxaccess *
+ (long) bptr->blocksperrow * SIZEOF(JBLOCK);
maximum_space += (long) bptr->rows_in_array *
(long) bptr->blocksperrow * SIZEOF(JBLOCK);
}
}
- if (space_per_unitheight <= 0)
+ if (space_per_minheight <= 0)
return; /* no unrealized arrays, no work */
/* Determine amount of memory to actually use; this is system-dependent. */
- avail_mem = jpeg_mem_available(cinfo, space_per_unitheight, maximum_space,
+ avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space,
mem->total_space_allocated);
/* If the maximum space needed is available, make all the buffers full
- * height; otherwise parcel it out with the same number of unitheights
+ * height; otherwise parcel it out with the same number of minheights
* in each buffer.
*/
if (avail_mem >= maximum_space)
- max_unitheights = 1000000000L;
+ max_minheights = 1000000000L;
else {
- max_unitheights = avail_mem / space_per_unitheight;
+ max_minheights = avail_mem / space_per_minheight;
/* If there doesn't seem to be enough space, try to get the minimum
* anyway. This allows a "stub" implementation of jpeg_mem_available().
*/
- if (max_unitheights <= 0)
- max_unitheights = 1;
+ if (max_minheights <= 0)
+ max_minheights = 1;
}
/* Allocate the in-memory buffers and initialize backing store as needed. */
for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) {
if (sptr->mem_buffer == NULL) { /* if not realized yet */
- unitheights = ((long) sptr->rows_in_array - 1L) / sptr->unitheight + 1L;
- if (unitheights <= max_unitheights) {
+ minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L;
+ if (minheights <= max_minheights) {
/* This buffer fits in memory */
sptr->rows_in_mem = sptr->rows_in_array;
} else {
/* It doesn't fit in memory, create backing store. */
- sptr->rows_in_mem = (JDIMENSION) (max_unitheights * sptr->unitheight);
+ sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess);
jpeg_open_backing_store(cinfo, & sptr->b_s_info,
(long) sptr->rows_in_array *
(long) sptr->samplesperrow *
@@ -666,19 +655,20 @@
sptr->samplesperrow, sptr->rows_in_mem);
sptr->rowsperchunk = mem->last_rowsperchunk;
sptr->cur_start_row = 0;
+ sptr->first_undef_row = 0;
sptr->dirty = FALSE;
}
}
for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) {
if (bptr->mem_buffer == NULL) { /* if not realized yet */
- unitheights = ((long) bptr->rows_in_array - 1L) / bptr->unitheight + 1L;
- if (unitheights <= max_unitheights) {
+ minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L;
+ if (minheights <= max_minheights) {
/* This buffer fits in memory */
bptr->rows_in_mem = bptr->rows_in_array;
} else {
/* It doesn't fit in memory, create backing store. */
- bptr->rows_in_mem = (JDIMENSION) (max_unitheights * bptr->unitheight);
+ bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess);
jpeg_open_backing_store(cinfo, & bptr->b_s_info,
(long) bptr->rows_in_array *
(long) bptr->blocksperrow *
@@ -689,6 +679,7 @@
bptr->blocksperrow, bptr->rows_in_mem);
bptr->rowsperchunk = mem->last_rowsperchunk;
bptr->cur_start_row = 0;
+ bptr->first_undef_row = 0;
bptr->dirty = FALSE;
}
}
@@ -699,7 +690,7 @@
do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing)
/* Do backing store read or write of a virtual sample array */
{
- long bytesperrow, file_offset, byte_count, rows, i;
+ long bytesperrow, file_offset, byte_count, rows, thisrow, i;
bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE);
file_offset = ptr->cur_start_row * bytesperrow;
@@ -707,9 +698,11 @@
for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) {
/* One chunk, but check for short chunk at end of buffer */
rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i);
+ /* Transfer no more than is currently defined */
+ thisrow = (long) ptr->cur_start_row + i;
+ rows = MIN(rows, (long) ptr->first_undef_row - thisrow);
/* Transfer no more than fits in file */
- rows = MIN(rows, (long) ptr->rows_in_array -
- ((long) ptr->cur_start_row + i));
+ rows = MIN(rows, (long) ptr->rows_in_array - thisrow);
if (rows <= 0) /* this chunk might be past end of file! */
break;
byte_count = rows * bytesperrow;
@@ -730,7 +723,7 @@
do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing)
/* Do backing store read or write of a virtual coefficient-block array */
{
- long bytesperrow, file_offset, byte_count, rows, i;
+ long bytesperrow, file_offset, byte_count, rows, thisrow, i;
bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK);
file_offset = ptr->cur_start_row * bytesperrow;
@@ -738,9 +731,11 @@
for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) {
/* One chunk, but check for short chunk at end of buffer */
rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i);
+ /* Transfer no more than is currently defined */
+ thisrow = (long) ptr->cur_start_row + i;
+ rows = MIN(rows, (long) ptr->first_undef_row - thisrow);
/* Transfer no more than fits in file */
- rows = MIN(rows, (long) ptr->rows_in_array -
- ((long) ptr->cur_start_row + i));
+ rows = MIN(rows, (long) ptr->rows_in_array - thisrow);
if (rows <= 0) /* this chunk might be past end of file! */
break;
byte_count = rows * bytesperrow;
@@ -759,18 +754,23 @@
METHODDEF JSAMPARRAY
access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr,
- JDIMENSION start_row, boolean writable)
+ JDIMENSION start_row, JDIMENSION num_rows,
+ boolean writable)
/* Access the part of a virtual sample array starting at start_row */
-/* and extending for ptr->unitheight rows. writable is true if */
+/* and extending for num_rows rows. writable is true if */
/* caller intends to modify the accessed area. */
{
+ JDIMENSION end_row = start_row + num_rows;
+ JDIMENSION undef_row;
+
/* debugging check */
- if (start_row >= ptr->rows_in_array || ptr->mem_buffer == NULL)
+ if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess ||
+ ptr->mem_buffer == NULL)
ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
/* Make the desired part of the virtual array accessible */
if (start_row < ptr->cur_start_row ||
- start_row+ptr->unitheight > ptr->cur_start_row+ptr->rows_in_mem) {
+ end_row > ptr->cur_start_row+ptr->rows_in_mem) {
if (! ptr->b_s_open)
ERREXIT(cinfo, JERR_VIRTUAL_BUG);
/* Flush old buffer contents if necessary */
@@ -791,18 +791,42 @@
/* use long arithmetic here to avoid overflow & unsigned problems */
long ltemp;
- ltemp = (long) start_row + (long) ptr->unitheight -
- (long) ptr->rows_in_mem;
+ ltemp = (long) end_row - (long) ptr->rows_in_mem;
if (ltemp < 0)
ltemp = 0; /* don't fall off front end of file */
ptr->cur_start_row = (JDIMENSION) ltemp;
}
- /* If reading, read in the selected part of the array.
- * If we are writing, we need not pre-read the selected portion,
- * since the access sequence constraints ensure it would be garbage.
+ /* Read in the selected part of the array.
+ * During the initial write pass, we will do no actual read
+ * because the selected part is all undefined.
*/
- if (! writable) {
- do_sarray_io(cinfo, ptr, FALSE);
+ do_sarray_io(cinfo, ptr, FALSE);
+ }
+ /* Ensure the accessed part of the array is defined; prezero if needed.
+ * To improve locality of access, we only prezero the part of the array
+ * that the caller is about to access, not the entire in-memory array.
+ */
+ if (ptr->first_undef_row < end_row) {
+ if (ptr->first_undef_row < start_row) {
+ if (writable) /* writer skipped over a section of array */
+ ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
+ undef_row = start_row; /* but reader is allowed to read ahead */
+ } else {
+ undef_row = ptr->first_undef_row;
+ }
+ if (writable)
+ ptr->first_undef_row = end_row;
+ if (ptr->pre_zero) {
+ size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE);
+ undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */
+ end_row -= ptr->cur_start_row;
+ while (undef_row < end_row) {
+ jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow);
+ undef_row++;
+ }
+ } else {
+ if (! writable) /* reader looking at undefined data */
+ ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
}
}
/* Flag the buffer dirty if caller will write in it */
@@ -815,18 +839,23 @@
METHODDEF JBLOCKARRAY
access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr,
- JDIMENSION start_row, boolean writable)
+ JDIMENSION start_row, JDIMENSION num_rows,
+ boolean writable)
/* Access the part of a virtual block array starting at start_row */
-/* and extending for ptr->unitheight rows. writable is true if */
+/* and extending for num_rows rows. writable is true if */
/* caller intends to modify the accessed area. */
{
+ JDIMENSION end_row = start_row + num_rows;
+ JDIMENSION undef_row;
+
/* debugging check */
- if (start_row >= ptr->rows_in_array || ptr->mem_buffer == NULL)
+ if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess ||
+ ptr->mem_buffer == NULL)
ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
/* Make the desired part of the virtual array accessible */
if (start_row < ptr->cur_start_row ||
- start_row+ptr->unitheight > ptr->cur_start_row+ptr->rows_in_mem) {
+ end_row > ptr->cur_start_row+ptr->rows_in_mem) {
if (! ptr->b_s_open)
ERREXIT(cinfo, JERR_VIRTUAL_BUG);
/* Flush old buffer contents if necessary */
@@ -847,18 +876,42 @@
/* use long arithmetic here to avoid overflow & unsigned problems */
long ltemp;
- ltemp = (long) start_row + (long) ptr->unitheight -
- (long) ptr->rows_in_mem;
+ ltemp = (long) end_row - (long) ptr->rows_in_mem;
if (ltemp < 0)
ltemp = 0; /* don't fall off front end of file */
ptr->cur_start_row = (JDIMENSION) ltemp;
}
- /* If reading, read in the selected part of the array.
- * If we are writing, we need not pre-read the selected portion,
- * since the access sequence constraints ensure it would be garbage.
+ /* Read in the selected part of the array.
+ * During the initial write pass, we will do no actual read
+ * because the selected part is all undefined.
*/
- if (! writable) {
- do_barray_io(cinfo, ptr, FALSE);
+ do_barray_io(cinfo, ptr, FALSE);
+ }
+ /* Ensure the accessed part of the array is defined; prezero if needed.
+ * To improve locality of access, we only prezero the part of the array
+ * that the caller is about to access, not the entire in-memory array.
+ */
+ if (ptr->first_undef_row < end_row) {
+ if (ptr->first_undef_row < start_row) {
+ if (writable) /* writer skipped over a section of array */
+ ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
+ undef_row = start_row; /* but reader is allowed to read ahead */
+ } else {
+ undef_row = ptr->first_undef_row;
+ }
+ if (writable)
+ ptr->first_undef_row = end_row;
+ if (ptr->pre_zero) {
+ size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK);
+ undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */
+ end_row -= ptr->cur_start_row;
+ while (undef_row < end_row) {
+ jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow);
+ undef_row++;
+ }
+ } else {
+ if (! writable) /* reader looking at undefined data */
+ ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS);
}
}
/* Flag the buffer dirty if caller will write in it */