mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-27 00:00:02 -04:00
Major rework/standardization:
* DDSEncoderType, DX10DXGIFormat merged with DDSType for a single way to describe a DDS format * Added constants for DXGI formats * DDSImageWriteParam is now mutable and supports standard way of setting compression type * DDSImageMetadata now supports more of the format Performance: * DDSReader now use seek() to jump to correct mipmap instead of reading all bytes * DDSImageWriter now uses getTile(0, 0) instead of getData() for better performance
This commit is contained in:
+3
-2
@@ -23,6 +23,7 @@ import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
* {@link ImageTypeSpecifier}.
|
||||
* Other values or overrides may be specified using the builder.
|
||||
*
|
||||
* @see <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/javax/imageio/metadata/doc-files/standard_metadata.html">Standard (Plug-in Neutral) Metadata Format Specification</a>
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
public class StandardImageMetadataSupport extends AbstractMetadata {
|
||||
@@ -79,11 +80,11 @@ public class StandardImageMetadataSupport extends AbstractMetadata {
|
||||
textEntries = builder.textEntries;
|
||||
}
|
||||
|
||||
public static Builder builder(ImageTypeSpecifier type) {
|
||||
protected static Builder builder(ImageTypeSpecifier type) {
|
||||
return new Builder(type);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
protected static class Builder {
|
||||
private final ImageTypeSpecifier type;
|
||||
private ColorSpaceType colorSpaceType;
|
||||
private boolean blackIsZero = true;
|
||||
|
||||
@@ -35,6 +35,8 @@ import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
import javax.imageio.IIOParam;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.spi.IIOServiceProvider;
|
||||
import javax.imageio.spi.ServiceRegistry;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
@@ -45,7 +47,9 @@ import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -359,4 +363,109 @@ public final class IIOUtil {
|
||||
System.arraycopy(srcRow, srcPos + x, destRow, destPos + x / samplePeriod, pixelStride);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all the standard param values from source to destination.
|
||||
* <p>
|
||||
* Typical use (in some imaginary {@code FooImageWriter} class):
|
||||
* <pre>
|
||||
* ImageWriteParam param = ...
|
||||
* FooImageWriteparam fooParam = param instanceof FooImageWriteParam
|
||||
* ? (FooImageWriteParam) param
|
||||
* : copyStandardParams(param, getDefaultWriteParam());
|
||||
* </pre>
|
||||
* </p>
|
||||
* May also be useful for {@code ImageReader}s that delegate reading to other plugins
|
||||
* (like a TIFF plugin delegating JPEG format decoding to a {@code JPEGImageReader}).
|
||||
*
|
||||
* @param source the source parameter, may be {@code null}
|
||||
* @param destination the destination parameter
|
||||
* @return destination
|
||||
*
|
||||
* @param <T> the plugin specific subclass of {@code IIOParam}
|
||||
*
|
||||
* @throws NullPointerException if destination is {@code null}
|
||||
*/
|
||||
public static <T extends IIOParam> T copyStandardParams(IIOParam source, T destination) {
|
||||
Objects.requireNonNull(destination);
|
||||
Validate.isTrue(source != destination, "source must be different from destination");
|
||||
|
||||
if (source != null) {
|
||||
destination.setController(source.getController());
|
||||
destination.setSourceSubsampling(
|
||||
source.getSourceXSubsampling(), source.getSourceYSubsampling(),
|
||||
source.getSubsamplingXOffset(), source.getSubsamplingYOffset()
|
||||
);
|
||||
destination.setSourceRegion(source.getSourceRegion());
|
||||
destination.setSourceBands(source.getSourceBands());
|
||||
destination.setDestinationOffset(source.getDestinationOffset());
|
||||
destination.setDestinationType(source.getDestinationType());
|
||||
|
||||
// TODO: API & usage... Is it ever useful to copy from a read to a write param or vice versa?
|
||||
// If not, maybe throw an IllegalArgumentException instead
|
||||
|
||||
if (source instanceof ImageReadParam && destination instanceof ImageReadParam) {
|
||||
ImageReadParam sourceReadParam = (ImageReadParam) source;
|
||||
ImageReadParam destinationReadParam = (ImageReadParam) destination;
|
||||
|
||||
destinationReadParam.setDestination(sourceReadParam.getDestination());
|
||||
destinationReadParam.setDestinationBands(sourceReadParam.getDestinationBands());
|
||||
|
||||
if (destinationReadParam.canSetSourceRenderSize()) {
|
||||
destinationReadParam.setSourceRenderSize(sourceReadParam.getSourceRenderSize());
|
||||
}
|
||||
|
||||
destinationReadParam.setSourceProgressivePasses(
|
||||
sourceReadParam.getSourceMinProgressivePass(),
|
||||
sourceReadParam.getSourceMaxProgressivePass()
|
||||
);
|
||||
}
|
||||
|
||||
if (source instanceof ImageWriteParam && destination instanceof ImageWriteParam) {
|
||||
ImageWriteParam sourceWriteParam = (ImageWriteParam) source;
|
||||
ImageWriteParam destinationWriteParam = (ImageWriteParam) destination;
|
||||
|
||||
// TODO: Usage... It's very unlikely that compression settings of one plugin is compatible with another...
|
||||
// Is the the below useful?
|
||||
// Also, is it okay to just silently ignore settings from one format that isn't compatible with another?
|
||||
|
||||
// Quirky API, we can't query for compression mode, unless source.canWriteCompressed is true...
|
||||
if (sourceWriteParam.canWriteCompressed() && destinationWriteParam.canWriteCompressed()) {
|
||||
int compressionMode = sourceWriteParam.getCompressionMode();
|
||||
destinationWriteParam.setCompressionMode(compressionMode);
|
||||
|
||||
if (compressionMode == ImageWriteParam.MODE_EXPLICIT
|
||||
&& sourceWriteParam.getCompressionType() != null) {
|
||||
if (Arrays.asList(destinationWriteParam.getCompressionTypes())
|
||||
.contains(sourceWriteParam.getCompressionType())) {
|
||||
destinationWriteParam.setCompressionType(sourceWriteParam.getCompressionType());
|
||||
destinationWriteParam.setCompressionQuality(sourceWriteParam.getCompressionQuality());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceWriteParam.canWriteProgressive() && destinationWriteParam.canWriteProgressive()) {
|
||||
destinationWriteParam.setProgressiveMode(sourceWriteParam.getProgressiveMode());
|
||||
}
|
||||
if (sourceWriteParam.canWriteTiles() && destinationWriteParam.canWriteTiles()) {
|
||||
int tilingMode = sourceWriteParam.getTilingMode();
|
||||
destinationWriteParam.setTilingMode(tilingMode);
|
||||
|
||||
if (tilingMode == ImageWriteParam.MODE_EXPLICIT) {
|
||||
// TODO: What if source can offset (and has offsets) and dest can't? Is it ok to just ignore the setting?
|
||||
boolean canWriteOffsetTiles =
|
||||
sourceWriteParam.canOffsetTiles() && destinationWriteParam.canOffsetTiles();
|
||||
|
||||
destinationWriteParam.setTiling(
|
||||
sourceWriteParam.getTileWidth(), sourceWriteParam.getTileHeight(),
|
||||
canWriteOffsetTiles ? sourceWriteParam.getTileGridXOffset() : 0,
|
||||
canWriteOffsetTiles ? sourceWriteParam.getTileGridYOffset() : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,20 @@
|
||||
package com.twelvemonkeys.imageio.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.function.Executable;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.ImageWriteParam;
|
||||
import javax.imageio.plugins.bmp.BMPImageWriteParam;
|
||||
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
|
||||
|
||||
/**
|
||||
* IIOUtilTest
|
||||
*/
|
||||
@@ -204,4 +216,220 @@ public class IIOUtilTest {
|
||||
private int divCeil(int numerator, int denominator) {
|
||||
return (numerator + denominator - 1) / denominator;
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsDestinationNull() {
|
||||
assertThrows(NullPointerException.class, () -> IIOUtil.copyStandardParams(null, null));
|
||||
assertThrows(NullPointerException.class, () -> IIOUtil.copyStandardParams(new ImageReadParam(), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsSame() {
|
||||
ImageReadParam param = new ImageReadParam();
|
||||
assertThrows(IllegalArgumentException.class, () -> IIOUtil.copyStandardParams(param, param));
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsSourceNull() {
|
||||
ImageReadParam param = new ImageReadParam() {
|
||||
@Override
|
||||
public void setSourceRegion(Rectangle sourceRegion) {
|
||||
fail("Should not be invoked");
|
||||
}
|
||||
};
|
||||
|
||||
assertSame(param, IIOUtil.copyStandardParams(null, param));
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsImageReadParam() {
|
||||
int sourceXSubsampling = 3;
|
||||
int sourceYSubsampling = 4;
|
||||
int subsamplingXOffset = 1;
|
||||
int subsamplingYOffset = 2;
|
||||
Rectangle sourceRegion = new Rectangle(1, 2, 42, 43);
|
||||
int[] sourceBands = { 0, 1, 2 };
|
||||
|
||||
Point destinationOffset = new Point(7, 9);
|
||||
int[] destinationBands = { 2, 1, 0 };
|
||||
|
||||
ImageReadParam sourceParam = new ImageReadParam();
|
||||
sourceParam.setSourceRegion(sourceRegion);
|
||||
sourceParam.setSourceSubsampling(sourceXSubsampling, sourceYSubsampling, subsamplingXOffset, subsamplingYOffset);
|
||||
sourceParam.setSourceBands(sourceBands);
|
||||
|
||||
sourceParam.setDestinationOffset(destinationOffset);
|
||||
sourceParam.setDestinationBands(destinationBands);
|
||||
|
||||
JPEGImageReadParam jpegParam = IIOUtil.copyStandardParams(sourceParam, new JPEGImageReadParam());
|
||||
|
||||
assertEquals(sourceRegion, jpegParam.getSourceRegion());
|
||||
assertEquals(sourceXSubsampling, jpegParam.getSourceXSubsampling());
|
||||
assertEquals(sourceYSubsampling, jpegParam.getSourceYSubsampling());
|
||||
assertEquals(subsamplingXOffset, jpegParam.getSubsamplingXOffset());
|
||||
assertEquals(subsamplingYOffset, jpegParam.getSubsamplingYOffset());
|
||||
assertArrayEquals(sourceBands, jpegParam.getSourceBands());
|
||||
|
||||
assertEquals(destinationOffset, jpegParam.getDestinationOffset());
|
||||
assertArrayEquals(destinationBands, jpegParam.getDestinationBands());
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsImageReadParamDestination() {
|
||||
// Destination and destination type is mutually exclusive
|
||||
BufferedImage destination = new BufferedImage(2, 2, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
ImageReadParam sourceParam = new ImageReadParam();
|
||||
sourceParam.setDestination(destination);
|
||||
|
||||
assertEquals(destination, IIOUtil.copyStandardParams(sourceParam, new JPEGImageReadParam()).getDestination());
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsImageReadParamDestinationType() {
|
||||
// Destination and destination type is mutually exclusive
|
||||
ImageTypeSpecifier destinationType = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
|
||||
|
||||
ImageReadParam sourceParam = new ImageReadParam();
|
||||
sourceParam.setDestinationType(destinationType);
|
||||
|
||||
assertEquals(destinationType, IIOUtil.copyStandardParams(sourceParam, new JPEGImageReadParam()).getDestinationType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsReadToWrite() {
|
||||
int sourceXSubsampling = 3;
|
||||
int sourceYSubsampling = 4;
|
||||
int subsamplingXOffset = 1;
|
||||
int subsamplingYOffset = 2;
|
||||
Rectangle sourceRegion = new Rectangle(1, 2, 42, 43);
|
||||
int[] sourceBands = { 0, 1, 2 };
|
||||
|
||||
Point destinationOffset = new Point(7, 9);
|
||||
|
||||
ImageWriteParam sourceParam = new ImageWriteParam(null);
|
||||
sourceParam.setSourceRegion(sourceRegion);
|
||||
sourceParam.setSourceSubsampling(sourceXSubsampling, sourceYSubsampling, subsamplingXOffset, subsamplingYOffset);
|
||||
sourceParam.setSourceBands(sourceBands);
|
||||
|
||||
sourceParam.setDestinationOffset(destinationOffset);
|
||||
|
||||
JPEGImageReadParam jpegParam = IIOUtil.copyStandardParams(sourceParam, new JPEGImageReadParam());
|
||||
|
||||
assertEquals(sourceRegion, jpegParam.getSourceRegion());
|
||||
assertEquals(sourceXSubsampling, jpegParam.getSourceXSubsampling());
|
||||
assertEquals(sourceYSubsampling, jpegParam.getSourceYSubsampling());
|
||||
assertEquals(subsamplingXOffset, jpegParam.getSubsamplingXOffset());
|
||||
assertEquals(subsamplingYOffset, jpegParam.getSubsamplingYOffset());
|
||||
assertArrayEquals(sourceBands, jpegParam.getSourceBands());
|
||||
|
||||
assertEquals(destinationOffset, jpegParam.getDestinationOffset());
|
||||
assertNull(jpegParam.getDestinationBands()); // Only in read param
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsImageWriteParam() {
|
||||
int sourceXSubsampling = 3;
|
||||
int sourceYSubsampling = 4;
|
||||
int subsamplingXOffset = 1;
|
||||
int subsamplingYOffset = 2;
|
||||
Rectangle sourceRegion = new Rectangle(1, 2, 42, 43);
|
||||
int[] sourceBands = { 0, 1, 2 };
|
||||
|
||||
Point destinationOffset = new Point(7, 9);
|
||||
|
||||
ImageWriteParam sourceParam = new ImageWriteParam(null);
|
||||
sourceParam.setSourceRegion(sourceRegion);
|
||||
sourceParam.setSourceSubsampling(sourceXSubsampling, sourceYSubsampling, subsamplingXOffset, subsamplingYOffset);
|
||||
sourceParam.setSourceBands(sourceBands);
|
||||
|
||||
sourceParam.setDestinationOffset(destinationOffset);
|
||||
|
||||
BMPImageWriteParam fooParam = IIOUtil.copyStandardParams(sourceParam, new BMPImageWriteParam());
|
||||
|
||||
assertEquals(sourceRegion, fooParam.getSourceRegion());
|
||||
assertEquals(sourceXSubsampling, fooParam.getSourceXSubsampling());
|
||||
assertEquals(sourceYSubsampling, fooParam.getSourceYSubsampling());
|
||||
assertEquals(subsamplingXOffset, fooParam.getSubsamplingXOffset());
|
||||
assertEquals(subsamplingYOffset, fooParam.getSubsamplingYOffset());
|
||||
assertArrayEquals(sourceBands, fooParam.getSourceBands());
|
||||
|
||||
assertEquals(destinationOffset, fooParam.getDestinationOffset());
|
||||
}
|
||||
|
||||
@Test
|
||||
void copyStandardParamsImageWriteParamEverything() {
|
||||
int sourceXSubsampling = 3;
|
||||
int sourceYSubsampling = 4;
|
||||
int subsamplingXOffset = 1;
|
||||
int subsamplingYOffset = 2;
|
||||
Rectangle sourceRegion = new Rectangle(1, 2, 42, 43);
|
||||
int[] sourceBands = { 0, 1, 2 };
|
||||
|
||||
Point destinationOffset = new Point(7, 9);
|
||||
|
||||
String compressionType = "Foo";
|
||||
float quality = 0.42f;
|
||||
|
||||
ImageWriteParam sourceParam = new ImageWriteParam() {
|
||||
{
|
||||
canWriteProgressive = true;
|
||||
|
||||
canWriteTiles = true;
|
||||
canOffsetTiles = true;
|
||||
|
||||
canWriteCompressed = true;
|
||||
compressionTypes = new String[] { "Foo", "Bar" };
|
||||
}
|
||||
};
|
||||
sourceParam.setSourceRegion(sourceRegion);
|
||||
sourceParam.setSourceSubsampling(sourceXSubsampling, sourceYSubsampling, subsamplingXOffset, subsamplingYOffset);
|
||||
sourceParam.setSourceBands(sourceBands);
|
||||
|
||||
sourceParam.setDestinationOffset(destinationOffset);
|
||||
|
||||
sourceParam.setProgressiveMode(ImageWriteParam.MODE_DEFAULT); // Default is COPY_FROM_METADATA...
|
||||
sourceParam.setTilingMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
sourceParam.setTiling(1, 2, 3, 4);
|
||||
sourceParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||
sourceParam.setCompressionType(compressionType);
|
||||
sourceParam.setCompressionQuality(quality);
|
||||
|
||||
FooImageWriteParam fooParam = IIOUtil.copyStandardParams(sourceParam, new FooImageWriteParam());
|
||||
|
||||
assertEquals(sourceRegion, fooParam.getSourceRegion());
|
||||
assertEquals(sourceXSubsampling, fooParam.getSourceXSubsampling());
|
||||
assertEquals(sourceYSubsampling, fooParam.getSourceYSubsampling());
|
||||
assertEquals(subsamplingXOffset, fooParam.getSubsamplingXOffset());
|
||||
assertEquals(subsamplingYOffset, fooParam.getSubsamplingYOffset());
|
||||
assertArrayEquals(sourceBands, fooParam.getSourceBands());
|
||||
|
||||
assertEquals(destinationOffset, fooParam.getDestinationOffset());
|
||||
|
||||
assertEquals(ImageWriteParam.MODE_DEFAULT, fooParam.getProgressiveMode());
|
||||
|
||||
assertEquals(ImageWriteParam.MODE_EXPLICIT, fooParam.getTilingMode());
|
||||
assertEquals(1, fooParam.getTileWidth());
|
||||
assertEquals(2, fooParam.getTileHeight());
|
||||
assertEquals(3, fooParam.getTileGridXOffset());
|
||||
assertEquals(4, fooParam.getTileGridYOffset());
|
||||
|
||||
assertEquals(ImageWriteParam.MODE_EXPLICIT, fooParam.getCompressionMode());
|
||||
assertEquals(compressionType, fooParam.getCompressionType());
|
||||
assertEquals(quality, fooParam.getCompressionQuality());
|
||||
}
|
||||
|
||||
// A basic param that supports "everything"
|
||||
static class FooImageWriteParam extends ImageWriteParam {
|
||||
FooImageWriteParam() {
|
||||
canWriteProgressive = true;
|
||||
|
||||
canWriteTiles = true;
|
||||
canOffsetTiles = true;
|
||||
|
||||
canWriteCompressed = true;
|
||||
compressionType = "Unset";
|
||||
compressionTypes = new String[] { "Bar", "Foo" };
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user