mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 19:15:29 -04:00
Cleaned up the NIO buffered image classes.
This commit is contained in:
parent
9d6f263b86
commit
f419cfecdd
102
sandbox/pom.xml
102
sandbox/pom.xml
@ -25,36 +25,89 @@
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-image</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-image</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jmock</groupId>
|
||||
<artifactId>jmock-cglib</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-image</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.swing</groupId>
|
||||
<artifactId>swing-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.swing</groupId>
|
||||
<artifactId>swing-application</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-io</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.3.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jmock</groupId>
|
||||
<artifactId>jmock-cglib</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@ -64,7 +117,7 @@
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@ -84,5 +137,18 @@
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<inherited>true</inherited>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
@ -15,4 +15,32 @@
|
||||
The TwelveMonkeys Sandbox Common. Experimental stuff.
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-io</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-image</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-io</artifactId>
|
||||
<scope>test</scope>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<scope>test</scope>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -6,7 +6,9 @@ import java.awt.image.SampleModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
/**
|
||||
* GenericWritableRaster
|
||||
* A generic writable raster.
|
||||
* For use when factory methods from {@link java.awt.image.Raster} can't be used,
|
||||
* typically because of custom data buffers.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
@ -19,6 +21,13 @@ class GenericWritableRaster extends WritableRaster {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%x: w = %s h = %s", getClass().getSimpleName(), System.identityHashCode(this), getWidth(), getHeight());
|
||||
return String.format(
|
||||
"%s: %s width = %s height = %s #Bands = %s xOff = %s yOff = %s %s",
|
||||
getClass().getSimpleName(),
|
||||
sampleModel,
|
||||
getWidth(), getHeight(), getNumBands(),
|
||||
sampleModelTranslateX, sampleModelTranslateY,
|
||||
dataBuffer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,22 @@
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* MappedBufferImage
|
||||
@ -15,53 +26,99 @@ import java.util.Random;
|
||||
* @version $Id: MappedBufferImage.java,v 1.0 Jun 13, 2010 7:33:19 PM haraldk Exp$
|
||||
*/
|
||||
public class MappedBufferImage {
|
||||
private static final boolean ALPHA = true;
|
||||
private static int threads = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
int w = args.length > 0 ? Integer.parseInt(args[0]) : 6000;
|
||||
int h = args.length > 1 ? Integer.parseInt(args[1]) : w * 2 / 3;
|
||||
int w;
|
||||
int h;
|
||||
BufferedImage image;
|
||||
File file = args.length > 0 ? new File(args[0]) : null;
|
||||
|
||||
GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||
BufferedImage image = MappedImageFactory.createCompatibleMappedImage(w, h, configuration, MappedBufferImage.ALPHA ? Transparency.TRANSLUCENT : Transparency.OPAQUE);
|
||||
if (file != null && file.exists()) {
|
||||
// Load image using ImageIO
|
||||
ImageInputStream input = ImageIO.createImageInputStream(file);
|
||||
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
|
||||
|
||||
System.out.println("image = " + image);
|
||||
|
||||
DataBuffer buffer = image.getRaster().getDataBuffer();
|
||||
|
||||
// Mix in some nice colors
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
int r = (int) ((x * y * 255.0) / (h * w));
|
||||
int g = (int) (((w - x) * y * 255.0) / (h * w));
|
||||
int b = (int) ((x * (h - y) * 255.0) / (h * w));
|
||||
int a = ALPHA ? (int) (((w - x) * (h - y) * 255.0) / (h * w)) : 0;
|
||||
|
||||
switch (buffer.getDataType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
int off = (y * w + x) * (ALPHA ? 4 : 3);
|
||||
if (ALPHA) {
|
||||
buffer.setElem(off++, 255 - a);
|
||||
buffer.setElem(off++, b);
|
||||
buffer.setElem(off++, g);
|
||||
buffer.setElem(off, r);
|
||||
}
|
||||
else {
|
||||
// TODO: Why the RGB / ABGR byte order inconsistency??
|
||||
buffer.setElem(off++, r);
|
||||
buffer.setElem(off++, g);
|
||||
buffer.setElem(off, b);
|
||||
}
|
||||
break;
|
||||
case DataBuffer.TYPE_INT:
|
||||
buffer.setElem(y * w + x, (255 - a) << 24 | r << 16 | g << 8 | b);
|
||||
break;
|
||||
default:
|
||||
System.err.println("Transfer type not supported: " + buffer.getDataType());
|
||||
}
|
||||
if (!readers.hasNext()) {
|
||||
System.err.println("No image reader found for input: " + file.getAbsolutePath());
|
||||
System.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
ImageReader reader = readers.next();
|
||||
reader.setInput(input);
|
||||
|
||||
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
|
||||
ImageTypeSpecifier type = types.next();
|
||||
|
||||
// TODO: Negotiate best layout according to the GraphicsConfiguration.
|
||||
|
||||
w = reader.getWidth(0);
|
||||
h = reader.getHeight(0);
|
||||
|
||||
// GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||
// ColorModel cm2 = configuration.getColorModel(cm.getTransparency());
|
||||
|
||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, cm2);
|
||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, cm);
|
||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
|
||||
image = MappedImageFactory.createCompatibleMappedImage(w, h, type);
|
||||
// image = type.createBufferedImage(w, h);
|
||||
|
||||
System.out.println("image = " + image);
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setDestination(image);
|
||||
|
||||
reader.read(0, param);
|
||||
}
|
||||
else {
|
||||
w = args.length > 0 ? Integer.parseInt(args[0]) : 6000;
|
||||
h = args.length > 1 ? Integer.parseInt(args[1]) : w * 2 / 3;
|
||||
|
||||
GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
|
||||
image = MappedImageFactory.createCompatibleMappedImage(w, h, configuration, Transparency.TRANSLUCENT);
|
||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, configuration, Transparency.OPAQUE);
|
||||
// image = MappedImageFactory.createCompatibleMappedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
|
||||
|
||||
System.out.println("image = " + image);
|
||||
|
||||
DataBuffer buffer = image.getRaster().getDataBuffer();
|
||||
final boolean alpha = image.getColorModel().hasAlpha();
|
||||
|
||||
// Mix in some nice colors
|
||||
createBackground(w, h, buffer, alpha);
|
||||
|
||||
// Add some random dots (get out the coffee)
|
||||
paintDots(w, h, image);
|
||||
}
|
||||
|
||||
// Add some random dots (get out the coffee)
|
||||
int bytesPerPixel = image.getColorModel().getPixelSize() / 8; // Calculate first to avoid overflow
|
||||
JFrame frame = new JFrame(String.format("Test [%s x %s] (%s)", w, h, toHumanReadableSize(w * h * bytesPerPixel))) {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
// TODO: This looks like a useful util method...
|
||||
DisplayMode displayMode = getGraphicsConfiguration().getDevice().getDisplayMode();
|
||||
Dimension size = super.getPreferredSize();
|
||||
|
||||
size.width = Math.min(size.width, displayMode.getWidth());
|
||||
size.height = Math.min(size.height, displayMode.getHeight());
|
||||
|
||||
return size;
|
||||
}
|
||||
};
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
JScrollPane scroll = new JScrollPane(new ImageComponent(image));
|
||||
scroll.setBorder(BorderFactory.createEmptyBorder());
|
||||
frame.add(scroll);
|
||||
frame.pack();
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static void paintDots(int w, int h, BufferedImage image) {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
int s = 300;
|
||||
int ws = w / s;
|
||||
int hs = h / s;
|
||||
@ -72,9 +129,28 @@ public class MappedBufferImage {
|
||||
};
|
||||
|
||||
Random r = new Random();
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
for (int y = 0; y < hs - 1; y++) {
|
||||
int step = (int) Math.ceil(hs / (double) threads);
|
||||
|
||||
for (int i = 0; i < threads; i++) {
|
||||
executorService.submit(new PaintDotsTask(image, s, ws, colors, r, i * step, i * step + step));
|
||||
}
|
||||
System.err.printf("Started painting in %d threads, waiting for execution to complete...%n", threads);
|
||||
|
||||
Boolean done = null;
|
||||
try {
|
||||
executorService.shutdown();
|
||||
done = executorService.awaitTermination(3L, TimeUnit.MINUTES);
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
|
||||
System.out.printf("%s painting %d dots in %d ms%n", (done == null ? "Interrupted" : !done ? "Timed out" : "Done"), Math.max(0, hs - 1) * Math.max(0, ws - 1), System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
private static void paintDots0(BufferedImage image, int s, int ws, Color[] colors, Random r, final int first, final int last) {
|
||||
for (int y = first; y < last; y++) {
|
||||
for (int x = 0; x < ws - 1; x++) {
|
||||
BufferedImage tile = image.getSubimage(x * s, y * s, 2 * s, 2 * s);
|
||||
Graphics2D g;
|
||||
@ -100,26 +176,75 @@ public class MappedBufferImage {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.printf("Done painting %d dots in %d ms%n", hs * ws, System.currentTimeMillis() - start);
|
||||
private static void createBackground(int w, int h, DataBuffer buffer, boolean alpha) {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
JFrame frame = new JFrame(String.format("Test [%s x %s] (%s)", w, h, toHumanReadableSize(w * h * (ALPHA ? 4 : 3))));
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
JScrollPane scroll = new JScrollPane(new ImageComponent(image));
|
||||
scroll.setBorder(BorderFactory.createEmptyBorder());
|
||||
frame.add(scroll);
|
||||
frame.pack();
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setVisible(true);
|
||||
int step = (int) Math.ceil(h / (double) threads);
|
||||
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
||||
for (int i = 0; i < threads; i++) {
|
||||
executorService.submit(new PaintBackgroundTask(w, h, buffer, alpha, i * step, i * step + step));
|
||||
}
|
||||
System.err.printf("Started painting in %d threads, waiting for execution to complete...%n", threads);
|
||||
|
||||
Boolean done = null;
|
||||
try {
|
||||
executorService.shutdown();
|
||||
done = executorService.awaitTermination(3L, TimeUnit.MINUTES);
|
||||
}
|
||||
catch (InterruptedException ignore) {
|
||||
}
|
||||
|
||||
System.out.printf("%s creating background in %d ms%n", (done == null ? "Interrupted" : !done ? "Timed out" : "Done"), System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
private static void paintBackground0(int w, int h, DataBuffer buffer, boolean alpha, final int first, final int last) {
|
||||
for (int y = first; y < last; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
int r = (int) ((x * y * 255.0) / (h * w));
|
||||
int g = (int) (((w - x) * y * 255.0) / (h * w));
|
||||
int b = (int) ((x * (h - y) * 255.0) / (h * w));
|
||||
int a = alpha ? (int) (((w - x) * (h - y) * 255.0) / (h * w)) : 0;
|
||||
|
||||
switch (buffer.getDataType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
int off = (y * w + x) * (alpha ? 4 : 3);
|
||||
if (alpha) {
|
||||
buffer.setElem(off++, 255 - a);
|
||||
buffer.setElem(off++, b);
|
||||
buffer.setElem(off++, g);
|
||||
buffer.setElem(off, r);
|
||||
}
|
||||
else {
|
||||
// TODO: Why the RGB / ABGR byte order inconsistency??
|
||||
buffer.setElem(off++, r);
|
||||
buffer.setElem(off++, g);
|
||||
buffer.setElem(off, b);
|
||||
}
|
||||
break;
|
||||
case DataBuffer.TYPE_INT:
|
||||
buffer.setElem(y * w + x, (255 - a) << 24 | r << 16 | g << 8 | b);
|
||||
break;
|
||||
default:
|
||||
System.err.println("Transfer type not supported: " + buffer.getDataType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String toHumanReadableSize(long size) {
|
||||
return String.format("%,d MB", (int) (size / (double) (1024L << 10)));
|
||||
return String.format("%,d MB", (long) (size / (double) (1024L << 10)));
|
||||
}
|
||||
|
||||
/**
|
||||
* A fairly optimized component for displaying a BufferedImage
|
||||
*/
|
||||
private static class ImageComponent extends JComponent implements Scrollable {
|
||||
private final BufferedImage image;
|
||||
private Paint texture;
|
||||
double zoom = 1;
|
||||
|
||||
public ImageComponent(final BufferedImage image) {
|
||||
setOpaque(true); // Very important when subclassing JComponent...
|
||||
@ -166,6 +291,11 @@ public class MappedBufferImage {
|
||||
g2.setPaint(texture);
|
||||
g2.fillRect(rect.x, rect.y, rect.width, rect.height);
|
||||
|
||||
if (zoom != 1) {
|
||||
AffineTransform transform = AffineTransform.getScaleInstance(zoom, zoom);
|
||||
g2.setTransform(transform);
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
repaintImage(rect, g2);
|
||||
System.err.println("repaint: " + (System.currentTimeMillis() - start) + " ms");
|
||||
@ -204,14 +334,13 @@ public class MappedBufferImage {
|
||||
// e.printStackTrace();
|
||||
// Happens whenever apple.awt.OSXCachingSufraceManager runs out of memory
|
||||
// TODO: Figure out why repaint(x,y,w,h) doesn't work any more..?
|
||||
// repaint(rect.x, rect.y, rect.width, rect.height); // NOTE: Will cause a brief flash while the component is redrawn
|
||||
repaint(); // NOTE: Might cause a brief flash while the component is redrawn
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(image.getWidth(), image.getHeight());
|
||||
return new Dimension((int) (image.getWidth() * zoom), (int) (image.getHeight() * zoom));
|
||||
}
|
||||
|
||||
public Dimension getPreferredScrollableViewportSize() {
|
||||
@ -240,4 +369,50 @@ public class MappedBufferImage {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PaintDotsTask implements Runnable {
|
||||
private final BufferedImage image;
|
||||
private final int s;
|
||||
private final int wstep;
|
||||
private final Color[] colors;
|
||||
private final Random random;
|
||||
private final int last;
|
||||
private final int first;
|
||||
|
||||
public PaintDotsTask(BufferedImage image, int s, int wstep, Color[] colors, Random random, int first, int last) {
|
||||
this.image = image;
|
||||
this.s = s;
|
||||
this.wstep = wstep;
|
||||
this.colors = colors;
|
||||
this.random = random;
|
||||
this.last = last;
|
||||
this.first = first;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
paintDots0(image, s, wstep, colors, random, first, last);
|
||||
}
|
||||
}
|
||||
|
||||
private static class PaintBackgroundTask implements Runnable {
|
||||
private final int w;
|
||||
private final int h;
|
||||
private final DataBuffer buffer;
|
||||
private final boolean alpha;
|
||||
private final int first;
|
||||
private final int last;
|
||||
|
||||
public PaintBackgroundTask(int w, int h, DataBuffer buffer, boolean alpha, int first, int last) {
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
this.buffer = buffer;
|
||||
this.alpha = alpha;
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
paintBackground0(w, h, buffer, alpha, first, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,14 +8,18 @@ import java.nio.*;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* MappedFileBuffer
|
||||
* A {@code DataBuffer} implementation that is backed by a memory mapped file.
|
||||
* Memory will be allocated outside the normal JVM heap, allowing more efficient
|
||||
* memory usage for large buffers.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: MappedFileBuffer.java,v 1.0 Jun 12, 2010 4:56:51 PM haraldk Exp$
|
||||
*
|
||||
* @see java.nio.channels.FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)
|
||||
*/
|
||||
public abstract class MappedFileBuffer extends DataBuffer {
|
||||
final Buffer buffer;
|
||||
private final Buffer buffer;
|
||||
|
||||
private MappedFileBuffer(final int type, final int size, final int numBanks) throws IOException {
|
||||
super(type, size, numBanks);
|
||||
@ -24,23 +28,10 @@ public abstract class MappedFileBuffer extends DataBuffer {
|
||||
throw new IllegalArgumentException("Integer overflow for size: " + size);
|
||||
}
|
||||
|
||||
int componentSize;
|
||||
switch (type) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
componentSize = 1;
|
||||
break;
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
componentSize = 2;
|
||||
break;
|
||||
case DataBuffer.TYPE_INT:
|
||||
componentSize = 4;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported data type: " + type);
|
||||
}
|
||||
int componentSize = DataBuffer.getDataTypeSize(type) / 8;
|
||||
|
||||
File tempFile = File.createTempFile(String.format("%s-", getClass().getSimpleName()), ".tmp");
|
||||
tempFile.deleteOnExit();
|
||||
// Create temp file to get a file handle to use for memory mapping
|
||||
File tempFile = File.createTempFile(String.format("%s-", getClass().getSimpleName().toLowerCase()), ".tmp");
|
||||
|
||||
try {
|
||||
RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
|
||||
@ -52,7 +43,6 @@ public abstract class MappedFileBuffer extends DataBuffer {
|
||||
|
||||
// Map entire file into memory, let OS virtual memory/paging do the heavy lifting
|
||||
MappedByteBuffer byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);
|
||||
// MappedByteBuffer byteBuffer = channel.map(FileChannel.MapMode.PRIVATE, 0, length);
|
||||
|
||||
switch (type) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
@ -72,15 +62,21 @@ public abstract class MappedFileBuffer extends DataBuffer {
|
||||
channel.close();
|
||||
}
|
||||
finally {
|
||||
// NOTE: File can't be deleted right now on Windows, as the file is open. Let JVM clean up later
|
||||
if (!tempFile.delete()) {
|
||||
System.err.println("Could not delete temp file: " + tempFile.getAbsolutePath());
|
||||
tempFile.deleteOnExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("MappedFileBuffer: %s", buffer);
|
||||
}
|
||||
|
||||
// TODO: Is throws IOException a good idea?
|
||||
|
||||
public static MappedFileBuffer create(final int type, final int size, final int numBanks) throws IOException {
|
||||
public static DataBuffer create(final int type, final int size, final int numBanks) throws IOException {
|
||||
switch (type) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
return new DataBufferByte(size, numBanks);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.twelvemonkeys.image;
|
||||
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
@ -8,42 +9,39 @@ import java.awt.image.SampleModel;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* MappedImageFactory
|
||||
* A factory for creating {@link BufferedImage}s backed by memory mapped files.
|
||||
* The data buffers will be allocated outside the normal JVM heap, allowing more efficient
|
||||
* memory usage for large images.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: MappedImageFactory.java,v 1.0 May 26, 2010 5:07:01 PM haraldk Exp$
|
||||
*/
|
||||
public class MappedImageFactory {
|
||||
|
||||
public static BufferedImage createCompatibleMappedImage(int width, int height, int type) throws IOException {
|
||||
return createCompatibleMappedImage(width, height, new BufferedImage(1, 1, type).getColorModel());
|
||||
BufferedImage temp = new BufferedImage(1, 1, type);
|
||||
return createCompatibleMappedImage(width, height, temp.getSampleModel().createCompatibleSampleModel(width, height), temp.getColorModel());
|
||||
}
|
||||
|
||||
public static BufferedImage createCompatibleMappedImage(int width, int height, GraphicsConfiguration configuration, int transparency) throws IOException {
|
||||
ColorModel cm = configuration.getColorModel(transparency);
|
||||
// ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
// ColorModel cm = new ComponentColorModel(cs, ALPHA, false, ALPHA ? TRANSLUCENT : OPAQUE, DataBuffer.TYPE_BYTE);
|
||||
return createCompatibleMappedImage(width, height, cm);
|
||||
// TODO: Should we also use the sample model?
|
||||
return createCompatibleMappedImage(width, height, configuration.getColorModel(transparency));
|
||||
}
|
||||
|
||||
private static BufferedImage createCompatibleMappedImage(int width, int height, ColorModel cm) throws IOException {
|
||||
SampleModel sm = cm.createCompatibleSampleModel(width, height);
|
||||
public static BufferedImage createCompatibleMappedImage(int width, int height, ImageTypeSpecifier type) throws IOException {
|
||||
return createCompatibleMappedImage(width, height, type.getSampleModel(width, height), type.getColorModel());
|
||||
}
|
||||
|
||||
// System.err.println("cm: " + cm);
|
||||
// System.err.println("cm.getNumComponents(): " + cm.getNumComponents());
|
||||
// System.err.println("cm.getPixelSize(): " + cm.getPixelSize());
|
||||
// System.err.println("cm.getComponentSize(): " + Arrays.toString(cm.getComponentSize()));
|
||||
// System.err.println("sm.getNumDataElements(): " + sm.getNumDataElements());
|
||||
// System.err.println("sm.getNumBands(): " + sm.getNumBands());
|
||||
// System.err.println("sm.getSampleSize(): " + Arrays.toString(sm.getSampleSize()));
|
||||
static BufferedImage createCompatibleMappedImage(int width, int height, ColorModel cm) throws IOException {
|
||||
return createCompatibleMappedImage(width, height, cm.createCompatibleSampleModel(width, height), cm);
|
||||
}
|
||||
|
||||
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 = sm.createDataBuffer();
|
||||
// DataBuffer buffer = DirectBuffer.create(sm.getTransferType(), width * height * sm.getNumDataElements(), 1);
|
||||
|
||||
BufferedImage image = new BufferedImage(cm, new GenericWritableRaster(sm, buffer, new Point()), cm.isAlphaPremultiplied(), null);
|
||||
// BufferedImage image = new BufferedImage(cm, new SunWritableRaster(sm, buffer, new Point()), cm.isAlphaPremultiplied(), null);
|
||||
// BufferedImage image = new BufferedImage(cm, Raster.createWritableRaster(sm, buffer, null), false null);
|
||||
return image;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user