/*
* $Id$
*
* Copyright (c) 2010, 2011 by Joel Uckelman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.tools.image.tilecache;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import VASSAL.tools.image.GeneralFilter;
import VASSAL.tools.lang.Callback;
/**
* Slices an image into tiles.
*
* @since 3.2.0
* @author Joel Uckelman
*/
public class TileSlicerImpl implements TileSlicer {
/**
* Slices an image into tiles.
*
* @param src the source image
* @param iname the basename for the tiles
* @param tpath the path for the tiles
* @param tw the tile width
* @param th the tile height
* @param exec the executor in which to run tasks
* @param progress a callback for indicating progress
*/
public void slice(
BufferedImage src,
String iname,
String tpath,
int tw,
int th,
ExecutorService exec,
Callback<Void> progress
) throws IOException
{
final int sw = src.getWidth();
final int sh = src.getHeight();
final List<Future<Void>> futures = new ArrayList<Future<Void>>();
// slice unscaled 1:1 tiles
final TaskMaker unscaled = new TaskMaker() {
public TileTask make(BufferedImage src, File f,
int tx, int ty, int tw, int th, int sw, int sh) {
return new TileTask(src, f, tx, ty, tw, th, sw, sh);
}
};
queueTileTasks(
src, iname, tpath, 1, tw, th, sw, sh, unscaled, exec, futures
);
// slice scaled tiles, starting at 1:2
final TaskMaker scaled = new TaskMaker() {
private final GeneralFilter.Filter filter =
new GeneralFilter.Lanczos3Filter();
public TileTask make(BufferedImage src, File f,
int tx, int ty, int tw, int th, int dw, int dh) {
return new ScaledTileTask(src, f, filter, tx, ty, tw, th, dw, dh);
}
};
for (int div = 2; sw/div > 0 && sh/div > 0; div <<= 1) {
final int dw = sw/div;
final int dh = sh/div;
queueTileTasks(
src, iname, tpath, div, tw, th, dw, dh, scaled, exec, futures
);
}
// wait for all tiles to complete
try {
for (Future<Void> f : futures) {
f.get();
progress.receive(null);
}
}
catch (CancellationException e) {
// should never happen
throw new IllegalStateException(e);
}
catch (ExecutionException e) {
throw (IOException) new IOException().initCause(e);
}
catch (InterruptedException e) {
// should never happen
throw new IllegalStateException(e);
}
finally {
// cancel everything if anything fails
for (Future<Void> f : futures) {
if (!f.isDone()) f.cancel(true);
}
}
}
protected static interface TaskMaker {
public TileTask make(BufferedImage src, File f,
int tx, int ty, int tw, int th, int dw, int dh);
}
protected static void queueTileTasks(
BufferedImage src,
String iname,
String tpath,
int div,
int tw,
int th,
int dw,
int dh,
TaskMaker tm,
ExecutorService exec,
List<Future<Void>> futures
)
{
final int tcols = (int) Math.ceil((double) dw / tw);
final int trows = (int) Math.ceil((double) dh / th);
for (int tx = 0; tx < tcols; ++tx) {
for (int ty = 0; ty < trows; ++ty) {
final String tn = TileUtils.tileName(iname, tx, ty, div);
final File f = new File(tpath, tn);
final TileTask tt = tm.make(src, f, tx, ty, tw, th, dw, dh);
futures.add(exec.submit(tt));
}
}
}
}