/**
  * Returns {@code true} if at least one tile in the given collection has at "<cite>grid to real
  * world</cite>" transform waiting to be processed by {@link RegionCalculator}. It is okay to
  * conservatively returns {@code true} in situations where we would have got {@code false} if
  * synchronization was performed on every tiles.
  */
 private static boolean hasPendingGridToCRS(final Collection<Tile> tiles) {
   for (final Tile tile : tiles) {
     if (tile.getPendingGridToCRS(false) != null) {
       return true;
     }
   }
   return false;
 }
  private List<Tile> toTiles(
      ImageReaderSpi provider, TileReaderPool readers, Set<ImageReaderSpi> providers, Path input)
      throws IOException {

    // Creates the tile for the first image, which usually have the maximal resolution.
    // The Tile constructor will read the TFW file and infer a provider if the given
    // 'provider' argument is null. If this is a new provider, then we need to declare
    // it to the pool of image readers before to use it.
    final List<Tile> tiles = new ArrayList<>();
    final Tile root = new Tile(provider, input, 0);
    if (providers.add(root.getImageReaderSpi())) {
      readers.setProviders(providers);
    }

    final AffineTransform scaledGridToCRS = new AffineTransform();
    final AffineTransform gridToCRS = root.getPendingGridToCRS(false);
    final ImageReader reader = root.getImageReader(readers, true, true);
    final int numImages = reader.getNumImages(false); // Result may be -1.
    for (int index = 0; index != numImages; index++) { // Intentional use of !=, not <.
      final int width, height;
      try {
        width = reader.getWidth(index);
        height = reader.getHeight(index);
      } catch (IndexOutOfBoundsException e) {
        // As explained in ImageReader javadoc, this approach is sometime
        // more efficient than invoking reader.getNumImages(true) first.
        break;
      }
      final Tile tile;
      if (index == 0) {
        tile = root;
      } else {
        final Rectangle region = root.getRegion();
        scaledGridToCRS.setTransform(new AffineTransform(gridToCRS));
        scaledGridToCRS.scale(region.width / (double) width, region.height / (double) height);
        tile = new Tile(root.getImageReaderSpi(), input, index, region, scaledGridToCRS);
      }
      tile.setSize(width, height);
      tiles.add(tile);
    }
    reader.dispose();
    return tiles;
  }
 /**
  * Creates tile managers from the specified collection of tiles. This method usually returns a
  * single tile manager, but more could be returned if this factory has been unable to put every
  * tiles in a single mosaic (for example if the ratio between {@linkplain AffineTransform affine
  * transform} given to {@linkplain Tile#Tile(ImageReaderSpi,Object,int,Rectangle,AffineTransform)
  * tile constructor} would lead to fractional {@linkplain Tile#getSubsampling subsampling}).
  *
  * @param tiles The tiles to give to a tile manager.
  * @return A tile manager created from the given tiles.
  * @throws IOException If an I/O operation was required and failed.
  */
 public TileManager[] create(Collection<Tile> tiles) throws IOException {
   int count = 0;
   final TileManager[] managers;
   if (!hasPendingGridToCRS(tiles)) {
     /*
      * There is no tile having a "gridToCRS" transform pending RegionCalculator work. So we
      * can create (at the end of this method) a single TileManager using all those tiles.
      */
     if (!tiles.isEmpty()) {
       count = 1;
     }
     managers = new TileManager[count];
   } else {
     /*
      * At least one tile have a pending "gridToCRS" transform (actually we should have
      * more than one - typically all of them - otherwise the RegionCalculator work will
      * be useless). Computes their region now. Note that we could execute this block
      * unconditionally. The 'hasPendingGridToCRS' check we just for avoiding the cost
      * of creating RegionCalculator in the common case where it is not needed. So it is
      * not a big deal if 'hasPendingGridToCRS' conservatively returned 'true'.
      */
     final Collection<Tile> remainings = new ArrayList<>(Math.min(16, tiles.size()));
     final RegionCalculator calculator = new RegionCalculator();
     for (final Tile tile : tiles) {
       if (!calculator.add(tile)) {
         remainings.add(tile);
       }
     }
     if (!remainings.isEmpty()) {
       count = 1;
     }
     final Map<ImageGeometry, Tile[]> split = calculator.tiles();
     managers = new TileManager[split.size() + count];
     for (final Map.Entry<ImageGeometry, Tile[]> entry : split.entrySet()) {
       final TileManager manager = createGeneric(entry.getValue());
       manager.setGridGeometry(entry.getKey());
       managers[count++] = manager;
     }
     tiles = remainings;
   }
   /*
    * The collection now contains tiles that has not been processed by RegionCalculator,
    * because their 'gridToCRS' transform is flagged as already computed. Create a mosaic
    * for them, and use the affine transform having the finest resolution as the "global"
    * one.
    */
   if (!tiles.isEmpty()) {
     final TileManager manager = createGeneric(tiles.toArray(new Tile[tiles.size()]));
     final Rectangle imageBounds = new Rectangle(-1, -1);
     AffineTransform gridToCRS = null;
     Dimension subsampling = null;
     double scale = Double.POSITIVE_INFINITY;
     for (final Tile tile : tiles) {
       imageBounds.add(tile.getAbsoluteRegion());
       final AffineTransform candidate = tile.getGridToCRS();
       if (candidate != null && !candidate.equals(gridToCRS)) {
         final double cs = XAffineTransform.getScale(candidate);
         if (cs < scale) {
           // Found a new tile at a finer resolution.
           scale = cs;
           gridToCRS = candidate;
           subsampling = tile.getSubsampling();
         } else if (cs == scale) {
           // Inconsistent transform at the finest level.
           // Abandon the attempt to create a grid geometry.
           gridToCRS = null;
           break;
         }
       }
     }
     if (gridToCRS != null) {
       if (subsampling.width != 1 || subsampling.height != 1) {
         gridToCRS = new AffineTransform(gridToCRS);
         gridToCRS.scale(subsampling.width, subsampling.height);
       }
       manager.setGridGeometry(new ImageGeometry(imageBounds, gridToCRS));
     }
     managers[0] = manager;
   }
   return managers;
 }