public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
    checkIndex(imageIndex);
    readHeader();

    if (iis == null) throw new IllegalStateException("input is null");

    BufferedImage img;

    clearAbortRequest();
    processImageStarted(imageIndex);

    if (param == null) param = getDefaultReadParam();

    sourceRegion = new Rectangle(0, 0, 0, 0);
    destinationRegion = new Rectangle(0, 0, 0, 0);

    computeRegions(
        param, this.width, this.height, param.getDestination(), sourceRegion, destinationRegion);

    scaleX = param.getSourceXSubsampling();
    scaleY = param.getSourceYSubsampling();

    // If the destination band is set used it
    sourceBands = param.getSourceBands();
    destBands = param.getDestinationBands();

    seleBand = (sourceBands != null) && (destBands != null);
    noTransform = destinationRegion.equals(new Rectangle(0, 0, width, height)) || seleBand;

    if (!seleBand) {
      sourceBands = new int[colorPlanes];
      destBands = new int[colorPlanes];
      for (int i = 0; i < colorPlanes; i++) destBands[i] = sourceBands[i] = i;
    }

    // If the destination is provided, then use it.  Otherwise, create new one
    bi = param.getDestination();

    // Get the image data.
    WritableRaster raster = null;

    if (bi == null) {
      if (sampleModel != null && colorModel != null) {
        sampleModel =
            sampleModel.createCompatibleSampleModel(
                destinationRegion.width + destinationRegion.x,
                destinationRegion.height + destinationRegion.y);
        if (seleBand) sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
        raster = Raster.createWritableRaster(sampleModel, new Point(0, 0));
        bi = new BufferedImage(colorModel, raster, false, null);
      }
    } else {
      raster = bi.getWritableTile(0, 0);
      sampleModel = bi.getSampleModel();
      colorModel = bi.getColorModel();

      noTransform &= destinationRegion.equals(raster.getBounds());
    }

    byte bdata[] = null; // buffer for byte data

    if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
      bdata = (byte[]) ((DataBufferByte) raster.getDataBuffer()).getData();

    readImage(bdata);

    if (abortRequested()) processReadAborted();
    else processImageComplete();

    return bi;
  }
  public void write(BufferedImage image, AVList params) throws IOException {
    if (image == null) {
      String msg = Logging.getMessage("nullValue.ImageSource");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    if (0 == image.getWidth() || 0 == image.getHeight()) {
      String msg =
          Logging.getMessage("generic.InvalidImageSize", image.getWidth(), image.getHeight());
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    if (null == params || 0 == params.getValues().size()) {
      String reason = Logging.getMessage("nullValue.AVListIsNull");
      Logging.logger().finest(Logging.getMessage("GeotiffWriter.GeoKeysMissing", reason));
      params = new AVListImpl();
    } else {
      this.validateParameters(params, image.getWidth(), image.getHeight());
    }

    // how we proceed in part depends upon the image type...
    int type = image.getType();

    // handle CUSTOM type which comes from our GeoTiffreader (for now)
    if (BufferedImage.TYPE_CUSTOM == type) {
      int numColorComponents = 0, numComponents = 0, pixelSize = 0, dataType = 0, csType = 0;
      boolean hasAlpha = false;

      if (null != image.getColorModel()) {
        ColorModel cm = image.getColorModel();

        numColorComponents = cm.getNumColorComponents();
        numComponents = cm.getNumComponents();
        pixelSize = cm.getPixelSize();
        hasAlpha = cm.hasAlpha();

        ColorSpace cs = cm.getColorSpace();
        if (null != cs) csType = cs.getType();
      }

      if (null != image.getSampleModel()) {
        SampleModel sm = image.getSampleModel();
        dataType = sm.getDataType();
      }

      if (dataType == DataBuffer.TYPE_FLOAT && pixelSize == Float.SIZE && numComponents == 1) {
        type = BufferedImage_TYPE_ELEVATION_FLOAT32;
      } else if (dataType == DataBuffer.TYPE_SHORT
          && pixelSize == Short.SIZE
          && numComponents == 1) {
        type = BufferedImage_TYPE_ELEVATION_SHORT16;
      } else if (ColorSpace.CS_GRAY == csType && pixelSize == Byte.SIZE) {
        type = BufferedImage.TYPE_BYTE_GRAY;
      } else if (dataType == DataBuffer.TYPE_USHORT
          && ColorSpace.CS_GRAY == csType
          && pixelSize == Short.SIZE) {
        type = BufferedImage.TYPE_USHORT_GRAY;
      } else if (ColorSpace.TYPE_RGB == csType
          && pixelSize == 3 * Byte.SIZE
          && numColorComponents == 3) {
        type = BufferedImage.TYPE_3BYTE_BGR;
      } else if (ColorSpace.TYPE_RGB == csType
          && hasAlpha
          && pixelSize == 4 * Byte.SIZE
          && numComponents == 4) {
        type = BufferedImage.TYPE_4BYTE_ABGR;
      }
    }

    switch (type) {
      case BufferedImage.TYPE_3BYTE_BGR:
      case BufferedImage.TYPE_4BYTE_ABGR:
      case BufferedImage.TYPE_4BYTE_ABGR_PRE:
      case BufferedImage.TYPE_INT_RGB:
      case BufferedImage.TYPE_INT_BGR:
      case BufferedImage.TYPE_INT_ARGB:
      case BufferedImage.TYPE_INT_ARGB_PRE:
        {
          this.writeColorImage(image, params);
        }
        break;

      case BufferedImage.TYPE_USHORT_GRAY:
      case BufferedImage.TYPE_BYTE_GRAY:
        {
          this.writeGrayscaleImage(image, params);
        }
        break;

      case BufferedImage_TYPE_ELEVATION_SHORT16:
      case BufferedImage_TYPE_ELEVATION_FLOAT32:
        {
          String msg = Logging.getMessage("GeotiffWriter.FeatureNotImplementedd", type);
          Logging.logger().severe(msg);
          throw new IllegalArgumentException(msg);
        }
        //            break;

      case BufferedImage.TYPE_CUSTOM:
      default:
        {
          ColorModel cm = image.getColorModel();
          SampleModel sm = image.getSampleModel();

          StringBuffer sb =
              new StringBuffer(Logging.getMessage("GeotiffWriter.UnsupportedType", type));

          sb.append("\n");
          sb.append("NumBands=").append(sm.getNumBands()).append("\n");
          sb.append("NumDataElements=").append(sm.getNumDataElements()).append("\n");
          sb.append("NumColorComponents=").append(cm.getNumColorComponents()).append("\n");
          sb.append("NumComponents=").append(cm.getNumComponents()).append("\n");
          sb.append("PixelSize=").append(cm.getPixelSize()).append("\n");
          sb.append("hasAlpha=").append(cm.hasAlpha());

          String msg = sb.toString();
          Logging.logger().severe(msg);
          throw new IllegalArgumentException(msg);
        }
    }
  }