/** Constructor to set locales. */
  public J2KImageWriteParamJava(IIOImage image, ImageWriteParam param) {
    super(param.getLocale());
    if (image != null) {
      if (image.hasRaster()) setDefaults(image.getRaster());
      else setDefaults(image.getRenderedImage());
    }

    setSourceRegion(param.getSourceRegion());
    setSourceBands(param.getSourceBands());
    try {
      setTiling(
          param.getTileWidth(), param.getTileHeight(),
          param.getTileGridXOffset(), param.getTileGridYOffset());
    } catch (IllegalStateException e) {
      // tileing is not set do nothing.
    }

    setDestinationOffset(param.getDestinationOffset());
    setSourceSubsampling(
        param.getSourceXSubsampling(),
        param.getSourceYSubsampling(),
        param.getSubsamplingXOffset(),
        param.getSubsamplingYOffset());
    setDestinationType(param.getDestinationType());

    J2KImageWriteParam j2kParam;
    if (param instanceof J2KImageWriteParam) {
      j2kParam = (J2KImageWriteParam) param;
    } else {
      j2kParam = new J2KImageWriteParam();
    }

    setDecompositionLevel("" + j2kParam.getNumDecompositionLevels());
    setEncodingRate(j2kParam.getEncodingRate());
    setLossless(j2kParam.getLossless());
    setFilters(j2kParam.getFilter());
    setEPH("" + j2kParam.getEPH());
    setSOP("" + j2kParam.getSOP());
    setProgressionName(j2kParam.getProgressionType());
    int[] size = j2kParam.getCodeBlockSize();
    setCodeBlockSize("" + size[0] + " " + size[1]);
    enableCT = j2kParam.getComponentTransformation();
    setComponentTransformation("" + enableCT);
  }
  public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param)
      throws IOException {
    if (stream == null) {
      throw new IllegalStateException(I18N.getString("WBMPImageWriter3"));
    }

    if (image == null) {
      throw new IllegalArgumentException(I18N.getString("WBMPImageWriter4"));
    }

    clearAbortRequest();
    processImageStarted(0);
    if (param == null) param = getDefaultWriteParam();

    RenderedImage input = null;
    Raster inputRaster = null;
    boolean writeRaster = image.hasRaster();
    Rectangle sourceRegion = param.getSourceRegion();
    SampleModel sampleModel = null;

    if (writeRaster) {
      inputRaster = image.getRaster();
      sampleModel = inputRaster.getSampleModel();
    } else {
      input = image.getRenderedImage();
      sampleModel = input.getSampleModel();

      inputRaster = input.getData();
    }

    checkSampleModel(sampleModel);
    if (sourceRegion == null) sourceRegion = inputRaster.getBounds();
    else sourceRegion = sourceRegion.intersection(inputRaster.getBounds());

    if (sourceRegion.isEmpty()) throw new RuntimeException(I18N.getString("WBMPImageWriter1"));

    int scaleX = param.getSourceXSubsampling();
    int scaleY = param.getSourceYSubsampling();
    int xOffset = param.getSubsamplingXOffset();
    int yOffset = param.getSubsamplingYOffset();

    sourceRegion.translate(xOffset, yOffset);
    sourceRegion.width -= xOffset;
    sourceRegion.height -= yOffset;

    int minX = sourceRegion.x / scaleX;
    int minY = sourceRegion.y / scaleY;
    int w = (sourceRegion.width + scaleX - 1) / scaleX;
    int h = (sourceRegion.height + scaleY - 1) / scaleY;

    Rectangle destinationRegion = new Rectangle(minX, minY, w, h);
    sampleModel = sampleModel.createCompatibleSampleModel(w, h);

    SampleModel destSM = sampleModel;

    // If the data are not formatted nominally then reformat.
    if (sampleModel.getDataType() != DataBuffer.TYPE_BYTE
        || !(sampleModel instanceof MultiPixelPackedSampleModel)
        || ((MultiPixelPackedSampleModel) sampleModel).getDataBitOffset() != 0) {
      destSM = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, w, h, 1, w + 7 >> 3, 0);
    }

    if (!destinationRegion.equals(sourceRegion)) {
      if (scaleX == 1 && scaleY == 1)
        inputRaster =
            inputRaster.createChild(
                inputRaster.getMinX(), inputRaster.getMinY(), w, h, minX, minY, null);
      else {
        WritableRaster ras = Raster.createWritableRaster(destSM, new Point(minX, minY));

        byte[] data = ((DataBufferByte) ras.getDataBuffer()).getData();

        for (int j = minY, y = sourceRegion.y, k = 0; j < minY + h; j++, y += scaleY) {

          for (int i = 0, x = sourceRegion.x; i < w; i++, x += scaleX) {
            int v = inputRaster.getSample(x, y, 0);
            data[k + (i >> 3)] |= v << (7 - (i & 7));
          }
          k += w + 7 >> 3;
        }
        inputRaster = ras;
      }
    }

    // If the data are not formatted nominally then reformat.
    if (!destSM.equals(inputRaster.getSampleModel())) {
      WritableRaster raster =
          Raster.createWritableRaster(
              destSM, new Point(inputRaster.getMinX(), inputRaster.getMinY()));
      raster.setRect(inputRaster);
      inputRaster = raster;
    }

    // Check whether the image is white-is-zero.
    boolean isWhiteZero = false;
    if (!writeRaster && input.getColorModel() instanceof IndexColorModel) {
      IndexColorModel icm = (IndexColorModel) input.getColorModel();
      isWhiteZero = icm.getRed(0) > icm.getRed(1);
    }

    // Get the line stride, bytes per row, and data array.
    int lineStride = ((MultiPixelPackedSampleModel) destSM).getScanlineStride();
    int bytesPerRow = (w + 7) / 8;
    byte[] bdata = ((DataBufferByte) inputRaster.getDataBuffer()).getData();

    // Write WBMP header.
    stream.write(0); // TypeField
    stream.write(0); // FixHeaderField
    stream.write(intToMultiByte(w)); // width
    stream.write(intToMultiByte(h)); // height

    // Write the data.
    if (!isWhiteZero && lineStride == bytesPerRow) {
      // Write the entire image.
      stream.write(bdata, 0, h * bytesPerRow);
      processImageProgress(100.0F);
    } else {
      // Write the image row-by-row.
      int offset = 0;
      if (!isWhiteZero) {
        // Black-is-zero
        for (int row = 0; row < h; row++) {
          if (abortRequested()) break;
          stream.write(bdata, offset, bytesPerRow);
          offset += lineStride;
          processImageProgress(100.0F * row / h);
        }
      } else {
        // White-is-zero: need to invert data.
        byte[] inverted = new byte[bytesPerRow];
        for (int row = 0; row < h; row++) {
          if (abortRequested()) break;
          for (int col = 0; col < bytesPerRow; col++) {
            inverted[col] = (byte) (~(bdata[col + offset]));
          }
          stream.write(inverted, 0, bytesPerRow);
          offset += lineStride;
          processImageProgress(100.0F * row / h);
        }
      }
    }

    if (abortRequested()) processWriteAborted();
    else {
      processImageComplete();
      stream.flushBefore(stream.getStreamPosition());
    }
  }