mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 19:15:29 -04:00
Rewritten threading code to use latches, to avoid shutting down the executor service between steps.
This commit is contained in:
parent
6ba32b657a
commit
280407d9c0
@ -28,6 +28,9 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||||
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageReadParam;
|
import javax.imageio.ImageReadParam;
|
||||||
import javax.imageio.ImageReader;
|
import javax.imageio.ImageReader;
|
||||||
@ -42,6 +45,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -55,14 +59,19 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
public class MappedBufferImage {
|
public class MappedBufferImage {
|
||||||
private static int threads = Runtime.getRuntime().availableProcessors();
|
private static int threads = Runtime.getRuntime().availableProcessors();
|
||||||
|
private static ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
|
int argIndex = 0;
|
||||||
|
File file = args.length > 0 ? new File(args[argIndex]) : null;
|
||||||
|
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
BufferedImage image;
|
BufferedImage image;
|
||||||
File file = args.length > 0 ? new File(args[0]) : null;
|
|
||||||
|
|
||||||
if (file != null && file.exists()) {
|
if (file != null && file.exists()) {
|
||||||
|
argIndex++;
|
||||||
|
|
||||||
// Load image using ImageIO
|
// Load image using ImageIO
|
||||||
ImageInputStream input = ImageIO.createImageInputStream(file);
|
ImageInputStream input = ImageIO.createImageInputStream(file);
|
||||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||||
@ -74,36 +83,47 @@ public class MappedBufferImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImageReader reader = readers.next();
|
ImageReader reader = readers.next();
|
||||||
reader.setInput(input);
|
try {
|
||||||
|
reader.setInput(input);
|
||||||
|
|
||||||
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
|
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
|
||||||
ImageTypeSpecifier type = types.next();
|
ImageTypeSpecifier type = types.next();
|
||||||
|
|
||||||
// TODO: Negotiate best layout according to the GraphicsConfiguration.
|
// TODO: Negotiate best layout according to the GraphicsConfiguration.
|
||||||
|
|
||||||
w = reader.getWidth(0);
|
w = reader.getWidth(0);
|
||||||
h = reader.getHeight(0);
|
h = reader.getHeight(0);
|
||||||
|
|
||||||
// GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
// GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||||
// ColorModel cm2 = configuration.getColorModel(cm.getTransparency());
|
// ColorModel cm2 = configuration.getColorModel(cm.getTransparency());
|
||||||
|
|
||||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, cm2);
|
// image = MappedImageFactory.createCompatibleMappedImage(w, h, cm2);
|
||||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, cm);
|
// image = MappedImageFactory.createCompatibleMappedImage(w, h, cm);
|
||||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
|
// image = MappedImageFactory.createCompatibleMappedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, BufferedImage.TYPE_INT_BGR);
|
// image = MappedImageFactory.createCompatibleMappedImage(w, h, BufferedImage.TYPE_INT_BGR);
|
||||||
image = MappedImageFactory.createCompatibleMappedImage(w, h, type);
|
// image = MappedImageFactory.createCompatibleMappedImage(w, h, type);
|
||||||
// image = type.createBufferedImage(w, h);
|
// if (w > 1024 || h > 1024) {
|
||||||
|
image = MappedImageFactory.createCompatibleMappedImage(w, h, type);
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// image = type.createBufferedImage(w, h);
|
||||||
|
// }
|
||||||
|
|
||||||
System.out.println("image = " + image);
|
System.out.println("image = " + image);
|
||||||
|
|
||||||
ImageReadParam param = reader.getDefaultReadParam();
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
param.setDestination(image);
|
param.setDestination(image);
|
||||||
|
|
||||||
reader.read(0, param);
|
reader.addIIOReadProgressListener(new ConsoleProgressListener());
|
||||||
|
reader.read(0, param);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
w = args.length > 0 ? Integer.parseInt(args[0]) : 6000;
|
w = args.length > argIndex && StringUtil.isNumber(args[argIndex]) ? Integer.parseInt(args[argIndex++]) : 6000;
|
||||||
h = args.length > 1 ? Integer.parseInt(args[1]) : w * 2 / 3;
|
h = args.length > argIndex && StringUtil.isNumber(args[argIndex]) ? Integer.parseInt(args[argIndex++]) : w * 2 / 3;
|
||||||
|
|
||||||
GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||||
image = MappedImageFactory.createCompatibleMappedImage(w, h, configuration, Transparency.TRANSLUCENT);
|
image = MappedImageFactory.createCompatibleMappedImage(w, h, configuration, Transparency.TRANSLUCENT);
|
||||||
@ -122,8 +142,8 @@ public class MappedBufferImage {
|
|||||||
paintDots(w, h, image);
|
paintDots(w, h, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make re-sampling optional
|
// Resample down to some fixed size
|
||||||
if (true) {
|
if (args.length > argIndex && "-scale".equals(args[argIndex++])) {
|
||||||
image = resampleImage(image, 800);
|
image = resampleImage(image, 800);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,14 +181,14 @@ public class MappedBufferImage {
|
|||||||
float aspect = image.getHeight() / (float) image.getWidth();
|
float aspect = image.getHeight() / (float) image.getWidth();
|
||||||
int height = Math.round(width * aspect);
|
int height = Math.round(width * aspect);
|
||||||
|
|
||||||
ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
|
||||||
|
|
||||||
// NOTE: The createCompatibleDestImage takes the byte order/layout into account, unlike the cm.createCompatibleWritableRaster
|
// NOTE: The createCompatibleDestImage takes the byte order/layout into account, unlike the cm.createCompatibleWritableRaster
|
||||||
final BufferedImage output = new ResampleOp(width, height).createCompatibleDestImage(image, null);
|
final BufferedImage output = new ResampleOp(width, height).createCompatibleDestImage(image, null);
|
||||||
|
|
||||||
final int inStep = (int) Math.ceil(image.getHeight() / (double) threads);
|
final int inStep = (int) Math.ceil(image.getHeight() / (double) threads);
|
||||||
final int outStep = (int) Math.ceil(height / (double) threads);
|
final int outStep = (int) Math.ceil(height / (double) threads);
|
||||||
|
|
||||||
|
final CountDownLatch latch = new CountDownLatch(threads);
|
||||||
|
|
||||||
// Resample image in slices
|
// Resample image in slices
|
||||||
for (int i = 0; i < threads; i++) {
|
for (int i = 0; i < threads; i++) {
|
||||||
final int inY = i * inStep;
|
final int inY = i * inStep;
|
||||||
@ -181,15 +201,17 @@ public class MappedBufferImage {
|
|||||||
BufferedImage in = image.getSubimage(0, inY, image.getWidth(), inHeight);
|
BufferedImage in = image.getSubimage(0, inY, image.getWidth(), inHeight);
|
||||||
BufferedImage out = output.getSubimage(0, outY, width, outHeight);
|
BufferedImage out = output.getSubimage(0, outY, width, outHeight);
|
||||||
new ResampleOp(width, outHeight, ResampleOp.FILTER_LANCZOS).filter(in, out);
|
new ResampleOp(width, outHeight, ResampleOp.FILTER_LANCZOS).filter(in, out);
|
||||||
|
// new ResampleOp(width, outHeight, ResampleOp.FILTER_LANCZOS).resample(in, out, ResampleOp.createFilter(ResampleOp.FILTER_LANCZOS));
|
||||||
// BufferedImage out = new ResampleOp(width, outHeight, ResampleOp.FILTER_LANCZOS).filter(in, null);
|
// BufferedImage out = new ResampleOp(width, outHeight, ResampleOp.FILTER_LANCZOS).filter(in, null);
|
||||||
// ImageUtil.drawOnto(output.getSubimage(0, outY, width, outHeight), out);
|
// ImageUtil.drawOnto(output.getSubimage(0, outY, width, outHeight), out);
|
||||||
|
|
||||||
// showIt(width, outHeight, out, "foo");
|
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -200,8 +222,7 @@ public class MappedBufferImage {
|
|||||||
|
|
||||||
Boolean done = null;
|
Boolean done = null;
|
||||||
try {
|
try {
|
||||||
executorService.shutdown();
|
done = latch.await(5L, TimeUnit.MINUTES);
|
||||||
done = executorService.awaitTermination(5L, TimeUnit.MINUTES);
|
|
||||||
}
|
}
|
||||||
catch (InterruptedException ignore) {
|
catch (InterruptedException ignore) {
|
||||||
}
|
}
|
||||||
@ -223,21 +244,19 @@ public class MappedBufferImage {
|
|||||||
Color.GRAY, Color.GREEN, Color.YELLOW, Color.PINK, Color.LIGHT_GRAY, Color.DARK_GRAY
|
Color.GRAY, Color.GREEN, Color.YELLOW, Color.PINK, Color.LIGHT_GRAY, Color.DARK_GRAY
|
||||||
};
|
};
|
||||||
|
|
||||||
ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
CountDownLatch latch = new CountDownLatch(threads);
|
||||||
|
|
||||||
int step = (int) Math.ceil(hs / (double) threads);
|
int step = (int) Math.ceil(hs / (double) threads);
|
||||||
Random r = new Random();
|
Random r = new Random();
|
||||||
|
|
||||||
for (int i = 0; i < threads; i++) {
|
for (int i = 0; i < threads; i++) {
|
||||||
executorService.submit(new PaintDotsTask(image, s, ws, colors, r, i * step, i * step + step));
|
executorService.submit(new PaintDotsTask(image, s, ws, colors, r, i * step, i * step + step, latch));
|
||||||
}
|
}
|
||||||
|
|
||||||
System.err.printf("Started painting in %d threads, waiting for execution to complete...%n", threads);
|
System.err.printf("Started painting in %d threads, waiting for execution to complete...%n", threads);
|
||||||
|
|
||||||
Boolean done = null;
|
Boolean done = null;
|
||||||
try {
|
try {
|
||||||
executorService.shutdown();
|
done = latch.await(3L, TimeUnit.MINUTES);
|
||||||
done = executorService.awaitTermination(3L, TimeUnit.MINUTES);
|
|
||||||
}
|
}
|
||||||
catch (InterruptedException ignore) {
|
catch (InterruptedException ignore) {
|
||||||
}
|
}
|
||||||
@ -279,16 +298,15 @@ public class MappedBufferImage {
|
|||||||
|
|
||||||
int step = (int) Math.ceil(h / (double) threads);
|
int step = (int) Math.ceil(h / (double) threads);
|
||||||
|
|
||||||
ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
CountDownLatch latch = new CountDownLatch(threads);
|
||||||
for (int i = 0; i < threads; i++) {
|
for (int i = 0; i < threads; i++) {
|
||||||
executorService.submit(new PaintBackgroundTask(w, h, buffer, alpha, i * step, i * step + step));
|
executorService.submit(new PaintBackgroundTask(w, h, buffer, alpha, i * step, i * step + step, latch));
|
||||||
}
|
}
|
||||||
System.err.printf("Started painting in %d threads, waiting for execution to complete...%n", threads);
|
System.err.printf("Started painting in %d threads, waiting for execution to complete...%n", threads);
|
||||||
|
|
||||||
Boolean done = null;
|
Boolean done = null;
|
||||||
try {
|
try {
|
||||||
executorService.shutdown();
|
done = latch.await(3L, TimeUnit.MINUTES);
|
||||||
done = executorService.awaitTermination(3L, TimeUnit.MINUTES);
|
|
||||||
}
|
}
|
||||||
catch (InterruptedException ignore) {
|
catch (InterruptedException ignore) {
|
||||||
}
|
}
|
||||||
@ -474,8 +492,9 @@ public class MappedBufferImage {
|
|||||||
private final Random random;
|
private final Random random;
|
||||||
private final int last;
|
private final int last;
|
||||||
private final int first;
|
private final int first;
|
||||||
|
private final CountDownLatch latch;
|
||||||
|
|
||||||
public PaintDotsTask(BufferedImage image, int s, int wstep, Color[] colors, Random random, int first, int last) {
|
public PaintDotsTask(BufferedImage image, int s, int wstep, Color[] colors, Random random, int first, int last, CountDownLatch latch) {
|
||||||
this.image = image;
|
this.image = image;
|
||||||
this.s = s;
|
this.s = s;
|
||||||
this.wstep = wstep;
|
this.wstep = wstep;
|
||||||
@ -483,10 +502,16 @@ public class MappedBufferImage {
|
|||||||
this.random = random;
|
this.random = random;
|
||||||
this.last = last;
|
this.last = last;
|
||||||
this.first = first;
|
this.first = first;
|
||||||
|
this.latch = latch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
paintDots0(image, s, wstep, colors, random, first, last);
|
try {
|
||||||
|
paintDots0(image, s, wstep, colors, random, first, last);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,18 +522,53 @@ public class MappedBufferImage {
|
|||||||
private final boolean alpha;
|
private final boolean alpha;
|
||||||
private final int first;
|
private final int first;
|
||||||
private final int last;
|
private final int last;
|
||||||
|
private final CountDownLatch latch;
|
||||||
|
|
||||||
public PaintBackgroundTask(int w, int h, DataBuffer buffer, boolean alpha, int first, int last) {
|
public PaintBackgroundTask(int w, int h, DataBuffer buffer, boolean alpha, int first, int last, CountDownLatch latch) {
|
||||||
this.w = w;
|
this.w = w;
|
||||||
this.h = h;
|
this.h = h;
|
||||||
this.buffer = buffer;
|
this.buffer = buffer;
|
||||||
this.alpha = alpha;
|
this.alpha = alpha;
|
||||||
this.first = first;
|
this.first = first;
|
||||||
this.last = last;
|
this.last = last;
|
||||||
|
this.latch = latch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
paintBackground0(w, h, buffer, alpha, first, last);
|
try {
|
||||||
|
paintBackground0(w, h, buffer, alpha, first, last);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ConsoleProgressListener extends ProgressListenerBase {
|
||||||
|
static final int COLUMNS = System.getenv("COLUMNS") != null ? Integer.parseInt(System.getenv("COLUMNS")) - 2 : 78;
|
||||||
|
int left = COLUMNS;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void imageComplete(ImageReader source) {
|
||||||
|
for (; left > 0; left--) {
|
||||||
|
System.out.print(".");
|
||||||
|
}
|
||||||
|
System.out.println("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void imageProgress(ImageReader source, float percentageDone) {
|
||||||
|
int progress = COLUMNS - Math.round(COLUMNS * percentageDone / 100f);
|
||||||
|
if (progress < left) {
|
||||||
|
for (; left > progress; left--) {
|
||||||
|
System.out.print(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void imageStarted(ImageReader source, int imageIndex) {
|
||||||
|
System.out.print("[");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,10 @@ import java.io.IOException;
|
|||||||
* @version $Id: MappedImageFactory.java,v 1.0 May 26, 2010 5:07:01 PM haraldk Exp$
|
* @version $Id: MappedImageFactory.java,v 1.0 May 26, 2010 5:07:01 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public final class MappedImageFactory {
|
public final class MappedImageFactory {
|
||||||
|
|
||||||
|
// TODO: Create a way to do ColorConvertOp (or other color space conversion) on these images.
|
||||||
|
// - Current implementation of CCOp delegates to internal sun.awt classes that assumes java.awt.DataBufferByte for type byte buffers :-/
|
||||||
|
|
||||||
private MappedImageFactory() {}
|
private MappedImageFactory() {}
|
||||||
|
|
||||||
public static BufferedImage createCompatibleMappedImage(int width, int height, int type) throws IOException {
|
public static BufferedImage createCompatibleMappedImage(int width, int height, int type) throws IOException {
|
||||||
@ -69,8 +73,6 @@ public final class MappedImageFactory {
|
|||||||
static BufferedImage createCompatibleMappedImage(int width, int height, SampleModel sm, ColorModel cm) throws IOException {
|
static BufferedImage createCompatibleMappedImage(int width, int height, SampleModel sm, ColorModel cm) throws IOException {
|
||||||
DataBuffer buffer = MappedFileBuffer.create(sm.getTransferType(), width * height * sm.getNumDataElements(), 1);
|
DataBuffer buffer = MappedFileBuffer.create(sm.getTransferType(), width * height * sm.getNumDataElements(), 1);
|
||||||
|
|
||||||
// TODO: Figure out if it's better to use SunWritableRaster when available? -- Haven't seen any improvements yet
|
|
||||||
// return new BufferedImage(cm, new SunWritableRaster(sm, buffer, new Point()), cm.isAlphaPremultiplied(), null);
|
|
||||||
return new BufferedImage(cm, new GenericWritableRaster(sm, buffer, new Point()), cm.isAlphaPremultiplied(), null);
|
return new BufferedImage(cm, new GenericWritableRaster(sm, buffer, new Point()), cm.isAlphaPremultiplied(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user