/**
   * Extract the ImageLayout from the provided reader for the first available image.
   *
   * @param reader an istance of {@link ImageReader}
   * @throws IOException in case an error occurs
   */
  protected void setLayout(ImageReader reader) throws IOException {

    Utilities.ensureNonNull("reader", reader);
    // save ImageLayout
    ImageLayout2 layout = new ImageLayout2();
    ImageTypeSpecifier its = reader.getImageTypes(0).next();
    layout.setColorModel(its.getColorModel()).setSampleModel(its.getSampleModel());
    layout.setMinX(0).setMinY(0).setWidth(reader.getWidth(0)).setHeight(reader.getHeight(0));
    layout
        .setTileGridXOffset(0)
        .setTileGridYOffset(0)
        .setTileWidth(reader.getTileWidth(0))
        .setTileHeight(reader.getTileHeight(0));
    setlayout(layout);
  }
  private byte[] getROIData(ROI roi, Rectangle rectIMG) {
    byte[] dataROI;
    PlanarImage roiIMG = roi.getAsImage();
    Rectangle rectROI = roiIMG.getBounds();
    // Forcing to component colormodel in order to avoid packed bits
    ImageWorker w = new ImageWorker();
    w.setImage(roiIMG);
    w.forceComponentColorModel();
    RenderedImage img = w.getRenderedImage();
    //
    BufferedImage test =
        new BufferedImage(rectIMG.width, rectIMG.height, BufferedImage.TYPE_BYTE_GRAY);
    ImageLayout2 layout = new ImageLayout2(test);
    layout.setMinX(img.getMinX());
    layout.setMinY(img.getMinY());
    layout.setWidth(img.getWidth());
    layout.setHeight(img.getHeight());
    // Lookup
    byte[] lut = new byte[256];
    lut[255] = 1;
    lut[1] = 1;
    LookupTableJAI table = new LookupTableJAI(lut);
    RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
    RenderedOp transformed = LookupDescriptor.create(img, table, hints);

    Graphics2D gc2d = null;
    // Translation parameters in order to position the ROI data correctly in the Raster Space
    int trX = -rectIMG.x + rectROI.x - rectIMG.x;
    int trY = -rectIMG.y + rectROI.y - rectIMG.y;
    try {
      gc2d = test.createGraphics();
      gc2d.drawRenderedImage(transformed, AffineTransform.getTranslateInstance(trX, trY));
    } finally {
      gc2d.dispose();
    }
    Rectangle testRect = new Rectangle(rectIMG.width, rectIMG.height);
    DataBufferByte dbRoi = (DataBufferByte) test.getData(testRect).getDataBuffer();
    dataROI = dbRoi.getData();
    // BufferedImage is stored in memory so the planarImage chain before can be disposed
    ImageUtilities.disposePlanarImageChain(transformed);
    // Flush of the BufferedImage
    test.flush();

    return dataROI;
  }
  /**
   * Returns the specified property.
   *
   * @param name Property name.
   * @param opNode Operation node.
   */
  public Object getProperty(String name, Object opNode) {
    validate(name, opNode);

    if (opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) {
      RenderedOp op = (RenderedOp) opNode;

      ParameterBlock pb = op.getParameterBlock();

      // Retrieve the rendered source image and its ROI.
      RenderedImage src = pb.getRenderedSource(0);
      Object property = src.getProperty("ROI");
      if (property == null
          || property.equals(java.awt.Image.UndefinedProperty)
          || !(property instanceof ROI)) {
        return java.awt.Image.UndefinedProperty;
      }

      // Return undefined also if source ROI is empty.
      ROI srcROI = (ROI) property;
      if (srcROI.getBounds().isEmpty()) {
        return java.awt.Image.UndefinedProperty;
      }

      // Retrieve the Interpolation object.
      Interpolation interp = (Interpolation) pb.getObjectParameter(1);

      // Determine the effective source bounds.
      Rectangle srcBounds = null;
      PlanarImage dst = op.getRendering();
      if (dst instanceof GeometricOpImage && ((GeometricOpImage) dst).getBorderExtender() == null) {
        srcBounds =
            new Rectangle(
                src.getMinX() + interp.getLeftPadding(),
                src.getMinY() + interp.getTopPadding(),
                src.getWidth() - interp.getWidth() + 1,
                src.getHeight() - interp.getHeight() + 1);
      } else {
        srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight());
      }

      // If necessary, clip the ROI to the effective source bounds.
      if (!srcBounds.contains(srcROI.getBounds())) {
        srcROI = srcROI.intersect(new ROIShape(srcBounds));
      }

      // Retrieve the Warp object.
      Warp warp = (Warp) pb.getObjectParameter(0);

      // Setting constant image to be warped as a ROI
      Rectangle dstBounds = op.getBounds();

      // Setting layout of the constant image
      ImageLayout2 layout = new ImageLayout2();
      int minx = (int) srcBounds.getMinX();
      int miny = (int) srcBounds.getMinY();
      int w = (int) srcBounds.getWidth();
      int h = (int) srcBounds.getHeight();
      layout.setMinX(minx);
      layout.setMinY(miny);
      layout.setWidth(w);
      layout.setHeight(h);
      RenderingHints hints = op.getRenderingHints();
      hints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout));

      final PlanarImage constantImage =
          ConstantDescriptor.create(new Float(w), new Float(h), new Byte[] {(byte) 255}, hints);

      PlanarImage roiImage = null;

      // Make sure to specify tileCache, tileScheduler, tileRecyclier, by cloning hints.
      RenderingHints warpingHints = op.getRenderingHints();
      warpingHints.remove(JAI.KEY_IMAGE_LAYOUT);

      // Creating warped roi by the same way (Warp, Interpolation, source ROI) we warped the
      // input image.
      final ParameterBlock paramBlk = new ParameterBlock();
      paramBlk.addSource(constantImage);
      paramBlk.add(warp);
      paramBlk.add(interp);
      paramBlk.add(null);
      paramBlk.add(srcROI);

      // force in the image layout, this way we get exactly the same
      // as the affine we're eliminating
      Hints localHints = new Hints(op.getRenderingHints());
      localHints.remove(JAI.KEY_IMAGE_LAYOUT);
      ImageLayout il = new ImageLayout();
      il.setMinX(dstBounds.x);
      il.setMinY(dstBounds.y);
      il.setWidth(dstBounds.width);
      il.setHeight(dstBounds.height);
      localHints.put(JAI.KEY_IMAGE_LAYOUT, il);
      roiImage = JAI.create("Warp", paramBlk, localHints);
      ROI dstROI = new ROI(roiImage, 1);

      // If necessary, clip the warped ROI to the destination bounds.
      if (!dstBounds.contains(dstROI.getBounds())) {
        dstROI = dstROI.intersect(new ROIShape(dstBounds));
      }

      // Return the warped and possibly clipped ROI.
      return dstROI;
    }

    return java.awt.Image.UndefinedProperty;
  }