public boolean canEncodeImage(ImageTypeSpecifier type) {
    int dataType = type.getSampleModel().getDataType();
    if (dataType < DataBuffer.TYPE_BYTE || dataType > DataBuffer.TYPE_INT) return false;

    SampleModel sm = type.getSampleModel();
    int numBands = sm.getNumBands();
    if (!(numBands == 1 || numBands == 3)) return false;

    if (numBands == 1 && dataType != DataBuffer.TYPE_BYTE) return false;

    if (dataType > DataBuffer.TYPE_BYTE && !(sm instanceof SinglePixelPackedSampleModel))
      return false;

    return true;
  }
  public boolean canEncodeImage(ImageTypeSpecifier type) {
    SampleModel sm = type.getSampleModel();
    if (!(sm instanceof MultiPixelPackedSampleModel)) return false;
    if (sm.getSampleSize(0) != 1) return false;

    return true;
  }
  private void prepareRead(int imageIndex, ImageReadParam param) throws IOException {
    if (stream == null) {
      throw new IllegalStateException("Input not set!");
    }

    // A null ImageReadParam means we use the default
    if (param == null) {
      param = getDefaultReadParam();
    }

    this.imageReadParam = param;

    seekToImage(imageIndex);

    this.tileOrStripWidth = getTileOrStripWidth();
    this.tileOrStripHeight = getTileOrStripHeight();
    this.planarConfiguration = getPlanarConfiguration();

    this.sourceBands = param.getSourceBands();
    if (sourceBands == null) {
      sourceBands = new int[numBands];
      for (int i = 0; i < numBands; i++) {
        sourceBands[i] = i;
      }
    }

    // Initialize the destination image
    Iterator imageTypes = getImageTypes(imageIndex);
    ImageTypeSpecifier theImageType = ImageUtil.getDestinationType(param, imageTypes);

    int destNumBands = theImageType.getSampleModel().getNumBands();

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

    if (sourceBands.length != destinationBands.length) {
      throw new IllegalArgumentException("sourceBands.length != destinationBands.length");
    }

    for (int i = 0; i < sourceBands.length; i++) {
      int sb = sourceBands[i];
      if (sb < 0 || sb >= numBands) {
        throw new IllegalArgumentException("Source band out of range!");
      }
      int db = destinationBands[i];
      if (db < 0 || db >= destNumBands) {
        throw new IllegalArgumentException("Destination band out of range!");
      }
    }
  }
  /**
   * 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);
  }
  public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
    GIFWritableImageMetadata imageMetadata = new GIFWritableImageMetadata();

    // Image dimensions

    SampleModel sampleModel = imageType.getSampleModel();

    Rectangle sourceBounds = new Rectangle(sampleModel.getWidth(), sampleModel.getHeight());
    Dimension destSize = new Dimension();
    computeRegions(sourceBounds, destSize, param);

    imageMetadata.imageWidth = destSize.width;
    imageMetadata.imageHeight = destSize.height;

    // Interlacing

    if (param != null
        && param.canWriteProgressive()
        && param.getProgressiveMode() == ImageWriteParam.MODE_DISABLED) {
      imageMetadata.interlaceFlag = false;
    } else {
      imageMetadata.interlaceFlag = true;
    }

    // Local color table

    ColorModel colorModel = imageType.getColorModel();

    imageMetadata.localColorTable = createColorTable(colorModel, sampleModel);

    // Transparency

    if (colorModel instanceof IndexColorModel) {
      int transparentIndex = ((IndexColorModel) colorModel).getTransparentPixel();
      if (transparentIndex != -1) {
        imageMetadata.transparentColorFlag = true;
        imageMetadata.transparentColorIndex = transparentIndex;
      }
    }

    return imageMetadata;
  }
  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;
  }
  public Iterator getImageTypes(int imageIndex) throws IIOException {
    List l; // List of ImageTypeSpecifiers

    Integer imageIndexInteger = new Integer(imageIndex);
    if (imageTypeMap.containsKey(imageIndexInteger)) {
      // Return the cached ITS List.
      l = (List) imageTypeMap.get(imageIndexInteger);
    } else {
      // Create a new ITS List.
      l = new ArrayList(1);

      // Create the ITS and cache if for later use so that this method
      // always returns an Iterator containing the same ITS objects.
      seekToImage(imageIndex);
      ImageTypeSpecifier itsRaw =
          TIFFDecompressor.getRawImageTypeSpecifier(
              photometricInterpretation,
              compression,
              samplesPerPixel,
              bitsPerSample,
              sampleFormat,
              extraSamples,
              colorMap);

      // Check for an ICCProfile field.
      TIFFField iccProfileField = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_ICC_PROFILE);

      // If an ICCProfile field is present change the ImageTypeSpecifier
      // to use it if the data layout is component type.
      if (iccProfileField != null && itsRaw.getColorModel() instanceof ComponentColorModel) {
        // Create a ColorSpace from the profile.
        byte[] iccProfileValue = iccProfileField.getAsBytes();
        ICC_Profile iccProfile = ICC_Profile.getInstance(iccProfileValue);
        ICC_ColorSpace iccColorSpace = new ICC_ColorSpace(iccProfile);

        // Get the raw sample and color information.
        ColorModel cmRaw = itsRaw.getColorModel();
        ColorSpace csRaw = cmRaw.getColorSpace();
        SampleModel smRaw = itsRaw.getSampleModel();

        // Get the number of samples per pixel and the number
        // of color components.
        int numBands = smRaw.getNumBands();
        int numComponents = iccColorSpace.getNumComponents();

        // Replace the ColorModel with the ICC ColorModel if the
        // numbers of samples and color components are amenable.
        if (numBands == numComponents || numBands == numComponents + 1) {
          // Set alpha flags.
          boolean hasAlpha = numComponents != numBands;
          boolean isAlphaPre = hasAlpha && cmRaw.isAlphaPremultiplied();

          // Create a ColorModel of the same class and with
          // the same transfer type.
          ColorModel iccColorModel =
              new ComponentColorModel(
                  iccColorSpace,
                  cmRaw.getComponentSize(),
                  hasAlpha,
                  isAlphaPre,
                  cmRaw.getTransparency(),
                  cmRaw.getTransferType());

          // Prepend the ICC profile-based ITS to the List. The
          // ColorModel and SampleModel are guaranteed to be
          // compatible as the old and new ColorModels are both
          // ComponentColorModels with the same transfer type
          // and the same number of components.
          l.add(new ImageTypeSpecifier(iccColorModel, smRaw));

          // Append the raw ITS to the List if and only if its
          // ColorSpace has the same type and number of components
          // as the ICC ColorSpace.
          if (csRaw.getType() == iccColorSpace.getType()
              && csRaw.getNumComponents() == iccColorSpace.getNumComponents()) {
            l.add(itsRaw);
          }
        } else { // ICCProfile not compatible with SampleModel.
          // Append the raw ITS to the List.
          l.add(itsRaw);
        }
      } else { // No ICCProfile field or raw ColorModel not component.
        // Append the raw ITS to the List.
        l.add(itsRaw);
      }

      // Cache the ITS List.
      imageTypeMap.put(imageIndexInteger, l);
    }

    return l.iterator();
  }
  /**
   * Initializes these instance variables from the image metadata:
   *
   * <pre>
   * compression
   * width
   * height
   * samplesPerPixel
   * numBands
   * colorMap
   * photometricInterpretation
   * sampleFormat
   * bitsPerSample
   * extraSamples
   * tileOrStripWidth
   * tileOrStripHeight
   * </pre>
   */
  private void initializeFromMetadata() {
    TIFFField f;

    // Compression
    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COMPRESSION);
    if (f == null) {
      processWarningOccurred("Compression field is missing; assuming no compression");
      compression = BaselineTIFFTagSet.COMPRESSION_NONE;
    } else {
      compression = f.getAsInt(0);
    }

    // Whether key dimensional information is absent.
    boolean isMissingDimension = false;

    // ImageWidth -> width
    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH);
    if (f != null) {
      this.width = f.getAsInt(0);
    } else {
      processWarningOccurred("ImageWidth field is missing.");
      isMissingDimension = true;
    }

    // ImageLength -> height
    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH);
    if (f != null) {
      this.height = f.getAsInt(0);
    } else {
      processWarningOccurred("ImageLength field is missing.");
      isMissingDimension = true;
    }

    // SamplesPerPixel
    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL);
    if (f != null) {
      samplesPerPixel = f.getAsInt(0);
    } else {
      samplesPerPixel = 1;
      isMissingDimension = true;
    }

    // If any dimension is missing and there is a JPEG stream available
    // get the information from it.
    int defaultBitDepth = 1;
    if (isMissingDimension
        && (f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_JPEG_INTERCHANGE_FORMAT))
            != null) {
      Iterator iter = ImageIO.getImageReadersByFormatName("JPEG");
      if (iter != null && iter.hasNext()) {
        ImageReader jreader = (ImageReader) iter.next();
        try {
          stream.mark();
          stream.seek(f.getAsLong(0));
          jreader.setInput(stream);
          if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_WIDTH) == null) {
            this.width = jreader.getWidth(0);
          }
          if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_IMAGE_LENGTH) == null) {
            this.height = jreader.getHeight(0);
          }
          ImageTypeSpecifier imageType = jreader.getRawImageType(0);
          if (imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLES_PER_PIXEL) == null) {
            this.samplesPerPixel = imageType.getSampleModel().getNumBands();
          }
          stream.reset();
          defaultBitDepth = imageType.getColorModel().getComponentSize(0);
        } catch (IOException e) {
          // Ignore it and proceed: an error will occur later.
        }
        jreader.dispose();
      }
    }

    if (samplesPerPixel < 1) {
      processWarningOccurred("Samples per pixel < 1!");
    }

    // SamplesPerPixel -> numBands
    numBands = samplesPerPixel;

    // ColorMap
    this.colorMap = null;
    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_COLOR_MAP);
    if (f != null) {
      // Grab color map
      colorMap = f.getAsChars();
    }

    // PhotometricInterpretation
    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION);
    if (f == null) {
      if (compression == BaselineTIFFTagSet.COMPRESSION_CCITT_RLE
          || compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_4
          || compression == BaselineTIFFTagSet.COMPRESSION_CCITT_T_6) {
        processWarningOccurred(
            "PhotometricInterpretation field is missing; " + "assuming WhiteIsZero");
        photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO;
      } else if (this.colorMap != null) {
        photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR;
      } else if (samplesPerPixel == 3 || samplesPerPixel == 4) {
        photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_RGB;
      } else {
        processWarningOccurred(
            "PhotometricInterpretation field is missing; " + "assuming BlackIsZero");
        photometricInterpretation = BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO;
      }
    } else {
      photometricInterpretation = f.getAsInt(0);
    }

    // SampleFormat
    boolean replicateFirst = false;
    int first = -1;

    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_SAMPLE_FORMAT);
    sampleFormat = new int[samplesPerPixel];
    replicateFirst = false;
    if (f == null) {
      replicateFirst = true;
      first = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
    } else if (f.getCount() != samplesPerPixel) {
      replicateFirst = true;
      first = f.getAsInt(0);
    }

    for (int i = 0; i < samplesPerPixel; i++) {
      sampleFormat[i] = replicateFirst ? first : f.getAsInt(i);
      if (sampleFormat[i] != BaselineTIFFTagSet.SAMPLE_FORMAT_UNSIGNED_INTEGER
          && sampleFormat[i] != BaselineTIFFTagSet.SAMPLE_FORMAT_SIGNED_INTEGER
          && sampleFormat[i] != BaselineTIFFTagSet.SAMPLE_FORMAT_FLOATING_POINT
          && sampleFormat[i] != BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED) {
        processWarningOccurred("Illegal value for SAMPLE_FORMAT, assuming SAMPLE_FORMAT_UNDEFINED");
        sampleFormat[i] = BaselineTIFFTagSet.SAMPLE_FORMAT_UNDEFINED;
      }
    }

    // BitsPerSample
    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
    this.bitsPerSample = new int[samplesPerPixel];
    replicateFirst = false;
    if (f == null) {
      replicateFirst = true;
      first = defaultBitDepth;
    } else if (f.getCount() != samplesPerPixel) {
      replicateFirst = true;
      first = f.getAsInt(0);
    }

    for (int i = 0; i < samplesPerPixel; i++) {
      // Replicate initial value if not enough values provided
      bitsPerSample[i] = replicateFirst ? first : f.getAsInt(i);

      if (DEBUG) {
        System.out.println("bitsPerSample[" + i + "] = " + bitsPerSample[i]);
      }
    }

    // ExtraSamples
    this.extraSamples = null;
    f = imageMetadata.getTIFFField(BaselineTIFFTagSet.TAG_EXTRA_SAMPLES);
    if (f != null) {
      extraSamples = f.getAsInts();
    }

    //         System.out.println("colorMap = " + colorMap);
    //         if (colorMap != null) {
    //             for (int i = 0; i < colorMap.length; i++) {
    //              System.out.println("colorMap[" + i + "] = " + (int)(colorMap[i]));
    //             }
    //         }

  }
  /**
   * @see GridCoverageReader#read(GeneralParameterValue[])
   * @return A new {@linkplain GridCoverage grid coverage} from the input source, or {@code null} if
   *     the requested envelope is outside the data bounds
   */
  public GridCoverage2D read(GeneralParameterValue[] params) throws IOException {
    final GeneralEnvelope requestedEnvelope;
    final GridEnvelope requestedDim;
    final OverviewPolicy overviewPolicy;
    {
      final ReadParameters opParams = parseReadParams(params);
      overviewPolicy = opParams.overviewPolicy;
      requestedEnvelope = opParams.requestedEnvelope;
      requestedDim = opParams.dim;
    }

    /*
     * For each raster in the raster dataset, obtain the tiles, pixel range, and resulting
     * envelope
     */
    final List<RasterQueryInfo> queries;
    queries = findMatchingRasters(requestedEnvelope, requestedDim, overviewPolicy);
    if (queries.isEmpty()) {
      if (requestedEnvelope.intersects(getOriginalEnvelope(), true)) {
        /*
         * No matching rasters but envelopes intersect, meaning it's a raster catalog with
         * irregular coverage and the request lies on an area with no coverage
         */
        ImageTypeSpecifier imageTypeSpecifier;
        imageTypeSpecifier = RasterUtils.createFullImageTypeSpecifier(rasterInfo, 0);
        SampleModel sampleModel = imageTypeSpecifier.getSampleModel();
        Point location = new Point(0, 0);
        WritableRaster raster = Raster.createWritableRaster(sampleModel, location);
        GridCoverage2D emptyCoverage;
        emptyCoverage = coverageFactory.create(coverageName, raster, requestedEnvelope);
        return emptyCoverage;
      }
      /*
       * none of the rasters match the requested envelope.
       */
      return null;
    }

    final LoggingHelper log = new LoggingHelper();

    /*
     * Once we collected the matching rasters and their image subsets, find out where in the
     * overall resulting mosaic they fit. If the rasters does not share the spatial resolution,
     * the QueryInfo.resultDimension and QueryInfo.mosaicLocation width or height won't match
     */
    final GridEnvelope mosaicGeometry;
    mosaicGeometry = RasterUtils.setMosaicLocations(rasterInfo, queries);

    if (mosaicGeometry.getSpan(0) == 0 || mosaicGeometry.getSpan(1) == 0) {
      LOGGER.finer(
          "Mosaic geometry width or height is zero,"
              + " returning fake coverage for pixels "
              + mosaicGeometry);
      return null;
    }
    /*
     * Gather the rendered images for each of the rasters that match the requested envelope
     */
    final TiledRasterReader rasterReader = rasterReaderFactory.create(rasterInfo);

    try {
      readAllTiledRasters(queries, rasterReader, log);
    } finally {
      // rasterReader.dispose();
    }

    log.log(LoggingHelper.REQ_ENV);
    log.log(LoggingHelper.RES_ENV);
    log.log(LoggingHelper.MOSAIC_ENV);
    log.log(LoggingHelper.MOSAIC_EXPECTED);

    final RenderedImage coverageRaster = createMosaic(queries, mosaicGeometry, log);

    assert mosaicGeometry.getSpan(0) == coverageRaster.getWidth();
    assert mosaicGeometry.getSpan(1) == coverageRaster.getHeight();

    /*
     * BUILDING COVERAGE
     */
    GridSampleDimension[] bands = getSampleDimensions(coverageRaster);

    final GeneralEnvelope resultEnvelope = getResultEnvelope(queries, mosaicGeometry);
    log.appendLoggingGeometries(LoggingHelper.REQ_ENV, requestedEnvelope);
    log.appendLoggingGeometries(LoggingHelper.RES_ENV, resultEnvelope);

    GridCoverage2D resultCoverage =
        coverageFactory.create(coverageName, coverageRaster, resultEnvelope, bands, null, null);

    // MathTransform gridToCRS = rasterInfo.getRasterToModel(queries.get(0).getRasterIndex(),
    // queries.get(0).getPyramidLevel());
    //
    // GridGeometry2D gridGeometry = new GridGeometry2D(PixelInCell.CELL_CORNER, gridToCRS,
    // resultEnvelope, hints);
    //
    // GridCoverage[] sources = null;
    // Map<?, ?> properties = null;
    // GridCoverage2D resultCoverage = coverageFactory.create(coverageName, coverageRaster,
    // gridGeometry, bands, sources, properties);
    return resultCoverage;
  }
 public SampleModel getSampleModel() {
   return its.getSampleModel();
 }