mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 03:55:28 -04:00
#25 CMYK JPEG write support
This commit is contained in:
parent
c294c5869c
commit
65a83d76e0
@ -29,6 +29,7 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.ImageWriterBase;
|
import com.twelvemonkeys.imageio.ImageWriterBase;
|
||||||
|
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
||||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||||
|
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.IIOImage;
|
||||||
@ -37,14 +38,19 @@ import javax.imageio.ImageWriteParam;
|
|||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.event.IIOWriteWarningListener;
|
import javax.imageio.event.IIOWriteWarningListener;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.image.Raster;
|
import java.awt.color.ICC_ColorSpace;
|
||||||
import java.awt.image.RenderedImage;
|
import java.awt.color.ICC_Profile;
|
||||||
|
import java.awt.image.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.jpeg.JPEGImage10MetadataCleaner.JAVAX_IMAGEIO_JPEG_IMAGE_1_0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPEGImageWriter
|
* JPEGImageWriter
|
||||||
*
|
*
|
||||||
@ -53,8 +59,6 @@ import java.util.Locale;
|
|||||||
* @version $Id: JPEGImageWriter.java,v 1.0 06.02.12 16:39 haraldk Exp$
|
* @version $Id: JPEGImageWriter.java,v 1.0 06.02.12 16:39 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public final class JPEGImageWriter extends ImageWriterBase {
|
public final class JPEGImageWriter extends ImageWriterBase {
|
||||||
// TODO: Extend with functionality to be able to write CMYK JPEGs as well?
|
|
||||||
|
|
||||||
/** Our JPEG writing delegate */
|
/** Our JPEG writing delegate */
|
||||||
private final ImageWriter delegate;
|
private final ImageWriter delegate;
|
||||||
|
|
||||||
@ -75,11 +79,13 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetMembers() {
|
protected void resetMembers() {
|
||||||
|
delegate.reset();
|
||||||
|
|
||||||
installListeners();
|
installListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOutput(Object output) {
|
public void setOutput(final Object output) {
|
||||||
super.setOutput(output);
|
super.setOutput(output);
|
||||||
|
|
||||||
delegate.setOutput(output);
|
delegate.setOutput(output);
|
||||||
@ -111,32 +117,32 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
|
public IIOMetadata getDefaultStreamMetadata(final ImageWriteParam param) {
|
||||||
return delegate.getDefaultStreamMetadata(param);
|
return delegate.getDefaultStreamMetadata(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
|
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
|
||||||
return delegate.getDefaultImageMetadata(imageType, param);
|
return delegate.getDefaultImageMetadata(imageType, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param) {
|
public IIOMetadata convertStreamMetadata(final IIOMetadata inData, final ImageWriteParam param) {
|
||||||
return delegate.convertStreamMetadata(inData, param);
|
return delegate.convertStreamMetadata(inData, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
|
public IIOMetadata convertImageMetadata(final IIOMetadata inData, final ImageTypeSpecifier imageType, final ImageWriteParam param) {
|
||||||
return delegate.convertImageMetadata(inData, imageType, param);
|
return delegate.convertImageMetadata(inData, imageType, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getNumThumbnailsSupported(ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) {
|
public int getNumThumbnailsSupported(final ImageTypeSpecifier imageType, final ImageWriteParam param, final IIOMetadata streamMetadata, final IIOMetadata imageMetadata) {
|
||||||
return delegate.getNumThumbnailsSupported(imageType, param, streamMetadata, imageMetadata);
|
return delegate.getNumThumbnailsSupported(imageType, param, streamMetadata, imageMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dimension[] getPreferredThumbnailSizes(ImageTypeSpecifier imageType, ImageWriteParam param, IIOMetadata streamMetadata, IIOMetadata imageMetadata) {
|
public Dimension[] getPreferredThumbnailSizes(final ImageTypeSpecifier imageType, final ImageWriteParam param, final IIOMetadata streamMetadata, final IIOMetadata imageMetadata) {
|
||||||
return delegate.getPreferredThumbnailSizes(imageType, param, streamMetadata, imageMetadata);
|
return delegate.getPreferredThumbnailSizes(imageType, param, streamMetadata, imageMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,18 +152,105 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IOException {
|
public void write(final IIOMetadata streamMetadata, final IIOImage image, final ImageWriteParam param) throws IOException {
|
||||||
|
if (isDestinationCMYK(image, param)) {
|
||||||
|
writeCMYK(streamMetadata, image, param);
|
||||||
|
}
|
||||||
|
else {
|
||||||
delegate.write(streamMetadata, image, param);
|
delegate.write(streamMetadata, image, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(IIOImage image) throws IOException {
|
|
||||||
delegate.write(image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean isDestinationCMYK(final IIOImage image, final ImageWriteParam param) {
|
||||||
public void write(RenderedImage image) throws IOException {
|
// If destination type != null, rendered image type doesn't matter
|
||||||
delegate.write(image);
|
return !image.hasRaster() && image.getRenderedImage().getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK
|
||||||
|
|| param != null && param.getDestinationType() != null && param.getDestinationType().getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeCMYK(final IIOMetadata streamMetadata, final IIOImage image, final ImageWriteParam param) throws IOException {
|
||||||
|
RenderedImage renderedImage = image.getRenderedImage();
|
||||||
|
boolean overrideDestination = param != null && param.getDestinationType() != null;
|
||||||
|
ImageTypeSpecifier destinationType = overrideDestination
|
||||||
|
? param.getDestinationType()
|
||||||
|
: ImageTypeSpecifier.createFromRenderedImage(renderedImage);
|
||||||
|
|
||||||
|
ColorSpace cmykCS = destinationType.getColorModel().getColorSpace();
|
||||||
|
|
||||||
|
IIOMetadata metadata = delegate.getDefaultImageMetadata(destinationType, param);
|
||||||
|
|
||||||
|
IIOMetadataNode jpegMeta = new IIOMetadataNode(JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
|
||||||
|
jpegMeta.appendChild(new IIOMetadataNode("JPEGVariety")); // Just leave as default
|
||||||
|
|
||||||
|
IIOMetadataNode markerSequence = new IIOMetadataNode("markerSequence");
|
||||||
|
jpegMeta.appendChild(markerSequence);
|
||||||
|
|
||||||
|
IIOMetadataNode app14Adobe = new IIOMetadataNode("app14Adobe");
|
||||||
|
app14Adobe.setAttribute("transform", "0"); // 0 for CMYK, 2 for YCCK
|
||||||
|
markerSequence.appendChild(app14Adobe);
|
||||||
|
|
||||||
|
if (cmykCS instanceof ICC_ColorSpace) {
|
||||||
|
ICC_Profile profile = ((ICC_ColorSpace) cmykCS).getProfile();
|
||||||
|
byte[] profileData = profile.getData();
|
||||||
|
|
||||||
|
String segmentId = "ICC_PROFILE";
|
||||||
|
int idLength = segmentId.length();
|
||||||
|
byte[] segmentIdBytes = segmentId.getBytes(StandardCharsets.US_ASCII);
|
||||||
|
|
||||||
|
int maxSegmentLength = Short.MAX_VALUE - Short.MIN_VALUE - idLength - 3 - 2;
|
||||||
|
|
||||||
|
int count = (int) Math.ceil(profileData.length / (float) maxSegmentLength);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Insert unknown marker tags, as app2ICC can only be subtag of jpegVariety/JFIF :-P
|
||||||
|
IIOMetadataNode icc = new IIOMetadataNode("unknown");
|
||||||
|
icc.setAttribute("MarkerTag", String.valueOf(JPEG.APP2 & 0xFF));
|
||||||
|
|
||||||
|
int segmentLength = Math.min(maxSegmentLength, profileData.length - i * maxSegmentLength);
|
||||||
|
byte[] data = new byte[idLength + 3 + segmentLength];
|
||||||
|
|
||||||
|
System.arraycopy(segmentIdBytes, 0, data, 0, idLength);
|
||||||
|
data[idLength] = 0; // null-terminator
|
||||||
|
data[idLength + 1] = (byte) (1 + i); // index
|
||||||
|
data[idLength + 2] = (byte) count;
|
||||||
|
System.arraycopy(profileData, i * maxSegmentLength, data, idLength + 3, segmentLength);
|
||||||
|
|
||||||
|
icc.setUserObject(data);
|
||||||
|
|
||||||
|
markerSequence.appendChild(icc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata.mergeTree(JAVAX_IMAGEIO_JPEG_IMAGE_1_0, jpegMeta);
|
||||||
|
|
||||||
|
Raster raster = new InvertedRaster(getRaster(renderedImage));
|
||||||
|
|
||||||
|
// TODO: For YCCK we need oposite conversion
|
||||||
|
// for (int i = 0; i < data.length; i += 4) {
|
||||||
|
// YCbCrConverter.convertYCbCr2RGB(data, data, i);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (overrideDestination) {
|
||||||
|
// Avoid javax.imageio.IIOException: Invalid argument to native writeImage
|
||||||
|
param.setDestinationType(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
delegate.write(streamMetadata, new IIOImage(raster, null, metadata), param);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (overrideDestination) {
|
||||||
|
param.setDestinationType(destinationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Candidate util method
|
||||||
|
private static Raster getRaster(final RenderedImage image) {
|
||||||
|
return image instanceof BufferedImage
|
||||||
|
? ((BufferedImage) image).getRaster()
|
||||||
|
: image.getNumXTiles() == 1 && image.getNumYTiles() == 1
|
||||||
|
? image.getTile(0, 0)
|
||||||
|
: image.getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -166,12 +259,12 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareWriteSequence(IIOMetadata streamMetadata) throws IOException {
|
public void prepareWriteSequence(final IIOMetadata streamMetadata) throws IOException {
|
||||||
delegate.prepareWriteSequence(streamMetadata);
|
delegate.prepareWriteSequence(streamMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToSequence(IIOImage image, ImageWriteParam param) throws IOException {
|
public void writeToSequence(final IIOImage image, final ImageWriteParam param) throws IOException {
|
||||||
delegate.writeToSequence(image, param);
|
delegate.writeToSequence(image, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,37 +279,37 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void replaceStreamMetadata(IIOMetadata streamMetadata) throws IOException {
|
public void replaceStreamMetadata(final IIOMetadata streamMetadata) throws IOException {
|
||||||
delegate.replaceStreamMetadata(streamMetadata);
|
delegate.replaceStreamMetadata(streamMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canReplaceImageMetadata(int imageIndex) throws IOException {
|
public boolean canReplaceImageMetadata(final int imageIndex) throws IOException {
|
||||||
return delegate.canReplaceImageMetadata(imageIndex);
|
return delegate.canReplaceImageMetadata(imageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void replaceImageMetadata(int imageIndex, IIOMetadata imageMetadata) throws IOException {
|
public void replaceImageMetadata(final int imageIndex, final IIOMetadata imageMetadata) throws IOException {
|
||||||
delegate.replaceImageMetadata(imageIndex, imageMetadata);
|
delegate.replaceImageMetadata(imageIndex, imageMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canInsertImage(int imageIndex) throws IOException {
|
public boolean canInsertImage(final int imageIndex) throws IOException {
|
||||||
return delegate.canInsertImage(imageIndex);
|
return delegate.canInsertImage(imageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeInsert(int imageIndex, IIOImage image, ImageWriteParam param) throws IOException {
|
public void writeInsert(final int imageIndex, final IIOImage image, final ImageWriteParam param) throws IOException {
|
||||||
delegate.writeInsert(imageIndex, image, param);
|
delegate.writeInsert(imageIndex, image, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canRemoveImage(int imageIndex) throws IOException {
|
public boolean canRemoveImage(final int imageIndex) throws IOException {
|
||||||
return delegate.canRemoveImage(imageIndex);
|
return delegate.canRemoveImage(imageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeImage(int imageIndex) throws IOException {
|
public void removeImage(final int imageIndex) throws IOException {
|
||||||
delegate.removeImage(imageIndex);
|
delegate.removeImage(imageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +319,10 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareWriteEmpty(IIOMetadata streamMetadata, ImageTypeSpecifier imageType, int width, int height, IIOMetadata imageMetadata, List<? extends BufferedImage> thumbnails, ImageWriteParam param) throws IOException {
|
public void prepareWriteEmpty(final IIOMetadata streamMetadata, final ImageTypeSpecifier imageType,
|
||||||
|
final int width, final int height,
|
||||||
|
final IIOMetadata imageMetadata, final List<? extends BufferedImage> thumbnails,
|
||||||
|
final ImageWriteParam param) throws IOException {
|
||||||
delegate.prepareWriteEmpty(streamMetadata, imageType, width, height, imageMetadata, thumbnails, param);
|
delegate.prepareWriteEmpty(streamMetadata, imageType, width, height, imageMetadata, thumbnails, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,12 +332,15 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canInsertEmpty(int imageIndex) throws IOException {
|
public boolean canInsertEmpty(final int imageIndex) throws IOException {
|
||||||
return delegate.canInsertEmpty(imageIndex);
|
return delegate.canInsertEmpty(imageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareInsertEmpty(int imageIndex, ImageTypeSpecifier imageType, int width, int height, IIOMetadata imageMetadata, List<? extends BufferedImage> thumbnails, ImageWriteParam param) throws IOException {
|
public void prepareInsertEmpty(final int imageIndex, final ImageTypeSpecifier imageType,
|
||||||
|
final int width, final int height,
|
||||||
|
final IIOMetadata imageMetadata, final List<? extends BufferedImage> thumbnails,
|
||||||
|
final ImageWriteParam param) throws IOException {
|
||||||
delegate.prepareInsertEmpty(imageIndex, imageType, width, height, imageMetadata, thumbnails, param);
|
delegate.prepareInsertEmpty(imageIndex, imageType, width, height, imageMetadata, thumbnails, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,22 +350,22 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canReplacePixels(int imageIndex) throws IOException {
|
public boolean canReplacePixels(final int imageIndex) throws IOException {
|
||||||
return delegate.canReplacePixels(imageIndex);
|
return delegate.canReplacePixels(imageIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareReplacePixels(int imageIndex, Rectangle region) throws IOException {
|
public void prepareReplacePixels(final int imageIndex, final Rectangle region) throws IOException {
|
||||||
delegate.prepareReplacePixels(imageIndex, region);
|
delegate.prepareReplacePixels(imageIndex, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void replacePixels(RenderedImage image, ImageWriteParam param) throws IOException {
|
public void replacePixels(final RenderedImage image, final ImageWriteParam param) throws IOException {
|
||||||
delegate.replacePixels(image, param);
|
delegate.replacePixels(image, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void replacePixels(Raster raster, ImageWriteParam param) throws IOException {
|
public void replacePixels(final Raster raster, final ImageWriteParam param) throws IOException {
|
||||||
delegate.replacePixels(raster, param);
|
delegate.replacePixels(raster, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,6 +392,28 @@ public final class JPEGImageWriter extends ImageWriterBase {
|
|||||||
delegate.dispose();
|
delegate.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class, returns sample values inverted,
|
||||||
|
* as CMYK values needs to be written inverted (255 - value).
|
||||||
|
*/
|
||||||
|
private static class InvertedRaster extends WritableRaster {
|
||||||
|
InvertedRaster(final Raster raster) {
|
||||||
|
super(raster.getSampleModel(), new DataBuffer(raster.getDataBuffer().getDataType(), raster.getDataBuffer().getSize()) {
|
||||||
|
private final DataBuffer delegate = raster.getDataBuffer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getElem(final int bank, final int i) {
|
||||||
|
return (255 - delegate.getElem(bank, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setElem(int bank, int i, int val) {
|
||||||
|
throw new UnsupportedOperationException("setElem");
|
||||||
|
}
|
||||||
|
}, new Point());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class ProgressDelegator extends ProgressListenerBase implements IIOWriteWarningListener {
|
private class ProgressDelegator extends ProgressListenerBase implements IIOWriteWarningListener {
|
||||||
@Override
|
@Override
|
||||||
public void imageComplete(ImageWriter source) {
|
public void imageComplete(ImageWriter source) {
|
||||||
|
@ -28,18 +28,35 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTestCase;
|
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.*;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||||
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import javax.imageio.spi.IIORegistry;
|
import javax.imageio.spi.IIORegistry;
|
||||||
import javax.imageio.spi.ImageWriterSpi;
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.color.ColorSpace;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.RenderedImage;
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JPEGImageWriterTest
|
* JPEGImageWriterTest
|
||||||
*
|
*
|
||||||
@ -76,4 +93,88 @@ public class JPEGImageWriterTest extends ImageWriterAbstractTestCase {
|
|||||||
new BufferedImage(32, 20, BufferedImage.TYPE_BYTE_GRAY)
|
new BufferedImage(32, 20, BufferedImage.TYPE_BYTE_GRAY)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReaderForWriter() {
|
||||||
|
ImageWriter writer = createImageWriter();
|
||||||
|
ImageReader reader = ImageIO.getImageReader(writer);
|
||||||
|
assertNotNull(reader);
|
||||||
|
assertEquals(writer.getClass().getPackage(), reader.getClass().getPackage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ByteArrayOutputStream transcode(final ImageReader reader, final URL resource, final ImageWriter writer, int outCSType) throws IOException {
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(resource)) {
|
||||||
|
reader.setInput(input);
|
||||||
|
ImageTypeSpecifier specifier = null;
|
||||||
|
|
||||||
|
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
|
||||||
|
while (types.hasNext()) {
|
||||||
|
ImageTypeSpecifier type = types.next();
|
||||||
|
|
||||||
|
if (type.getColorModel().getColorSpace().getType() == outCSType) {
|
||||||
|
specifier = type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read image with requested color space
|
||||||
|
ImageReadParam readParam = reader.getDefaultReadParam();
|
||||||
|
readParam.setSourceRegion(new Rectangle(Math.min(100, reader.getWidth(0)), Math.min(100, reader.getHeight(0))));
|
||||||
|
readParam.setDestinationType(specifier);
|
||||||
|
IIOImage image = reader.readAll(0, readParam);
|
||||||
|
|
||||||
|
// Write it back
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024);
|
||||||
|
try (ImageOutputStream output = new MemoryCacheImageOutputStream(bytes)) {
|
||||||
|
writer.setOutput(output);
|
||||||
|
ImageWriteParam writeParam = writer.getDefaultWriteParam();
|
||||||
|
writeParam.setDestinationType(specifier);
|
||||||
|
writer.write(null, image, writeParam);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTranscodeWithMetadataRGBtoRGB() throws IOException {
|
||||||
|
ImageWriter writer = createImageWriter();
|
||||||
|
ImageReader reader = ImageIO.getImageReader(writer);
|
||||||
|
|
||||||
|
ByteArrayOutputStream stream = transcode(reader, getClassLoaderResource("/jpeg/jfif-jfxx-thumbnail-olympus-d320l.jpg"), writer, ColorSpace.TYPE_RGB);
|
||||||
|
|
||||||
|
// FileUtil.write(new File("/Downloads/foo-rgb.jpg"), stream.toByteArray());
|
||||||
|
|
||||||
|
// TODO: Validate that correct warnings are emitted (if any are needed?)
|
||||||
|
|
||||||
|
reader.reset();
|
||||||
|
reader.setInput(new ByteArrayImageInputStream(stream.toByteArray()));
|
||||||
|
BufferedImage image = reader.read(0);
|
||||||
|
assertNotNull(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTranscodeWithMetadataCMYKtoCMYK() throws IOException {
|
||||||
|
ImageWriter writer = createImageWriter();
|
||||||
|
ImageReader reader = ImageIO.getImageReader(writer);
|
||||||
|
|
||||||
|
// TODO: Find a smaller test sample, to waste less time?
|
||||||
|
ByteArrayOutputStream stream = transcode(reader, getClassLoaderResource("/jpeg/cmyk-sample-multiple-chunk-icc.jpg"), writer, ColorSpace.TYPE_CMYK);
|
||||||
|
|
||||||
|
reader.reset();
|
||||||
|
reader.setInput(new ByteArrayImageInputStream(stream.toByteArray()));
|
||||||
|
|
||||||
|
BufferedImage image = reader.read(0);
|
||||||
|
assertNotNull(image);
|
||||||
|
assertEquals(100, image.getWidth());
|
||||||
|
assertEquals(100, image.getHeight());
|
||||||
|
|
||||||
|
IIOMetadata metadata = reader.getImageMetadata(0);
|
||||||
|
IIOMetadataNode standard = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||||
|
NodeList colorSpaceType = standard.getElementsByTagName("ColorSpaceType");
|
||||||
|
assertEquals(1, colorSpaceType.getLength());
|
||||||
|
assertEquals("CMYK", ((IIOMetadataNode) colorSpaceType.item(0)).getAttribute("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: YCCK
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user