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

View File

@@ -0,0 +1,25 @@
Copyright (c) 2009, 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.

View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
<version>2.3-SNAPSHOT</version>
<name>TwelveMonkeys ImageIO Core</name>
<parent>
<artifactId>twelvemonkeys-imageio</artifactId>
<groupId>com.twelvemonkeys</groupId>
<version>2.3-SNAPSHOT</version>
</parent>
</project>

View File

@@ -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);
}
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}
}

View File

@@ -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";
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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!");
}
}
}

View File

@@ -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) {
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,97 @@
package com.twelvemonkeys.imageio.spi;
import junit.framework.TestCase;
import java.net.URL;
/**
* ProviderInfoTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ProviderInfoTestCase.java,v 1.0 Oct 31, 2009 3:51:22 PM haraldk Exp$
*/
public class ProviderInfoTestCase extends TestCase {
public void testCreateNorma() {
new ProviderInfo(Package.getPackage("java.util"));
}
public void testCreateNullPackage() {
try {
new ProviderInfo(null);
fail("IllegalArgumentException expected for null package");
}
catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().toLowerCase().contains("package"));
}
}
public void testGetVendorUnknownNonJARPackage() {
ProviderInfo info = new ProviderInfo(mockNonJARPackage("org.foo"));
String vendor = info.getVendorName();
assertNotNull(vendor);
assertEquals("org.foo", vendor);
String version = info.getVersion();
assertNotNull(version);
assertEquals("Unspecified", version);
}
public void testGetVendorNonJARTMPackage() {
ProviderInfo info = new ProviderInfo(mockNonJARPackage("com.twelvemonkeys"));
String vendor = info.getVendorName();
assertNotNull(vendor);
assertEquals("TwelveMonkeys", vendor);
String version = info.getVersion();
assertNotNull(version);
assertEquals("DEV", version);
}
public void testGetVendorKnownJARPackage() {
ProviderInfo info = new ProviderInfo(mockJARPackage("com.acme", "1.7u4-BETA-b39", "Acme"));
String vendor = info.getVendorName();
assertNotNull(vendor);
assertEquals("Acme", vendor);
String version = info.getVersion();
assertNotNull(version);
assertEquals("1.7u4-BETA-b39", version);
}
private Package mockNonJARPackage(final String pName) {
return new MockClassLoader().mockPackage(
pName,
null, null, null,
null, null, null,
null
);
}
private Package mockJARPackage(final String pName, final String pImplVersion, final String pImplVendor) {
return new MockClassLoader().mockPackage(
pName,
"The almighty specification", "1.0", "Acme Inc",
"The buggy implementation", pImplVersion, pImplVendor,
null
);
}
private static class MockClassLoader extends ClassLoader {
protected MockClassLoader() {
super(null);
}
public Package mockPackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) throws IllegalArgumentException {
return definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
}
@Override
protected Package getPackage(String name) {
return null; // Allow re-createing packages
}
}
}

View File

@@ -0,0 +1,145 @@
package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.io.ole2.CompoundDocument;
import com.twelvemonkeys.io.ole2.Entry;
import junit.framework.TestCase;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Random;
/**
* BufferedImageInputStreamTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: BufferedImageInputStreamTestCase.java,v 1.0 Jun 30, 2008 3:07:42 PM haraldk Exp$
*/
public class BufferedImageInputStreamTestCase extends TestCase {
protected final Random mRandom = new Random();
public void testCreate() throws IOException {
new BufferedImageInputStream(new ByteArrayImageInputStream(new byte[0]));
}
public void testCreateNull() throws IOException {
try {
new BufferedImageInputStream(null);
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertNotNull("Null exception message", expected.getMessage());
String message = expected.getMessage().toLowerCase();
assertTrue("Exception message does not contain parameter name", message.contains("stream"));
assertTrue("Exception message does not contain null", message.contains("null"));
}
}
// TODO: Write other tests
// TODO: Create test that exposes read += -1 (eof) bug
public void testArrayIndexOutOfBoundsBufferedReadBug() throws IOException {
// TODO: Create a more straight forward way to prove correctness, for now this is good enough to avoid regression
ImageInputStream input = new BufferedImageInputStream(new MemoryCacheImageInputStream(getClass().getResourceAsStream("/Thumbs-camera.db")));
input.setByteOrder(ByteOrder.LITTLE_ENDIAN);
Entry root = new CompoundDocument(input).getRootEntry();
Entry child = root.getChildEntry("Catalog");
assertNotNull("Input stream can never be null", child.getInputStream());
}
public void testReadResetReadDirectBufferBug() throws IOException {
// Make sure we use the exact size of the buffer
final int size = BufferedImageInputStream.DEFAULT_BUFFER_SIZE;
// Fill bytes
byte[] bytes = new byte[size * 2];
mRandom.nextBytes(bytes);
// Create wrapper stream
BufferedImageInputStream stream = new BufferedImageInputStream(new ByteArrayImageInputStream(bytes));
// Read to fill the buffer, then reset
stream.readLong();
stream.seek(0);
// Read fully and compare
byte[] result = new byte[size];
stream.readFully(result);
assertTrue(rangeEquals(bytes, 0, result, 0, size));
stream.readFully(result);
assertTrue(rangeEquals(bytes, size, result, 0, size));
}
public void testBufferPositionCorrect() throws IOException {
// Fill bytes
byte[] bytes = new byte[1024];
mRandom.nextBytes(bytes);
ByteArrayImageInputStream input = new ByteArrayImageInputStream(bytes);
input.readByte();
input.readByte();
input.skipBytes(124);
input.readByte();
input.readByte();
// Sanity check
assertEquals(128, input.getStreamPosition());
BufferedImageInputStream stream = new BufferedImageInputStream(input);
assertEquals(input.getStreamPosition(), stream.getStreamPosition());
stream.skipBytes(128);
//assertTrue(256 <= input.getStreamPosition());
assertEquals(256, stream.getStreamPosition());
stream.seek(1020);
assertEquals(1020, stream.getStreamPosition());
}
/**
* Test two arrays for range equality. That is, they contain the same elements for some specified range.
*
* @param pFirst one array to test for equality
* @param pFirstOffset the offset into the first array to start testing for equality
* @param pSecond the other array to test for equality
* @param pSecondOffset the offset into the second array to start testing for equality
* @param pLength the length of the range to check for equality
*
* @return {@code true} if both arrays are non-{@code null}
* and have at least {@code offset + pLength} elements
* and all elements in the range from the first array is equal to the elements from the second array,
* or if {@code pFirst == pSecond} (including both arrays being {@code null})
* and {@code pFirstOffset == pSecondOffset}.
* Otherwise {@code false}.
*/
static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
if (pFirst == pSecond && pFirstOffset == pSecondOffset) {
return true;
}
if (pFirst == null || pSecond == null) {
return false;
}
if (pFirst.length < pFirstOffset + pLength || pSecond.length < pSecondOffset + pLength) {
return false;
}
for (int i = 0; i < pLength; i++) {
if (pFirst[pFirstOffset + i] != pSecond[pSecondOffset + i]) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,102 @@
package com.twelvemonkeys.imageio.stream;
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTestCase.rangeEquals;
import junit.framework.TestCase;
import java.io.IOException;
import java.util.Random;
/**
* ByteArrayImageInputStreamTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: ByteArrayImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
*/
public class ByteArrayImageInputStreamTestCase extends TestCase {
protected final Random mRandom = new Random();
public void testCreate() {
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(new byte[0]);
assertEquals("Data length should be same as stream length", 0, stream.length());
}
public void testCreateNull() {
try {
new ByteArrayImageInputStream(null);
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertNotNull("Null exception message", expected.getMessage());
String message = expected.getMessage().toLowerCase();
assertTrue("Exception message does not contain parameter name", message.contains("data"));
assertTrue("Exception message does not contain null", message.contains("null"));
}
}
public void testRead() throws IOException {
byte[] data = new byte[1024 * 1024];
mRandom.nextBytes(data);
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
assertEquals("Data length should be same as stream length", data.length, stream.length());
for (byte b : data) {
assertEquals("Wrong data read", b & 0xff, stream.read());
}
}
public void testReadArray() throws IOException {
byte[] data = new byte[1024 * 1024];
mRandom.nextBytes(data);
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
assertEquals("Data length should be same as stream length", data.length, stream.length());
byte[] result = new byte[1024];
for (int i = 0; i < data.length / result.length; i++) {
stream.readFully(result);
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
}
}
public void testReadSkip() throws IOException {
byte[] data = new byte[1024 * 14];
mRandom.nextBytes(data);
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
assertEquals("Data length should be same as stream length", data.length, stream.length());
byte[] result = new byte[7];
for (int i = 0; i < data.length / result.length; i += 2) {
stream.readFully(result);
stream.skipBytes(result.length);
assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
}
}
public void testReadSeek() throws IOException {
byte[] data = new byte[1024 * 18];
mRandom.nextBytes(data);
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
assertEquals("Data length should be same as stream length", data.length, stream.length());
byte[] result = new byte[9];
for (int i = 0; i < data.length / result.length; i++) {
// Read backwards
long newPos = stream.length() - result.length - i * result.length;
stream.seek(newPos);
assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
stream.readFully(result);
assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
}
}
}

View File

@@ -0,0 +1,140 @@
/*
* 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.InputStreamAbstractTestCase;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
/**
* IIOInputStreamAdapter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IIOInputStreamAdapter.java,v 1.0 Apr 11, 2008 1:04:42 PM haraldk Exp$
*/
public class IIOInputStreamAdapterTestCase extends InputStreamAbstractTestCase {
public IIOInputStreamAdapterTestCase(String name) {
super(name);
}
protected InputStream makeInputStream(byte[] pBytes) {
return new IIOInputStreamAdapter(new MemoryCacheImageInputStream(new ByteArrayInputStream(pBytes)), pBytes.length);
}
public void testReadSubstreamOpenEnd() throws IOException {
byte[] bytes = new byte[20];
MemoryCacheImageInputStream input = new MemoryCacheImageInputStream(new ByteArrayInputStream(bytes));
input.seek(10);
assertEquals(10, input.getStreamPosition());
IIOInputStreamAdapter stream = new IIOInputStreamAdapter(input);
for (int i = 0; i < 10; i++) {
assertTrue("Unexpected end of stream", -1 != stream.read());
}
assertEquals("Read value after end of stream", -1, stream.read());
assertEquals("Read value after end of stream", -1, stream.read());
// Make sure underlying stream is positioned at end of substream after close
stream.close();
assertEquals(20, input.getStreamPosition());
input.close();
}
public void testReadSubstream() throws IOException {
byte[] bytes = new byte[20];
MemoryCacheImageInputStream input = new MemoryCacheImageInputStream(new ByteArrayInputStream(bytes));
IIOInputStreamAdapter stream = new IIOInputStreamAdapter(input, 9);
for (int i = 0; i < 9; i++) {
assertTrue("Unexpected end of stream", -1 != stream.read());
}
assertEquals("Read value after end of stream", -1, stream.read());
assertEquals("Read value after end of stream", -1, stream.read());
// Make sure we don't read outside stream boundaries
assertTrue(input.getStreamPosition() <= 9);
input.close();
}
public void testReadSubstreamRepositionOnClose() throws IOException {
byte[] bytes = new byte[20];
MemoryCacheImageInputStream input = new MemoryCacheImageInputStream(new ByteArrayInputStream(bytes));
IIOInputStreamAdapter stream = new IIOInputStreamAdapter(input, 10);
for (int i = 0; i < 7; i++) {
assertTrue("Unexpected end of stream", -1 != stream.read());
}
// Make sure we don't read outside stream boundaries
assertTrue(input.getStreamPosition() <= 7);
// Make sure underlying stream is positioned at end of substream after close
stream.close();
assertEquals(10, input.getStreamPosition());
input.close();
}
public void testSeekBeforeStreamNoEnd() throws IOException {
byte[] bytes = new byte[20];
MemoryCacheImageInputStream input = new MemoryCacheImageInputStream(new ByteArrayInputStream(bytes));
input.seek(10);
assertEquals(10, input.getStreamPosition());
IIOInputStreamAdapter stream = new IIOInputStreamAdapter(input);
assertEquals("Should not skip backwards", 0, stream.skip(-5));
assertEquals(10, input.getStreamPosition());
}
public void testSeekBeforeStream() throws IOException {
byte[] bytes = new byte[20];
MemoryCacheImageInputStream input = new MemoryCacheImageInputStream(new ByteArrayInputStream(bytes));
input.seek(10);
assertEquals(10, input.getStreamPosition());
IIOInputStreamAdapter stream = new IIOInputStreamAdapter(input, 9);
assertEquals("Should not skip backwards", 0, stream.skip(-5));
assertEquals(10, input.getStreamPosition());
}
}

View File

@@ -0,0 +1,322 @@
/*
* 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 org.jmock.Mock;
import org.jmock.cglib.MockObjectTestCase;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.event.IIOWriteProgressListener;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* ImageReaderAbstractTestCase class description.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: ImageReaderAbstractTestCase.java,v 1.0 18.nov.2004 17:38:33 haku Exp $
*/
public abstract class ImageWriterAbstractTestCase extends MockObjectTestCase {
protected abstract ImageWriter createImageWriter();
protected abstract RenderedImage getTestData();
public void testSetOutput() throws IOException {
// Should just pass with no exceptions
ImageWriter writer = createImageWriter();
assertNotNull(writer);
writer.setOutput(ImageIO.createImageOutputStream(new ByteArrayOutputStream()));
}
public void testSetOutputNull() {
// Should just pass with no exceptions
ImageWriter writer = createImageWriter();
assertNotNull(writer);
writer.setOutput(null);
}
public void testWrite() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer));
try {
writer.write(getTestData());
}
catch (IOException e) {
fail(e.getMessage());
}
assertTrue("No image data written", buffer.size() > 0);
}
public void testWrite2() {
// Note: There's a difference between new ImageOutputStreamImpl and
// ImageIO.createImageOutputStream... Make sure writers handle both
// cases
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try {
writer.setOutput(ImageIO.createImageOutputStream(buffer));
writer.write(getTestData());
}
catch (IOException e) {
fail(e.getMessage());
}
assertTrue("No image data written", buffer.size() > 0);
}
public void testWriteNull() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer));
try {
writer.write((RenderedImage) null);
}
catch(IllegalArgumentException ignore) {
}
catch (IOException e) {
fail(e.getMessage());
}
assertTrue("Image data written", buffer.size() == 0);
}
public void testWriteNoOutput() {
ImageWriter writer = createImageWriter();
try {
writer.write(getTestData());
}
catch (IllegalStateException ignore) {
}
catch (IOException e) {
fail(e.getMessage());
}
}
public void testGetDefaultWriteParam() {
ImageWriter writer = createImageWriter();
ImageWriteParam param = writer.getDefaultWriteParam();
assertNotNull("Default ImageWriteParam is null", param);
}
// TODO: Test writing with params
// TODO: Source region and subsampling at least
public void testAddIIOWriteProgressListener() {
ImageWriter writer = createImageWriter();
Mock mockListener = new Mock(IIOWriteProgressListener.class);
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListener.proxy());
}
public void testAddIIOWriteProgressListenerNull() {
ImageWriter writer = createImageWriter();
writer.addIIOWriteProgressListener(null);
}
public void testAddIIOWriteProgressListenerCallbacks() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer));
Mock mockListener = new Mock(IIOWriteProgressListener.class);
String started = "Started";
mockListener.expects(once()).method("imageStarted").withAnyArguments().id(started);
mockListener.stubs().method("imageProgress").withAnyArguments().after(started);
mockListener.expects(once()).method("imageComplete").withAnyArguments().after(started);
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListener.proxy());
try {
writer.write(getTestData());
}
catch (IOException e) {
fail("Could not write image");
}
// At least imageStarted and imageComplete, plus any number of imageProgress
mockListener.verify();
}
public void testMultipleAddIIOWriteProgressListenerCallbacks() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer));
Mock mockListener = new Mock(IIOWriteProgressListener.class);
String started = "Started";
mockListener.expects(once()).method("imageStarted").withAnyArguments().id(started);
mockListener.stubs().method("imageProgress").withAnyArguments().after(started);
mockListener.expects(once()).method("imageComplete").withAnyArguments().after(started);
Mock mockListenerToo = new Mock(IIOWriteProgressListener.class);
String startedToo = "Started Two";
mockListenerToo.expects(once()).method("imageStarted").withAnyArguments().id(startedToo);
mockListenerToo.stubs().method("imageProgress").withAnyArguments().after(startedToo);
mockListenerToo.expects(once()).method("imageComplete").withAnyArguments().after(startedToo);
Mock mockListenerThree = new Mock(IIOWriteProgressListener.class);
String startedThree = "Started Three";
mockListenerThree.expects(once()).method("imageStarted").withAnyArguments().id(startedThree);
mockListenerThree.stubs().method("imageProgress").withAnyArguments().after(startedThree);
mockListenerThree.expects(once()).method("imageComplete").withAnyArguments().after(startedThree);
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListener.proxy());
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListenerToo.proxy());
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListenerThree.proxy());
try {
writer.write(getTestData());
}
catch (IOException e) {
fail("Could not write image");
}
// At least imageStarted and imageComplete, plus any number of imageProgress
mockListener.verify();
mockListenerToo.verify();
mockListenerThree.verify();
}
public void testRemoveIIOWriteProgressListenerNull() {
ImageWriter writer = createImageWriter();
writer.removeIIOWriteProgressListener(null);
}
public void testRemoveIIOWriteProgressListenerNone() {
ImageWriter writer = createImageWriter();
Mock mockListener = new Mock(IIOWriteProgressListener.class);
writer.removeIIOWriteProgressListener((IIOWriteProgressListener) mockListener.proxy());
}
public void testRemoveIIOWriteProgressListener() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer));
Mock mockListener = new Mock(IIOWriteProgressListener.class);
IIOWriteProgressListener listener = (IIOWriteProgressListener) mockListener.proxy();
writer.addIIOWriteProgressListener(listener);
writer.removeIIOWriteProgressListener(listener);
try {
writer.write(getTestData());
}
catch (IOException e) {
fail("Could not write image");
}
// Should not have called any methods...
mockListener.verify();
}
public void testRemoveIIOWriteProgressListenerMultiple() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer));
Mock mockListener = new Mock(IIOWriteProgressListener.class);
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListener.proxy());
Mock mockListenerToo = new Mock(IIOWriteProgressListener.class);
mockListenerToo.stubs().method("imageStarted").withAnyArguments();
mockListenerToo.stubs().method("imageProgress").withAnyArguments();
mockListenerToo.stubs().method("imageComplete").withAnyArguments();
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListenerToo.proxy());
writer.removeIIOWriteProgressListener((IIOWriteProgressListener) mockListener.proxy());
try {
writer.write(getTestData());
}
catch (IOException e) {
fail("Could not write image");
}
// Should not have called any methods...
mockListener.verify();
mockListenerToo.verify();
}
public void testRemoveAllIIOWriteProgressListeners() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer));
Mock mockListener = new Mock(IIOWriteProgressListener.class);
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListener.proxy());
writer.removeAllIIOWriteProgressListeners();
try {
writer.write(getTestData());
}
catch (IOException e) {
fail("Could not write image");
}
// Should not have called any methods...
mockListener.verify();
}
public void testRemoveAllIIOWriteProgressListenersMultiple() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
writer.setOutput(ImageIO.createImageOutputStream(buffer));
Mock mockListener = new Mock(IIOWriteProgressListener.class);
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListener.proxy());
Mock mockListenerToo = new Mock(IIOWriteProgressListener.class);
writer.addIIOWriteProgressListener((IIOWriteProgressListener) mockListenerToo.proxy());
writer.removeAllIIOWriteProgressListeners();
try {
writer.write(getTestData());
}
catch (IOException e) {
fail("Could not write image");
}
// Should not have called any methods...
mockListener.verify();
mockListenerToo.verify();
}
}

View File

@@ -0,0 +1,28 @@
package com.twelvemonkeys.imageio.util;
import junit.framework.TestCase;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
/**
* IndexedImageTypeSpecifierTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IndexedImageTypeSpecifierTestCase.java,v 1.0 Jun 9, 2008 2:42:03 PM haraldk Exp$
*/
public class IndexedImageTypeSpecifierTestCase extends TestCase {
public void testEquals() {
IndexColorModel cm = new IndexColorModel(1, 2, new int[]{0xffffff, 0x00}, 0, false, -1, DataBuffer.TYPE_BYTE);
IndexedImageTypeSpecifier spec = new IndexedImageTypeSpecifier(cm);
IndexedImageTypeSpecifier other = new IndexedImageTypeSpecifier(cm);
assertEquals(spec, other);
assertEquals(other, spec);
assertTrue(spec.equals(other));
assertTrue(other.equals(spec));
}
}

Binary file not shown.

8
imageio/imageio-core/todo.txt Executable file
View File

@@ -0,0 +1,8 @@
- Rename to imageio-common?
- Separate modules for more for more plugins
- The BMP reader spports some special formats not supported by Sun reader
- PNM package is pretty complete, but useless, as it's provided by Sun? Licencse?
- WBMP?
- XBM?
DONE:
- Split up into separate plugins (modules), to allow easier configuration