/**
   * Creates a copy of <code>param</code>. The source subsampling and and bands settings and the
   * destination bands and offset settings are copied. If <code>param</code> is a <code>
   * TIFFImageReadParam</code> then the <code>TIFFDecompressor</code> and <code>TIFFColorConverter
   * </code> settings are also copied; otherwise they are explicitly set to <code>null</code>.
   *
   * @param param the parameters to be copied.
   * @param copyTagSets whether the <code>TIFFTagSet</code> settings should be copied if set.
   * @return copied parameters.
   */
  private ImageReadParam cloneImageReadParam(ImageReadParam param, boolean copyTagSets) {
    // Create a new TIFFImageReadParam.
    TIFFImageReadParam newParam = new TIFFImageReadParam();

    // Copy the basic settings.
    newParam.setSourceSubsampling(
        param.getSourceXSubsampling(),
        param.getSourceYSubsampling(),
        param.getSubsamplingXOffset(),
        param.getSubsamplingYOffset());
    newParam.setSourceBands(param.getSourceBands());
    newParam.setDestinationBands(param.getDestinationBands());
    newParam.setDestinationOffset(param.getDestinationOffset());

    // Set the decompressor and color converter.
    if (param instanceof TIFFImageReadParam) {
      // Copy the settings from the input parameter.
      TIFFImageReadParam tparam = (TIFFImageReadParam) param;
      newParam.setTIFFDecompressor(tparam.getTIFFDecompressor());
      newParam.setColorConverter(tparam.getColorConverter());

      if (copyTagSets) {
        List tagSets = tparam.getAllowedTagSets();
        if (tagSets != null) {
          Iterator tagSetIter = tagSets.iterator();
          if (tagSetIter != null) {
            while (tagSetIter.hasNext()) {
              TIFFTagSet tagSet = (TIFFTagSet) tagSetIter.next();
              newParam.addAllowedTagSet(tagSet);
            }
          }
        }
      }
    } else {
      // Set the decompressor and color converter to null.
      newParam.setTIFFDecompressor(null);
      newParam.setColorConverter(null);
    }

    return newParam;
  }
  public TIFFRenderedImage(
      TIFFImageReader reader, int imageIndex, ImageReadParam readParam, int width, int height)
      throws IOException {
    this.reader = reader;
    this.imageIndex = imageIndex;
    this.tileParam = cloneImageReadParam(readParam, false);

    this.subsampleX = tileParam.getSourceXSubsampling();
    this.subsampleY = tileParam.getSourceYSubsampling();

    this.isSubsampling = this.subsampleX != 1 || this.subsampleY != 1;

    this.width = width / subsampleX;
    this.height = height / subsampleY;

    // If subsampling is being used, we may not match the
    // true tile grid exactly, but everything should still work
    this.tileWidth = reader.getTileWidth(imageIndex) / subsampleX;
    this.tileHeight = reader.getTileHeight(imageIndex) / subsampleY;

    Iterator iter = reader.getImageTypes(imageIndex);
    this.its = (ImageTypeSpecifier) iter.next();
    tileParam.setDestinationType(its);
  }
Example #3
0
  public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {

    BufferedImage dst = null;
    try {
      // Calculate and return a Rectangle that identifies the region of
      // the
      // source image that should be read:
      //
      // 1. If param is null, the upper-left corner of the region is (0,
      // 0),
      // and the width and height are specified by the width and height
      // arguments. In other words, the entire image is read.
      //
      // 2. If param is not null
      //
      // 2.1 If param.getSourceRegion() returns a non-null Rectangle, the
      // region is calculated as the intersection of param's Rectangle
      // and the earlier (0, 0, width, height Rectangle).
      //
      // 2.2 param.getSubsamplingXOffset() is added to the region's x
      // coordinate and subtracted from its width.
      //
      // 2.3 param.getSubsamplingYOffset() is added to the region's y
      // coordinate and subtracted from its height.

      int width = getWidth(imageIndex);
      int height = getHeight(imageIndex);

      Rectangle sourceRegion = getSourceRegion(param, width, height);

      // Source subsampling is used to return a scaled-down source image.
      // Default 1 values for X and Y subsampling indicate that a
      // non-scaled
      // source image will be returned.

      int sourceXSubsampling = 1;
      int sourceYSubsampling = 1;

      // The destination offset determines the starting location in the
      // destination where decoded pixels are placed. Default (0, 0)
      // values indicate the upper-left corner.

      Point destinationOffset = new Point(0, 0);

      // If param is not null, override the source subsampling, and
      // destination offset defaults.

      if (param != null) {
        sourceXSubsampling = param.getSourceXSubsampling();
        sourceYSubsampling = param.getSourceYSubsampling();
        destinationOffset = param.getDestinationOffset();
      }

      // Obtain a BufferedImage into which decoded pixels will be placed.
      // This destination will be returned to the application.
      //
      // 1. If param is not null
      //
      // 1.1 If param.getDestination() returns a BufferedImage
      //
      // 1.1.1 Return this BufferedImage
      //
      // Else
      //
      // 1.1.2 Invoke param.getDestinationType ().
      //
      // 1.1.3 If the returned ImageTypeSpecifier equals
      // getImageTypes (0) (see below), return its BufferedImage.
      //
      // 2. If param is null or a BufferedImage has not been obtained
      //
      // 2.1 Return getImageTypes (0)'s BufferedImage.

      dst = getDestination(param, getImageTypes(0), width, height);

      // Create a WritableRaster for the destination.

      WritableRaster wrDst = dst.getRaster();

      JBIG2Bitmap bitmap =
          decoder
              .getPageAsJBIG2Bitmap(imageIndex)
              .getSlice(sourceRegion.x, sourceRegion.y, sourceRegion.width, sourceRegion.height);

      BufferedImage image = bitmap.getBufferedImage();

      int newWidth = (int) (image.getWidth() * (1 / (double) sourceXSubsampling));
      int newHeight = (int) (image.getHeight() * (1 / (double) sourceYSubsampling));

      BufferedImage scaledImage = scaleImage(image.getRaster(), newWidth, newHeight, 1, 1);

      Raster raster = null;

      if (scaledImage != null) {
        raster = scaledImage.getRaster();
      } else raster = image.getRaster();

      wrDst.setRect(destinationOffset.x, destinationOffset.y, raster);

    } catch (RuntimeException e) {
      e.printStackTrace();
    }

    return dst;
  }
Example #4
0
  public RawRenderedImage(
      RawImageInputStream iis, RawImageReader reader, ImageReadParam param, int imageIndex)
      throws IOException {
    this.iis = iis;
    this.reader = reader;
    this.param = param;
    this.imageIndex = imageIndex;
    this.position = iis.getImageOffset(imageIndex);
    this.originalDimension = iis.getImageDimension(imageIndex);

    ImageTypeSpecifier type = iis.getImageType();
    sampleModel = originalSampleModel = type.getSampleModel();
    colorModel = type.getColorModel();

    // If the destination band is set used it
    sourceBands = (param == null) ? null : param.getSourceBands();

    if (sourceBands == null) {
      nComp = originalSampleModel.getNumBands();
      sourceBands = new int[nComp];
      for (int i = 0; i < nComp; i++) sourceBands[i] = i;
    } else {
      sampleModel = originalSampleModel.createSubsetSampleModel(sourceBands);
      colorModel = ImageUtil.createColorModel(null, sampleModel);
    }

    nComp = sourceBands.length;

    destinationBands = (param == null) ? null : param.getDestinationBands();
    if (destinationBands == null) {
      destinationBands = new int[nComp];
      for (int i = 0; i < nComp; i++) destinationBands[i] = i;
    }

    Dimension dim = iis.getImageDimension(imageIndex);
    this.width = dim.width;
    this.height = dim.height;

    Rectangle sourceRegion = new Rectangle(0, 0, this.width, this.height);

    originalRegion = (Rectangle) sourceRegion.clone();

    destinationRegion = (Rectangle) sourceRegion.clone();

    if (param != null) {
      RawImageReader.computeRegionsWrapper(
          param, this.width, this.height, param.getDestination(), sourceRegion, destinationRegion);
      scaleX = param.getSourceXSubsampling();
      scaleY = param.getSourceYSubsampling();
      xOffset = param.getSubsamplingXOffset();
      yOffset = param.getSubsamplingYOffset();
    }

    sourceOrigin = new Point(sourceRegion.x, sourceRegion.y);
    if (!destinationRegion.equals(sourceRegion)) noTransform = false;

    this.tileDataSize = ImageUtil.getTileSize(originalSampleModel);

    this.tileWidth = originalSampleModel.getWidth();
    this.tileHeight = originalSampleModel.getHeight();
    this.tileGridXOffset = destinationRegion.x;
    this.tileGridYOffset = destinationRegion.y;
    this.originalNumXTiles = getNumXTiles();

    this.width = destinationRegion.width;
    this.height = destinationRegion.height;
    this.minX = destinationRegion.x;
    this.minY = destinationRegion.y;

    sampleModel = sampleModel.createCompatibleSampleModel(tileWidth, tileHeight);

    maxXTile = originalDimension.width / tileWidth;
    maxYTile = originalDimension.height / tileHeight;
  }
Example #5
0
  public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
    try {
      init(pIndex);

      processImageStarted(pIndex);

      // Some more waste of time and space...
      Dimension size = mSize;

      if (pParam != null) {
        // Source region
        // TODO: Maybe have to do some tests, to check if we are within bounds...
        Rectangle sourceRegion = pParam.getSourceRegion();
        if (sourceRegion != null) {
          mImage = mImage.cropImage(sourceRegion);
          size = sourceRegion.getSize();
        }

        // Subsampling
        if (pParam.getSourceXSubsampling() > 1 || pParam.getSourceYSubsampling() > 1) {
          int w = size.width / pParam.getSourceXSubsampling();
          int h = size.height / pParam.getSourceYSubsampling();

          mImage = mImage.sampleImage(w, h);
          size = new Dimension(w, h);
        }
      }

      if (abortRequested()) {
        processReadAborted();
        return ImageUtil.createClear(size.width, size.height, null);
      }

      processImageProgress(10f);
      BufferedImage buffered = MagickUtil.toBuffered(mImage);
      processImageProgress(100f);

      /**/
      // System.out.println("Created image: " + buffered);
      // System.out.println("ColorModel: " + buffered.getColorModel().getClass().getName());
      // if (buffered.getColorModel() instanceof java.awt.image.IndexColorModel) {
      //    java.awt.image.IndexColorModel cm = (java.awt.image.IndexColorModel)
      // buffered.getColorModel();
      //    for (int i = 0; i < cm.getMapSize(); i++) {
      //        System.out.println("0x" + Integer.toHexString(cm.getRGB(i)));
      //    }
      // }
      // */

      /**
       * System.out.println("Colorspace: " + mImage.getColorspace()); System.out.println("Depth: " +
       * mImage.getDepth()); System.out.println("Format: " + mImage.getImageFormat());
       * System.out.println("Type: " + mImage.getImageType()); System.out.println("IPTCProfile: " +
       * StringUtil.deepToString(mImage.getIptcProfile())); System.out.println("StorageClass: " +
       * mImage.getStorageClass()); //
       */
      processImageComplete();

      return buffered;
    } catch (MagickException e) {
      // Wrap in IIOException
      throw new IIOException(e.getMessage(), e);
    }
  }
Example #6
0
  /**
   * Load a specified a raster as a portion of the granule describe by this {@link
   * GranuleDescriptor}.
   *
   * @param imageReadParameters the {@link ImageReadParam} to use for reading.
   * @param index the index to use for the {@link ImageReader}.
   * @param cropBBox the bbox to use for cropping.
   * @param mosaicWorldToGrid the cropping grid to world transform.
   * @param request the incoming request to satisfy.
   * @param hints {@link Hints} to be used for creating this raster.
   * @return a specified a raster as a portion of the granule describe by this {@link
   *     GranuleDescriptor}.
   * @throws IOException in case an error occurs.
   */
  public GranuleLoadingResult loadRaster(
      final ImageReadParam imageReadParameters,
      final int index,
      final ReferencedEnvelope cropBBox,
      final MathTransform2D mosaicWorldToGrid,
      final RasterLayerRequest request,
      final Hints hints)
      throws IOException {

    if (LOGGER.isLoggable(java.util.logging.Level.FINER)) {
      final String name = Thread.currentThread().getName();
      LOGGER.finer(
          "Thread:" + name + " Loading raster data for granuleDescriptor " + this.toString());
    }
    ImageReadParam readParameters = null;
    int imageIndex;
    final ReferencedEnvelope bbox =
        inclusionGeometry != null
            ? new ReferencedEnvelope(
                granuleBBOX.intersection(inclusionGeometry.getEnvelopeInternal()),
                granuleBBOX.getCoordinateReferenceSystem())
            : granuleBBOX;
    boolean doFiltering = false;
    if (filterMe) {
      doFiltering = Utils.areaIsDifferent(inclusionGeometry, baseGridToWorld, granuleBBOX);
    }

    // intersection of this tile bound with the current crop bbox
    final ReferencedEnvelope intersection =
        new ReferencedEnvelope(
            bbox.intersection(cropBBox), cropBBox.getCoordinateReferenceSystem());
    if (intersection.isEmpty()) {
      if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
        LOGGER.fine(
            new StringBuilder("Got empty intersection for granule ")
                .append(this.toString())
                .append(" with request ")
                .append(request.toString())
                .append(" Resulting in no granule loaded: Empty result")
                .toString());
      }
      return null;
    }

    ImageInputStream inStream = null;
    ImageReader reader = null;
    try {
      //
      // get info about the raster we have to read
      //

      // get a stream
      assert cachedStreamSPI != null : "no cachedStreamSPI available!";
      inStream =
          cachedStreamSPI.createInputStreamInstance(
              granuleUrl, ImageIO.getUseCache(), ImageIO.getCacheDirectory());
      if (inStream == null) return null;

      // get a reader and try to cache the relevant SPI
      if (cachedReaderSPI == null) {
        reader = ImageIOExt.getImageioReader(inStream);
        if (reader != null) cachedReaderSPI = reader.getOriginatingProvider();
      } else reader = cachedReaderSPI.createReaderInstance();
      if (reader == null) {
        if (LOGGER.isLoggable(java.util.logging.Level.WARNING)) {
          LOGGER.warning(
              new StringBuilder("Unable to get s reader for granuleDescriptor ")
                  .append(this.toString())
                  .append(" with request ")
                  .append(request.toString())
                  .append(" Resulting in no granule loaded: Empty result")
                  .toString());
        }
        return null;
      }
      // set input
      reader.setInput(inStream);

      // Checking for heterogeneous granules
      if (request.isHeterogeneousGranules()) {
        // create read parameters
        readParameters = new ImageReadParam();

        // override the overviews controller for the base layer
        imageIndex =
            ReadParamsController.setReadParams(
                request.getRequestedResolution(),
                request.getOverviewPolicy(),
                request.getDecimationPolicy(),
                readParameters,
                request.rasterManager,
                overviewsController);
      } else {
        imageIndex = index;
        readParameters = imageReadParameters;
      }

      // get selected level and base level dimensions
      final GranuleOverviewLevelDescriptor selectedlevel = getLevel(imageIndex, reader);

      // now create the crop grid to world which can be used to decide
      // which source area we need to crop in the selected level taking
      // into account the scale factors imposed by the selection of this
      // level together with the base level grid to world transformation
      AffineTransform2D cropWorldToGrid =
          new AffineTransform2D(selectedlevel.gridToWorldTransformCorner);
      cropWorldToGrid = (AffineTransform2D) cropWorldToGrid.inverse();
      // computing the crop source area which lives into the
      // selected level raster space, NOTICE that at the end we need to
      // take into account the fact that we might also decimate therefore
      // we cannot just use the crop grid to world but we need to correct
      // it.
      final Rectangle sourceArea =
          CRS.transform(cropWorldToGrid, intersection).toRectangle2D().getBounds();
      // gutter
      if (selectedlevel.baseToLevelTransform.isIdentity()) sourceArea.grow(2, 2);
      XRectangle2D.intersect(
          sourceArea,
          selectedlevel.rasterDimensions,
          sourceArea); // make sure roundings don't bother us
      // is it empty??
      if (sourceArea.isEmpty()) {
        if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
          LOGGER.fine(
              "Got empty area for granuleDescriptor "
                  + this.toString()
                  + " with request "
                  + request.toString()
                  + " Resulting in no granule loaded: Empty result");
        }
        return null;

      } else if (LOGGER.isLoggable(java.util.logging.Level.FINER)) {
        LOGGER.finer(
            "Loading level "
                + imageIndex
                + " with source region: "
                + sourceArea
                + " subsampling: "
                + readParameters.getSourceXSubsampling()
                + ","
                + readParameters.getSourceYSubsampling()
                + " for granule:"
                + granuleUrl);
      }

      // Setting subsampling
      int newSubSamplingFactor = 0;
      final String pluginName = cachedReaderSPI.getPluginClassName();
      if (pluginName != null && pluginName.equals(ImageUtilities.DIRECT_KAKADU_PLUGIN)) {
        final int ssx = readParameters.getSourceXSubsampling();
        final int ssy = readParameters.getSourceYSubsampling();
        newSubSamplingFactor = ImageIOUtilities.getSubSamplingFactor2(ssx, ssy);
        if (newSubSamplingFactor != 0) {
          if (newSubSamplingFactor > maxDecimationFactor && maxDecimationFactor != -1) {
            newSubSamplingFactor = maxDecimationFactor;
          }
          readParameters.setSourceSubsampling(newSubSamplingFactor, newSubSamplingFactor, 0, 0);
        }
      }

      // set the source region
      readParameters.setSourceRegion(sourceArea);
      final RenderedImage raster;
      try {
        // read
        raster =
            request
                .getReadType()
                .read(
                    readParameters,
                    imageIndex,
                    granuleUrl,
                    selectedlevel.rasterDimensions,
                    reader,
                    hints,
                    false);

      } catch (Throwable e) {
        if (LOGGER.isLoggable(java.util.logging.Level.FINE)) {
          LOGGER.log(
              java.util.logging.Level.FINE,
              "Unable to load raster for granuleDescriptor "
                  + this.toString()
                  + " with request "
                  + request.toString()
                  + " Resulting in no granule loaded: Empty result",
              e);
        }
        return null;
      }

      // use fixed source area
      sourceArea.setRect(readParameters.getSourceRegion());

      //
      // setting new coefficients to define a new affineTransformation
      // to be applied to the grid to world transformation
      // -----------------------------------------------------------------------------------
      //
      // With respect to the original envelope, the obtained planarImage
      // needs to be rescaled. The scaling factors are computed as the
      // ratio between the cropped source region sizes and the read
      // image sizes.
      //
      // place it in the mosaic using the coords created above;
      double decimationScaleX = ((1.0 * sourceArea.width) / raster.getWidth());
      double decimationScaleY = ((1.0 * sourceArea.height) / raster.getHeight());
      final AffineTransform decimationScaleTranform =
          XAffineTransform.getScaleInstance(decimationScaleX, decimationScaleY);

      // keep into account translation  to work into the selected level raster space
      final AffineTransform afterDecimationTranslateTranform =
          XAffineTransform.getTranslateInstance(sourceArea.x, sourceArea.y);

      // now we need to go back to the base level raster space
      final AffineTransform backToBaseLevelScaleTransform = selectedlevel.baseToLevelTransform;

      // now create the overall transform
      final AffineTransform finalRaster2Model = new AffineTransform(baseGridToWorld);
      finalRaster2Model.concatenate(CoverageUtilities.CENTER_TO_CORNER);
      final double x = finalRaster2Model.getTranslateX();
      final double y = finalRaster2Model.getTranslateY();

      if (!XAffineTransform.isIdentity(backToBaseLevelScaleTransform, Utils.AFFINE_IDENTITY_EPS))
        finalRaster2Model.concatenate(backToBaseLevelScaleTransform);
      if (!XAffineTransform.isIdentity(afterDecimationTranslateTranform, Utils.AFFINE_IDENTITY_EPS))
        finalRaster2Model.concatenate(afterDecimationTranslateTranform);
      if (!XAffineTransform.isIdentity(decimationScaleTranform, Utils.AFFINE_IDENTITY_EPS))
        finalRaster2Model.concatenate(decimationScaleTranform);

      // keep into account translation factors to place this tile
      finalRaster2Model.preConcatenate((AffineTransform) mosaicWorldToGrid);
      final Interpolation interpolation = request.getInterpolation();
      // paranoiac check to avoid that JAI freaks out when computing its internal layouT on images
      // that are too small
      Rectangle2D finalLayout =
          ImageUtilities.layoutHelper(
              raster,
              (float) finalRaster2Model.getScaleX(),
              (float) finalRaster2Model.getScaleY(),
              (float) finalRaster2Model.getTranslateX(),
              (float) finalRaster2Model.getTranslateY(),
              interpolation);
      if (finalLayout.isEmpty()) {
        if (LOGGER.isLoggable(java.util.logging.Level.INFO))
          LOGGER.info(
              "Unable to create a granuleDescriptor "
                  + this.toString()
                  + " due to jai scale bug creating a null source area");
        return null;
      }
      ROI granuleLoadingShape = null;
      if (granuleROIShape != null) {

        final Point2D translate =
            mosaicWorldToGrid.transform(new DirectPosition2D(x, y), (Point2D) null);
        AffineTransform tx2 = new AffineTransform();
        tx2.preConcatenate(
            AffineTransform.getScaleInstance(
                ((AffineTransform) mosaicWorldToGrid).getScaleX(),
                -((AffineTransform) mosaicWorldToGrid).getScaleY()));
        tx2.preConcatenate(
            AffineTransform.getScaleInstance(
                ((AffineTransform) baseGridToWorld).getScaleX(),
                -((AffineTransform) baseGridToWorld).getScaleY()));
        tx2.preConcatenate(
            AffineTransform.getTranslateInstance(translate.getX(), translate.getY()));
        granuleLoadingShape = (ROI) granuleROIShape.transform(tx2);
      }
      // apply the affine transform  conserving indexed color model
      final RenderingHints localHints =
          new RenderingHints(
              JAI.KEY_REPLACE_INDEX_COLOR_MODEL,
              interpolation instanceof InterpolationNearest ? Boolean.FALSE : Boolean.TRUE);
      if (XAffineTransform.isIdentity(finalRaster2Model, Utils.AFFINE_IDENTITY_EPS)) {
        return new GranuleLoadingResult(raster, granuleLoadingShape, granuleUrl, doFiltering);
      } else {
        //
        // In case we are asked to use certain tile dimensions we tile
        // also at this stage in case the read type is Direct since
        // buffered images comes up untiled and this can affect the
        // performances of the subsequent affine operation.
        //
        final Dimension tileDimensions = request.getTileDimensions();
        if (tileDimensions != null && request.getReadType().equals(ReadType.DIRECT_READ)) {
          final ImageLayout layout = new ImageLayout();
          layout.setTileHeight(tileDimensions.width).setTileWidth(tileDimensions.height);
          localHints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout));
        } else {
          if (hints != null && hints.containsKey(JAI.KEY_IMAGE_LAYOUT)) {
            final Object layout = hints.get(JAI.KEY_IMAGE_LAYOUT);
            if (layout != null && layout instanceof ImageLayout) {
              localHints.add(
                  new RenderingHints(JAI.KEY_IMAGE_LAYOUT, ((ImageLayout) layout).clone()));
            }
          }
        }
        if (hints != null && hints.containsKey(JAI.KEY_TILE_CACHE)) {
          final Object cache = hints.get(JAI.KEY_TILE_CACHE);
          if (cache != null && cache instanceof TileCache)
            localHints.add(new RenderingHints(JAI.KEY_TILE_CACHE, (TileCache) cache));
        }
        if (hints != null && hints.containsKey(JAI.KEY_TILE_SCHEDULER)) {
          final Object scheduler = hints.get(JAI.KEY_TILE_SCHEDULER);
          if (scheduler != null && scheduler instanceof TileScheduler)
            localHints.add(new RenderingHints(JAI.KEY_TILE_SCHEDULER, (TileScheduler) scheduler));
        }
        boolean addBorderExtender = true;
        if (hints != null && hints.containsKey(JAI.KEY_BORDER_EXTENDER)) {
          final Object extender = hints.get(JAI.KEY_BORDER_EXTENDER);
          if (extender != null && extender instanceof BorderExtender) {
            localHints.add(new RenderingHints(JAI.KEY_BORDER_EXTENDER, (BorderExtender) extender));
            addBorderExtender = false;
          }
        }
        // border extender
        if (addBorderExtender) {
          localHints.add(ImageUtilities.BORDER_EXTENDER_HINTS);
        }
        //                boolean hasScaleX=!(Math.abs(finalRaster2Model.getScaleX()-1) <
        // 1E-2/(raster.getWidth()+1-raster.getMinX()));
        //                boolean hasScaleY=!(Math.abs(finalRaster2Model.getScaleY()-1) <
        // 1E-2/(raster.getHeight()+1-raster.getMinY()));
        //                boolean hasShearX=!(finalRaster2Model.getShearX() == 0.0);
        //                boolean hasShearY=!(finalRaster2Model.getShearY() == 0.0);
        //                boolean hasTranslateX=!(Math.abs(finalRaster2Model.getTranslateX()) <
        // 0.01F);
        //                boolean hasTranslateY=!(Math.abs(finalRaster2Model.getTranslateY()) <
        // 0.01F);
        //                boolean isTranslateXInt=!(Math.abs(finalRaster2Model.getTranslateX() -
        // (int) finalRaster2Model.getTranslateX()) <  0.01F);
        //                boolean isTranslateYInt=!(Math.abs(finalRaster2Model.getTranslateY() -
        // (int) finalRaster2Model.getTranslateY()) <  0.01F);
        //
        //                boolean isIdentity = finalRaster2Model.isIdentity() &&
        // !hasScaleX&&!hasScaleY &&!hasTranslateX&&!hasTranslateY;

        //                // TODO how can we check that the a skew is harmelss????
        //                if(isIdentity){
        //                    // TODO check if we are missing anything like tiling or such that
        // comes from hints
        //                    return new GranuleLoadingResult(raster, granuleLoadingShape,
        // granuleUrl, doFiltering);
        //                }
        //
        //                // TOLERANCE ON PIXELS SIZE
        //
        //                // Check and see if the affine transform is in fact doing
        //                // a Translate operation. That is a scale by 1 and no rotation.
        //                // In which case call translate. Note that only integer translate
        //                // is applicable. For non-integer translate we'll have to do the
        //                // affine.
        //                // If the hints contain an ImageLayout hint, we can't use
        //                // TranslateIntOpImage since it isn't capable of dealing with that.
        //                // Get ImageLayout from renderHints if any.
        //                ImageLayout layout = RIFUtil.getImageLayoutHint(localHints);
        //                if ( !hasScaleX &&
        //                     !hasScaleY  &&
        //                      !hasShearX&&
        //                      !hasShearY&&
        //                      isTranslateXInt&&
        //                      isTranslateYInt&&
        //                    layout == null) {
        //                    // It's a integer translate
        //                    return new GranuleLoadingResult(new TranslateIntOpImage(raster,
        //                                                    localHints,
        //                                                   (int) finalRaster2Model.getShearX(),
        //                                                   (int)
        // finalRaster2Model.getShearY()),granuleLoadingShape, granuleUrl, doFiltering);
        //                }

        ImageWorker iw = new ImageWorker(raster);
        iw.setRenderingHints(localHints);
        iw.affine(finalRaster2Model, interpolation, request.getBackgroundValues());
        return new GranuleLoadingResult(
            iw.getRenderedImage(), granuleLoadingShape, granuleUrl, doFiltering);
      }

    } catch (IllegalStateException e) {
      if (LOGGER.isLoggable(java.util.logging.Level.WARNING)) {
        LOGGER.log(
            java.util.logging.Level.WARNING,
            new StringBuilder("Unable to load raster for granuleDescriptor ")
                .append(this.toString())
                .append(" with request ")
                .append(request.toString())
                .append(" Resulting in no granule loaded: Empty result")
                .toString(),
            e);
      }
      return null;
    } catch (org.opengis.referencing.operation.NoninvertibleTransformException e) {
      if (LOGGER.isLoggable(java.util.logging.Level.WARNING)) {
        LOGGER.log(
            java.util.logging.Level.WARNING,
            new StringBuilder("Unable to load raster for granuleDescriptor ")
                .append(this.toString())
                .append(" with request ")
                .append(request.toString())
                .append(" Resulting in no granule loaded: Empty result")
                .toString(),
            e);
      }
      return null;
    } catch (TransformException e) {
      if (LOGGER.isLoggable(java.util.logging.Level.WARNING)) {
        LOGGER.log(
            java.util.logging.Level.WARNING,
            new StringBuilder("Unable to load raster for granuleDescriptor ")
                .append(this.toString())
                .append(" with request ")
                .append(request.toString())
                .append(" Resulting in no granule loaded: Empty result")
                .toString(),
            e);
      }
      return null;

    } finally {
      try {
        if (request.getReadType() != ReadType.JAI_IMAGEREAD && inStream != null) {
          inStream.close();
        }
      } finally {
        if (request.getReadType() != ReadType.JAI_IMAGEREAD && reader != null) {
          reader.dispose();
        }
      }
    }
  }
Example #7
0
  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;
  }
Example #8
0
  public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
    prepareRead(imageIndex, param);
    this.theImage = getDestination(param, getImageTypes(imageIndex), width, height);

    srcXSubsampling = imageReadParam.getSourceXSubsampling();
    srcYSubsampling = imageReadParam.getSourceYSubsampling();

    Point p = imageReadParam.getDestinationOffset();
    dstXOffset = p.x;
    dstYOffset = p.y;

    // This could probably be made more efficient...
    Rectangle srcRegion = new Rectangle(0, 0, 0, 0);
    Rectangle destRegion = new Rectangle(0, 0, 0, 0);

    computeRegions(imageReadParam, width, height, theImage, srcRegion, destRegion);

    // Initial source pixel, taking source region and source
    // subsamplimg offsets into account
    sourceXOffset = srcRegion.x;
    sourceYOffset = srcRegion.y;

    pixelsToRead = destRegion.width * destRegion.height;
    pixelsRead = 0;

    processImageStarted(imageIndex);
    processImageProgress(0.0f);

    tilesAcross = (width + tileOrStripWidth - 1) / tileOrStripWidth;
    tilesDown = (height + tileOrStripHeight - 1) / tileOrStripHeight;

    int compression = getCompression();

    // Attempt to get decompressor and color converted from the read param

    TIFFColorConverter colorConverter = null;
    if (imageReadParam instanceof TIFFImageReadParam) {
      TIFFImageReadParam tparam = (TIFFImageReadParam) imageReadParam;
      this.decompressor = tparam.getTIFFDecompressor();
      colorConverter = tparam.getColorConverter();
    }

    // If we didn't find one, use a standard decompressor
    if (this.decompressor == null) {
      if (compression == BaselineTIFFTagSet.COMPRESSION_NONE) {
        // Get the fillOrder field.
        TIFFField fillOrderField = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_FILL_ORDER);

        // Set the decompressor based on the fill order.
        if (fillOrderField != null && fillOrderField.getAsInt(0) == 2) {
          this.decompressor = new TIFFLSBDecompressor();
        } else {
          this.decompressor = new TIFFNullDecompressor();
        }
      } else if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
        this.decompressor = new TIFFFaxDecompressor();
      } else if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4) {
        this.decompressor = new TIFFFaxDecompressor();
      } else if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE) {
        this.decompressor = new TIFFFaxDecompressor();
      } else if (compression == BaselineTIFFTagSet.COMPRESSION_PACKBITS) {
        if (DEBUG) {
          System.out.println("Using TIFFPackBitsDecompressor");
        }
        this.decompressor = new TIFFPackBitsDecompressor();
      } else if (compression == BaselineTIFFTagSet.COMPRESSION_LZW) {
        if (DEBUG) {
          System.out.println("Using TIFFLZWDecompressor");
        }
        TIFFField predictorField = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR);
        int predictor =
            ((predictorField == null)
                ? BaselineTIFFTagSet.PREDICTOR_NONE
                : predictorField.getAsInt(0));
        this.decompressor = new TIFFLZWDecompressor(predictor);
      } else if (compression == BaselineTIFFTagSet.COMPRESSION_JPEG) {
        this.decompressor = new TIFFJPEGDecompressor();
      } else if (compression == BaselineTIFFTagSet.COMPRESSION_ZLIB
          || compression == BaselineTIFFTagSet.COMPRESSION_DEFLATE) {
        TIFFField predictorField = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PREDICTOR);
        int predictor =
            ((predictorField == null)
                ? BaselineTIFFTagSet.PREDICTOR_NONE
                : predictorField.getAsInt(0));
        this.decompressor = new TIFFDeflateDecompressor(predictor);
      } else if (compression == BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
        TIFFField JPEGProcField = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_PROC);
        if (JPEGProcField == null) {
          processWarningOccurred(
              "JPEGProc field missing; assuming baseline sequential JPEG process.");
        } else if (JPEGProcField.getAsInt(0) != BaselineTIFFTagSet.JPEG_PROC_BASELINE) {
          throw new IIOException(
              "Old-style JPEG supported for baseline sequential JPEG process only!");
        }
        this.decompressor = new TIFFOldJPEGDecompressor();
        // throw new IIOException("Old-style JPEG not supported!");
      } else {
        throw new IIOException("Unsupported compression type (tag number = " + compression + ")!");
      }

      if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
          && compression != BaselineTIFFTagSet.COMPRESSION_JPEG
          && compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
        boolean convertYCbCrToRGB =
            theImage.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_RGB;
        TIFFDecompressor wrappedDecompressor =
            this.decompressor instanceof TIFFNullDecompressor ? null : this.decompressor;
        this.decompressor = new TIFFYCbCrDecompressor(wrappedDecompressor, convertYCbCrToRGB);
      }
    }

    if (DEBUG) {
      System.out.println("\nDecompressor class = " + decompressor.getClass().getName() + "\n");
    }

    if (colorConverter == null) {
      if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_CIELAB
          && theImage.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_RGB) {
        colorConverter = new TIFFCIELabColorConverter();
      } else if (photometricInterpretation == BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_Y_CB_CR
          && !(this.decompressor instanceof TIFFYCbCrDecompressor)
          && compression != BaselineTIFFTagSet.COMPRESSION_JPEG
          && compression != BaselineTIFFTagSet.COMPRESSION_OLD_JPEG) {
        colorConverter = new TIFFYCbCrColorConverter(imageMetadata);
      }
    }

    decompressor.setReader(this);
    decompressor.setMetadata(imageMetadata);
    decompressor.setImage(theImage);

    decompressor.setPhotometricInterpretation(photometricInterpretation);
    decompressor.setCompression(compression);
    decompressor.setSamplesPerPixel(samplesPerPixel);
    decompressor.setBitsPerSample(bitsPerSample);
    decompressor.setSampleFormat(sampleFormat);
    decompressor.setExtraSamples(extraSamples);
    decompressor.setColorMap(colorMap);

    decompressor.setColorConverter(colorConverter);

    decompressor.setSourceXOffset(sourceXOffset);
    decompressor.setSourceYOffset(sourceYOffset);
    decompressor.setSubsampleX(srcXSubsampling);
    decompressor.setSubsampleY(srcYSubsampling);

    decompressor.setDstXOffset(dstXOffset);
    decompressor.setDstYOffset(dstYOffset);

    decompressor.setSourceBands(sourceBands);
    decompressor.setDestinationBands(destinationBands);

    // Compute bounds on the tile indices for this source region.
    int minTileX = TIFFImageWriter.XToTileX(srcRegion.x, 0, tileOrStripWidth);
    int minTileY = TIFFImageWriter.YToTileY(srcRegion.y, 0, tileOrStripHeight);
    int maxTileX = TIFFImageWriter.XToTileX(srcRegion.x + srcRegion.width - 1, 0, tileOrStripWidth);
    int maxTileY =
        TIFFImageWriter.YToTileY(srcRegion.y + srcRegion.height - 1, 0, tileOrStripHeight);

    boolean isAbortRequested = false;
    if (planarConfiguration == BaselineTIFFTagSet.PLANAR_CONFIGURATION_PLANAR) {

      decompressor.setPlanar(true);

      int[] sb = new int[1];
      int[] db = new int[1];
      for (int tj = minTileY; tj <= maxTileY; tj++) {
        for (int ti = minTileX; ti <= maxTileX; ti++) {
          for (int band = 0; band < numBands; band++) {
            sb[0] = sourceBands[band];
            decompressor.setSourceBands(sb);
            db[0] = destinationBands[band];
            decompressor.setDestinationBands(db);
            // XXX decompressor.beginDecoding();

            // The method abortRequested() is synchronized
            // so check it only once per loop just before
            // doing any actual decoding.
            if (abortRequested()) {
              isAbortRequested = true;
              break;
            }

            decodeTile(ti, tj, band);
          }

          if (isAbortRequested) break;

          reportProgress();
        }

        if (isAbortRequested) break;
      }
    } else {
      // XXX decompressor.beginDecoding();

      for (int tj = minTileY; tj <= maxTileY; tj++) {
        for (int ti = minTileX; ti <= maxTileX; ti++) {
          // The method abortRequested() is synchronized
          // so check it only once per loop just before
          // doing any actual decoding.
          if (abortRequested()) {
            isAbortRequested = true;
            break;
          }

          decodeTile(ti, tj, -1);

          reportProgress();
        }

        if (isAbortRequested) break;
      }
    }

    if (isAbortRequested) {
      processReadAborted();
    } else {
      processImageComplete();
    }

    return theImage;
  }
  /**
   * Tests the {@link OverviewsController} with support for different resolutions/different number
   * of overviews.
   *
   * <p>world_a.tif => Pixel Size = (0.833333333333333,-0.833333333333333); 4 overviews world_b.tif
   * => Pixel Size = (1.406250000000000,-1.406250000000000); 2 overviews
   *
   * @throws IOException
   * @throws MismatchedDimensionException
   * @throws FactoryException
   * @throws TransformException
   */
  @Test
  public void testHeterogeneousGranules()
      throws IOException, MismatchedDimensionException, FactoryException, TransformException {

    final CoordinateReferenceSystem WGS84 = CRS.decode("EPSG:4326", true);
    final ReferencedEnvelope TEST_BBOX_A = new ReferencedEnvelope(-180, 0, -90, 90, WGS84);
    final ReferencedEnvelope TEST_BBOX_B = new ReferencedEnvelope(0, 180, 0, 90, WGS84);

    URL heterogeneousGranulesURL = TestData.url(this, "heterogeneous");
    // //
    //
    // Initialize mosaic variables
    //
    // //
    final Hints hints = new Hints(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM, WGS84);
    final AbstractGridFormat format =
        (AbstractGridFormat) GridFormatFinder.findFormat(heterogeneousGranulesURL, hints);
    Assert.assertNotNull(format);
    Assert.assertFalse("UknownFormat", format instanceof UnknownFormat);

    final ImageMosaicReader reader =
        (ImageMosaicReader) format.getReader(heterogeneousGranulesURL, hints);
    Assert.assertNotNull(reader);

    final int nOv = reader.getNumberOfOvervies();
    final double[] hRes = reader.getHighestRes();
    final RasterManager rasterManager = new RasterManager(reader);

    // //
    //
    // Initialize granules related variables
    //
    // //
    final File g1File = new File(DataUtilities.urlToFile(heterogeneousGranulesURL), "world_a.tif");
    final File g2File = new File(DataUtilities.urlToFile(heterogeneousGranulesURL), "world_b.tif");
    final ImageReadParam readParamsG1 = new ImageReadParam();
    final ImageReadParam readParamsG2 = new ImageReadParam();
    int imageIndexG1 = 0;
    int imageIndexG2 = 0;

    final GranuleDescriptor granuleDescriptor1 =
        new GranuleDescriptor(g1File.getAbsolutePath(), TEST_BBOX_A, spi, (Geometry) null, true);
    final GranuleDescriptor granuleDescriptor2 =
        new GranuleDescriptor(g2File.getAbsolutePath(), TEST_BBOX_B, spi, (Geometry) null, true);
    assertNotNull(granuleDescriptor1.toString());
    assertNotNull(granuleDescriptor2.toString());

    final OverviewsController ovControllerG1 = granuleDescriptor1.overviewsController;
    final OverviewsController ovControllerG2 = granuleDescriptor2.overviewsController;

    // //
    //
    // Initializing read request
    //
    // //
    final GeneralEnvelope envelope = reader.getOriginalEnvelope();
    final GridEnvelope originalRange = reader.getOriginalGridRange();
    final Rectangle rasterArea =
        new Rectangle(
            0,
            0,
            (int) Math.ceil(originalRange.getSpan(0) / 9.0),
            (int) Math.ceil(originalRange.getSpan(1) / 9.0));
    final GridEnvelope2D range = new GridEnvelope2D(rasterArea);
    final GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(range, envelope);
    geMapper.setPixelAnchor(PixelInCell.CELL_CENTER);
    final AffineTransform gridToWorld = geMapper.createAffineTransform();
    final double requestedResolution[] =
        new double[] {
          XAffineTransform.getScaleX0(gridToWorld), XAffineTransform.getScaleY0(gridToWorld)
        };

    TestSet at = null;
    if (nOv == 4 && Math.abs(hRes[0] - 0.833333333333) <= THRESHOLD) {
      at = at1;
    } else if (nOv == 2 && Math.abs(hRes[0] - 1.40625) <= THRESHOLD) {
      at = at2;
    } else {
      return;
    }

    // //
    //
    // Starting OverviewsController tests
    //
    // //
    final OverviewPolicy[] ovPolicies =
        new OverviewPolicy[] {
          OverviewPolicy.QUALITY,
          OverviewPolicy.SPEED,
          OverviewPolicy.NEAREST,
          OverviewPolicy.IGNORE
        };
    for (int i = 0; i < ovPolicies.length; i++) {
      OverviewPolicy ovPolicy = ovPolicies[i];
      LOGGER.info("Testing with OverviewPolicy = " + ovPolicy.toString());
      imageIndexG1 =
          ReadParamsController.setReadParams(
              requestedResolution,
              ovPolicy,
              DecimationPolicy.ALLOW,
              readParamsG1,
              rasterManager,
              ovControllerG1);
      imageIndexG2 =
          ReadParamsController.setReadParams(
              requestedResolution,
              ovPolicy,
              DecimationPolicy.ALLOW,
              readParamsG2,
              rasterManager,
              ovControllerG2);
      assertSame(at.ot[i].g1.imageIndex, imageIndexG1);
      assertSame(at.ot[i].g2.imageIndex, imageIndexG2);
      assertSame(at.ot[i].g1.ssx, readParamsG1.getSourceXSubsampling());
      assertSame(at.ot[i].g1.ssy, readParamsG1.getSourceYSubsampling());
      assertSame(at.ot[i].g2.ssx, readParamsG2.getSourceXSubsampling());
      assertSame(at.ot[i].g2.ssy, readParamsG2.getSourceYSubsampling());
    }
  }
  @Override
  public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
    Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
    ImageTypeSpecifier rawType = getRawImageType(imageIndex);

    if (header.getPaletteInfo() != PCX.PALETTEINFO_COLOR
        && header.getPaletteInfo() != PCX.PALETTEINFO_GRAY) {
      processWarningOccurred(
          String.format(
              "Unsupported color mode: %d, colors may look incorrect", header.getPaletteInfo()));
    }

    int width = getWidth(imageIndex);
    int height = getHeight(imageIndex);

    BufferedImage destination = getDestination(param, imageTypes, width, height);

    Rectangle srcRegion = new Rectangle();
    Rectangle destRegion = new Rectangle();
    computeRegions(param, width, height, destination, srcRegion, destRegion);

    WritableRaster destRaster =
        clipToRect(
            destination.getRaster(),
            destRegion,
            param != null ? param.getDestinationBands() : null);
    checkReadParamBandSettings(param, rawType.getNumBands(), destRaster.getNumBands());

    int compression = header.getCompression();

    // Wrap input (COMPRESSION_RLE is really the only value allowed)
    DataInput input =
        compression == PCX.COMPRESSION_RLE
            ? new DataInputStream(
                new DecoderStream(IIOUtil.createStreamAdapter(imageInput), new RLEDecoder()))
            : imageInput;

    int xSub = param != null ? param.getSourceXSubsampling() : 1;
    int ySub = param != null ? param.getSourceYSubsampling() : 1;

    processImageStarted(imageIndex);

    if (rawType.getColorModel() instanceof IndexColorModel && header.getChannels() > 1) {
      // Bit planes!
      // Create raster from a default 8 bit layout
      WritableRaster rowRaster = GRAYSCALE.createBufferedImage(header.getWidth(), 1).getRaster();

      // Clip to source region
      Raster clippedRow =
          clipRowToRect(
              rowRaster,
              srcRegion,
              param != null ? param.getSourceBands() : null,
              param != null ? param.getSourceXSubsampling() : 1);

      int planeWidth = header.getBytesPerLine();
      byte[] planeData = new byte[planeWidth * 8];

      byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();

      for (int y = 0; y < height; y++) {
        switch (header.getBitsPerPixel()) {
          case 1:
            readRowByte(
                input,
                srcRegion,
                xSub,
                ySub,
                planeData,
                0,
                planeWidth * header.getChannels(),
                destRaster,
                clippedRow,
                y);
            break;
          default:
            throw new AssertionError();
        }

        int pixelPos = 0;
        for (int planePos = 0; planePos < planeWidth; planePos++) {
          BitRotator.bitRotateCW(planeData, planePos, planeWidth, rowDataByte, pixelPos, 1);
          pixelPos += 8;
        }

        processImageProgress(100f * y / height);

        if (y >= srcRegion.y + srcRegion.height) {
          break;
        }

        if (abortRequested()) {
          processReadAborted();
          break;
        }
      }
    } else if (header.getBitsPerPixel() == 24 || header.getBitsPerPixel() == 32) {
      // Can't use width here, as we need to take bytesPerLine into account, and re-create a width
      // based on this
      int rowWidth = (header.getBytesPerLine() * 8) / header.getBitsPerPixel();
      WritableRaster rowRaster = rawType.createBufferedImage(rowWidth, 1).getRaster();

      // Clip to source region
      Raster clippedRow =
          clipRowToRect(
              rowRaster,
              srcRegion,
              param != null ? param.getSourceBands() : null,
              param != null ? param.getSourceXSubsampling() : 1);

      for (int y = 0; y < height; y++) {
        byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
        readRowByte(
            input,
            srcRegion,
            xSub,
            ySub,
            rowDataByte,
            0,
            rowDataByte.length,
            destRaster,
            clippedRow,
            y);

        processImageProgress(100f * y / height);

        if (y >= srcRegion.y + srcRegion.height) {
          break;
        }

        if (abortRequested()) {
          processReadAborted();
          break;
        }
      }
    } else {
      // Can't use width here, as we need to take bytesPerLine into account, and re-create a width
      // based on this
      int rowWidth = (header.getBytesPerLine() * 8) / header.getBitsPerPixel();
      WritableRaster rowRaster = rawType.createBufferedImage(rowWidth, 1).getRaster();

      // Clip to source region
      Raster clippedRow =
          clipRowToRect(
              rowRaster,
              srcRegion,
              param != null ? param.getSourceBands() : null,
              param != null ? param.getSourceXSubsampling() : 1);

      for (int y = 0; y < height; y++) {
        for (int c = 0; c < header.getChannels(); c++) {
          WritableRaster destChannel =
              destRaster.createWritableChild(
                  destRaster.getMinX(),
                  destRaster.getMinY(),
                  destRaster.getWidth(),
                  destRaster.getHeight(),
                  0,
                  0,
                  new int[] {c});
          Raster srcChannel =
              clippedRow.createChild(
                  clippedRow.getMinX(), 0, clippedRow.getWidth(), 1, 0, 0, new int[] {c});

          switch (header.getBitsPerPixel()) {
            case 1:
            case 2:
            case 4:
            case 8:
              byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData(c);
              readRowByte(
                  input,
                  srcRegion,
                  xSub,
                  ySub,
                  rowDataByte,
                  0,
                  rowDataByte.length,
                  destChannel,
                  srcChannel,
                  y);
              break;
            default:
              throw new AssertionError();
          }

          if (abortRequested()) {
            break;
          }
        }

        processImageProgress(100f * y / height);

        if (y >= srcRegion.y + srcRegion.height) {
          break;
        }

        if (abortRequested()) {
          processReadAborted();
          break;
        }
      }
    }

    processImageComplete();

    return destination;
  }