public DisplayPlus(
      final ImageCache stitchedCache,
      AcquisitionEngine eng,
      JSONObject summaryMD,
      boolean invX,
      boolean invY,
      boolean swapXY) {
    eng_ = eng;
    invertX_ = invX;
    invertY_ = invY;
    swapXY_ = swapXY;
    try {
      MultiStagePosition pos0 = MMStudioMainFrame.getInstance().getPositionList().getPosition(0);
      pos0x_ = pos0.getX();
      pos0y_ = pos0.getY();
      tileWidth_ = MDUtils.getHeight(summaryMD);
      tileHeight_ = MDUtils.getWidth(summaryMD);
    } catch (Exception e) {
      ReportingUtils.showError("Couldnt get grid info");
    }
    vad_ =
        new VirtualAcquisitionDisplay(stitchedCache, eng, WINDOW_TITLE) {
          public void showImage(final JSONObject tags, boolean waitForDisplay)
              throws InterruptedException, InvocationTargetException {
            // Since this is multichannel camera, only show when last channel arrives
            try {
              if (MDUtils.getChannelIndex(tags) == super.getNumChannels() - 1) {
                super.showImage(tags, waitForDisplay);
              } else {
                ImagePlus ip = super.getHyperImage();
                if (ip != null) {
                  // canvas never gets painted so need to set painpending false
                  ip.getCanvas().setPaintPending(false);
                }
              }
            } catch (JSONException ex) {
            }
          }
        };
    DisplayControls controls = new Controls();

    // Add in custom controls
    try {
      JavaUtils.setRestrictedFieldValue(
          vad_, VirtualAcquisitionDisplay.class, "controls_", controls);
    } catch (NoSuchFieldException ex) {
      ReportingUtils.showError("Couldn't create display controls");
    }
    vad_.show();
    // Zoom to 100%
    vad_.getImagePlus().getWindow().getCanvas().unzoom();

    // add mouse listeners for moving grids
    addMouseListeners();

    stitchedCache.addImageCacheListener(this);
  }
 private void readIndexMap() throws IOException {
   MappedByteBuffer mapOffset = makeReadOnlyBuffer(8, 8);
   if (mapOffset.getInt() != MultipageTiffWriter.INDEX_MAP_OFFSET_HEADER) {
     ReportingUtils.logError("Image map offset header incorrect");
     indexMap_ = null;
     return;
   }
   long offset = unsignInt(mapOffset.getInt());
   MappedByteBuffer mapHeader = makeReadOnlyBuffer(offset, 8);
   int headerCode = mapHeader.getInt();
   if (headerCode != MultipageTiffWriter.INDEX_MAP_HEADER) {
     ReportingUtils.logError("Image index map header incorrect");
     indexMap_ = null;
     return;
   }
   int numMappings = mapHeader.getInt();
   MappedByteBuffer mapData = makeReadOnlyBuffer(offset + 8, 24 * numMappings);
   for (int i = 0; i < numMappings; i++) {
     int channel = mapData.getInt();
     int slice = mapData.getInt();
     int frame = mapData.getInt();
     int position = mapData.getInt();
     long imageOffset = mapData.getLong();
     indexMap_.put(MDUtils.generateLabel(channel, slice, frame, position), imageOffset);
   }
 }
  @Override
  public void imageReceived(TaggedImage taggedImage) {
    try {
      // duplicate so image storage doesnt see incorrect tags
      JSONObject newTags = new JSONObject(taggedImage.tags.toString());
      MDUtils.setPositionIndex(newTags, 0);
      taggedImage = new TaggedImage(taggedImage.pix, newTags);
    } catch (JSONException ex) {
      ReportingUtils.showError("Couldn't manipulate image tags for display");
    }

    vad_.imageReceived(taggedImage);
  }
 public void addToIndexMap(TaggedImage img, long offset) {
   indexMap_.put(MDUtils.getLabel(img.tags), offset);
 }