if ((passWidth == 0) || (passHeight == 0)) {
return;
}
WritableRaster imRas = theImage.getWritableTile(0, 0);
int dstMinX = imRas.getMinX();
int dstMaxX = dstMinX + imRas.getWidth() - 1;
int dstMinY = imRas.getMinY();
int dstMaxY = dstMinY + imRas.getHeight() - 1;
// Determine which pixels will be updated in this pass
int[] vals =
ReaderUtil.computeUpdatedPixels(sourceRegion,
destinationOffset,
dstMinX, dstMinY,
dstMaxX, dstMaxY,
sourceXSubsampling,
sourceYSubsampling,
xStart, yStart,
passWidth, passHeight,
xStep, yStep);
int updateMinX = vals[0];
int updateMinY = vals[1];
int updateWidth = vals[2];
int updateXStep = vals[4];
int updateYStep = vals[5];
int bitDepth = metadata.IHDR_bitDepth;
int inputBands = inputBandsForColorType[metadata.IHDR_colorType];
int bytesPerPixel = (bitDepth == 16) ? 2 : 1;
bytesPerPixel *= inputBands;
int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow;
// If no pixels need updating, just skip the input data
if (updateWidth == 0) {
for (int srcY = 0; srcY < passHeight; srcY++) {
// Update count of pixels read
updateImageProgress(passWidth);
// Skip filter byte and the remaining row bytes
pixelStream.skipBytes(1 + bytesPerRow);
}
return;
}
// Backwards map from destination pixels
// (dstX = updateMinX + k*updateXStep)
// to source pixels (sourceX), and then
// to offset and skip in passRow (srcX and srcXStep)
int sourceX =
(updateMinX - destinationOffset.x)*sourceXSubsampling +
sourceRegion.x;
int srcX = (sourceX - xStart)/xStep;
// Compute the step factor in the source
int srcXStep = updateXStep*sourceXSubsampling/xStep;
byte[] byteData = null;
short[] shortData = null;
byte[] curr = new byte[bytesPerRow];
byte[] prior = new byte[bytesPerRow];
// Create a 1-row tall Raster to hold the data
WritableRaster passRow = createRaster(passWidth, 1, inputBands,
eltsPerRow,
bitDepth);
// Create an array suitable for holding one pixel
int[] ps = passRow.getPixel(0, 0, (int[])null);
DataBuffer dataBuffer = passRow.getDataBuffer();
int type = dataBuffer.getDataType();
if (type == DataBuffer.TYPE_BYTE) {
byteData = ((DataBufferByte)dataBuffer).getData();
} else {
shortData = ((DataBufferUShort)dataBuffer).getData();
}
processPassStarted(theImage,
passNum,
sourceMinProgressivePass,
sourceMaxProgressivePass,
updateMinX, updateMinY,
updateXStep, updateYStep,
destinationBands);
// Handle source and destination bands
if (sourceBands != null) {
passRow = passRow.createWritableChild(0, 0,
passRow.getWidth(), 1,
0, 0,
sourceBands);
}
if (destinationBands != null) {
imRas = imRas.createWritableChild(0, 0,
imRas.getWidth(),
imRas.getHeight(),
0, 0,
destinationBands);
}
// Determine if all of the relevant output bands have the
// same bit depth as the source data
boolean adjustBitDepths = false;
int[] outputSampleSize = imRas.getSampleModel().getSampleSize();
int numBands = outputSampleSize.length;
for (int b = 0; b < numBands; b++) {
if (outputSampleSize[b] != bitDepth) {
adjustBitDepths = true;
break;
}
}
// If the bit depths differ, create a lookup table per band to perform
// the conversion
int[][] scale = null;
if (adjustBitDepths) {
int maxInSample = (1 << bitDepth) - 1;
int halfMaxInSample = maxInSample/2;
scale = new int[numBands][];
for (int b = 0; b < numBands; b++) {
int maxOutSample = (1 << outputSampleSize[b]) - 1;
scale[b] = new int[maxInSample + 1];
for (int s = 0; s <= maxInSample; s++) {
scale[b][s] =
(s*maxOutSample + halfMaxInSample)/maxInSample;
}
}
}
// Limit passRow to relevant area for the case where we
// will can setRect to copy a contiguous span
boolean useSetRect = srcXStep == 1 &&
updateXStep == 1 &&
!adjustBitDepths &&
(imRas instanceof ByteInterleavedRaster);
if (useSetRect) {
passRow = passRow.createWritableChild(srcX, 0,
updateWidth, 1,
0, 0,
null);
}
// Decode the (sub)image row-by-row
for (int srcY = 0; srcY < passHeight; srcY++) {
// Update count of pixels read
updateImageProgress(passWidth);
// Read the filter type byte and a row of data
int filter = pixelStream.read();
try {
// Swap curr and prior
byte[] tmp = prior;
prior = curr;
curr = tmp;
pixelStream.readFully(curr, 0, bytesPerRow);
} catch (java.util.zip.ZipException ze) {
// TODO - throw a more meaningful exception
throw ze;
}
switch (filter) {
case PNG_FILTER_NONE:
break;
case PNG_FILTER_SUB:
decodeSubFilter(curr, 0, bytesPerRow, bytesPerPixel);
break;
case PNG_FILTER_UP:
decodeUpFilter(curr, 0, prior, 0, bytesPerRow);
break;
case PNG_FILTER_AVERAGE:
decodeAverageFilter(curr, 0, prior, 0, bytesPerRow,
bytesPerPixel);
break;
case PNG_FILTER_PAETH:
decodePaethFilter(curr, 0, prior, 0, bytesPerRow,
bytesPerPixel);
break;
default:
throw new IIOException("Unknown row filter type (= " +
filter + ")!");
}
// Copy data into passRow byte by byte
if (bitDepth < 16) {
System.arraycopy(curr, 0, byteData, 0, bytesPerRow);
} else {
int idx = 0;
for (int j = 0; j < eltsPerRow; j++) {
shortData[j] =
(short)((curr[idx] << 8) | (curr[idx + 1] & 0xff));
idx += 2;
}
}
// True Y position in source
int sourceY = srcY*yStep + yStart;
if ((sourceY >= sourceRegion.y) &&
(sourceY < sourceRegion.y + sourceRegion.height) &&
(((sourceY - sourceRegion.y) %
sourceYSubsampling) == 0)) {
int dstY = destinationOffset.y +
(sourceY - sourceRegion.y)/sourceYSubsampling;
if (dstY < dstMinY) {
continue;
}
if (dstY > dstMaxY) {
break;
}
if (useSetRect) {
imRas.setRect(updateMinX, dstY, passRow);
} else {
int newSrcX = srcX;
for (int dstX = updateMinX;
dstX < updateMinX + updateWidth;
dstX += updateXStep) {
passRow.getPixel(newSrcX, 0, ps);
if (adjustBitDepths) {
for (int b = 0; b < numBands; b++) {
ps[b] = scale[b][ps[b]];
}
}