It all works

This commit is contained in:
Erlend Hamnaberg
2009-11-08 19:52:30 +01:00
parent b8faa6e36f
commit 0786949c1c
319 changed files with 0 additions and 0 deletions
@@ -0,0 +1,478 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio;
import com.twelvemonkeys.image.BufferedImageIcon;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Iterator;
/**
* Abstract base class for image readers.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ImageReaderBase.java,v 1.0 Sep 20, 2007 5:28:37 PM haraldk Exp$
*/
public abstract class ImageReaderBase extends ImageReader {
/**
* For convenience. Only set if the input is an {@code ImageInputStream}.
* @see #setInput(Object, boolean, boolean)
*/
protected ImageInputStream mImageInput;
/**
* Constructs an {@code ImageReader} and sets its
* {@code originatingProvider} field to the supplied value.
* <p/>
* <p> Subclasses that make use of extensions should provide a
* constructor with signature {@code (ImageReaderSpi,
* Object)} in order to retrieve the extension object. If
* the extension object is unsuitable, an
* {@code IllegalArgumentException} should be thrown.
*
* @param pProvider the {@code ImageReaderSpi} that is invoking this constructor, or {@code null}.
*/
protected ImageReaderBase(final ImageReaderSpi pProvider) {
super(pProvider);
}
/**
* Overrides {@code setInput}, to allow easy access to the input, in case
* it is an {@code ImageInputStream}.
*
* @param pInput the {@code ImageInputStream} or other
* {@code Object} to use for future decoding.
* @param pSeekForwardOnly if {@code true}, images and metadata
* may only be read in ascending order from this input source.
* @param pIgnoreMetadata if {@code true}, metadata
* may be ignored during reads.
*
* @exception IllegalArgumentException if {@code input} is
* not an instance of one of the classes returned by the
* originating service provider's {@code getInputTypes}
* method, or is not an {@code ImageInputStream}.
*
* @see ImageInputStream
*/
@Override
public void setInput(final Object pInput, final boolean pSeekForwardOnly, final boolean pIgnoreMetadata) {
resetMembers();
super.setInput(pInput, pSeekForwardOnly, pIgnoreMetadata);
if (pInput instanceof ImageInputStream) {
mImageInput = (ImageInputStream) pInput;
}
}
@Override
public void dispose() {
resetMembers();
super.dispose();
}
@Override
public void reset() {
resetMembers();
super.reset();
}
/**
* Resets all member variables. This method is by default invoked from:
* <ul>
* <li>{@link #setInput(Object, boolean, boolean)}</li>
* <li>{@link #dispose()}</li>
* <li>{@link #reset()}</li>
* </ul>
*
*/
protected abstract void resetMembers();
/**
* Default implementation that always returns {@code null}.
*
* @param pImageIndex ignored, unless overridden
* @return {@code null}, unless overridden
* @throws IOException never, unless overridden.
*/
public IIOMetadata getImageMetadata(int pImageIndex) throws IOException {
return null;
}
/**
* Default implementation that always returns {@code null}.
*
* @return {@code null}, unless overridden
* @throws IOException never, unless overridden.
*/
public IIOMetadata getStreamMetadata() throws IOException {
return null;
}
/**
* Default implementation that always returns {@code 1}.
*
* @param pAllowSearch ignored, unless overridden
* @return {@code 1}, unless overridden
* @throws IOException never, unless overridden
*/
public int getNumImages(boolean pAllowSearch) throws IOException {
assertInput();
return 1;
}
/**
* Convenience method to make sure image index is within bounds.
*
* @param pIndex the image index
*
* @throws java.io.IOException if an error occurs during reading
* @throws IndexOutOfBoundsException if not {@code minIndex <= pIndex < numImages}
*/
protected void checkBounds(int pIndex) throws IOException {
assertInput();
if (pIndex < getMinIndex()) {
throw new IndexOutOfBoundsException("index < minIndex");
}
else if (getNumImages(false) != -1 && pIndex >= getNumImages(false)) {
throw new IndexOutOfBoundsException("index >= numImages (" + pIndex + " >= " + getNumImages(false) + ")");
}
}
/**
* Makes sure input is set.
*
* @throws IllegalStateException if {@code getInput() == null}.
*/
protected void assertInput() {
if (getInput() == null) {
throw new IllegalStateException("getInput() == null");
}
}
/**
* Returns the {@code BufferedImage} to which decoded pixel
* data should be written.
* <p/>
* As {@link javax.imageio.ImageReader#getDestination} but tests if the explicit destination
* image (if set) is valid according to the {@code ImageTypeSpecifier}s given in {@code pTypes}
*
*
* @param pParam an {@code ImageReadParam} to be used to get
* the destination image or image type, or {@code null}.
* @param pTypes an {@code Iterator} of
* {@code ImageTypeSpecifier}s indicating the legal image
* types, with the default first.
* @param pWidth the true width of the image or tile begin decoded.
* @param pHeight the true width of the image or tile being decoded.
*
* @return the {@code BufferedImage} to which decoded pixel
* data should be written.
*
* @exception IIOException if the {@code ImageTypeSpecifier} or {@code BufferedImage}
* specified by {@code pParam} does not match any of the legal
* ones from {@code pTypes}.
* @throws IllegalArgumentException if {@code pTypes}
* is {@code null} or empty, or if an object not of type
* {@code ImageTypeSpecifier} is retrieved from it.
* Or, if the resulting image would have a width or height less than 1,
* or if the product of {@code pWidth} and {@code pHeight} is greater than
* {@code Integer.MAX_VALUE}.
*/
public static BufferedImage getDestination(final ImageReadParam pParam, final Iterator<ImageTypeSpecifier> pTypes,
final int pWidth, final int pHeight) throws IIOException {
BufferedImage image = ImageReader.getDestination(pParam, pTypes, pWidth, pHeight);
if (pParam != null) {
BufferedImage dest = pParam.getDestination();
if (dest != null) {
boolean found = false;
// NOTE: This is bad, as it relies on implementation details of super method...
// We know that the iterator has not been touched if explicit destination..
while (pTypes.hasNext()) {
ImageTypeSpecifier specifier = pTypes.next();
int imageType = specifier.getBufferedImageType();
if (imageType != 0 && imageType == dest.getType()) {
// Known types equal, perfect match
found = true;
break;
}
else {
// If types are different, or TYPE_CUSTOM, test if
// - transferType is ok
// - bands are ok
// TODO: Test if color model is ok?
if (specifier.getSampleModel().getTransferType() == dest.getSampleModel().getTransferType() &&
specifier.getNumBands() <= dest.getSampleModel().getNumBands()) {
found = true;
break;
}
}
}
if (!found) {
throw new IIOException(String.format("Illegal explicit destination image %s", dest));
}
}
}
return image;
}
/**
* Utility method for getting the area of interest (AOI) of an image.
* The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)}
* method.
* <p/>
* Note: If it is possible for the reader to read the AOI directly, such a
* method should be used instead, for efficiency.
*
* @param pImage the image to get AOI from
* @param pParam the param optionally specifying the AOI
*
* @return a {@code BufferedImage} containing the area of interest (source
* region), or the original image, if no source region was set, or
* {@code pParam} was {@code null}
*/
protected static BufferedImage fakeAOI(BufferedImage pImage, ImageReadParam pParam) {
return IIOUtil.fakeAOI(pImage, getSourceRegion(pParam, pImage.getWidth(), pImage.getHeight()));
}
/**
* Utility method for getting the subsampled image.
* The subsampling is defined by the
* {@link javax.imageio.IIOParam#setSourceSubsampling(int, int, int, int)}
* method.
* <p/>
* NOTE: This method does not take the subsampling offsets into
* consideration.
* <p/>
* Note: If it is possible for the reader to subsample directly, such a
* method should be used instead, for efficiency.
*
* @param pImage the image to subsample
* @param pParam the param optionally specifying subsampling
*
* @return an {@code Image} containing the subsampled image, or the
* original image, if no subsampling was specified, or
* {@code pParam} was {@code null}
*/
protected static Image fakeSubsampling(Image pImage, ImageReadParam pParam) {
return IIOUtil.fakeSubsampling(pImage, pParam);
}
public static void main(String[] pArgs) throws IOException {
BufferedImage image = ImageIO.read(new File(pArgs[0]));
if (image == null) {
System.err.println("Supported formats: " + Arrays.toString(ImageIO.getReaderFormatNames()));
System.exit(1);
}
showIt(image, pArgs[0]);
}
protected static void showIt(final BufferedImage pImage, final String pTitle) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
JFrame frame = new JFrame(pTitle);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
JPanel pane = new JPanel(new BorderLayout());
JScrollPane scroll = new JScrollPane(new ImageLabel(pImage));
scroll.setBorder(null);
pane.add(scroll);
frame.setContentPane(pane);
frame.pack();
frame.setVisible(true);
}
});
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static class ImageLabel extends JLabel {
Paint mBackground;
final Paint mCheckeredBG;
final Color mDefaultBG;
public ImageLabel(final BufferedImage pImage) {
super(new BufferedImageIcon(pImage));
setOpaque(false);
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
mCheckeredBG = createTexture();
// For indexed color, default to the color of the transparent pixel, if any
mDefaultBG = getDefaultBackground(pImage);
mBackground = mDefaultBG != null ? mDefaultBG : mCheckeredBG;
JPopupMenu popup = createBackgroundPopup();
setComponentPopupMenu(popup);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.isPopupTrigger()) {
getComponentPopupMenu().show(ImageLabel.this, e.getX(), e.getY());
}
}
});
}
private JPopupMenu createBackgroundPopup() {
JPopupMenu popup = new JPopupMenu();
ButtonGroup group = new ButtonGroup();
addCheckBoxItem(new ChangeBackgroundAction("Checkered", mCheckeredBG), popup, group);
popup.addSeparator();
addCheckBoxItem(new ChangeBackgroundAction("White", Color.WHITE), popup, group);
addCheckBoxItem(new ChangeBackgroundAction("Light", Color.LIGHT_GRAY), popup, group);
addCheckBoxItem(new ChangeBackgroundAction("Gray", Color.GRAY), popup, group);
addCheckBoxItem(new ChangeBackgroundAction("Dark", Color.DARK_GRAY), popup, group);
addCheckBoxItem(new ChangeBackgroundAction("Black", Color.BLACK), popup, group);
popup.addSeparator();
addCheckBoxItem(new ChooseBackgroundAction("Choose...", mDefaultBG != null ? mDefaultBG : Color.BLUE), popup, group);
return popup;
}
private void addCheckBoxItem(final Action pAction, final JPopupMenu pPopup, final ButtonGroup pGroup) {
JCheckBoxMenuItem item = new JCheckBoxMenuItem(pAction);
pGroup.add(item);
pPopup.add(item);
}
private static Color getDefaultBackground(BufferedImage pImage) {
if (pImage.getColorModel() instanceof IndexColorModel) {
IndexColorModel cm = (IndexColorModel) pImage.getColorModel();
int transparent = cm.getTransparentPixel();
if (transparent >= 0) {
return new Color(cm.getRGB(transparent), false);
}
}
return null;
}
private static Paint createTexture() {
GraphicsConfiguration graphicsConfiguration = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
BufferedImage pattern = graphicsConfiguration.createCompatibleImage(20, 20);
Graphics2D g = pattern.createGraphics();
try {
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, pattern.getWidth(), pattern.getHeight());
g.setColor(Color.GRAY);
g.fillRect(0, 0, pattern.getWidth() / 2, pattern.getHeight() / 2);
g.fillRect(pattern.getWidth() / 2, pattern.getHeight() / 2, pattern.getWidth() / 2, pattern.getHeight() / 2);
}
finally {
g.dispose();
}
return new TexturePaint(pattern, new Rectangle(pattern.getWidth(), pattern.getHeight()));
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D gr = (Graphics2D) g;
gr.setPaint(mBackground);
gr.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
private class ChangeBackgroundAction extends AbstractAction {
protected Paint mPaint;
public ChangeBackgroundAction(final String pName, final Paint pPaint) {
super(pName);
mPaint = pPaint;
}
public void actionPerformed(ActionEvent e) {
mBackground = mPaint;
repaint();
}
}
private class ChooseBackgroundAction extends ChangeBackgroundAction {
public ChooseBackgroundAction(final String pName, final Color pColor) {
super(pName, pColor);
putValue(Action.SMALL_ICON, new Icon() {
public void paintIcon(Component c, Graphics pGraphics, int x, int y) {
Graphics g = pGraphics.create();
g.setColor((Color) mPaint);
g.fillRect(x, y, 16, 16);
g.dispose();
}
public int getIconWidth() {
return 16;
}
public int getIconHeight() {
return 16;
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
Color selected = JColorChooser.showDialog(ImageLabel.this, "Choose background", (Color) mPaint);
if (selected != null) {
mPaint = selected;
super.actionPerformed(e);
}
}
}
}
}
@@ -0,0 +1,164 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* Abstract base class for image writers.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ImageWriterBase.java,v 1.0 Sep 24, 2007 12:22:28 AM haraldk Exp$
*/
public abstract class ImageWriterBase extends ImageWriter {
/**
* For convenience. Only set if the output is an {@code ImageInputStream}.
* @see #setOutput(Object)
*/
protected ImageOutputStream mImageOutput;
/**
* Constructs an {@code ImageWriter} and sets its
* {@code originatingProvider} instance variable to the
* supplied value.
* <p/>
* <p> Subclasses that make use of extensions should provide a
* constructor with signature {@code (ImageWriterSpi,
* Object)} in order to retrieve the extension object. If
* the extension object is unsuitable, an
* {@code IllegalArgumentException} should be thrown.
*
* @param pProvider the {@code ImageWriterSpi} that is constructing this object, or {@code null}.
*/
protected ImageWriterBase(final ImageWriterSpi pProvider) {
super(pProvider);
}
public String getFormatName() throws IOException {
return getOriginatingProvider().getFormatNames()[0];
}
@Override
public void setOutput(final Object pOutput) {
super.setOutput(pOutput);
if (pOutput instanceof ImageOutputStream) {
mImageOutput = (ImageOutputStream) pOutput;
}
}
/**
* Makes sure output is set.
*
* @throws IllegalStateException if {@code getOutput() == null}.
*/
protected void assertOutput() {
if (getOutput() == null) {
throw new IllegalStateException("getOutput() == null");
}
}
/**
* Returns {@code null}
*
* @param pParam ignored.
* @return {@code null}.
*/
public IIOMetadata getDefaultStreamMetadata(final ImageWriteParam pParam) {
return null;
}
/**
* Returns {@code null}
*
* @param pInData ignored.
* @param pParam ignored.
* @return {@code null}.
*/
public IIOMetadata convertStreamMetadata(final IIOMetadata pInData, final ImageWriteParam pParam) {
return null;
}
protected static Rectangle getSourceRegion(final ImageWriteParam pParam, final int pWidth, final int pHeight) {
return IIOUtil.getSourceRegion(pParam, pWidth, pHeight);
}
/**
* Utility method for getting the area of interest (AOI) of an image.
* The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)}
* method.
* <p/>
* Note: If it is possible for the reader to read the AOI directly, such a
* method should be used instead, for efficiency.
*
* @param pImage the image to get AOI from
* @param pParam the param optionally specifying the AOI
*
* @return a {@code BufferedImage} containing the area of interest (source
* region), or the original image, if no source region was set, or
* {@code pParam} was {@code null}
*/
protected static BufferedImage fakeAOI(final BufferedImage pImage, final ImageWriteParam pParam) {
return IIOUtil.fakeAOI(pImage, getSourceRegion(pParam, pImage.getWidth(), pImage.getHeight()));
}
/**
* Utility method for getting the subsampled image.
* The subsampling is defined by the
* {@link javax.imageio.IIOParam#setSourceSubsampling(int, int, int, int)}
* method.
* <p/>
* NOTE: This method does not take the subsampling offsets into
* consideration.
* <p/>
* Note: If it is possible for the reader to subsample directly, such a
* method should be used instead, for efficiency.
*
* @param pImage the image to subsample
* @param pParam the param optionally specifying subsampling
*
* @return an {@code Image} containing the subsampled image, or the
* original image, if no subsampling was specified, or
* {@code pParam} was {@code null}
*/
protected static Image fakeSubsampling(final Image pImage, final ImageWriteParam pParam) {
return IIOUtil.fakeSubsampling(pImage, pParam);
}
}
@@ -0,0 +1,93 @@
package com.twelvemonkeys.imageio.spi;
import com.twelvemonkeys.lang.Validate;
/**
* Provides provider info, like vendor name and version,
* for {@link javax.imageio.spi.ImageReaderWriterSpi} subclasses based on information in the manifest.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ProviderInfo.java,v 1.0 Oct 31, 2009 3:49:39 PM haraldk Exp$
*
* @see <a href="http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#JAR%20Manifest">JAR Manifest</a>
*/
public class ProviderInfo {
// TODO: Consider reading the META-INF/MANIFEST.MF from the class path using java.util.jar.Manifest.
// Use the manifest that is located in the same class path folder as the package.
private final String mTitle;
private final String mVendorName;
private final String mVersion;
/**
* Creates a provider information instance based on the given package.
*
* @param pPackage the package to get provider information from.
* This should typically be the package containing the Spi class.
*
* @throws IllegalArgumentException if {@code pPackage == null}
*/
public ProviderInfo(final Package pPackage) {
Validate.notNull(pPackage, "package");
String title = pPackage.getImplementationTitle();
mTitle = title != null ? title : pPackage.getName();
String vendor = pPackage.getImplementationVendor();
mVendorName = vendor != null ? vendor : fakeVendor(pPackage);
String version = pPackage.getImplementationVersion();
mVersion = version != null ? version : fakeVersion(pPackage);
}
private static String fakeVendor(final Package pPackage) {
String name = pPackage.getName();
return name.startsWith("com.twelvemonkeys") ? "TwelveMonkeys" : name;
}
private String fakeVersion(Package pPackage) {
String name = pPackage.getName();
return name.startsWith("com.twelvemonkeys") ? "DEV" : "Unspecified";
}
/**
* Returns the implementation title, as specified in the manifest entry
* {@code Implementation-Title} for the package.
* If the title is unavailable, the package name or some default name
* for known packages are used.
*
* @return the implementation title
*/
final String getImplementationTitle() {
return mTitle;
}
/**
* Returns the vendor name, as specified in the manifest entry
* {@code Implementation-Vendor} for the package.
* If the vendor name is unavailable, the package name or some default name
* for known packages are used.
*
* @return the vendor name.
*/
public final String getVendorName() {
return mVendorName;
}
/**
* Returns the version/build number string, as specified in the manifest entry
* {@code Implementation-Version} for the package.
* If the version is unavailable, some arbitrary (non-{@code null}) value is used.
*
* @return the vendor name.
*/
public final String getVersion() {
return mVersion;
}
@Override
public String toString() {
return mTitle + ", " + mVersion + " by " + mVendorName;
}
}
@@ -0,0 +1,185 @@
package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageInputStreamImpl;
import java.io.IOException;
/**
* A buffered {@code ImageInputStream}.
* Experimental - seems to be effective for {@link javax.imageio.stream.FileImageInputStream}
* and {@link javax.imageio.stream.FileCacheImageInputStream} when doing a lot of single-byte reads
* (or short byte-array reads) on OS X at least.
* Code that uses the {@code readFully} methods are not affected by the issue.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BufferedFileImageInputStream.java,v 1.0 May 15, 2008 4:36:49 PM haraldk Exp$
*/
// TODO: Create a provider for this (wrapping the FileIIS and FileCacheIIS classes), and disable the Sun built-in spis?
// TODO: Test on other platforms, might be just an OS X issue
public final class BufferedImageInputStream extends ImageInputStreamImpl implements ImageInputStream {
static final int DEFAULT_BUFFER_SIZE = 8192;
private ImageInputStream mStream;
private byte[] mBuffer;
private long mBufferStart = 0;
private int mBufferPos = 0;
private int mBufferLength = 0;
public BufferedImageInputStream(final ImageInputStream pStream) throws IOException {
this(pStream, DEFAULT_BUFFER_SIZE);
}
private BufferedImageInputStream(final ImageInputStream pStream, final int pBufferSize) throws IOException {
Validate.notNull(pStream, "stream");
mStream = pStream;
streamPos = pStream.getStreamPosition();
mBuffer = new byte[pBufferSize];
}
private void fillBuffer() throws IOException {
mBufferStart = streamPos;
mBufferLength = mStream.read(mBuffer, 0, mBuffer.length);
mBufferPos = 0;
}
private boolean isBufferValid() throws IOException {
return mBufferPos < mBufferLength && mBufferStart == mStream.getStreamPosition() - mBufferLength;
}
@Override
public int read() throws IOException {
if (!isBufferValid()) {
fillBuffer();
}
if (mBufferLength <= 0) {
return -1;
}
bitOffset = 0;
streamPos++;
return mBuffer[mBufferPos++] & 0xff;
}
@Override
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
bitOffset = 0;
if (!isBufferValid()) {
// Bypass cache if cache is empty for reads longer than buffer
if (pLength >= mBuffer.length) {
return readDirect(pBuffer, pOffset, pLength);
}
else {
fillBuffer();
}
}
return readBuffered(pBuffer, pOffset, pLength);
}
private int readDirect(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
// TODO: Figure out why reading more than the buffer length causes alignment issues...
int read = mStream.read(pBuffer, pOffset, Math.min(mBuffer.length, pLength));
if (read > 0) {
streamPos += read;
}
mBufferStart = mStream.getStreamPosition();
mBufferLength = 0;
return read;
}
private int readBuffered(final byte[] pBuffer, final int pOffset, final int pLength) {
if (mBufferLength <= 0) {
return -1;
}
// Read as much as possible from buffer
int length = Math.min(mBufferLength - mBufferPos, pLength);
if (length > 0) {
System.arraycopy(mBuffer, mBufferPos, pBuffer, pOffset, length);
mBufferPos += length;
}
streamPos += length;
return length;
}
@Override
public void seek(long pPosition) throws IOException {
// TODO: Could probably be optimized to not invalidate buffer if new position is within current buffer
mStream.seek(pPosition);
mBufferLength = 0; // Will invalidate buffer
streamPos = mStream.getStreamPosition();
}
@Override
public void flushBefore(long pos) throws IOException {
mStream.flushBefore(pos);
}
@Override
public long getFlushedPosition() {
return mStream.getFlushedPosition();
}
@Override
public boolean isCached() {
return mStream.isCached();
}
@Override
public boolean isCachedMemory() {
return mStream.isCachedMemory();
}
@Override
public boolean isCachedFile() {
return mStream.isCachedFile();
}
@Override
public void close() throws IOException {
if (mStream != null) {
mStream.close();
mStream = null;
mBuffer = null;
}
super.close();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
}
@Override
public long length() {
// WTF?! This method is allowed to throw IOException in the interface...
try {
return mStream.length();
}
catch (IOException e) {
throw unchecked(e, RuntimeException.class);
}
}
@SuppressWarnings({"unchecked", "UnusedDeclaration"})
private <T extends Throwable> T unchecked(IOException pExcption, Class<T> pClass) {
// Ugly hack to fool the compiler..
return (T) pExcption;
}
}
@@ -0,0 +1,56 @@
package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.stream.ImageInputStreamImpl;
import java.io.IOException;
/**
* Experimental
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ByteArrayImageInputStream.java,v 1.0 May 15, 2008 2:12:12 PM haraldk Exp$
*/
public final class ByteArrayImageInputStream extends ImageInputStreamImpl {
private final byte[] mData;
public ByteArrayImageInputStream(final byte[] pData) {
Validate.notNull(pData, "data");
mData = pData;
}
public int read() throws IOException {
if (streamPos >= mData.length) {
return -1;
}
bitOffset = 0;
return mData[((int) streamPos++)] & 0xff;
}
public int read(byte[] pBuffer, int pOffset, int pLength) throws IOException {
if (streamPos >= mData.length) {
return -1;
}
int length = (int) Math.min(mData.length - streamPos, pLength);
bitOffset = 0;
System.arraycopy(mData, (int) streamPos, pBuffer, pOffset, length);
streamPos += length;
return length;
}
@Override
public long length() {
return mData.length;
}
@Override
public boolean isCached() {
return true;
}
@Override
public boolean isCachedMemory() {
return true;
}
}
@@ -0,0 +1,36 @@
package com.twelvemonkeys.imageio.stream;
import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Locale;
/**
* ByteArrayImageInputStreamSpi
* Experimental
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ByteArrayImageInputStreamSpi.java,v 1.0 May 15, 2008 2:12:12 PM haraldk Exp$
*/
public class ByteArrayImageInputStreamSpi extends ImageInputStreamSpi {
public ByteArrayImageInputStreamSpi() {
super("TwelveMonkeys", "1.0 BETA", byte[].class);
}
public ImageInputStream createInputStreamInstance(Object pInput, boolean pUseCache, File pCacheDir) throws IOException {
if (pInput instanceof byte[]) {
return new ByteArrayImageInputStream((byte[]) pInput);
}
else {
throw new IllegalArgumentException("Expected input of type byte[]: " + pInput);
}
}
public String getDescription(Locale pLocale) {
return "Service provider that instantiates an ImageInputStream from a byte array";
}
}
@@ -0,0 +1,84 @@
package com.twelvemonkeys.imageio.stream;
import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.stream.FileCacheImageInputStream;
import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Locale;
/**
* URLImageInputStreamSpi
* Experimental
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: URLImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
*/
// TODO: URI instead of URL?
public class URLImageInputStreamSpi extends ImageInputStreamSpi {
public URLImageInputStreamSpi() {
super("TwelveMonkeys", "1.0 BETA", URL.class);
}
// TODO: Create a URI or URLImageInputStream class, with a getUR[I|L] method, to allow for multiple file formats
// The good thing with that is that it does not clash with the built-in Sun-stuff or other people's hacks
// The bad thing is that most people don't expect there to be an UR[I|L]ImageInputStreamSpi..
public ImageInputStream createInputStreamInstance(final Object pInput, final boolean pUseCache, final File pCacheDir) throws IOException {
if (pInput instanceof URL) {
URL url = (URL) pInput;
// Special case for file protocol, a lot faster than FileCacheImageInputStream
if ("file".equals(url.getProtocol())) {
try {
return new BufferedImageInputStream(new FileImageInputStream(new File(url.toURI())));
}
catch (URISyntaxException ignore) {
// This should never happen, but if it does, we'll fall back to using the stream
ignore.printStackTrace();
}
}
// Otherwise revert to cached
final InputStream stream = url.openStream();
if (pUseCache) {
return new BufferedImageInputStream(new FileCacheImageInputStream(stream, pCacheDir) {
@Override
public void close() throws IOException {
try {
super.close();
}
finally {
stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
}
}
});
}
else {
return new MemoryCacheImageInputStream(stream) {
@Override
public void close() throws IOException {
try {
super.close();
}
finally {
stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
}
}
};
}
}
else {
throw new IllegalArgumentException("Expected input of type URL: " + pInput);
}
}
public String getDescription(final Locale pLocale) {
return "Service provider that instantiates an ImageInputStream from a URL";
}
}
@@ -0,0 +1,177 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.util;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* IIOInputStreamAdapter
* <p/>
* Note: You should always wrap this stream in a {@code BufferedInputStream}.
* If not, performance may degrade significantly.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IIOInputStreamAdapter.java,v 1.0 Sep 26, 2007 11:35:59 AM haraldk Exp$
*/
class IIOInputStreamAdapter extends InputStream {
private ImageInputStream mInput;
private final boolean mHasLength;
private long mLeft;
private long mMarkPosition;
// TODO: Enforce stream boundaries!
// TODO: Stream start position....
/**
* Creates an {@code InputStream} that reads from the given {@code ImageInputStream}.
* The input stream will read from the current stream position, until the end of the
* underlying stream.
*
* @param pInput the {@code ImageInputStream} to read from.
*/
public IIOInputStreamAdapter(final ImageInputStream pInput) {
this(pInput, -1, false);
}
/**
* Creates an {@code InputStream} that reads from the given {@code ImageInputStream}.
* The input stream will read from the current stream position, until at most
* {@code pLength} bytes has been read.
*
* @param pInput the {@code ImageInputStream} to read from.
* @param pLength the length of the stream
*/
public IIOInputStreamAdapter(final ImageInputStream pInput, final long pLength) {
this(pInput, pLength, true);
}
private IIOInputStreamAdapter(ImageInputStream pInput, long pLength, boolean pHasLength) {
if (pInput == null) {
throw new IllegalArgumentException("stream == null");
}
if (pHasLength && pLength < 0) {
throw new IllegalArgumentException("length < 0");
}
mInput = pInput;
mHasLength = pHasLength;
mLeft = pLength;
}
/**
* Marks this stream as closed.
* This implementation does <em>not</em> close the underlying stream.
*/
public void close() throws IOException {
if (mHasLength) {
mInput.seek(mInput.getStreamPosition() + mLeft);
}
mLeft = 0;
mInput = null;
}
public int available() throws IOException {
if (mHasLength) {
return mLeft > 0 ? (int) Math.min(Integer.MAX_VALUE, mLeft) : 0;
}
return 0; // We don't really know, so we say 0 to be safe.
}
@Override
public boolean markSupported() {
return true;
}
public void mark(int pReadLimit) {
try {
mMarkPosition = mInput.getStreamPosition();
}
catch (IOException e) {
// Let's hope this never happens, because it's not possible to reset then...
throw new IllegalStateException("Could not read stream position: " + e.getMessage(), e);
}
}
public void reset() throws IOException {
long diff = mInput.getStreamPosition() - mMarkPosition;
mInput.seek(mMarkPosition);
mLeft += diff;
}
public int read() throws IOException {
if (mHasLength && mLeft-- <= 0) {
mLeft = 0;
return -1;
}
return mInput.read();
}
public final int read(byte[] pBytes) throws IOException {
return read(pBytes, 0, pBytes.length);
}
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (mHasLength && mLeft <= 0) {
return -1;
}
int read = mInput.read(pBytes, pOffset, (int) findMaxLen(pLength));
if (mHasLength) {
mLeft = read < 0 ? 0 : mLeft - read;
}
return read;
}
/**
* Finds the maximum number of bytes we can read or skip, from this stream.
* The number will be in the range {@code [0 ... bytes left]}.
*
* @param pLength the requested length
* @return the maximum number of bytes to read
*/
private long findMaxLen(long pLength) {
if (mHasLength && mLeft < pLength) {
return Math.max(mLeft, 0);
}
else {
return Math.max(pLength, 0);
}
}
public long skip(long pLength) throws IOException {
long skipped = mInput.skipBytes(findMaxLen(pLength)); // Skips 0 or more, never -1
mLeft -= skipped;
return skipped;
}
}
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.util;
import javax.imageio.stream.ImageOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* IIOOutputStreamAdapter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IIOOutputStreamAdapter.java,v 1.0 Sep 26, 2007 11:50:38 AM haraldk Exp$
*/
class IIOOutputStreamAdapter extends OutputStream {
private ImageOutputStream mOutput;
public IIOOutputStreamAdapter(final ImageOutputStream pOutput) {
mOutput = pOutput;
}
@Override
public void write(final byte[] pBytes) throws IOException {
mOutput.write(pBytes);
}
@Override
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
mOutput.write(pBytes, pOffset, pLength);
}
@Override
public void write(final int pByte) throws IOException {
mOutput.write(pByte);
}
@Override
public void flush() throws IOException {
mOutput.flush();
}
@Override
public void close() throws IOException {
mOutput = null;
}
}
@@ -0,0 +1,149 @@
package com.twelvemonkeys.imageio.util;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import javax.imageio.IIOParam;
import javax.imageio.spi.IIOServiceProvider;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* IIOUtil
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IIOUtil.java,v 1.0 May 8, 2008 3:04:54 PM haraldk Exp$
*/
public final class IIOUtil {
private IIOUtil() {}
/**
* Creates an {@code InputStream} adapter that reads from an underlying {@code ImageInputStream}.
* The input stream will read until the end of {@code pStream}.
*
* @param pStream the stream to read from.
* @return an {@code InputStream} reading from {@code pStream}.
*/
public static InputStream createStreamAdapter(final ImageInputStream pStream) {
// TODO: Include stream start pos?
// TODO: Skip buffering for known in-memory implementations?
return new BufferedInputStream(new IIOInputStreamAdapter(pStream));
}
/**
* Creates an {@code InputStream} adapter that reads from an underlying {@code ImageInputStream}.
* The input stream will read until the end of {@code pStream}, or at most {@code pLength} bytes has been read.
*
* @param pStream the stream to read from.
* @param pLength the maximum number of bytes that can be read from {@code pStream}.
* @return an {@code InputStream} reading from {@code pStream}.
*/
public static InputStream createStreamAdapter(final ImageInputStream pStream, final long pLength) {
// TODO: Include stream start pos?
// TODO: Skip buffering for known in-memory implementations?
return new BufferedInputStream(new IIOInputStreamAdapter(pStream, pLength));
}
/**
* Creates an {@code OutputStream} adapter that writes to an underlying {@code ImageOutputStream}.
*
* @param pStream the stream to write to.
* @return an {@code OutputSteam} writing to {@code pStream}.
*/
public static OutputStream createStreamAdapter(final ImageOutputStream pStream) {
return new BufferedOutputStream(new IIOOutputStreamAdapter(pStream));
}
public static Image fakeSubsampling(final Image pImage, final IIOParam pParam) {
if (pImage == null) {
return null;
}
if (pParam != null) {
int x = pParam.getSourceXSubsampling();
int y = pParam.getSourceYSubsampling();
// 1 is default
if (x > 1 || y > 1) {
int w = (ImageUtil.getWidth(pImage) + x - 1) / x;
int h = (ImageUtil.getHeight(pImage) + y - 1) / y;
// Fake subsampling by scaling fast
return pImage.getScaledInstance(w, h, Image.SCALE_FAST);
}
}
return pImage;
}
public static Rectangle getSourceRegion(final IIOParam pParam, final int pSrcWidth, final int pSrcHeight) {
Rectangle sourceRegion = new Rectangle(pSrcWidth, pSrcHeight);
// If param is present, calculate region
if (pParam != null) {
// Get intersection with source region
Rectangle region = pParam.getSourceRegion();
if (region != null) {
sourceRegion = sourceRegion.intersection(region);
}
// Scale according to subsampling offsets
int subsampleXOffset = pParam.getSubsamplingXOffset();
int subsampleYOffset = pParam.getSubsamplingYOffset();
sourceRegion.x += subsampleXOffset;
sourceRegion.y += subsampleYOffset;
sourceRegion.width -= subsampleXOffset;
sourceRegion.height -= subsampleYOffset;
}
return sourceRegion;
}
public static BufferedImage fakeAOI(final BufferedImage pImage, final Rectangle pSourceRegion) {
if (pImage == null) {
return null;
}
if (pSourceRegion != null) {
if (pSourceRegion.x != 0 || pSourceRegion.y != 0 || pSourceRegion.width != pImage.getWidth() || pSourceRegion.height != pImage.getHeight()) {
return pImage.getSubimage(pSourceRegion.x, pSourceRegion.y, pSourceRegion.width, pSourceRegion.height);
}
}
return pImage;
}
/**
* Creates a {@link ProviderInfo} instance for the given service provider.
*
* @param pProviderClass the provider class to get info for.
* @return the newly created {@link ProviderInfo}.
*/
public static ProviderInfo getProviderInfo(final Class<? extends IIOServiceProvider> pProviderClass) {
return new ProviderInfo(pProviderClass.getPackage());
}
/**
* THIS METHOD WILL ME MOVED/RENAMED, DO NOT USE.
*
* @param pRegistry the registry to unregister from
* @param pProvider the provider to unregister
* @param pCategory the category to unregister from
*
* @deprecated
*/
public static <T> void deregisterProvider(final ServiceRegistry pRegistry, final IIOServiceProvider pProvider, final Class<T> pCategory) {
// http://www.ibm.com/developerworks/java/library/j-jtp04298.html
// TODO: Consider placing this method in a ImageReaderSpiBase class or similar
pRegistry.deregisterServiceProvider(pCategory.cast(pProvider), pCategory);
}
}
@@ -0,0 +1,41 @@
package com.twelvemonkeys.imageio.util;
import javax.imageio.ImageTypeSpecifier;
import java.awt.image.IndexColorModel;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.util.Hashtable;
/**
* IndexedImageTypeSpecifier
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IndexedImageTypeSpecifier.java,v 1.0 May 19, 2008 11:04:28 AM haraldk Exp$
*/
public class IndexedImageTypeSpecifier extends ImageTypeSpecifier {
IndexedImageTypeSpecifier(IndexColorModel pColorModel) {
// For some reason, we need a sample model
super(pColorModel, pColorModel.createCompatibleSampleModel(1, 1));
}
public static ImageTypeSpecifier createFromIndexColorModel(final IndexColorModel pColorModel) {
return new IndexedImageTypeSpecifier(pColorModel);
}
@Override
public final BufferedImage createBufferedImage(int pWidth, int pHeight) {
try {
// This is a fix for the super-method, that first creates a sample model, and then
// creates a raster from it, using Raster.createWritableRaster. The problem with
// that approach, is that it always creates a TYPE_CUSTOM BufferedImage for indexed images.
WritableRaster raster = colorModel.createCompatibleWritableRaster(pWidth, pHeight);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), new Hashtable());
}
catch (NegativeArraySizeException e) {
// Exception most likely thrown from a DataBuffer constructor
throw new IllegalArgumentException("Array size > Integer.MAX_VALUE!");
}
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.util;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.event.IIOWriteProgressListener;
/**
* ProgressListenerBase
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: ProgressListenerBase.java,v 1.0 26.aug.2005 14:29:42 haku Exp$
*/
public abstract class ProgressListenerBase implements IIOReadProgressListener, IIOWriteProgressListener {
protected ProgressListenerBase() {
}
public void imageComplete(ImageReader pSource) {
}
public void imageProgress(ImageReader pSource, float pPercentageDone) {
}
public void imageStarted(ImageReader pSource, int pImageIndex) {
}
public void readAborted(ImageReader pSource) {
}
public void sequenceComplete(ImageReader pSource) {
}
public void sequenceStarted(ImageReader pSource, int pMinIndex) {
}
public void thumbnailComplete(ImageReader pSource) {
}
public void thumbnailProgress(ImageReader pSource, float pPercentageDone) {
}
public void thumbnailStarted(ImageReader pSource, int pImageIndex, int pThumbnailIndex) {
}
public void imageComplete(ImageWriter pSource) {
}
public void imageProgress(ImageWriter pSource, float pPercentageDone) {
}
public void imageStarted(ImageWriter pSource, int pImageIndex) {
}
public void thumbnailComplete(ImageWriter pSource) {
}
public void thumbnailProgress(ImageWriter pSource, float pPercentageDone) {
}
public void thumbnailStarted(ImageWriter pSource, int pImageIndex, int pThumbnailIndex) {
}
public void writeAborted(ImageWriter pSource) {
}
}
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.util;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.ImageIO;
import javax.swing.filechooser.FileFilter;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* ReaderFileSuffixFilter
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku$
* @version $Id: ReaderFileSuffixFilter.java,v 1.0 11.okt.2006 20:05:36 haku Exp$
*/
public final class ReaderFileSuffixFilter extends FileFilter implements java.io.FileFilter {
private final String mDescription;
private final Map<String, Boolean> mKnownSuffixes = new HashMap<String, Boolean>(32);
public ReaderFileSuffixFilter() {
this("Images (all supported input formats)");
}
public ReaderFileSuffixFilter(String pDescription) {
mDescription = pDescription;
}
public boolean accept(File pFile) {
// Directories are always supported
if (pFile.isDirectory()) {
return true;
}
// See if we have an ImageReader for this suffix
String suffix = FileUtil.getExtension(pFile);
return !StringUtil.isEmpty(suffix) && hasReaderForSuffix(suffix);
}
private boolean hasReaderForSuffix(String pSuffix) {
if (mKnownSuffixes.get(pSuffix) == Boolean.TRUE) {
return true;
}
try {
// Cahce lookup
Iterator iterator = ImageIO.getImageReadersBySuffix(pSuffix);
if (iterator.hasNext()) {
mKnownSuffixes.put(pSuffix, Boolean.TRUE);
return true;
}
else {
mKnownSuffixes.put(pSuffix, Boolean.FALSE);
return false;
}
}
catch (IllegalArgumentException iae) {
return false;
}
}
public String getDescription() {
return mDescription;
}
}
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.util;
import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.ImageIO;
import javax.swing.filechooser.FileFilter;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* WriterFileSuffixFilter
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku$
* @version $Id: WriterFileSuffixFilter.java,v 1.0 11.okt.2006 20:05:36 haku Exp$
*/
public final class WriterFileSuffixFilter extends FileFilter implements java.io.FileFilter {
private final String mDescription;
private Map<String, Boolean>mKnownSuffixes = new HashMap<String, Boolean>(32);
public WriterFileSuffixFilter() {
this("Images (all supported output formats)");
}
public WriterFileSuffixFilter(String pDescription) {
mDescription = pDescription;
}
public boolean accept(File pFile) {
// Directories are always supported
if (pFile.isDirectory()) {
return true;
}
// Test if we have an ImageWriter for this suffix
String suffix = FileUtil.getExtension(pFile);
return !StringUtil.isEmpty(suffix) && hasWriterForSuffix(suffix);
}
private boolean hasWriterForSuffix(String pSuffix) {
if (mKnownSuffixes.get(pSuffix) == Boolean.TRUE) {
return true;
}
try {
// Cahce lookup
Iterator iterator = ImageIO.getImageWritersBySuffix(pSuffix);
if (iterator.hasNext()) {
mKnownSuffixes.put(pSuffix, Boolean.TRUE);
return true;
}
else {
mKnownSuffixes.put(pSuffix, Boolean.FALSE);
return false;
}
}
catch (IllegalArgumentException iae) {
return false;
}
}
public String getDescription() {
return mDescription;
}
}