Skip to content

Issue#217 : problem using replacePixels method in TiffImageWriter when BigTiff is turned on #319

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ public void initialize(ImageInputStream stream,
TIFFIFD subIFD = new TIFFIFD(tagSets);

// XXX Use same ignore policy for sub-IFD fields?
subIFD.initialize(stream, ignoreUnknownFields);
subIFD.initialize(stream, ignoreUnknownFields, isBTIFF);
obj = subIFD;
stream.reset();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3400,7 +3400,7 @@ private TIFFIFD readIFD(int imageIndex) throws IOException {
TIFFIFD rootIFD = new TIFFIFD(tagSets);
// XXX Ignore unknown fields in metadata presumably because
// any fields needed to write pixels would be known?
rootIFD.initialize(stream, true);
rootIFD.initialize(stream, true, isBtiff);
stream.reset();

return rootIFD;
Expand Down Expand Up @@ -3465,8 +3465,7 @@ public void prepareReplacePixels(int imageIndex,
}

// Get the image dimensions.
f =
replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
f = replacePixelsIFD.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
if(f == null) {
throw new IIOException("Cannot read ImageWidth field.");
}
Expand Down Expand Up @@ -3907,20 +3906,24 @@ public void replacePixels(RenderedImage image, ImageWriteParam param)
stream.seek(replacePixelsTileOffsets[tileIndex]);
}

image = new SingleTileRenderedImage(raster, cm);

int numBytes = writeTile(image, tileRect, encoder);
int numBytes = writeTile(new SingleTileRenderedImage(raster, cm), tileRect, encoder);

if(isEmpty) {
// Update Strip/TileOffsets and
// Strip/TileByteCounts fields.
stream.mark();
stream.seek(replacePixelsOffsetsPosition +
4*tileIndex);
stream.writeInt((int)nextSpace);
stream.seek(replacePixelsByteCountsPosition +
4*tileIndex);
stream.writeInt(numBytes);

if (isBtiff) {
stream.seek(replacePixelsOffsetsPosition + 8 * tileIndex);
stream.writeLong(nextSpace);
stream.seek(replacePixelsByteCountsPosition + 8 * tileIndex);
stream.writeLong(numBytes);
} else {
stream.seek(replacePixelsOffsetsPosition + 4 * tileIndex);
stream.writeInt((int)nextSpace);
stream.seek(replacePixelsByteCountsPosition + 4 * tileIndex);
stream.writeInt(numBytes);
}
stream.reset();

// Increment location of next available space.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@

import javax.imageio.IIOImage;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.event.IIOWriteProgressListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.FileImageOutputStream;
import javax.media.jai.PlanarImage;
Expand Down Expand Up @@ -263,6 +265,62 @@ public void imageComplete(ImageWriter source) {
reader.dispose();
}

@Test
public void replacePixelsWriteTiff() throws IOException {
final File inputFile = TestData.file(this, "test.tif");
final File outputFile = TestData.temp(this, "testw.tif");
TIFFImageReader reader = (TIFFImageReader) new TIFFImageReaderSpi().createReaderInstance();
reader.setInput(new FileImageInputStream(inputFile));
BufferedImage image = reader.read(0);
final TIFFImageWriter writer = (TIFFImageWriter) new TIFFImageWriterSpi().createWriterInstance(null);
final TIFFImageWriteParam writeParam = new TIFFImageWriteParam(Locale.getDefault());
writeParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);

writer.setOutput(new FileImageOutputStream(outputFile));

final IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(writeParam);
final ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromRenderedImage(image);
IIOMetadata imageMetadata = writer.getDefaultImageMetadata(imageTypeSpecifier, writeParam);
writer.prepareWriteEmpty(streamMetadata, imageTypeSpecifier, image.getWidth(), image.getHeight(), imageMetadata, null, writeParam);
writer.endWriteEmpty();

writer.prepareReplacePixels(0, new Rectangle(image.getWidth(), image.getHeight()));
writer.replacePixels(image, writeParam);
writer.endReplacePixels();

writer.dispose();
TIFFReadTest.assertImagesEqual(image, TIFFReadTest.readTiff(outputFile));
}

@Test
public void writeBigTiff() throws IOException {
final File inputFile = TestData.file(this, "test.tif");
final File outputFile = TestData.temp(this, "testw.tif");
TIFFImageReader reader = (TIFFImageReader) new TIFFImageReaderSpi()
.createReaderInstance();
reader.setInput(new FileImageInputStream(inputFile));
BufferedImage image = reader.read(0);
final TIFFImageWriter writer = (TIFFImageWriter) new TIFFImageWriterSpi().createWriterInstance(null);
final TIFFImageWriteParam writeParam = new TIFFImageWriteParam(Locale.getDefault());
writeParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
writeParam.setForceToBigTIFF(true);

writer.setOutput(new FileImageOutputStream(outputFile));

final IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(writeParam);
final ImageTypeSpecifier imageTypeSpecifier = new ImageTypeSpecifier(image);
Copy link
Preview

Copilot AI Mar 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To ensure consistency and compatibility, use ImageTypeSpecifier.createFromRenderedImage(image) instead of invoking a constructor that may not behave as expected.

Suggested change
final ImageTypeSpecifier imageTypeSpecifier = new ImageTypeSpecifier(image);
final ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromRenderedImage(image);

Copilot uses AI. Check for mistakes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bad idea, the static method has dedicated paths for BufferedImage.

IIOMetadata imageMetadata = writer.getDefaultImageMetadata(imageTypeSpecifier, writeParam);
writer.prepareWriteEmpty(streamMetadata, imageTypeSpecifier, image.getWidth(), image.getHeight(), imageMetadata, null, writeParam);
writer.endWriteEmpty();

writer.prepareReplacePixels(0, new Rectangle(image.getWidth(), image.getHeight()));
writer.replacePixels(image, writeParam);
writer.endReplacePixels();

writer.dispose();
TIFFReadTest.assertImagesEqual(image, TIFFReadTest.readTiff(outputFile));
}

@Test
public void writeZSTD() throws IOException {
final File inputFile =TestData.file(this, "test.tif");
Expand Down