public static void markDirty(WritableRaster wr) {
   if (wr instanceof SunWritableRaster) {
     ((SunWritableRaster) wr).markDirty();
   } else {
     markDirty(wr.getDataBuffer());
   }
 }
  public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {

    if (iis == null) {
      throw new IllegalStateException(I18N.getString("WBMPImageReader1"));
    }

    checkIndex(imageIndex);
    clearAbortRequest();
    processImageStarted(imageIndex);
    if (param == null) param = getDefaultReadParam();

    // read header
    readHeader();

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

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

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

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

    if (bi == null)
      bi =
          new BufferedImage(
              destinationRegion.x + destinationRegion.width,
              destinationRegion.y + destinationRegion.height,
              BufferedImage.TYPE_BYTE_BINARY);

    boolean noTransform =
        destinationRegion.equals(new Rectangle(0, 0, width, height))
            && destinationRegion.equals(new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));

    // Get the image data.
    WritableRaster tile = bi.getWritableTile(0, 0);

    // Get the SampleModel.
    MultiPixelPackedSampleModel sm = (MultiPixelPackedSampleModel) bi.getSampleModel();

    if (noTransform) {
      if (abortRequested()) {
        processReadAborted();
        return bi;
      }

      // If noTransform is necessary, read the data.
      iis.read(
          ((DataBufferByte) tile.getDataBuffer()).getData(), 0, height * sm.getScanlineStride());
      processImageUpdate(bi, 0, 0, width, height, 1, 1, new int[] {0});
      processImageProgress(100.0F);
    } else {
      int len = (this.width + 7) / 8;
      byte[] buf = new byte[len];
      byte[] data = ((DataBufferByte) tile.getDataBuffer()).getData();
      int lineStride = sm.getScanlineStride();
      iis.skipBytes(len * sourceRegion.y);
      int skipLength = len * (scaleY - 1);

      // cache the values to avoid duplicated computation
      int[] srcOff = new int[destinationRegion.width];
      int[] destOff = new int[destinationRegion.width];
      int[] srcPos = new int[destinationRegion.width];
      int[] destPos = new int[destinationRegion.width];

      for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
          i < destinationRegion.x + destinationRegion.width;
          i++, j++, x += scaleX) {
        srcPos[j] = x >> 3;
        srcOff[j] = 7 - (x & 7);
        destPos[j] = i >> 3;
        destOff[j] = 7 - (i & 7);
      }

      for (int j = 0, y = sourceRegion.y, k = destinationRegion.y * lineStride;
          j < destinationRegion.height;
          j++, y += scaleY) {

        if (abortRequested()) break;
        iis.read(buf, 0, len);
        for (int i = 0; i < destinationRegion.width; i++) {
          // get the bit and assign to the data buffer of the raster
          int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
          data[k + destPos[i]] |= v << destOff[i];
        }

        k += lineStride;
        iis.skipBytes(skipLength);
        processImageUpdate(bi, 0, j, destinationRegion.width, 1, 1, 1, new int[] {0});
        processImageProgress(100.0F * j / destinationRegion.height);
      }
    }

    if (abortRequested()) processReadAborted();
    else processImageComplete();
    return bi;
  }