TMI-40: Fixed subsampling offset bug (and removed the slow, stepwise reading + simplified the code, at the cost of higher memory consumption).

This commit is contained in:
Harald Kuhr 2015-03-19 23:38:14 +01:00
parent 406ae28da7
commit 26475eb004
2 changed files with 30 additions and 90 deletions

View File

@ -447,29 +447,17 @@ public class JPEGImageReader extends ImageReaderBase {
Rectangle dstRegion = new Rectangle();
computeRegions(param, origWidth, origHeight, image, srcRegion, dstRegion);
// We're ready to go
processImageStarted(imageIndex);
// Need to undo the subsampling offset translations, as they are applied again in delegate.readRaster
int gridX = param.getSubsamplingXOffset();
int gridY = param.getSubsamplingYOffset();
srcRegion.translate(-gridX, -gridY);
srcRegion.width += gridX;
srcRegion.height += gridY;
// Unfortunately looping is slower than reading all at once, but
// that requires 2 x memory or more, so a few steps is an ok compromise I guess
// Unfortunately, reading the image in steps, is increasingly slower
// for each iteration, so we'll read all at once.
try {
final int step = Math.max(1024, srcRegion.height / 10); // TODO: Using a multiple of 8 is probably a good idea for JPEG
final int srcMaxY = srcRegion.y + srcRegion.height;
int destY = dstRegion.y;
for (int y = srcRegion.y; y < srcMaxY; y += step) {
int scan = Math.min(step, srcMaxY - y);
// Let the progress delegator handle progress, using corrected range
progressDelegator.updateProgressRange(100f * (y + scan) / srcRegion.height);
// Make sure subsampling is within bounds
if (scan <= param.getSubsamplingYOffset()) {
param.setSourceSubsampling(param.getSourceXSubsampling(), param.getSourceYSubsampling(), param.getSubsamplingXOffset(), scan - 1);
}
Rectangle subRegion = new Rectangle(srcRegion.x, y, srcRegion.width, scan);
param.setSourceRegion(subRegion);
param.setSourceRegion(srcRegion);
Raster raster = delegate.readRaster(imageIndex, param); // non-converted
// Apply source color conversion from implicit color space
@ -484,38 +472,21 @@ public class JPEGImageReader extends ImageReaderBase {
}
// ...else assume the raster is already converted
int destHeight = Math.min(raster.getHeight(), dstRegion.height - destY); // Avoid off-by-one
Raster src = raster.createChild(0, 0, raster.getWidth(), destHeight, 0, 0, param.getSourceBands());
WritableRaster dest = destination.createWritableChild(dstRegion.x, destY, raster.getWidth(), destHeight, 0, 0, param.getDestinationBands());
WritableRaster dest = destination.createWritableChild(dstRegion.x, dstRegion.y, raster.getWidth(), raster.getHeight(), 0, 0, param.getDestinationBands());
// Apply further color conversion for explicit color space, or just copy the pixels into place
if (convert != null) {
convert.filter(src, dest);
// WritableRaster filtered = convert.filter(src, null);
// new AffineTransformOp(AffineTransform.getRotateInstance(2 * Math.PI, filtered.getWidth() / 2.0, filtered.getHeight() / 2.0), null).filter(filtered, dest);
convert.filter(raster, dest);
}
else {
dest.setRect(0, 0, src);
}
destY += raster.getHeight();
if (abortRequested()) {
processReadAborted();
break;
}
dest.setRect(0, 0, raster);
}
}
finally {
// Restore normal read progress processing
progressDelegator.resetProgressRange();
// NOTE: Would be cleaner to clone the param, unfortunately it can't be done easily...
param.setSourceRegion(origSourceRegion);
}
processImageComplete();
return image;
}
@ -1232,55 +1203,26 @@ public class JPEGImageReader extends ImageReaderBase {
}
private class ProgressDelegator extends ProgressListenerBase implements IIOReadUpdateListener, IIOReadWarningListener {
float readProgressStart = -1;
float readProgressStop = -1;
void resetProgressRange() {
readProgressStart = -1;
readProgressStop = -1;
}
private boolean isProgressRangeCorrected() {
return readProgressStart == -1 && readProgressStop == -1;
}
void updateProgressRange(float limit) {
Validate.isTrue(limit >= 0, limit, "Negative range limit");
readProgressStart = readProgressStop != -1 ? readProgressStop : 0;
readProgressStop = limit;
}
@Override
public void imageComplete(ImageReader source) {
if (isProgressRangeCorrected()) {
processImageComplete();
}
}
@Override
public void imageProgress(ImageReader source, float percentageDone) {
if (isProgressRangeCorrected()) {
processImageProgress(percentageDone);
}
else {
processImageProgress(readProgressStart + (percentageDone * (readProgressStop - readProgressStart) / 100f));
}
}
@Override
public void imageStarted(ImageReader source, int imageIndex) {
if (isProgressRangeCorrected()) {
processImageStarted(imageIndex);
}
}
@Override
public void readAborted(ImageReader source) {
if (isProgressRangeCorrected()) {
processReadAborted();
}
}
@Override
public void sequenceComplete(ImageReader source) {

View File

@ -163,7 +163,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(new Rectangle(800, 800, 64, 8));
param.setSourceSubsampling(8, 8, 1, 1);
param.setSourceSubsampling(8, 8, 2, 2);
BufferedImage image = reader.read(0, param);
assertNotNull(image);
@ -180,7 +180,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
private static void assertJPEGPixelsEqual(byte[] expected, byte[] actual, int actualOffset) {
for (int i = 0; i < expected.length; i++) {
assertEquals(expected[i], actual[i + actualOffset], 5);
assertEquals(String.format("Difference in pixel %d", i), expected[i], actual[i + actualOffset], 5);
}
}
@ -947,7 +947,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
assertNotNull(image);
}
@Ignore
@Test
public void testReadSubsamplingNotSkippingLines1028() throws IOException {
JPEGImageReader reader = createReader();
@ -1012,7 +1011,6 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTestCase<JPEGImageRe
assertNotNull(image);
}
@Ignore
@Test
public void testReadSubsamplingNotSkippingLines1025() throws IOException {
JPEGImageReader reader = createReader();