diff --git a/pom.xml b/pom.xml
index dbcc656..d967ccc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,11 @@
https://ulrik.is/writing
skalarproduktraum
+
+ Eugene Katrukha
+ https://imagej.net/people/ekatrukha
+ ekatrukha
+
diff --git a/src/main/java/bvv/core/blocks/ByteUtils.java b/src/main/java/bvv/core/blocks/ByteUtils.java
index aab67b0..b653a92 100644
--- a/src/main/java/bvv/core/blocks/ByteUtils.java
+++ b/src/main/java/bvv/core/blocks/ByteUtils.java
@@ -43,6 +43,7 @@ public class ByteUtils
private static final long BUFFER_ADDRESS_OFFSET;
private static final long BYTE_ARRAY_OFFSET;
private static final long SHORT_ARRAY_OFFSET;
+ private static final long FLOAT_ARRAY_OFFSET;
static
{
@@ -66,6 +67,7 @@ public Unsafe run() throws Exception
BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset( byte[].class );
SHORT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset( short[].class );
+ FLOAT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset( float[].class );
}
catch ( final Exception ex )
{
@@ -98,6 +100,17 @@ public static void copyBytes( final byte[] src, final long dst, final long sox,
{
UNSAFE.copyMemory( src, BYTE_ARRAY_OFFSET + sox, null, dst, csx );
}
+
+ public static void setFloats( final float src, final long dst, final long csx )
+ {
+ for ( int i = 0; i < csx; ++i )
+ UNSAFE.putFloat( dst + 4 * i, src );
+ }
+
+ public static void copyFloats( final float[] src, final long dst, final long sox, final long csx )
+ {
+ UNSAFE.copyMemory( src, FLOAT_ARRAY_OFFSET + 4 * sox, null, dst, 4 * csx );
+ }
public interface Address
{
diff --git a/src/main/java/bvv/core/blocks/CopySubArrayImp.java b/src/main/java/bvv/core/blocks/CopySubArrayImp.java
index b441d98..1556568 100644
--- a/src/main/java/bvv/core/blocks/CopySubArrayImp.java
+++ b/src/main/java/bvv/core/blocks/CopySubArrayImp.java
@@ -66,6 +66,23 @@ public void copysubarray3d( final byte[] src, final int sox, final int soy, fina
copysubarray3dn( copy, sox, soy, soz, ssx, ssy, dox, doy, doz, dsx, dsy, csx, csy, csz );
}
}
+
+ public static class FloatToAddress implements CopySubArray< float[], ByteUtils.Address >
+ {
+ @Override
+ public void clearsubarray3d( final ByteUtils.Address dst, final int dox, final int doy, final int doz, final int dsx, final int dsy, final int csx, final int csy, final int csz )
+ {
+ final ArrayFill fill = ( o, l ) -> ByteUtils.setFloats( 0, dst.getAddress() + 4 * o, l );
+ fillsubarray3dn( fill, dox, doy, doz, dsx, dsy, csx, csy, csz );
+ }
+
+ @Override
+ public void copysubarray3d( final float[] src, final int sox, final int soy, final int soz, final int ssx, final int ssy, final ByteUtils.Address dst, final int dox, final int doy, final int doz, final int dsx, final int dsy, final int csx, final int csy, final int csz )
+ {
+ final ArrayCopy copy = ( so, o, l ) -> ByteUtils.copyFloats( src, dst.getAddress() + 4 * o, so, l );
+ copysubarray3dn( copy, sox, soy, soz, ssx, ssy, dox, doy, doz, dsx, dsy, csx, csy, csz );
+ }
+ }
static void copysubarray3dn(
ArrayCopy copysubarray1dn,
diff --git a/src/main/java/bvv/core/blocks/TileAccess.java b/src/main/java/bvv/core/blocks/TileAccess.java
index a5dc43e..a52a97a 100644
--- a/src/main/java/bvv/core/blocks/TileAccess.java
+++ b/src/main/java/bvv/core/blocks/TileAccess.java
@@ -44,6 +44,7 @@
import static net.imglib2.type.PrimitiveType.BYTE;
import static net.imglib2.type.PrimitiveType.SHORT;
+import static net.imglib2.type.PrimitiveType.FLOAT;
/**
* Copy blocks from a {@link ResolutionLevel3D} source to an {@link UploadBuffer}.
@@ -137,6 +138,16 @@ else if ( cellimg && primitive == BYTE && cacheSpec.format() == Texture.Internal
cacheSpec
);
}
+ else if ( cellimg && primitive == FLOAT && cacheSpec.format() == Texture.InternalFormat.R32F )
+ {
+ return new TileAccess<>(
+ volatil
+ ? new GridDataAccessImp.VolatileCells<>( ( AbstractCellImg ) img )
+ : new GridDataAccessImp.Cells<>( ( AbstractCellImg ) img ),
+ new CopySubArrayImp.FloatToAddress(),
+ cacheSpec
+ );
+ }
}
throw new UnsupportedOperationException( "pixel and/or image type not supported (yet)." );
@@ -150,7 +161,7 @@ public static boolean isSupportedType( final Object type )
{
final PrimitiveType primitive = ( ( NativeType ) type ).getNativeTypeFactory().getPrimitiveType();
final Fraction epp = ( ( NativeType ) type ).getEntitiesPerPixel();
- return ( primitive == SHORT || primitive == BYTE ) && epp.getNumerator() == epp.getDenominator();
+ return ( primitive == SHORT || primitive == BYTE || primitive == FLOAT ) && epp.getNumerator() == epp.getDenominator();
}
return false;
diff --git a/src/main/java/bvv/core/cache/TextureCache.java b/src/main/java/bvv/core/cache/TextureCache.java
index 172f04c..ad3ec61 100644
--- a/src/main/java/bvv/core/cache/TextureCache.java
+++ b/src/main/java/bvv/core/cache/TextureCache.java
@@ -316,6 +316,13 @@ private void initializeBlockedTiles( ArrayList< TileFillTask > tileFillTasks )
return true;
} , () -> true ), oobTile ) );
}
+ if(spec.format() == InternalFormat.R32F)
+ {
+ tileFillTasks.add( new TileFillTask( new DefaultFillTask( oobDummyKey, buf -> {
+ ByteUtils.setFloats( 0, buf.getAddress(), elementsPerTile );
+ return true;
+ } , () -> true ), oobTile ) );
+ }
}
private List< Tile > assignFillTiles( final int size, final int currentTimestamp )
diff --git a/src/main/java/bvv/core/render/DefaultSimpleStackManager.java b/src/main/java/bvv/core/render/DefaultSimpleStackManager.java
index 39f37cc..01187f4 100644
--- a/src/main/java/bvv/core/render/DefaultSimpleStackManager.java
+++ b/src/main/java/bvv/core/render/DefaultSimpleStackManager.java
@@ -30,6 +30,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.HashMap;
@@ -41,6 +42,7 @@
import net.imglib2.type.numeric.ARGBType;
import net.imglib2.type.numeric.integer.UnsignedByteType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
+import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.util.Intervals;
import net.imglib2.view.Views;
@@ -54,6 +56,7 @@ public class DefaultSimpleStackManager implements SimpleStackManager
{
private final HashMap< SimpleStack3D< ? >, VolumeTextureU16 > texturesU16;
private final HashMap< SimpleStack3D< ? >, VolumeTextureU8 > texturesU8;
+ private final HashMap< SimpleStack3D< ? >, VolumeTextureU32 > texturesU32;
private final HashMap< SimpleStack3D< ? >, VolumeTextureRGBA8 > texturesRGBA8;
private final HashMap< Texture3D, Integer > timestamps;
@@ -61,6 +64,7 @@ public class DefaultSimpleStackManager implements SimpleStackManager
public DefaultSimpleStackManager()
{
+ texturesU32 = new HashMap<>();
texturesU16 = new HashMap<>();
texturesU8 = new HashMap<>();
texturesRGBA8 = new HashMap<>();
@@ -90,6 +94,12 @@ else if ( type instanceof UnsignedByteType )
sourceMax = new Vector3f( image.max( 0 ), image.max( 1 ), image.max( 2 ) );
sourceMin = new Vector3f( image.min( 0 ), image.min( 1 ), image.min( 2 ) );
}
+ else if ( type instanceof FloatType )
+ {
+ texture = texturesU32.computeIfAbsent( stack, s -> uploadToTextureU32( context, ( RandomAccessibleInterval< FloatType > ) image ) );
+ sourceMax = new Vector3f( image.max( 0 ), image.max( 1 ), image.max( 2 ) );
+ sourceMin = new Vector3f( image.min( 0 ), image.min( 1 ), image.min( 2 ) );
+ }
else if ( type instanceof ARGBType )
{
texture = texturesRGBA8.computeIfAbsent( stack, s -> uploadToTextureRGBA8( context, ( RandomAccessibleInterval< ARGBType > ) image ) );
@@ -162,6 +172,30 @@ private static void copyToBufferU16( final RandomAccessibleInterval< UnsignedSho
while ( cursor.hasNext() )
sdata.put( i++, cursor.next().getShort() );
}
+
+ private static VolumeTextureU32 uploadToTextureU32( final GpuContext context, final RandomAccessibleInterval< FloatType > rai )
+ {
+ final VolumeTextureU32 texture = new VolumeTextureU32();
+ texture.init( Intervals.dimensionsAsIntArray( rai ) );
+
+ final int numBytes = ( int ) ( 4 * Intervals.numElements( rai ) );
+ final ByteBuffer data = ByteBuffer.allocateDirect( numBytes ); // allocate a bit more than needed...
+ data.order( ByteOrder.nativeOrder() );
+ copyToBufferU32( rai, data );
+ texture.upload( context, data );
+ return texture;
+ }
+
+ private static void copyToBufferU32( final RandomAccessibleInterval< FloatType > rai, final ByteBuffer buffer )
+ {
+ // TODO handle specific RAI types more efficiently
+ // TODO multithreading
+ final Cursor< FloatType > cursor = Views.flatIterable( rai ).cursor();
+ final FloatBuffer sdata = buffer.asFloatBuffer();
+ int i = 0;
+ while ( cursor.hasNext() )
+ sdata.put( i++, cursor.next().getRealFloat() );
+ }
private static VolumeTextureU8 uploadToTextureU8( final GpuContext context, final RandomAccessibleInterval< UnsignedByteType > rai )
{
diff --git a/src/main/java/bvv/core/render/MultiVolumeShaderMip.java b/src/main/java/bvv/core/render/MultiVolumeShaderMip.java
index b169eed..c4f0362 100644
--- a/src/main/java/bvv/core/render/MultiVolumeShaderMip.java
+++ b/src/main/java/bvv/core/render/MultiVolumeShaderMip.java
@@ -184,6 +184,7 @@ public MultiVolumeShaderMip( VolumeShaderSignature signature, final boolean useD
switch ( volumeSignature.getPixelType() )
{
default:
+ case SFLOAT:
case USHORT:
case UBYTE:
convert = templateConvert.instantiate();
@@ -520,6 +521,9 @@ public ConverterSegment( final SegmentedShader prog, final Segment segment, fina
case ARGB:
rangeScale = 0xff;
break;
+ case SFLOAT:
+ rangeScale = 1.0;
+ break;
}
}
diff --git a/src/main/java/bvv/core/render/VolumeRenderer.java b/src/main/java/bvv/core/render/VolumeRenderer.java
index b8734bc..c918617 100644
--- a/src/main/java/bvv/core/render/VolumeRenderer.java
+++ b/src/main/java/bvv/core/render/VolumeRenderer.java
@@ -37,6 +37,7 @@
import static com.jogamp.opengl.GL.GL_UNPACK_ALIGNMENT;
import static bvv.core.backend.Texture.InternalFormat.R8;
import static bvv.core.backend.Texture.InternalFormat.R16;
+import static bvv.core.backend.Texture.InternalFormat.R32F;
import static bvv.core.render.VolumeRenderer.RepaintType.DITHER;
import static bvv.core.render.VolumeRenderer.RepaintType.FULL;
import static bvv.core.render.VolumeRenderer.RepaintType.LOAD;
@@ -44,10 +45,12 @@
import static bvv.core.render.VolumeShaderSignature.PixelType.ARGB;
import static bvv.core.render.VolumeShaderSignature.PixelType.UBYTE;
import static bvv.core.render.VolumeShaderSignature.PixelType.USHORT;
+import static bvv.core.render.VolumeShaderSignature.PixelType.SFLOAT;
import static bvv.core.multires.SourceStacks.SourceStackType.MULTIRESOLUTION;
import static bvv.core.multires.SourceStacks.SourceStackType.SIMPLE;
import static net.imglib2.type.PrimitiveType.BYTE;
import static net.imglib2.type.PrimitiveType.SHORT;
+import static net.imglib2.type.PrimitiveType.FLOAT;
import bvv.core.backend.Texture.InternalFormat;
import bvv.core.cache.CacheSpec;
@@ -69,8 +72,10 @@
import net.imglib2.type.numeric.ARGBType;
import net.imglib2.type.numeric.integer.UnsignedByteType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
+import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.type.volatiles.VolatileUnsignedByteType;
import net.imglib2.type.volatiles.VolatileUnsignedShortType;
+import net.imglib2.type.volatiles.VolatileFloatType;
import org.joml.Matrix4f;
@@ -145,6 +150,8 @@ void request( final RepaintType type )
private final TextureCacheAndPboChain cacheR8;
private final TextureCacheAndPboChain cacheR16;
+
+ private final TextureCacheAndPboChain cacheR32F;
private final ForkJoinPool forkJoinPool;
@@ -208,6 +215,7 @@ public VolumeRenderer(
// used for the first time.
cacheR8 = new TextureCacheAndPboChain( R8, cacheBlockSize, maxCacheSizeInMB );
cacheR16 = new TextureCacheAndPboChain( R16, cacheBlockSize, maxCacheSizeInMB );
+ cacheR32F = new TextureCacheAndPboChain( R32F, cacheBlockSize, maxCacheSizeInMB );
final int parallelism = Math.max( 1, Runtime.getRuntime().availableProcessors() / 2 );
forkJoinPool = new ForkJoinPool( parallelism );
@@ -306,6 +314,8 @@ else if ( type == LOAD )
volumeSignatures.add( new VolumeSignature( MULTIRESOLUTION, USHORT ) );
else if ( ( pixelType instanceof UnsignedByteType ) || ( pixelType instanceof VolatileUnsignedByteType ) )
volumeSignatures.add( new VolumeSignature( MULTIRESOLUTION, UBYTE ) );
+ else if ( ( pixelType instanceof FloatType ) || ( pixelType instanceof VolatileFloatType ) )
+ volumeSignatures.add( new VolumeSignature( MULTIRESOLUTION, SFLOAT ) );
else
throw new IllegalArgumentException( "Multiresolution stack with pixel type " + pixelType.getClass().getName() + " unsupported in BigVolumeViewer." );
}
@@ -316,6 +326,8 @@ else if ( stack instanceof SimpleStack3D )
volumeSignatures.add( new VolumeSignature( SIMPLE, USHORT ) );
else if ( pixelType instanceof UnsignedByteType )
volumeSignatures.add( new VolumeSignature( SIMPLE, UBYTE ) );
+ else if ( pixelType instanceof FloatType )
+ volumeSignatures.add( new VolumeSignature( SIMPLE, SFLOAT ) );
else if ( pixelType instanceof ARGBType )
volumeSignatures.add( new VolumeSignature( SIMPLE, ARGB ) );
else
@@ -523,6 +535,9 @@ private void updateBlocks(
final List< MultiResolutionStack3D< ? > > multiResStacksR16 = new ArrayList<>();
final List< VolumeBlocks > volumesR16 = new ArrayList<>();
+
+ final List< MultiResolutionStack3D< ? > > multiResStacksR32 = new ArrayList<>();
+ final List< VolumeBlocks > volumesR32 = new ArrayList<>();
for ( int i = 0; i < multiResStacks.size(); i++ )
{
@@ -539,6 +554,11 @@ else if ( primitiveType == SHORT )
multiResStacksR16.add( stack );
volumesR16.add( volume );
}
+ else if ( primitiveType == FLOAT )
+ {
+ multiResStacksR32.add( stack );
+ volumesR32.add( volume );
+ }
else
{
throw new IllegalArgumentException();
@@ -548,6 +568,8 @@ else if ( primitiveType == SHORT )
boolean complete = true;
complete &= updateBlocks( context, multiResStacksR8, volumesR8, cacheR8, forkJoinPool, renderWidth, pv );
complete &= updateBlocks( context, multiResStacksR16, volumesR16, cacheR16, forkJoinPool, renderWidth, pv );
+ complete &= updateBlocks( context, multiResStacksR32, volumesR32, cacheR32F, forkJoinPool, renderWidth, pv );
+
if ( !complete )
nextRequestedRepaint.request( LOAD );
}
diff --git a/src/main/java/bvv/core/render/VolumeShaderSignature.java b/src/main/java/bvv/core/render/VolumeShaderSignature.java
index dfe8c5c..7ac8e45 100644
--- a/src/main/java/bvv/core/render/VolumeShaderSignature.java
+++ b/src/main/java/bvv/core/render/VolumeShaderSignature.java
@@ -36,6 +36,7 @@ public final class VolumeShaderSignature
{
public enum PixelType
{
+ SFLOAT,
USHORT,
UBYTE,
ARGB
diff --git a/src/main/java/bvv/core/render/VolumeTextureU32.java b/src/main/java/bvv/core/render/VolumeTextureU32.java
new file mode 100644
index 0000000..f1987be
--- /dev/null
+++ b/src/main/java/bvv/core/render/VolumeTextureU32.java
@@ -0,0 +1,98 @@
+/*-
+ * #%L
+ * Volume rendering of bdv datasets
+ * %%
+ * Copyright (C) 2018 - 2025 Tobias Pietzsch
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package bvv.core.render;
+
+import static bvv.core.backend.Texture.InternalFormat.R32F;
+
+import java.nio.Buffer;
+
+import bvv.core.backend.GpuContext;
+import bvv.core.backend.Texture3D;
+
+public class VolumeTextureU32 implements Texture3D
+{
+ private final int[] size = new int[ 3 ];
+
+ /**
+ * Reinitialize.
+ */
+ public void init( final int[] size )
+ {
+ for ( int d = 0; d < 3; d++ )
+ this.size[ d ] = size[ d ];
+ }
+
+ public void upload( final GpuContext context, final Buffer data )
+ {
+ context.delete( this ); // TODO: is this necessary everytime?
+ context.texSubImage3D( this, 0, 0, 0, texWidth(), texHeight(), texDepth(), data );
+ }
+
+ @Override
+ public InternalFormat texInternalFormat()
+ {
+ return R32F;
+ }
+
+ @Override
+ public int texWidth()
+ {
+ return size[ 0 ];
+ }
+
+ @Override
+ public int texHeight()
+ {
+ return size[ 1 ];
+ }
+
+ @Override
+ public int texDepth()
+ {
+ return size[ 2 ];
+ }
+
+ @Override
+ public MinFilter texMinFilter()
+ {
+ return MinFilter.LINEAR;
+ }
+
+ @Override
+ public MagFilter texMagFilter()
+ {
+ return MagFilter.LINEAR;
+ }
+
+ @Override
+ public Wrap texWrap()
+ {
+ return Wrap.CLAMP_TO_BORDER_ZERO;
+ }
+}
diff --git a/src/test/java/bvv/debug/Debug32bitInput.java b/src/test/java/bvv/debug/Debug32bitInput.java
new file mode 100644
index 0000000..be4c0af
--- /dev/null
+++ b/src/test/java/bvv/debug/Debug32bitInput.java
@@ -0,0 +1,151 @@
+package bvv.debug;
+
+import java.util.ArrayList;
+
+import net.imglib2.Cursor;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.cache.img.ReadOnlyCachedCellImgFactory;
+import net.imglib2.cache.img.ReadOnlyCachedCellImgOptions;
+import net.imglib2.img.Img;
+import net.imglib2.img.array.ArrayImg;
+import net.imglib2.img.array.ArrayImgFactory;
+import net.imglib2.img.array.ArrayLocalizingCursor;
+import net.imglib2.realtransform.AffineTransform3D;
+import net.imglib2.type.NativeType;
+import net.imglib2.type.numeric.ARGBType;
+import net.imglib2.type.numeric.RealType;
+import net.imglib2.type.numeric.integer.UnsignedByteType;
+import net.imglib2.type.numeric.integer.UnsignedShortType;
+import net.imglib2.type.numeric.real.FloatType;
+
+import bdv.util.volatiles.VolatileViews;
+import bvv.vistools.Bvv;
+import bvv.vistools.BvvFunctions;
+import bvv.vistools.BvvOptions;
+
+import bvv.vistools.BvvStackSource;
+
+/** Test all three data types (unsigned byte/short + float) in one BVV
+ * using two modes: "simplevolume" and "multiresolution" cached **/
+
+public class Debug32bitInput
+{
+ @SuppressWarnings( "unchecked" )
+ public static < T extends RealType< T > & NativeType< T >> void main( final String[] args )
+ {
+ Bvv bvv = BvvFunctions.show( BvvOptions.options().frameTitle( "Test all types" ));
+
+ //setup testing types
+ ArrayList