diff --git a/settings.gradle.kts b/settings.gradle.kts index df2a39d..91cbc22 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,7 @@ pluginManagement { rootProject.name = "qupath-imglib2" // Used for version catalogs (including Java compatibility) -val qupathVersion = "0.6.0" +val qupathVersion = "0.7.0" val sciJavaVersion = "43.0.0" dependencyResolutionManagement { diff --git a/src/main/java/qupath/ext/imglib2/accesses/AccessTools.java b/src/main/java/qupath/ext/imglib2/accesses/AccessTools.java index fd37643..5a0d631 100644 --- a/src/main/java/qupath/ext/imglib2/accesses/AccessTools.java +++ b/src/main/java/qupath/ext/imglib2/accesses/AccessTools.java @@ -45,19 +45,16 @@ public static boolean isSampleModelDirectlyUsable(Raster raster) { * @return the size of the provided data buffer in bytes */ public static int getSizeOfDataBufferInBytes(DataBuffer dataBuffer) { - int bytesPerPixel; - if (dataBuffer instanceof DataBufferByte) { - bytesPerPixel = 1; - } else if (dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { - bytesPerPixel = 2; - } else if (dataBuffer instanceof DataBufferInt || dataBuffer instanceof DataBufferFloat) { - bytesPerPixel = 4; - } else if (dataBuffer instanceof DataBufferDouble) { - bytesPerPixel = 8; - } else { - logger.warn("Unexpected data buffer {}. Considering each element of it takes 1 byte", dataBuffer); - bytesPerPixel = 1; - } + int bytesPerPixel = switch (dataBuffer) { + case DataBufferByte _ -> 1; + case DataBufferShort _, DataBufferUShort _ -> 2; + case DataBufferInt _, DataBufferFloat _ -> 4; + case DataBufferDouble _ -> 8; + default -> { + logger.warn("Unexpected data buffer {}. Considering each element of it takes 1 byte", dataBuffer); + yield 1; + } + }; return bytesPerPixel * dataBuffer.getSize() * dataBuffer.getNumBanks(); } diff --git a/src/main/java/qupath/ext/imglib2/accesses/ArgbBufferedImageAccess.java b/src/main/java/qupath/ext/imglib2/accesses/ArgbBufferedImageAccess.java index 6c359ea..8dd1223 100644 --- a/src/main/java/qupath/ext/imglib2/accesses/ArgbBufferedImageAccess.java +++ b/src/main/java/qupath/ext/imglib2/accesses/ArgbBufferedImageAccess.java @@ -1,6 +1,6 @@ package qupath.ext.imglib2.accesses; -import net.imglib2.img.basictypeaccess.IntAccess; +import net.imglib2.img.basictypeaccess.array.IntArray; import net.imglib2.img.basictypeaccess.volatiles.VolatileAccess; import qupath.ext.imglib2.SizableDataAccess; import qupath.lib.common.ColorTools; @@ -8,27 +8,22 @@ import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; +import java.awt.image.Raster; import java.awt.image.SinglePixelPackedSampleModel; /** - * An {@link IntAccess} whose elements are computed from an (A)RGB {@link BufferedImage}. + * An {@link IntArray} whose elements are computed from an (A)RGB {@link BufferedImage}. *

* If the alpha component is not provided (e.g. if the {@link BufferedImage} has the {@link BufferedImage#TYPE_INT_RGB} type), * then the alpha component of each pixel is considered to be 255. *

- * This {@link IntAccess} is immutable; any attempt to changes its values will result in a + * This {@link IntArray} is immutable; any attempt to changes its values will result in a * {@link UnsupportedOperationException}. *

* This data access is marked as volatile but always contain valid data. */ -public class ArgbBufferedImageAccess implements IntAccess, SizableDataAccess, VolatileAccess { +public class ArgbBufferedImageAccess extends IntArray implements SizableDataAccess, VolatileAccess { - private final BufferedImage image; - private final DataBuffer dataBuffer; - private final int width; - private final int planeSize; - private final boolean canUseDataBuffer; - private final boolean alphaProvided; private final int size; /** @@ -38,39 +33,45 @@ public class ArgbBufferedImageAccess implements IntAccess, SizableDataAccess, Vo * @throws NullPointerException if the provided image is null */ public ArgbBufferedImageAccess(BufferedImage image) { - this.image = image; - this.dataBuffer = this.image.getRaster().getDataBuffer(); + super(createArrayFromImage(image)); - this.width = this.image.getWidth(); - this.planeSize = width * this.image.getHeight(); - - this.canUseDataBuffer = image.getRaster().getDataBuffer() instanceof DataBufferInt && - image.getRaster().getSampleModel() instanceof SinglePixelPackedSampleModel; - this.alphaProvided = image.getType() == BufferedImage.TYPE_INT_ARGB; - - this.size = AccessTools.getSizeOfDataBufferInBytes(this.dataBuffer); + this.size = AccessTools.getSizeOfDataBufferInBytes(image.getRaster().getDataBuffer()); } - @Override - public int getValue(int index) { - int xyIndex = index % planeSize; + private static int[] createArrayFromImage(BufferedImage image) { + Raster raster = image.getRaster(); + int width = raster.getWidth(); + int height = raster.getHeight(); + int planeSize = width * height; - if (canUseDataBuffer) { - int pixel = dataBuffer.getElem(0, xyIndex); + int[] array = new int[planeSize]; + if (raster.getSampleModel() instanceof SinglePixelPackedSampleModel && raster.getDataBuffer() instanceof DataBufferInt) { + DataBuffer dataBuffer = raster.getDataBuffer(); + boolean alphaProvided = image.getType() == BufferedImage.TYPE_INT_ARGB; - if (alphaProvided) { - return pixel; - } else { - return ColorTools.packARGB( - 255, - ColorTools.red(pixel), - ColorTools.green(pixel), - ColorTools.blue(pixel) - ); + for (int i=0; i * The alpha component is not taken into account. *

- * This {@link ByteAccess} is immutable; any attempt to changes its values will result in a + * This {@link ByteArray} is immutable; any attempt to changes its values will result in a * {@link UnsupportedOperationException}. *

* This data access is marked as volatile but always contain valid data. */ -public class ByteBufferedImageAccess implements ByteAccess, SizableDataAccess, VolatileAccess { +public class ByteBufferedImageAccess extends ByteArray implements SizableDataAccess, VolatileAccess { - private final BufferedImage image; - private final DataBuffer dataBuffer; - private final int width; - private final int planeSize; - private final boolean canUseDataBuffer; private final int size; /** @@ -36,33 +32,9 @@ public class ByteBufferedImageAccess implements ByteAccess, SizableDataAccess, V * @throws NullPointerException if the provided image is null */ public ByteBufferedImageAccess(BufferedImage image) { - this.image = image; - this.dataBuffer = this.image.getRaster().getDataBuffer(); + super(createArrayFromImage(image)); - this.width = this.image.getWidth(); - this.planeSize = width * this.image.getHeight(); - - this.canUseDataBuffer = image.getRaster().getDataBuffer() instanceof DataBufferInt && - image.getRaster().getSampleModel() instanceof SinglePixelPackedSampleModel; - - this.size = AccessTools.getSizeOfDataBufferInBytes(this.dataBuffer); - } - - @Override - public byte getValue(int index) { - int channel = index / planeSize; - int xyIndex = index % planeSize; - - int pixel = canUseDataBuffer ? - dataBuffer.getElem(0, xyIndex) : - image.getRGB(xyIndex % width, xyIndex / width); - - return switch (channel) { - case 0 -> (byte) ColorTools.red(pixel); - case 1 -> (byte) ColorTools.green(pixel); - case 2 -> (byte) ColorTools.blue(pixel); - default -> throw new IllegalArgumentException(String.format("The provided index %d is out of bounds", index)); - }; + this.size = AccessTools.getSizeOfDataBufferInBytes(image.getRaster().getDataBuffer()); } @Override @@ -79,4 +51,47 @@ public int getSizeBytes() { public boolean isValid() { return true; } + + private static byte[] createArrayFromImage(BufferedImage image) { + Raster raster = image.getRaster(); + int width = raster.getWidth(); + int height = raster.getHeight(); + int planeSize = width * height; + int numBands = 3; + + byte[] array = new byte[planeSize * numBands]; + if (raster.getSampleModel() instanceof SinglePixelPackedSampleModel && raster.getDataBuffer() instanceof DataBufferInt) { + DataBuffer dataBuffer = raster.getDataBuffer(); + + for (int b=0; b ColorTools.red(pixel); + case 1 -> ColorTools.green(pixel); + case 2 -> ColorTools.blue(pixel); + default -> throw new IllegalArgumentException(String.format("The provided channel %d is out of bounds", b)); + }; + } + } + } else { + for (int b=0; b ColorTools.red(pixel); + case 1 -> ColorTools.green(pixel); + case 2 -> ColorTools.blue(pixel); + default -> throw new IllegalArgumentException(String.format("The provided channel %d is out of bounds", b)); + }; + } + } + } + } + + return array; + } } diff --git a/src/main/java/qupath/ext/imglib2/accesses/ByteRasterAccess.java b/src/main/java/qupath/ext/imglib2/accesses/ByteRasterAccess.java index 299f394..fd7b2cf 100644 --- a/src/main/java/qupath/ext/imglib2/accesses/ByteRasterAccess.java +++ b/src/main/java/qupath/ext/imglib2/accesses/ByteRasterAccess.java @@ -1,6 +1,6 @@ package qupath.ext.imglib2.accesses; -import net.imglib2.img.basictypeaccess.ByteAccess; +import net.imglib2.img.basictypeaccess.array.ByteArray; import net.imglib2.img.basictypeaccess.volatiles.VolatileAccess; import qupath.ext.imglib2.SizableDataAccess; @@ -9,20 +9,15 @@ import java.awt.image.Raster; /** - * A {@link ByteAccess} whose elements are computed from a {@link Raster}. + * A {@link ByteArray} whose elements are computed from a {@link Raster}. *

- * This {@link ByteAccess} is immutable; any attempt to changes its values will result in a + * This {@link ByteArray} is immutable; any attempt to changes its values will result in a * {@link UnsupportedOperationException}. *

* This data access is marked as volatile but always contain valid data. */ -public class ByteRasterAccess implements ByteAccess, SizableDataAccess, VolatileAccess { +public class ByteRasterAccess extends ByteArray implements SizableDataAccess, VolatileAccess { - private final Raster raster; - private final DataBuffer dataBuffer; - private final int width; - private final int planeSize; - private final boolean canUseDataBuffer; private final int size; /** @@ -32,28 +27,9 @@ public class ByteRasterAccess implements ByteAccess, SizableDataAccess, Volatile * @throws NullPointerException if the provided image is null */ public ByteRasterAccess(Raster raster) { - this.raster = raster; - this.dataBuffer = this.raster.getDataBuffer(); + super(createArrayFromRaster(raster)); - this.width = this.raster.getWidth(); - this.planeSize = width * this.raster.getHeight(); - - this.canUseDataBuffer = this.dataBuffer instanceof DataBufferByte && - AccessTools.isSampleModelDirectlyUsable(this.raster); - - this.size = AccessTools.getSizeOfDataBufferInBytes(this.dataBuffer); - } - - @Override - public byte getValue(int index) { - int b = index / planeSize; - int xyIndex = index % planeSize; - - if (canUseDataBuffer) { - return (byte) dataBuffer.getElem(b, xyIndex); - } else { - return (byte) raster.getSample(xyIndex % width, xyIndex / width, b); - } + this.size = AccessTools.getSizeOfDataBufferInBytes(raster.getDataBuffer()); } @Override @@ -70,4 +46,32 @@ public int getSizeBytes() { public boolean isValid() { return true; } + + private static byte[] createArrayFromRaster(Raster raster) { + int width = raster.getWidth(); + int height = raster.getHeight(); + int planeSize = width * height; + int numBands = raster.getNumBands(); + + byte[] array = new byte[planeSize * numBands]; + if (AccessTools.isSampleModelDirectlyUsable(raster) && raster.getDataBuffer() instanceof DataBufferByte) { + DataBuffer dataBuffer = raster.getDataBuffer(); + + for (int b=0; b - * This {@link DoubleAccess} is immutable; any attempt to changes its values will result in a + * This {@link DoubleArray} is immutable; any attempt to changes its values will result in a * {@link UnsupportedOperationException}. *

* This data access is marked as volatile but always contain valid data. */ -public class DoubleRasterAccess implements DoubleAccess, SizableDataAccess, VolatileAccess { +public class DoubleRasterAccess extends DoubleArray implements SizableDataAccess, VolatileAccess { - private final Raster raster; - private final DataBuffer dataBuffer; - private final int width; - private final int planeSize; - private final boolean canUseDataBuffer; private final int size; /** @@ -32,28 +27,9 @@ public class DoubleRasterAccess implements DoubleAccess, SizableDataAccess, Vola * @throws NullPointerException if the provided image is null */ public DoubleRasterAccess(Raster raster) { - this.raster = raster; - this.dataBuffer = this.raster.getDataBuffer(); + super(createArrayFromRaster(raster)); - this.width = this.raster.getWidth(); - this.planeSize = width * this.raster.getHeight(); - - this.canUseDataBuffer = this.dataBuffer instanceof DataBufferDouble && - AccessTools.isSampleModelDirectlyUsable(this.raster); - - this.size = AccessTools.getSizeOfDataBufferInBytes(this.dataBuffer); - } - - @Override - public double getValue(int index) { - int b = index / planeSize; - int xyIndex = index % planeSize; - - if (canUseDataBuffer) { - return dataBuffer.getElemDouble(b, xyIndex); - } else { - return raster.getSampleDouble(xyIndex % width, xyIndex / width, b); - } + this.size = AccessTools.getSizeOfDataBufferInBytes(raster.getDataBuffer()); } @Override @@ -70,4 +46,32 @@ public int getSizeBytes() { public boolean isValid() { return true; } + + private static double[] createArrayFromRaster(Raster raster) { + int width = raster.getWidth(); + int height = raster.getHeight(); + int planeSize = width * height; + int numBands = raster.getNumBands(); + + double[] array = new double[planeSize * numBands]; + if (AccessTools.isSampleModelDirectlyUsable(raster) && raster.getDataBuffer() instanceof DataBufferDouble) { + DataBuffer dataBuffer = raster.getDataBuffer(); + + for (int b=0; b - * This {@link FloatAccess} is immutable; any attempt to changes its values will result in a + * This {@link FloatArray} is immutable; any attempt to changes its values will result in a * {@link UnsupportedOperationException}. *

* This data access is marked as volatile but always contain valid data. */ -public class FloatRasterAccess implements FloatAccess, SizableDataAccess, VolatileAccess { +public class FloatRasterAccess extends FloatArray implements SizableDataAccess, VolatileAccess { - private final Raster raster; - private final DataBuffer dataBuffer; - private final int width; - private final int planeSize; - private final boolean canUseDataBuffer; private final int size; /** @@ -32,28 +27,9 @@ public class FloatRasterAccess implements FloatAccess, SizableDataAccess, Volati * @throws NullPointerException if the provided image is null */ public FloatRasterAccess(Raster raster) { - this.raster = raster; - this.dataBuffer = this.raster.getDataBuffer(); + super(createArrayFromRaster(raster)); - this.width = this.raster.getWidth(); - this.planeSize = width * this.raster.getHeight(); - - this.canUseDataBuffer = this.dataBuffer instanceof DataBufferFloat && - AccessTools.isSampleModelDirectlyUsable(this.raster); - - this.size = AccessTools.getSizeOfDataBufferInBytes(this.dataBuffer); - } - - @Override - public float getValue(int index) { - int b = index / planeSize; - int xyIndex = index % planeSize; - - if (canUseDataBuffer) { - return dataBuffer.getElemFloat(b, xyIndex); - } else { - return raster.getSampleFloat(xyIndex % width, xyIndex / width, b); - } + this.size = AccessTools.getSizeOfDataBufferInBytes(raster.getDataBuffer()); } @Override @@ -70,4 +46,32 @@ public int getSizeBytes() { public boolean isValid() { return true; } + + private static float[] createArrayFromRaster(Raster raster) { + int width = raster.getWidth(); + int height = raster.getHeight(); + int planeSize = width * height; + int numBands = raster.getNumBands(); + + float[] array = new float[planeSize * numBands]; + if (AccessTools.isSampleModelDirectlyUsable(raster) && raster.getDataBuffer() instanceof DataBufferFloat) { + DataBuffer dataBuffer = raster.getDataBuffer(); + + for (int b=0; b - * This {@link IntAccess} is immutable; any attempt to changes its values will result in a + * This {@link IntArray} is immutable; any attempt to changes its values will result in a * {@link UnsupportedOperationException}. *

* This data access is marked as volatile but always contain valid data. */ -public class IntRasterAccess implements IntAccess, SizableDataAccess, VolatileAccess { +public class IntRasterAccess extends IntArray implements SizableDataAccess, VolatileAccess { - private final Raster raster; - private final DataBuffer dataBuffer; - private final int width; - private final int planeSize; - private final boolean canUseDataBuffer; private final int size; /** * Create the int raster access. * * @param raster the raster containing the values to return. Its pixels are expected to be stored in the int format - * @throws NullPointerException if the provided image is null + * @throws NullPointerException if the provided raster is null */ public IntRasterAccess(Raster raster) { - this.raster = raster; - this.dataBuffer = this.raster.getDataBuffer(); + super(createArrayFromRaster(raster)); - this.width = this.raster.getWidth(); - this.planeSize = width * this.raster.getHeight(); - - this.canUseDataBuffer = this.dataBuffer instanceof DataBufferInt && - AccessTools.isSampleModelDirectlyUsable(this.raster); - - this.size = AccessTools.getSizeOfDataBufferInBytes(this.dataBuffer); - } - - @Override - public int getValue(int index) { - int b = index / planeSize; - int xyIndex = index % planeSize; - - if (canUseDataBuffer) { - return dataBuffer.getElem(b, xyIndex); - } else { - return raster.getSample(xyIndex % width, xyIndex / width, b); - } + this.size = AccessTools.getSizeOfDataBufferInBytes(raster.getDataBuffer()); } @Override @@ -70,4 +46,32 @@ public int getSizeBytes() { public boolean isValid() { return true; } + + private static int[] createArrayFromRaster(Raster raster) { + int width = raster.getWidth(); + int height = raster.getHeight(); + int planeSize = width * height; + int numBands = raster.getNumBands(); + + int[] array = new int[planeSize * numBands]; + if (AccessTools.isSampleModelDirectlyUsable(raster) && raster.getDataBuffer() instanceof DataBufferInt) { + DataBuffer dataBuffer = raster.getDataBuffer(); + + for (int b=0; b - * This {@link ShortAccess} is immutable; any attempt to changes its values will result in a + * This {@link ShortArray} is immutable; any attempt to changes its values will result in a * {@link UnsupportedOperationException}. *

* This data access is marked as volatile but always contain valid data. */ -public class ShortRasterAccess implements ShortAccess, SizableDataAccess, VolatileAccess { +public class ShortRasterAccess extends ShortArray implements SizableDataAccess, VolatileAccess { - private final Raster raster; - private final DataBuffer dataBuffer; - private final int width; - private final int planeSize; - private final boolean canUseDataBuffer; private final int size; /** @@ -33,28 +28,9 @@ public class ShortRasterAccess implements ShortAccess, SizableDataAccess, Volati * @throws NullPointerException if the provided image is null */ public ShortRasterAccess(Raster raster) { - this.raster = raster; - this.dataBuffer = this.raster.getDataBuffer(); + super(createArrayFromRaster(raster)); - this.width = this.raster.getWidth(); - this.planeSize = width * this.raster.getHeight(); - - this.canUseDataBuffer = (this.dataBuffer instanceof DataBufferUShort || this.dataBuffer instanceof DataBufferShort) && - AccessTools.isSampleModelDirectlyUsable(this.raster); - - this.size = AccessTools.getSizeOfDataBufferInBytes(this.dataBuffer); - } - - @Override - public short getValue(int index) { - int b = index / planeSize; - int xyIndex = index % planeSize; - - if (canUseDataBuffer) { - return (short) dataBuffer.getElem(b, xyIndex); - } else { - return (short) raster.getSample(xyIndex % width, xyIndex / width, b); - } + this.size = AccessTools.getSizeOfDataBufferInBytes(raster.getDataBuffer()); } @Override @@ -71,4 +47,32 @@ public int getSizeBytes() { public boolean isValid() { return true; } + + private static short[] createArrayFromRaster(Raster raster) { + int width = raster.getWidth(); + int height = raster.getHeight(); + int planeSize = width * height; + int numBands = raster.getNumBands(); + + short[] array = new short[planeSize * numBands]; + if (AccessTools.isSampleModelDirectlyUsable(raster) && (raster.getDataBuffer() instanceof DataBufferUShort || raster.getDataBuffer() instanceof DataBufferShort)) { + DataBuffer dataBuffer = raster.getDataBuffer(); + + for (int b=0; b