/*
   * Makes sure calculation of IJ1 channels is correctly invertible
   */
  @Test
  public void testRasterization() {
    final long[][] dimsList = {{1, 1, 1}, {1, 2, 3}, {2, 3, 4}, {5, 4, 3}, {4, 2, 7}};
    final AxisType[] axes = {Axes.CHANNEL, SCIFIOAxes.SPECTRA, SCIFIOAxes.FREQUENCY};
    for (long[] dims : dimsList) {
      // setup
      long numChannels = 1;
      for (long dim : dims) numChannels *= dim;

      // test from long index back to long index
      for (long channel = 0; channel < numChannels; channel++) {
        long[] channelPositions = new long[dims.length];
        LegacyUtils.fillChannelIndices(dims, axes, channel, channelPositions);
        long ij1Pos = LegacyUtils.calcIJ1ChannelPos(dims, axes, channelPositions);
        assertEquals(channel, ij1Pos);
      }

      // test from long[] index back to long[] index
      long[] channelPositions1 = new long[dims.length];
      long[] channelPositions2 = new long[dims.length];
      Extents extents = new Extents(dims);
      Position pos = extents.createPosition();
      while (pos.hasNext()) {
        pos.fwd();
        pos.localize(channelPositions1);
        long ij1Channel = LegacyUtils.calcIJ1ChannelPos(dims, axes, channelPositions1);
        LegacyUtils.fillChannelIndices(dims, axes, ij1Channel, channelPositions2);
        for (int i = 0; i < channelPositions1.length; i++)
          assertEquals(channelPositions1[i], channelPositions2[i]);
      }
    }
  }
  /**
   * Makes a color {@link Dataset} from an {@link ImagePlus}. Color Datasets have isRgbMerged()
   * true, channels == 3, and bitsperPixel == 8. Does not populate the data of the returned Dataset.
   * That is left to other utility methods. Does not set metadata of Dataset. Throws exceptions if
   * input ImagePlus is not single channel RGB.
   */
  private Dataset makeColorDataset(final ImagePlus imp, final AxisType[] preferredOrder) {
    final int x = imp.getWidth();
    final int y = imp.getHeight();
    final int c = imp.getNChannels();
    final int z = imp.getNSlices();
    final int t = imp.getNFrames();

    if (imp.getType() != ImagePlus.COLOR_RGB) {
      throw new IllegalArgumentException("can't make a color Dataset from a nonRGB ImagePlus");
    }

    if (c != 1) {
      throw new IllegalArgumentException(
          "can't make a color Dataset from a multichannel ColorProcessor stack");
    }

    final int[] inputDims = new int[] {x, y, 3, z, t};
    final AxisType[] axes = LegacyUtils.orderedAxes(preferredOrder, inputDims);
    final long[] dims = LegacyUtils.orderedDims(axes, inputDims);
    final String name = imp.getTitle();
    final int bitsPerPixel = 8;
    final boolean signed = false;
    final boolean floating = false;
    final boolean virtual = imp.getStack().isVirtual();
    final Dataset ds =
        datasetService.create(dims, name, axes, bitsPerPixel, signed, floating, virtual);

    ds.setRGBMerged(true);

    DatasetUtils.initColorTables(ds);

    return ds;
  }
  /**
   * Makes a color {@link ImagePlus} from a color {@link Dataset}. The ImagePlus will have the same
   * X, Y, Z, & T dimensions. C will be 1. The data values and metadata are not assigned. Throws an
   * exception if the dataset is not color compatible.
   */
  private ImagePlus makeColorImagePlus(final Dataset ds) {
    if (!LegacyUtils.isColorCompatible(ds)) {
      throw new IllegalArgumentException("Dataset is not color compatible");
    }

    final int[] dimIndices = new int[5];
    final int[] dimValues = new int[5];
    LegacyUtils.getImagePlusDims(ds, dimIndices, dimValues);
    final int w = dimValues[0];
    final int h = dimValues[1];
    final int c = dimValues[2] / 3;
    final int z = dimValues[3];
    final int t = dimValues[4];

    final ImageStack stack = new ImageStack(w, h, c * z * t);

    for (int i = 0; i < c * z * t; i++) stack.setPixels(new int[w * h], i + 1);

    final ImagePlus imp = new ImagePlus(ds.getName(), stack);

    imp.setDimensions(c, z, t);

    return imp;
  }
 /**
  * Assigns the data values of a {@link Dataset} from a paired {@link ImagePlus}. Assumes the
  * Dataset and ImagePlus have compatible dimensions and that the data planes are not directly
  * mapped. Gets values via {@link ImageProcessor}::getf(). In cases where there is a narrowing of
  * data into modern ImageJ types the data is range clamped. Does not change the Dataset's
  * metadata.
  */
 @Override
 public void updateDataset(final Dataset ds, final ImagePlus imp) {
   final RealType<?> type = ds.getType();
   final double typeMin = type.getMinValue();
   final double typeMax = type.getMaxValue();
   final boolean signed16BitData = type instanceof ShortType;
   final RandomAccess<? extends RealType<?>> accessor = ds.getImgPlus().randomAccess();
   final long[] dims = ds.getDims();
   final AxisType[] axes = ds.getAxes();
   final int xIndex = ds.getAxisIndex(Axes.X);
   final int yIndex = ds.getAxisIndex(Axes.Y);
   final int zIndex = ds.getAxisIndex(Axes.Z);
   final int tIndex = ds.getAxisIndex(Axes.TIME);
   final int xSize = imp.getWidth();
   final int ySize = imp.getHeight();
   final int zSize = imp.getNSlices();
   final int tSize = imp.getNFrames();
   final int cSize = imp.getNChannels();
   final ImageStack stack = imp.getStack();
   int planeNum = 1;
   final long[] pos = new long[dims.length];
   for (int t = 0; t < tSize; t++) {
     if (tIndex >= 0) pos[tIndex] = t;
     for (int z = 0; z < zSize; z++) {
       if (zIndex >= 0) pos[zIndex] = z;
       for (int c = 0; c < cSize; c++) {
         LegacyUtils.fillChannelIndices(dims, axes, c, pos);
         final ImageProcessor proc = stack.getProcessor(planeNum++);
         for (int x = 0; x < xSize; x++) {
           if (xIndex >= 0) pos[xIndex] = x;
           for (int y = 0; y < ySize; y++) {
             if (yIndex >= 0) pos[yIndex] = y;
             accessor.setPosition(pos);
             double value = proc.getf(x, y);
             if (signed16BitData) value -= 32768.0;
             if (value < typeMin) value = typeMin;
             else if (value > typeMax) value = typeMax;
             accessor.get().setReal(value);
           }
         }
       }
     }
   }
   ds.update();
 }
  /**
   * Creates a {@link ImageDisplay} from an {@link ImagePlus}. Shares planes of data when possible.
   */
  @Override
  public ImageDisplay createDisplay(final ImagePlus imp) {

    return createDisplay(imp, LegacyUtils.getPreferredAxisOrder());
  }