From ab29d5767f0cfa92c5b9cb11546609332f397614 Mon Sep 17 00:00:00 2001
From: Vojtech Moravec <vojtech.moravec.st@vsb.cz>
Date: Tue, 13 Oct 2020 14:25:06 +0200
Subject: [PATCH] Add CallbackInputData.

CallbackInputData is very generic loader, which is right now useful for Hdf5 reading.

The callback which is required to be implemented, must return pixel data at pixel specified by the x, y and z coordinates.
---
 .../cz/it4i/qcmp/io/CallbackInputData.java    |  43 +++++++
 src/main/java/cz/it4i/qcmp/io/InputData.java  |   3 +-
 .../it4i/qcmp/io/loader/CallbackLoader.java   | 105 ++++++++++++++++++
 .../qcmp/io/loader/PlaneLoaderFactory.java    |   7 +-
 4 files changed, 153 insertions(+), 5 deletions(-)
 create mode 100644 src/main/java/cz/it4i/qcmp/io/CallbackInputData.java
 create mode 100644 src/main/java/cz/it4i/qcmp/io/loader/CallbackLoader.java

diff --git a/src/main/java/cz/it4i/qcmp/io/CallbackInputData.java b/src/main/java/cz/it4i/qcmp/io/CallbackInputData.java
new file mode 100644
index 0000000..ef046c2
--- /dev/null
+++ b/src/main/java/cz/it4i/qcmp/io/CallbackInputData.java
@@ -0,0 +1,43 @@
+package cz.it4i.qcmp.io;
+
+import cz.it4i.qcmp.data.V3i;
+
+public class CallbackInputData extends InputData {
+
+    private final String cacheHint;
+    private final LoadCallback pixelLoadCallback;
+
+    /**
+     * Interface defining method for pixel data loading.
+     */
+    public interface LoadCallback {
+        /**
+         * Callback to load pixel value at specified position.
+         *
+         * @param x X position.
+         * @param y Y position.
+         * @param z Z position.
+         * @return Pixel value at specified position.
+         */
+        int getValueAt(final int x, final int y, final int z);
+    }
+
+    public CallbackInputData(final LoadCallback pixelLoadCallback,
+                             final V3i dimensions,
+                             final String cacheHint) {
+        this.pixelLoadCallback = pixelLoadCallback;
+        this.cacheHint = cacheHint;
+        setDataLoaderType(DataLoaderType.CallbackLoader);
+        setPixelType(PixelType.Gray16);
+        setDimension(dimensions);
+    }
+
+    @Override
+    public String getCacheFileName() {
+        return cacheHint;
+    }
+
+    public final LoadCallback getPixelLoadCallback() {
+        return pixelLoadCallback;
+    }
+}
diff --git a/src/main/java/cz/it4i/qcmp/io/InputData.java b/src/main/java/cz/it4i/qcmp/io/InputData.java
index d6a3fe3..0cda830 100644
--- a/src/main/java/cz/it4i/qcmp/io/InputData.java
+++ b/src/main/java/cz/it4i/qcmp/io/InputData.java
@@ -11,7 +11,8 @@ public abstract class InputData {
         RawDataLoader,
         SCIFIOLoader,
         ImageJBufferLoader,
-        FlatBufferLoader
+        FlatBufferLoader,
+        CallbackLoader
     }
 
     public enum PixelType {
diff --git a/src/main/java/cz/it4i/qcmp/io/loader/CallbackLoader.java b/src/main/java/cz/it4i/qcmp/io/loader/CallbackLoader.java
new file mode 100644
index 0000000..629ac23
--- /dev/null
+++ b/src/main/java/cz/it4i/qcmp/io/loader/CallbackLoader.java
@@ -0,0 +1,105 @@
+package cz.it4i.qcmp.io.loader;
+
+import cz.it4i.qcmp.data.Range;
+import cz.it4i.qcmp.data.V2i;
+import cz.it4i.qcmp.data.V3i;
+import cz.it4i.qcmp.io.CallbackInputData;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class CallbackLoader extends BasicLoader implements IPlaneLoader {
+
+    private final CallbackInputData callbackInputData;
+    private final CallbackInputData.LoadCallback pixelLoad;
+
+    public CallbackLoader(final CallbackInputData callbackInputData) {
+        super(callbackInputData.getDimensions());
+        this.callbackInputData = callbackInputData;
+        this.pixelLoad = callbackInputData.getPixelLoadCallback();
+    }
+
+    @Override
+    protected int valueAt(final int plane, final int x, final int y, final int width) {
+        return pixelLoad.getValueAt(x, y, plane);
+    }
+
+
+    @Override
+    public int[] loadPlaneData(final int plane) {
+        final int planePixelCount = dims.getX() * dims.getY();
+        final int[] planeData = new int[planePixelCount];
+
+        int index = 0;
+        for (int y = 0; y < dims.getY(); y++) {
+            for (int x = 0; x < dims.getX(); x++) {
+                planeData[index++] = pixelLoad.getValueAt(x, y, plane);
+            }
+        }
+        return planeData;
+    }
+
+    @Override
+    public int[] loadPlanesU16Data(final int[] planes) throws IOException {
+        if (planes.length < 1) {
+            return new int[0];
+        } else if (planes.length == 1) {
+            return loadPlaneData(planes[0]);
+        } else if (planes.length == dims.getZ()) {
+            return loadAllPlanesU16Data();
+        }
+        final int planePixelCount = dims.getX() * dims.getY();
+        final long totalValueCount = (long) planePixelCount * (long) planes.length;
+        if (totalValueCount > (long) Integer.MAX_VALUE) {
+            throw new IOException("Unable to load image data for planes, file size is too big.");
+        }
+
+        Arrays.sort(planes);
+
+        final int[] destBuffer = new int[(int) totalValueCount];
+        int index = 0;
+        for (final int plane : planes) {
+            for (int y = 0; y < dims.getY(); y++) {
+                for (int x = 0; x < dims.getX(); x++) {
+                    destBuffer[index++] = pixelLoad.getValueAt(x, y, plane);
+                }
+            }
+        }
+        return destBuffer;
+    }
+
+    @Override
+    public int[] loadAllPlanesU16Data() throws IOException {
+        final long totalValueCount = dims.multiplyTogether();
+        if (totalValueCount > (long) Integer.MAX_VALUE) {
+            throw new IOException("Unable to load all image data, file size is too big.");
+        }
+
+        final int[] destBuffer = new int[(int) totalValueCount];
+        int index = 0;
+        for (int z = 0; z < dims.getZ(); z++) {
+            for (int y = 0; y < dims.getY(); y++) {
+                for (int x = 0; x < dims.getX(); x++) {
+                    destBuffer[index++] = pixelLoad.getValueAt(x, y, z);
+                }
+            }
+        }
+
+        return destBuffer;
+    }
+
+    @Override
+    public int[][] loadRowVectors(final int vectorSize, final Range<Integer> planeRange) {
+        return loadRowVectorsImplByValueAt(vectorSize, planeRange);
+    }
+
+    @Override
+    public int[][] loadBlocks(final V2i blockDim, final Range<Integer> planeRange) {
+        return loadBlocksImplByValueAt(blockDim, planeRange);
+    }
+
+    @Override
+    public int[][] loadVoxels(final V3i voxelDim, final Range<Integer> planeRange) {
+        return loadVoxelsImplByValueAt(voxelDim, planeRange);
+    }
+}
diff --git a/src/main/java/cz/it4i/qcmp/io/loader/PlaneLoaderFactory.java b/src/main/java/cz/it4i/qcmp/io/loader/PlaneLoaderFactory.java
index e35ff32..ce597f2 100644
--- a/src/main/java/cz/it4i/qcmp/io/loader/PlaneLoaderFactory.java
+++ b/src/main/java/cz/it4i/qcmp/io/loader/PlaneLoaderFactory.java
@@ -1,9 +1,6 @@
 package cz.it4i.qcmp.io.loader;
 
-import cz.it4i.qcmp.io.BufferInputData;
-import cz.it4i.qcmp.io.FileInputData;
-import cz.it4i.qcmp.io.FlatBufferInputData;
-import cz.it4i.qcmp.io.InputData;
+import cz.it4i.qcmp.io.*;
 
 public final class PlaneLoaderFactory {
 
@@ -24,6 +21,8 @@ public final class PlaneLoaderFactory {
                 return new ImageJBufferLoader((BufferInputData) inputDataInfo);
             case FlatBufferLoader:
                 return new FlatBufferLoader((FlatBufferInputData) inputDataInfo);
+            case CallbackLoader:
+                return new CallbackLoader((CallbackInputData) inputDataInfo);
             default:
                 throw new Exception("Unsupported data loader.");
         }
-- 
GitLab